diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 4c445f635d7d2..19078c1b842ae 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -14,3 +14,5 @@ c34fbfaad38cf5829ef5cfe780dc9d58480adeaa cf2dff2b1e3fa55fa5415d524200070d0d7aacfe # Run rustfmt on bootstrap b39a1d6f1a30ba29f25d7141038b9a5bf0126e36 +# reorder fluent message files +f97fddab91fbf290ea5b691fe355d6f915220b6e diff --git a/.gitmodules b/.gitmodules index 4596ae17d0238..6b7160bfe1530 100644 --- a/.gitmodules +++ b/.gitmodules @@ -25,7 +25,7 @@ [submodule "src/llvm-project"] path = src/llvm-project url = https://github.com/rust-lang/llvm-project.git - branch = rustc/16.0-2023-04-05 + branch = rustc/16.0-2023-06-05 [submodule "src/doc/embedded-book"] path = src/doc/embedded-book url = https://github.com/rust-embedded/book.git diff --git a/Cargo.lock b/Cargo.lock index e7aa317ad7501..ef6b0a5959f8c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -294,7 +294,7 @@ dependencies = [ "serde_json", "sha2", "tar", - "toml", + "toml 0.5.7", "xz2", ] @@ -311,14 +311,14 @@ dependencies = [ "indexmap", "serde", "serde_json", - "toml", + "toml 0.5.7", ] [[package]] name = "bumpalo" -version = "3.12.1" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b1ce199063694f33ffb7dd4e0ee620741495c32833cde5aa08f02a0bf96f0c8" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" [[package]] name = "bytecount" @@ -581,7 +581,7 @@ checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" [[package]] name = "clippy" -version = "0.1.71" +version = "0.1.72" dependencies = [ "clap 4.2.1", "clippy_lints", @@ -596,7 +596,6 @@ dependencies = [ "quote", "regex", "rustc-semver", - "rustc-workspace-hack", "rustc_tools_util", "serde", "syn 2.0.8", @@ -604,7 +603,7 @@ dependencies = [ "termize", "tester", "tokio", - "toml", + "toml 0.7.4", "walkdir", ] @@ -623,7 +622,7 @@ dependencies = [ [[package]] name = "clippy_lints" -version = "0.1.71" +version = "0.1.72" dependencies = [ "arrayvec", "cargo_metadata 0.15.3", @@ -639,7 +638,7 @@ dependencies = [ "serde", "serde_json", "tempfile", - "toml", + "toml 0.7.4", "unicode-normalization", "unicode-script", "url", @@ -647,7 +646,7 @@ dependencies = [ [[package]] name = "clippy_utils" -version = "0.1.71" +version = "0.1.72" dependencies = [ "arrayvec", "if_chain", @@ -849,7 +848,7 @@ dependencies = [ "autocfg", "cfg-if", "crossbeam-utils", - "memoffset", + "memoffset 0.8.0", "scopeguard", ] @@ -872,16 +871,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "cstr" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c11a39d776a3b35896711da8a04dc1835169dcd36f710878187637314e47941b" -dependencies = [ - "proc-macro2", - "quote", -] - [[package]] name = "ctrlc" version = "3.3.1" @@ -930,7 +919,7 @@ checksum = "a0afaad2b26fa326569eb264b1363e8ae3357618c43982b3f285f0774ce76b69" [[package]] name = "declare_clippy_lint" -version = "0.1.71" +version = "0.1.72" dependencies = [ "itertools", "quote", @@ -1204,11 +1193,11 @@ dependencies = [ [[package]] name = "field-offset" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3cf3a800ff6e860c863ca6d4b16fd999db8b752819c1606884047b73e468535" +checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" dependencies = [ - "memoffset", + "memoffset 0.9.0", "rustc_version", ] @@ -1220,7 +1209,7 @@ checksum = "8a3de6e8d11b22ff9edc6d916f890800597d60f8b2da1caf2955c274638d6412" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.2.10", "windows-sys 0.45.0", ] @@ -1762,7 +1751,6 @@ dependencies = [ "flate2", "num_cpus", "rayon", - "remove_dir_all", "tar", "walkdir", "xz2", @@ -1914,9 +1902,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.143" +version = "0.2.146" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edc207893e85c5d6be840e969b496b53d94cec8be2d501b214f50daa97fa8024" +checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" dependencies = [ "rustc-std-workspace-core", ] @@ -2111,7 +2099,7 @@ dependencies = [ "serde_json", "shlex", "tempfile", - "toml", + "toml 0.5.7", "topological-sort", ] @@ -2157,6 +2145,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + [[package]] name = "mime" version = "0.3.16" @@ -2446,7 +2443,7 @@ dependencies = [ "cfg-if", "instant", "libc", - "redox_syscall", + "redox_syscall 0.2.10", "smallvec", "winapi", ] @@ -2459,7 +2456,7 @@ checksum = "4dc9e0dc2adc1c69d09143aff38d3d30c5c3f0df0dad82e6d25547af174ebec0" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.2.10", "smallvec", "windows-sys 0.42.0", ] @@ -2802,6 +2799,15 @@ dependencies = [ "bitflags", ] +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags", +] + [[package]] name = "redox_users" version = "0.4.0" @@ -2809,7 +2815,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" dependencies = [ "getrandom", - "redox_syscall", + "redox_syscall 0.2.10", ] [[package]] @@ -2861,15 +2867,6 @@ version = "0.1.0" name = "remote-test-server" version = "0.1.0" -[[package]] -name = "remove_dir_all" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] - [[package]] name = "replace-version-placeholder" version = "0.1.0" @@ -3180,7 +3177,6 @@ name = "rustc_codegen_llvm" version = "0.0.0" dependencies = [ "bitflags", - "cstr", "libc", "measureme", "object 0.31.1", @@ -3608,6 +3604,7 @@ dependencies = [ name = "rustc_interface" version = "0.0.0" dependencies = [ + "atty", "libloading", "rustc-rayon", "rustc-rayon-core", @@ -3988,7 +3985,7 @@ version = "0.0.0" dependencies = [ "field-offset", "measureme", - "memoffset", + "memoffset 0.9.0", "rustc-rayon-core", "rustc_ast", "rustc_data_structures", @@ -4072,6 +4069,7 @@ dependencies = [ name = "rustc_session" version = "0.0.0" dependencies = [ + "atty", "getopts", "libc", "rustc_ast", @@ -4363,7 +4361,7 @@ dependencies = [ "serde_json", "term", "thiserror", - "toml", + "toml 0.5.7", "unicode-segmentation", "unicode-width", "unicode_categories", @@ -4484,6 +4482,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93107647184f6027e3b7dcb2e11034cf95ffa1e3a682c67951963ac69c1c007d" +dependencies = [ + "serde", +] + [[package]] name = "sha1" version = "0.10.5" @@ -4781,16 +4788,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.3.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" dependencies = [ "cfg-if", "fastrand", - "libc", - "redox_syscall", - "remove_dir_all", - "winapi", + "redox_syscall 0.3.5", + "rustix", + "windows-sys 0.45.0", ] [[package]] @@ -4976,14 +4982,14 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.8.4" +version = "1.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50dae83881bc9b0403dd5b44ea9deed3e939856cc8722d5be37f0d6e5c6d53dd" +checksum = "94d7b1cfd2aa4011f2de74c2c4c63665e27a71006b0a192dcd2710272e73dfa2" dependencies = [ "autocfg", "bytes", - "memchr", "pin-project-lite", + "windows-sys 0.48.0", ] [[package]] @@ -4995,6 +5001,40 @@ dependencies = [ "serde", ] +[[package]] +name = "toml" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6135d499e69981f9ff0ef2167955a5333c35e36f6937d382974566b3d5b94ec" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a76a9312f5ba4c2dec6b9161fdf25d87ad8a09256ccea5a556fef03c706a10f" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380d56e8670370eee6566b0bfd4265f65b3f432e8c6d85623f728d4fa31f739" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "topological-sort" version = "0.2.2" @@ -5358,7 +5398,6 @@ dependencies = [ "idna", "matches", "percent-encoding", - "serde", ] [[package]] @@ -5685,6 +5724,15 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +[[package]] +name = "winnow" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699" +dependencies = [ + "memchr", +] + [[package]] name = "writeable" version = "0.5.1" diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 2ee63c286ba5c..e1b9987f57816 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -414,7 +414,9 @@ pub struct Size { // Safety: Ord is implement as just comparing numerical values and numerical values // are not changed by (de-)serialization. #[cfg(feature = "nightly")] -unsafe impl StableOrd for Size {} +unsafe impl StableOrd for Size { + const CAN_USE_UNSTABLE_SORT: bool = true; +} // This is debug-printed a lot in larger structs, don't waste too much space there impl fmt::Debug for Size { diff --git a/compiler/rustc_ast/src/expand/mod.rs b/compiler/rustc_ast/src/expand/mod.rs index 2ee1bfe0ae71b..942347383ce31 100644 --- a/compiler/rustc_ast/src/expand/mod.rs +++ b/compiler/rustc_ast/src/expand/mod.rs @@ -1,3 +1,20 @@ //! Definitions shared by macros / syntax extensions and e.g. `rustc_middle`. +use rustc_span::{def_id::DefId, symbol::Ident}; + +use crate::MetaItem; + pub mod allocator; + +#[derive(Debug, Clone, Encodable, Decodable, HashStable_Generic)] +pub struct StrippedCfgItem { + pub parent_module: ModId, + pub name: Ident, + pub cfg: MetaItem, +} + +impl StrippedCfgItem { + pub fn map_mod_id(self, f: impl FnOnce(ModId) -> New) -> StrippedCfgItem { + StrippedCfgItem { parent_module: f(self.parent_module), name: self.name, cfg: self.cfg } + } +} diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 5e0ab80c6ac9f..f52797c4f3f1d 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -71,9 +71,13 @@ impl<'hir> LoweringContext<'_, 'hir> { let kind = match &e.kind { ExprKind::Array(exprs) => hir::ExprKind::Array(self.lower_exprs(exprs)), - ExprKind::ConstBlock(anon_const) => { - let anon_const = self.lower_anon_const(anon_const); - hir::ExprKind::ConstBlock(anon_const) + ExprKind::ConstBlock(c) => { + let c = self.with_new_scopes(|this| hir::ConstBlock { + def_id: this.local_def_id(c.id), + hir_id: this.lower_node_id(c.id), + body: this.lower_const_body(c.value.span, Some(&c.value)), + }); + hir::ExprKind::ConstBlock(c) } ExprKind::Repeat(expr, count) => { let expr = self.lower_expr(expr); diff --git a/compiler/rustc_ast_lowering/src/index.rs b/compiler/rustc_ast_lowering/src/index.rs index 2e66c81eb0d05..ce847906fb99a 100644 --- a/compiler/rustc_ast_lowering/src/index.rs +++ b/compiler/rustc_ast_lowering/src/index.rs @@ -223,6 +223,14 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { }); } + fn visit_inline_const(&mut self, constant: &'hir ConstBlock) { + self.insert(DUMMY_SP, constant.hir_id, Node::ConstBlock(constant)); + + self.with_parent(constant.hir_id, |this| { + intravisit::walk_inline_const(this, constant); + }); + } + fn visit_expr(&mut self, expr: &'hir Expr<'hir>) { self.insert(expr.span, expr.hir_id, Node::Expr(expr)); diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 274f931e43f9d..2125349909ef1 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -2,7 +2,6 @@ use rustc_ast as ast; use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor}; use rustc_ast::{attr, AssocConstraint, AssocConstraintKind, NodeId}; use rustc_ast::{PatKind, RangeEnd}; -use rustc_errors::{Applicability, StashKey}; use rustc_feature::{AttributeGate, BuiltinAttribute, Features, GateIssue, BUILTIN_ATTRIBUTE_MAP}; use rustc_session::parse::{feature_err, feature_err_issue, feature_warn}; use rustc_session::Session; @@ -374,55 +373,8 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { } } - fn visit_stmt(&mut self, stmt: &'a ast::Stmt) { - if let ast::StmtKind::Semi(expr) = &stmt.kind - && let ast::ExprKind::Assign(lhs, _, _) = &expr.kind - && let ast::ExprKind::Type(..) = lhs.kind - && self.sess.parse_sess.span_diagnostic.err_count() == 0 - && !self.features.type_ascription - && !lhs.span.allows_unstable(sym::type_ascription) - { - // When we encounter a statement of the form `foo: Ty = val;`, this will emit a type - // ascription error, but the likely intention was to write a `let` statement. (#78907). - feature_err( - &self.sess.parse_sess, - sym::type_ascription, - lhs.span, - "type ascription is experimental", - ).span_suggestion_verbose( - lhs.span.shrink_to_lo(), - "you might have meant to introduce a new binding", - "let ", - Applicability::MachineApplicable, - ).emit(); - } - visit::walk_stmt(self, stmt); - } - fn visit_expr(&mut self, e: &'a ast::Expr) { match e.kind { - ast::ExprKind::Type(..) => { - if self.sess.parse_sess.span_diagnostic.err_count() == 0 { - // To avoid noise about type ascription in common syntax errors, - // only emit if it is the *only* error. - gate_feature_post!( - &self, - type_ascription, - e.span, - "type ascription is experimental" - ); - } else { - // And if it isn't, cancel the early-pass warning. - if let Some(err) = self - .sess - .parse_sess - .span_diagnostic - .steal_diagnostic(e.span, StashKey::EarlySyntaxWarning) - { - err.cancel() - } - } - } ast::ExprKind::TryBlock(_) => { gate_feature_post!(&self, try_blocks, e.span, "`try` expression is experimental"); } @@ -629,7 +581,6 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) { gate_all!(box_patterns, "box pattern syntax is experimental"); gate_all!(exclusive_range_pattern, "exclusive range pattern syntax is experimental"); gate_all!(try_blocks, "`try` blocks are unstable"); - gate_all!(type_ascription, "type ascription is experimental"); visit::walk_crate(&mut visitor, krate); } diff --git a/compiler/rustc_borrowck/src/constraint_generation.rs b/compiler/rustc_borrowck/src/constraint_generation.rs index 2aa09a3f26c17..743d117e00f2b 100644 --- a/compiler/rustc_borrowck/src/constraint_generation.rs +++ b/compiler/rustc_borrowck/src/constraint_generation.rs @@ -4,8 +4,8 @@ use rustc_infer::infer::InferCtxt; use rustc_middle::mir::visit::TyContext; use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::{ - BasicBlock, BasicBlockData, Body, Local, Location, Place, PlaceRef, ProjectionElem, Rvalue, - SourceInfo, Statement, StatementKind, Terminator, TerminatorKind, UserTypeProjection, + Body, Local, Location, Place, PlaceRef, ProjectionElem, Rvalue, SourceInfo, Statement, + StatementKind, Terminator, TerminatorKind, UserTypeProjection, }; use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::visit::TypeVisitable; @@ -49,10 +49,6 @@ struct ConstraintGeneration<'cg, 'tcx> { } impl<'cg, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'tcx> { - fn visit_basic_block_data(&mut self, bb: BasicBlock, data: &BasicBlockData<'tcx>) { - self.super_basic_block_data(bb, data); - } - /// We sometimes have `substs` within an rvalue, or within a /// call. Make them live at the location where they appear. fn visit_substs(&mut self, substs: &SubstsRef<'tcx>, location: Location) { diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs index 2daa82aef3957..5f837816ba76d 100644 --- a/compiler/rustc_borrowck/src/dataflow.rs +++ b/compiler/rustc_borrowck/src/dataflow.rs @@ -59,7 +59,7 @@ macro_rules! impl_visitable { } fn reconstruct_before_statement_effect( - &self, + &mut self, state: &mut Self::FlowState, stmt: &mir::Statement<'tcx>, loc: Location, @@ -69,7 +69,7 @@ macro_rules! impl_visitable { } fn reconstruct_statement_effect( - &self, + &mut self, state: &mut Self::FlowState, stmt: &mir::Statement<'tcx>, loc: Location, @@ -79,7 +79,7 @@ macro_rules! impl_visitable { } fn reconstruct_before_terminator_effect( - &self, + &mut self, state: &mut Self::FlowState, term: &mir::Terminator<'tcx>, loc: Location, @@ -89,7 +89,7 @@ macro_rules! impl_visitable { } fn reconstruct_terminator_effect( - &self, + &mut self, state: &mut Self::FlowState, term: &mir::Terminator<'tcx>, loc: Location, @@ -343,7 +343,7 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> { type Idx = BorrowIndex; fn before_statement_effect( - &self, + &mut self, trans: &mut impl GenKill, _statement: &mir::Statement<'tcx>, location: Location, @@ -352,7 +352,7 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> { } fn statement_effect( - &self, + &mut self, trans: &mut impl GenKill, stmt: &mir::Statement<'tcx>, location: Location, @@ -400,7 +400,7 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> { } fn before_terminator_effect( - &self, + &mut self, trans: &mut impl GenKill, _terminator: &mir::Terminator<'tcx>, location: Location, @@ -409,7 +409,7 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> { } fn terminator_effect( - &self, + &mut self, trans: &mut impl GenKill, terminator: &mir::Terminator<'tcx>, _location: Location, @@ -426,7 +426,7 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> { } fn call_return_effect( - &self, + &mut self, _trans: &mut impl GenKill, _block: mir::BasicBlock, _return_places: CallReturnPlaces<'_, 'tcx>, diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index d0e17bf5a0848..34d466db2b409 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -416,12 +416,28 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { _, ) = pat.kind { - err.span_suggestion( - upvar_ident.span, - "consider changing this to be mutable", - format!("mut {}", upvar_ident.name), - Applicability::MachineApplicable, - ); + if upvar_ident.name == kw::SelfLower { + for (_, node) in self.infcx.tcx.hir().parent_iter(upvar_hir_id) { + if let Some(fn_decl) = node.fn_decl() { + if !matches!(fn_decl.implicit_self, hir::ImplicitSelfKind::ImmRef | hir::ImplicitSelfKind::MutRef) { + err.span_suggestion( + upvar_ident.span, + "consider changing this to be mutable", + format!("mut {}", upvar_ident.name), + Applicability::MachineApplicable, + ); + break; + } + } + } + } else { + err.span_suggestion( + upvar_ident.span, + "consider changing this to be mutable", + format!("mut {}", upvar_ident.name), + Applicability::MachineApplicable, + ); + } } let tcx = self.infcx.tcx; diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index a53ea100c2242..99a988f2c629e 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -368,7 +368,7 @@ fn do_mir_borrowck<'tcx>( // Compute and report region errors, if any. mbcx.report_region_errors(nll_errors); - let results = BorrowckResults { + let mut results = BorrowckResults { ever_inits: flow_ever_inits, uninits: flow_uninits, borrows: flow_borrows, @@ -379,7 +379,7 @@ fn do_mir_borrowck<'tcx>( rustc_mir_dataflow::visit_results( body, traversal::reverse_postorder(body).map(|(bb, _)| bb), - &results, + &mut results, &mut mbcx, ); @@ -598,11 +598,12 @@ struct MirBorrowckCtxt<'cx, 'tcx> { // 2. loans made in overlapping scopes do not conflict // 3. assignments do not affect things loaned out as immutable // 4. moves do not affect things loaned out in any way -impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'tcx> { +impl<'cx, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx, R> for MirBorrowckCtxt<'cx, 'tcx> { type FlowState = Flows<'cx, 'tcx>; fn visit_statement_before_primary_effect( &mut self, + _results: &R, flow_state: &Flows<'cx, 'tcx>, stmt: &'cx Statement<'tcx>, location: Location, @@ -672,6 +673,7 @@ impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtx fn visit_terminator_before_primary_effect( &mut self, + _results: &R, flow_state: &Flows<'cx, 'tcx>, term: &'cx Terminator<'tcx>, loc: Location, @@ -782,6 +784,7 @@ impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtx fn visit_terminator_after_primary_effect( &mut self, + _results: &R, flow_state: &Flows<'cx, 'tcx>, term: &'cx Terminator<'tcx>, loc: Location, diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index 13e346b86bce3..a561496b02639 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -285,7 +285,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { let infcx = self .tcx .infer_ctxt() - .with_opaque_type_inference(if self.tcx.trait_solver_next() { + .with_opaque_type_inference(if self.next_trait_solver() { DefiningAnchor::Bind(def_id) } else { DefiningAnchor::Bubble diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 908ff3da5ca0b..00fd3762fa773 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -188,7 +188,7 @@ pub(crate) fn type_check<'mir, 'tcx>( // FIXME(-Ztrait-solver=next): A bit dubious that we're only registering // predefined opaques in the typeck root. - if infcx.tcx.trait_solver_next() && !infcx.tcx.is_typeck_child(body.source.def_id()) { + if infcx.next_trait_solver() && !infcx.tcx.is_typeck_child(body.source.def_id()) { checker.register_predefined_opaques_in_new_solver(); } @@ -477,9 +477,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { fn visit_body(&mut self, body: &Body<'tcx>) { self.sanitize_type(&"return type", body.return_ty()); - for local_decl in &body.local_decls { - self.sanitize_type(local_decl, local_decl.ty); - } + // The types of local_decls are checked above which is called in super_body. self.super_body(body); } } diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index c59a733c05568..4c878785b7b45 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -554,9 +554,6 @@ fn report_missing_placeholders( fmt_span: Span, ) { let mut diag = if let &[(span, named)] = &unused[..] { - //let mut diag = ecx.struct_span_err(span, msg); - //diag.span_label(span, msg); - //diag ecx.create_err(errors::FormatUnusedArg { span, named }) } else { let unused_labels = diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs index aad9a9647f8c9..d143bcc96ef93 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs @@ -54,8 +54,8 @@ impl OngoingCodegen { self, sess: &Session, backend_config: &BackendConfig, - ) -> (CodegenResults, FxHashMap) { - let mut work_products = FxHashMap::default(); + ) -> (CodegenResults, FxIndexMap) { + let mut work_products = FxIndexMap::default(); let mut modules = vec![]; for module_codegen in self.modules { diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index 0a513b08b74f5..1e83c30bd677a 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -475,9 +475,7 @@ fn codegen_regular_intrinsic_call<'tcx>( sym::unchecked_add | sym::unchecked_sub | sym::unchecked_mul - | sym::unchecked_div | sym::exact_div - | sym::unchecked_rem | sym::unchecked_shl | sym::unchecked_shr => { intrinsic_args!(fx, args => (x, y); intrinsic); @@ -487,8 +485,7 @@ fn codegen_regular_intrinsic_call<'tcx>( sym::unchecked_add => BinOp::Add, sym::unchecked_sub => BinOp::Sub, sym::unchecked_mul => BinOp::Mul, - sym::unchecked_div | sym::exact_div => BinOp::Div, - sym::unchecked_rem => BinOp::Rem, + sym::exact_div => BinOp::Div, sym::unchecked_shl => BinOp::Shl, sym::unchecked_shr => BinOp::Shr, _ => unreachable!(), diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index 9966cc2ef3c12..095fbe62c1902 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -88,7 +88,7 @@ mod prelude { }; pub(crate) use rustc_target::abi::{Abi, FieldIdx, Scalar, Size, VariantIdx, FIRST_VARIANT}; - pub(crate) use rustc_data_structures::fx::FxHashMap; + pub(crate) use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; pub(crate) use rustc_index::Idx; @@ -223,7 +223,7 @@ impl CodegenBackend for CraneliftCodegenBackend { ongoing_codegen: Box, sess: &Session, _outputs: &OutputFilenames, - ) -> Result<(CodegenResults, FxHashMap), ErrorGuaranteed> { + ) -> Result<(CodegenResults, FxIndexMap), ErrorGuaranteed> { Ok(ongoing_codegen .downcast::() .unwrap() diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 442ce0ea54209..ea013c4428cce 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -75,7 +75,7 @@ use rustc_codegen_ssa::back::write::{CodegenContext, FatLTOInput, ModuleConfig, use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule}; use rustc_codegen_ssa::target_features::supported_target_features; use rustc_codegen_ssa::traits::{CodegenBackend, ExtraBackendMethods, ModuleBufferMethods, ThinBufferMethods, WriteBackendMethods}; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::FxIndexMap; use rustc_errors::{DiagnosticMessage, ErrorGuaranteed, Handler, SubdiagnosticMessage}; use rustc_fluent_macro::fluent_messages; use rustc_metadata::EncodedMetadata; @@ -137,7 +137,7 @@ impl CodegenBackend for GccCodegenBackend { Box::new(res) } - fn join_codegen(&self, ongoing_codegen: Box, sess: &Session, _outputs: &OutputFilenames) -> Result<(CodegenResults, FxHashMap), ErrorGuaranteed> { + fn join_codegen(&self, ongoing_codegen: Box, sess: &Session, _outputs: &OutputFilenames) -> Result<(CodegenResults, FxIndexMap), ErrorGuaranteed> { let (codegen_results, work_products) = ongoing_codegen .downcast::>() .expect("Expected GccCodegenBackend's OngoingCodegen, found Box") diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml index ad51f2d095857..39ff3a0ba2d22 100644 --- a/compiler/rustc_codegen_llvm/Cargo.toml +++ b/compiler/rustc_codegen_llvm/Cargo.toml @@ -8,7 +8,6 @@ test = false [dependencies] bitflags = "1.0" -cstr = "0.2" libc = "0.2" measureme = "10.0.0" object = { version = "0.31.1", default-features = false, features = [ diff --git a/compiler/rustc_codegen_llvm/src/allocator.rs b/compiler/rustc_codegen_llvm/src/allocator.rs index a57508815d6f8..ad0636894b793 100644 --- a/compiler/rustc_codegen_llvm/src/allocator.rs +++ b/compiler/rustc_codegen_llvm/src/allocator.rs @@ -77,7 +77,7 @@ pub(crate) unsafe fn codegen( llvm::LLVMRustGetOrInsertFunction(llmod, callee.as_ptr().cast(), callee.len(), ty); llvm::LLVMRustSetVisibility(callee, llvm::Visibility::Hidden); - let llbb = llvm::LLVMAppendBasicBlockInContext(llcx, llfn, "entry\0".as_ptr().cast()); + let llbb = llvm::LLVMAppendBasicBlockInContext(llcx, llfn, c"entry".as_ptr().cast()); let llbuilder = llvm::LLVMCreateBuilderInContext(llcx); llvm::LLVMPositionBuilderAtEnd(llbuilder, llbb); @@ -129,7 +129,7 @@ pub(crate) unsafe fn codegen( attributes::apply_to_llfn(callee, llvm::AttributePlace::Function, &[no_return]); llvm::LLVMRustSetVisibility(callee, llvm::Visibility::Hidden); - let llbb = llvm::LLVMAppendBasicBlockInContext(llcx, llfn, "entry\0".as_ptr().cast()); + let llbb = llvm::LLVMAppendBasicBlockInContext(llcx, llfn, c"entry".as_ptr().cast()); let llbuilder = llvm::LLVMCreateBuilderInContext(llcx); llvm::LLVMPositionBuilderAtEnd(llbuilder, llbb); diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 604f68eb6a47e..8b05af7bed909 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -595,7 +595,7 @@ pub(crate) fn run_pass_manager( llvm::LLVMRustAddModuleFlag( module.module_llvm.llmod(), llvm::LLVMModFlagBehavior::Error, - "LTOPostLink\0".as_ptr().cast(), + c"LTOPostLink".as_ptr().cast(), 1, ); } diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index ca2eab28f872b..53b4296802ef7 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -891,11 +891,11 @@ unsafe fn embed_bitcode( let llglobal = llvm::LLVMAddGlobal( llmod, common::val_ty(llconst), - "rustc.embedded.module\0".as_ptr().cast(), + c"rustc.embedded.module".as_ptr().cast(), ); llvm::LLVMSetInitializer(llglobal, llconst); - let section = if is_apple { "__LLVM,__bitcode\0" } else { ".llvmbc\0" }; + let section = if is_apple { c"__LLVM,__bitcode" } else { c".llvmbc" }; llvm::LLVMSetSection(llglobal, section.as_ptr().cast()); llvm::LLVMRustSetLinkage(llglobal, llvm::Linkage::PrivateLinkage); llvm::LLVMSetGlobalConstant(llglobal, llvm::True); @@ -904,10 +904,10 @@ unsafe fn embed_bitcode( let llglobal = llvm::LLVMAddGlobal( llmod, common::val_ty(llconst), - "rustc.embedded.cmdline\0".as_ptr().cast(), + c"rustc.embedded.cmdline".as_ptr().cast(), ); llvm::LLVMSetInitializer(llglobal, llconst); - let section = if is_apple { "__LLVM,__cmdline\0" } else { ".llvmcmd\0" }; + let section = if is_apple { c"__LLVM,__cmdline" } else { c".llvmcmd" }; llvm::LLVMSetSection(llglobal, section.as_ptr().cast()); llvm::LLVMRustSetLinkage(llglobal, llvm::Linkage::PrivateLinkage); } else { diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index 5b2bbdb4bde1e..2f7eb08ad3d4c 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -19,8 +19,6 @@ use crate::context::CodegenCx; use crate::llvm; use crate::value::Value; -use cstr::cstr; - use rustc_codegen_ssa::base::maybe_create_entry_wrapper; use rustc_codegen_ssa::mono_item::MonoItemExt; use rustc_codegen_ssa::traits::*; @@ -110,11 +108,11 @@ pub fn compile_codegen_unit(tcx: TyCtxt<'_>, cgu_name: Symbol) -> (ModuleCodegen // Create the llvm.used and llvm.compiler.used variables. if !cx.used_statics.borrow().is_empty() { - cx.create_used_variable_impl(cstr!("llvm.used"), &*cx.used_statics.borrow()); + cx.create_used_variable_impl(c"llvm.used", &*cx.used_statics.borrow()); } if !cx.compiler_used_statics.borrow().is_empty() { cx.create_used_variable_impl( - cstr!("llvm.compiler.used"), + c"llvm.compiler.used", &*cx.compiler_used_statics.borrow(), ); } diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 5968e70b1ccce..b4aa001547c4c 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -6,7 +6,6 @@ use crate::llvm::{self, AtomicOrdering, AtomicRmwBinOp, BasicBlock, False, True} use crate::type_::Type; use crate::type_of::LayoutLlvmExt; use crate::value::Value; -use cstr::cstr; use libc::{c_char, c_uint}; use rustc_codegen_ssa::common::{IntPredicate, RealPredicate, SynchronizationScope, TypeKind}; use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue}; @@ -25,7 +24,6 @@ use rustc_symbol_mangling::typeid::{kcfi_typeid_for_fnabi, typeid_for_fnabi, Typ use rustc_target::abi::{self, call::FnAbi, Align, Size, WrappingRange}; use rustc_target::spec::{HasTargetSpec, SanitizerSet, Target}; use std::borrow::Cow; -use std::ffi::CStr; use std::iter; use std::ops::Deref; use std::ptr; @@ -45,13 +43,10 @@ impl Drop for Builder<'_, '_, '_> { } } -// FIXME(eddyb) use a checked constructor when they become `const fn`. -const EMPTY_C_STR: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"\0") }; - /// Empty string, to be used where LLVM expects an instruction name, indicating /// that the instruction is to be left unnamed (i.e. numbered, in textual IR). // FIXME(eddyb) pass `&CStr` directly to FFI once it's a thin pointer. -const UNNAMED: *const c_char = EMPTY_C_STR.as_ptr(); +const UNNAMED: *const c_char = c"".as_ptr(); impl<'ll, 'tcx> BackendTypes for Builder<'_, 'll, 'tcx> { type Value = as BackendTypes>::Value; @@ -1010,14 +1005,13 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } fn cleanup_pad(&mut self, parent: Option<&'ll Value>, args: &[&'ll Value]) -> Funclet<'ll> { - let name = cstr!("cleanuppad"); let ret = unsafe { llvm::LLVMBuildCleanupPad( self.llbuilder, parent, args.as_ptr(), args.len() as c_uint, - name.as_ptr(), + c"cleanuppad".as_ptr(), ) }; Funclet::new(ret.expect("LLVM does not have support for cleanuppad")) @@ -1031,14 +1025,13 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } fn catch_pad(&mut self, parent: &'ll Value, args: &[&'ll Value]) -> Funclet<'ll> { - let name = cstr!("catchpad"); let ret = unsafe { llvm::LLVMBuildCatchPad( self.llbuilder, parent, args.as_ptr(), args.len() as c_uint, - name.as_ptr(), + c"catchpad".as_ptr(), ) }; Funclet::new(ret.expect("LLVM does not have support for catchpad")) @@ -1050,14 +1043,13 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { unwind: Option<&'ll BasicBlock>, handlers: &[&'ll BasicBlock], ) -> &'ll Value { - let name = cstr!("catchswitch"); let ret = unsafe { llvm::LLVMBuildCatchSwitch( self.llbuilder, parent, unwind, handlers.len() as c_uint, - name.as_ptr(), + c"catchswitch".as_ptr(), ) }; let ret = ret.expect("LLVM does not have support for catchswitch"); diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index df52f50f86f05..2087754c66b84 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -8,7 +8,6 @@ use crate::llvm::{self, True}; use crate::type_::Type; use crate::type_of::LayoutLlvmExt; use crate::value::Value; -use cstr::cstr; use rustc_codegen_ssa::traits::*; use rustc_hir::def_id::DefId; use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; @@ -482,9 +481,9 @@ impl<'ll> StaticMethods for CodegenCx<'ll, '_> { .all(|&byte| byte == 0); let sect_name = if all_bytes_are_zero { - cstr!("__DATA,__thread_bss") + c"__DATA,__thread_bss" } else { - cstr!("__DATA,__thread_data") + c"__DATA,__thread_data" }; llvm::LLVMSetSection(g, sect_name.as_ptr()); } @@ -513,7 +512,7 @@ impl<'ll> StaticMethods for CodegenCx<'ll, '_> { let val = llvm::LLVMMetadataAsValue(self.llcx, meta); llvm::LLVMAddNamedMetadataOperand( self.llmod, - "wasm.custom_sections\0".as_ptr().cast(), + c"wasm.custom_sections".as_ptr().cast(), val, ); } diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index ca0f771f5b9e4..e8a7afcc632c2 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -8,7 +8,6 @@ use crate::llvm_util; use crate::type_::Type; use crate::value::Value; -use cstr::cstr; use rustc_codegen_ssa::base::wants_msvc_seh; use rustc_codegen_ssa::traits::*; use rustc_data_structures::base_n; @@ -224,36 +223,42 @@ pub unsafe fn create_module<'ll>( // If skipping the PLT is enabled, we need to add some module metadata // to ensure intrinsic calls don't use it. if !sess.needs_plt() { - let avoid_plt = "RtLibUseGOT\0".as_ptr().cast(); - llvm::LLVMRustAddModuleFlag(llmod, llvm::LLVMModFlagBehavior::Warning, avoid_plt, 1); + llvm::LLVMRustAddModuleFlag( + llmod, + llvm::LLVMModFlagBehavior::Warning, + c"RtLibUseGOT".as_ptr().cast(), + 1, + ); } // Enable canonical jump tables if CFI is enabled. (See https://reviews.llvm.org/D65629.) if sess.is_sanitizer_cfi_canonical_jump_tables_enabled() && sess.is_sanitizer_cfi_enabled() { - let canonical_jump_tables = "CFI Canonical Jump Tables\0".as_ptr().cast(); llvm::LLVMRustAddModuleFlag( llmod, llvm::LLVMModFlagBehavior::Override, - canonical_jump_tables, + c"CFI Canonical Jump Tables".as_ptr().cast(), 1, ); } // Enable LTO unit splitting if specified or if CFI is enabled. (See https://reviews.llvm.org/D53891.) if sess.is_split_lto_unit_enabled() || sess.is_sanitizer_cfi_enabled() { - let enable_split_lto_unit = "EnableSplitLTOUnit\0".as_ptr().cast(); llvm::LLVMRustAddModuleFlag( llmod, llvm::LLVMModFlagBehavior::Override, - enable_split_lto_unit, + c"EnableSplitLTOUnit".as_ptr().cast(), 1, ); } // Add "kcfi" module flag if KCFI is enabled. (See https://reviews.llvm.org/D119296.) if sess.is_sanitizer_kcfi_enabled() { - let kcfi = "kcfi\0".as_ptr().cast(); - llvm::LLVMRustAddModuleFlag(llmod, llvm::LLVMModFlagBehavior::Override, kcfi, 1); + llvm::LLVMRustAddModuleFlag( + llmod, + llvm::LLVMModFlagBehavior::Override, + c"kcfi".as_ptr().cast(), + 1, + ); } // Control Flow Guard is currently only supported by the MSVC linker on Windows. @@ -265,7 +270,7 @@ pub unsafe fn create_module<'ll>( llvm::LLVMRustAddModuleFlag( llmod, llvm::LLVMModFlagBehavior::Warning, - "cfguard\0".as_ptr() as *const _, + c"cfguard".as_ptr() as *const _, 1, ) } @@ -274,7 +279,7 @@ pub unsafe fn create_module<'ll>( llvm::LLVMRustAddModuleFlag( llmod, llvm::LLVMModFlagBehavior::Warning, - "cfguard\0".as_ptr() as *const _, + c"cfguard".as_ptr() as *const _, 2, ) } @@ -292,26 +297,26 @@ pub unsafe fn create_module<'ll>( llvm::LLVMRustAddModuleFlag( llmod, behavior, - "branch-target-enforcement\0".as_ptr().cast(), + c"branch-target-enforcement".as_ptr().cast(), bti.into(), ); llvm::LLVMRustAddModuleFlag( llmod, behavior, - "sign-return-address\0".as_ptr().cast(), + c"sign-return-address".as_ptr().cast(), pac_ret.is_some().into(), ); let pac_opts = pac_ret.unwrap_or(PacRet { leaf: false, key: PAuthKey::A }); llvm::LLVMRustAddModuleFlag( llmod, behavior, - "sign-return-address-all\0".as_ptr().cast(), + c"sign-return-address-all".as_ptr().cast(), pac_opts.leaf.into(), ); llvm::LLVMRustAddModuleFlag( llmod, behavior, - "sign-return-address-with-bkey\0".as_ptr().cast(), + c"sign-return-address-with-bkey".as_ptr().cast(), u32::from(pac_opts.key == PAuthKey::B), ); } else { @@ -327,7 +332,7 @@ pub unsafe fn create_module<'ll>( llvm::LLVMRustAddModuleFlag( llmod, llvm::LLVMModFlagBehavior::Override, - "cf-protection-branch\0".as_ptr().cast(), + c"cf-protection-branch".as_ptr().cast(), 1, ) } @@ -335,7 +340,7 @@ pub unsafe fn create_module<'ll>( llvm::LLVMRustAddModuleFlag( llmod, llvm::LLVMModFlagBehavior::Override, - "cf-protection-return\0".as_ptr().cast(), + c"cf-protection-return".as_ptr().cast(), 1, ) } @@ -344,7 +349,7 @@ pub unsafe fn create_module<'ll>( llvm::LLVMRustAddModuleFlag( llmod, llvm::LLVMModFlagBehavior::Error, - "Virtual Function Elim\0".as_ptr().cast(), + c"Virtual Function Elim".as_ptr().cast(), 1, ); } @@ -476,14 +481,13 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { } pub(crate) fn create_used_variable_impl(&self, name: &'static CStr, values: &[&'ll Value]) { - let section = cstr!("llvm.metadata"); let array = self.const_array(self.type_ptr_to(self.type_i8()), values); unsafe { let g = llvm::LLVMAddGlobal(self.llmod, self.val_ty(array), name.as_ptr()); llvm::LLVMSetInitializer(g, array); llvm::LLVMRustSetLinkage(g, llvm::Linkage::AppendingLinkage); - llvm::LLVMSetSection(g, section.as_ptr()); + llvm::LLVMSetSection(g, c"llvm.metadata".as_ptr()); } } } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs index 37f30917609ae..8be54b7eb7188 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs @@ -38,7 +38,6 @@ pub fn get_or_insert_gdb_debug_scripts_section_global<'ll>(cx: &CodegenCx<'ll, ' unsafe { llvm::LLVMGetNamedGlobal(cx.llmod, c_section_var_name.as_ptr().cast()) }; section_var.unwrap_or_else(|| { - let section_name = b".debug_gdb_scripts\0"; let mut section_contents = Vec::new(); // Add the pretty printers for the standard library first. @@ -71,7 +70,7 @@ pub fn get_or_insert_gdb_debug_scripts_section_global<'ll>(cx: &CodegenCx<'ll, ' let section_var = cx .define_global(section_var_name, llvm_type) .unwrap_or_else(|| bug!("symbol `{}` is already defined", section_var_name)); - llvm::LLVMSetSection(section_var, section_name.as_ptr().cast()); + llvm::LLVMSetSection(section_var, c".debug_gdb_scripts".as_ptr().cast()); llvm::LLVMSetInitializer(section_var, cx.const_bytes(section_contents)); llvm::LLVMSetGlobalConstant(section_var, llvm::True); llvm::LLVMSetUnnamedAddress(section_var, llvm::UnnamedAddr::Global); diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index bd2fba1260282..166454d3ae74c 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -20,7 +20,6 @@ use crate::llvm::debuginfo::{ }; use crate::value::Value; -use cstr::cstr; use rustc_codegen_ssa::debuginfo::type_names::cpp_like_debuginfo; use rustc_codegen_ssa::debuginfo::type_names::VTableNameKind; use rustc_codegen_ssa::traits::*; @@ -812,7 +811,6 @@ pub fn build_compile_unit_di_node<'ll, 'tcx>( let name_in_debuginfo = name_in_debuginfo.to_string_lossy(); let work_dir = tcx.sess.opts.working_dir.to_string_lossy(FileNameDisplayPreference::Remapped); - let flags = "\0"; let output_filenames = tcx.output_filenames(()); let split_name = if tcx.sess.target_can_use_split_dwarf() { output_filenames @@ -849,7 +847,7 @@ pub fn build_compile_unit_di_node<'ll, 'tcx>( producer.as_ptr().cast(), producer.len(), tcx.sess.opts.optimize != config::OptLevel::No, - flags.as_ptr().cast(), + c"".as_ptr().cast(), 0, // NB: this doesn't actually have any perceptible effect, it seems. LLVM will instead // put the path supplied to `MCSplitDwarfFile` into the debug info of the final @@ -878,8 +876,7 @@ pub fn build_compile_unit_di_node<'ll, 'tcx>( ); let val = llvm::LLVMMetadataAsValue(debug_context.llcontext, gcov_metadata); - let llvm_gcov_ident = cstr!("llvm.gcov"); - llvm::LLVMAddNamedMetadataOperand(debug_context.llmod, llvm_gcov_ident.as_ptr(), val); + llvm::LLVMAddNamedMetadataOperand(debug_context.llmod, c"llvm.gcov".as_ptr(), val); } // Insert `llvm.ident` metadata on the wasm targets since that will @@ -892,7 +889,7 @@ pub fn build_compile_unit_di_node<'ll, 'tcx>( ); llvm::LLVMAddNamedMetadataOperand( debug_context.llmod, - cstr!("llvm.ident").as_ptr(), + c"llvm.ident".as_ptr(), llvm::LLVMMDNodeInContext(debug_context.llcontext, &name_metadata, 1), ); } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index c3f0a0033b0ea..aa7ae9355bcfa 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -113,7 +113,7 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> { llvm::LLVMRustAddModuleFlag( self.llmod, llvm::LLVMModFlagBehavior::Warning, - "Dwarf Version\0".as_ptr().cast(), + c"Dwarf Version".as_ptr().cast(), dwarf_version, ); } else { @@ -121,17 +121,16 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> { llvm::LLVMRustAddModuleFlag( self.llmod, llvm::LLVMModFlagBehavior::Warning, - "CodeView\0".as_ptr().cast(), + c"CodeView".as_ptr().cast(), 1, ) } // Prevent bitcode readers from deleting the debug info. - let ptr = "Debug Info Version\0".as_ptr(); llvm::LLVMRustAddModuleFlag( self.llmod, llvm::LLVMModFlagBehavior::Warning, - ptr.cast(), + c"Debug Info Version".as_ptr().cast(), llvm::LLVMRustDebugMetadataVersion(), ); } diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 805843e5863e6..24968e00cc8e5 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -11,6 +11,7 @@ #![feature(let_chains)] #![feature(never_type)] #![feature(impl_trait_in_assoc_type)] +#![feature(c_str_literals)] #![recursion_limit = "256"] #![allow(rustc::potential_query_instability)] #![deny(rustc::untranslatable_diagnostic)] @@ -33,7 +34,7 @@ use rustc_codegen_ssa::back::write::{ use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::ModuleCodegen; use rustc_codegen_ssa::{CodegenResults, CompiledModule}; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::FxIndexMap; use rustc_errors::{DiagnosticMessage, ErrorGuaranteed, FatalError, Handler, SubdiagnosticMessage}; use rustc_fluent_macro::fluent_messages; use rustc_metadata::EncodedMetadata; @@ -355,7 +356,7 @@ impl CodegenBackend for LlvmCodegenBackend { ongoing_codegen: Box, sess: &Session, outputs: &OutputFilenames, - ) -> Result<(CodegenResults, FxHashMap), ErrorGuaranteed> { + ) -> Result<(CodegenResults, FxIndexMap), ErrorGuaranteed> { let (codegen_results, work_products) = ongoing_codegen .downcast::>() .expect("Expected LlvmCodegenBackend's OngoingCodegen, found Box") diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs index d3fad5699c800..4ffa2b9c6a39d 100644 --- a/compiler/rustc_codegen_llvm/src/type_.rs +++ b/compiler/rustc_codegen_llvm/src/type_.rs @@ -288,6 +288,9 @@ impl<'ll, 'tcx> LayoutTypeMethods<'tcx> for CodegenCx<'ll, 'tcx> { fn reg_backend_type(&self, ty: &Reg) -> &'ll Type { ty.llvm_type(self) } + fn scalar_copy_backend_type(&self, layout: TyAndLayout<'tcx>) -> Option { + layout.scalar_copy_llvm_type(self) + } } impl<'ll, 'tcx> TypeMembershipMethods<'tcx> for CodegenCx<'ll, 'tcx> { diff --git a/compiler/rustc_codegen_llvm/src/type_of.rs b/compiler/rustc_codegen_llvm/src/type_of.rs index a493c9c0548e9..3339e4e07edd3 100644 --- a/compiler/rustc_codegen_llvm/src/type_of.rs +++ b/compiler/rustc_codegen_llvm/src/type_of.rs @@ -6,6 +6,7 @@ use rustc_middle::bug; use rustc_middle::ty::layout::{FnAbiOf, LayoutOf, TyAndLayout}; use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths}; use rustc_middle::ty::{self, Ty, TypeVisitableExt}; +use rustc_target::abi::HasDataLayout; use rustc_target::abi::{Abi, Align, FieldsShape}; use rustc_target::abi::{Int, Pointer, F32, F64}; use rustc_target::abi::{PointeeInfo, Scalar, Size, TyAbiInterface, Variants}; @@ -192,6 +193,7 @@ pub trait LayoutLlvmExt<'tcx> { ) -> &'a Type; fn llvm_field_index<'a>(&self, cx: &CodegenCx<'a, 'tcx>, index: usize) -> u64; fn pointee_info_at<'a>(&self, cx: &CodegenCx<'a, 'tcx>, offset: Size) -> Option; + fn scalar_copy_llvm_type<'a>(&self, cx: &CodegenCx<'a, 'tcx>) -> Option<&'a Type>; } impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> { @@ -414,4 +416,35 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> { cx.pointee_infos.borrow_mut().insert((self.ty, offset), result); result } + + fn scalar_copy_llvm_type<'a>(&self, cx: &CodegenCx<'a, 'tcx>) -> Option<&'a Type> { + debug_assert!(self.is_sized()); + + // FIXME: this is a fairly arbitrary choice, but 128 bits on WASM + // (matching the 128-bit SIMD types proposal) and 256 bits on x64 + // (like AVX2 registers) seems at least like a tolerable starting point. + let threshold = cx.data_layout().pointer_size * 4; + if self.layout.size() > threshold { + return None; + } + + // Vectors, even for non-power-of-two sizes, have the same layout as + // arrays but don't count as aggregate types + if let FieldsShape::Array { count, .. } = self.layout.fields() + && let element = self.field(cx, 0) + && element.ty.is_integral() + { + // `cx.type_ix(bits)` is tempting here, but while that works great + // for things that *stay* as memory-to-memory copies, it also ends + // up suppressing vectorization as it introduces shifts when it + // extracts all the individual values. + + let ety = element.llvm_type(cx); + return Some(cx.type_vector(ety, *count)); + } + + // FIXME: The above only handled integer arrays; surely more things + // would also be possible. Be careful about provenance, though! + None + } } diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 0ac12d32be535..984efa2104446 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -48,7 +48,7 @@ libc = "0.2.50" [dependencies.object] version = "0.31.1" default-features = false -features = ["read_core", "elf", "macho", "pe", "unaligned", "archive", "write"] +features = ["read_core", "elf", "macho", "pe", "xcoff", "unaligned", "archive", "write"] [target.'cfg(windows)'.dependencies.windows] version = "0.48.0" diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 9aa2b2e2b2ed0..5ecb63986fe1f 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -9,6 +9,8 @@ codegen_ssa_archive_build_failure = codegen_ssa_atomic_compare_exchange = Atomic compare-exchange intrinsic missing failure memory ordering +codegen_ssa_binary_output_to_tty = option `-o` or `--emit` is used to write binary output type `{$shorthand}` to stdout, but stdout is a tty + codegen_ssa_check_installed_visual_studio = please ensure that Visual Studio 2017 or later, or Build Tools for Visual Studio were installed with the Visual C++ option. codegen_ssa_copy_path = could not copy {$from} to {$to}: {$error} diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index f8bb9bf2bb50b..8d346c8c0d0de 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -8,7 +8,7 @@ use rustc_errors::{ErrorGuaranteed, Handler}; use rustc_fs_util::fix_windows_verbatim_for_gcc; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_metadata::find_native_static_library; -use rustc_metadata::fs::{emit_wrapper_file, METADATA_FILENAME}; +use rustc_metadata::fs::{copy_to_stdout, emit_wrapper_file, METADATA_FILENAME}; use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use rustc_middle::middle::dependency_format::Linkage; use rustc_middle::middle::exported_symbols::SymbolExportKind; @@ -68,6 +68,7 @@ pub fn link_binary<'a>( ) -> Result<(), ErrorGuaranteed> { let _timer = sess.timer("link_binary"); let output_metadata = sess.opts.output_types.contains_key(&OutputType::Metadata); + let mut tempfiles_for_stdout_output: Vec = Vec::new(); for &crate_type in sess.crate_types().iter() { // Ignore executable crates if we have -Z no-codegen, as they will error. if (sess.opts.unstable_opts.no_codegen || !sess.opts.output_types.should_codegen()) @@ -97,12 +98,15 @@ pub fn link_binary<'a>( .tempdir() .unwrap_or_else(|error| sess.emit_fatal(errors::CreateTempDir { error })); let path = MaybeTempDir::new(tmpdir, sess.opts.cg.save_temps); - let out_filename = out_filename( + let output = out_filename( sess, crate_type, outputs, codegen_results.crate_info.local_crate_name, ); + let crate_name = format!("{}", codegen_results.crate_info.local_crate_name); + let out_filename = + output.file_for_writing(outputs, OutputType::Exe, Some(crate_name.as_str())); match crate_type { CrateType::Rlib => { let _timer = sess.timer("link_rlib"); @@ -152,6 +156,17 @@ pub fn link_binary<'a>( ); } } + + if output.is_stdout() { + if output.is_tty() { + sess.emit_err(errors::BinaryOutputToTty { + shorthand: OutputType::Exe.shorthand(), + }); + } else if let Err(e) = copy_to_stdout(&out_filename) { + sess.emit_err(errors::CopyPath::new(&out_filename, output.as_path(), e)); + } + tempfiles_for_stdout_output.push(out_filename); + } } } @@ -189,6 +204,11 @@ pub fn link_binary<'a>( remove_temps_from_module(allocator_module); } + // Remove the temporary files if output goes to stdout + for temp in tempfiles_for_stdout_output { + ensure_removed(sess.diagnostic(), &temp); + } + // If no requested outputs require linking, then the object temporaries should // be kept. if !sess.opts.output_types.should_link() { @@ -2097,7 +2117,14 @@ fn linker_with_args<'a>( cmd.add_as_needed(); // Local native libraries of all kinds. - add_local_native_libraries(cmd, sess, archive_builder_builder, codegen_results, tmpdir); + add_local_native_libraries( + cmd, + sess, + archive_builder_builder, + codegen_results, + tmpdir, + link_output_kind, + ); // Upstream rust crates and their non-dynamic native libraries. add_upstream_rust_crates( @@ -2107,10 +2134,18 @@ fn linker_with_args<'a>( codegen_results, crate_type, tmpdir, + link_output_kind, ); // Dynamic native libraries from upstream crates. - add_upstream_native_libraries(cmd, sess, archive_builder_builder, codegen_results, tmpdir); + add_upstream_native_libraries( + cmd, + sess, + archive_builder_builder, + codegen_results, + tmpdir, + link_output_kind, + ); // Link with the import library generated for any raw-dylib functions. for (raw_dylib_name, raw_dylib_imports) in @@ -2256,11 +2291,13 @@ fn add_order_independent_options( } else if flavor == LinkerFlavor::Bpf { cmd.arg("--cpu"); cmd.arg(&codegen_results.crate_info.target_cpu); - cmd.arg("--cpu-features"); - cmd.arg(match &sess.opts.cg.target_feature { - feat if !feat.is_empty() => feat.as_ref(), - _ => sess.target.options.features.as_ref(), - }); + if let Some(feat) = [sess.opts.cg.target_feature.as_str(), &sess.target.options.features] + .into_iter() + .find(|feat| !feat.is_empty()) + { + cmd.arg("--cpu-features"); + cmd.arg(feat); + } } cmd.linker_plugin_lto(); @@ -2365,6 +2402,7 @@ fn add_native_libs_from_crate( cnum: CrateNum, link_static: bool, link_dynamic: bool, + link_output_kind: LinkOutputKind, ) { if !sess.opts.unstable_opts.link_native_libraries { // If `-Zlink-native-libraries=false` is set, then the assumption is that an @@ -2444,8 +2482,16 @@ fn add_native_libs_from_crate( } } NativeLibKind::Unspecified => { - if link_dynamic { - cmd.link_dylib(name, verbatim, true); + // If we are generating a static binary, prefer static library when the + // link kind is unspecified. + if !link_output_kind.can_link_dylib() && !sess.target.crt_static_allows_dylibs { + if link_static { + cmd.link_staticlib(name, verbatim) + } + } else { + if link_dynamic { + cmd.link_dylib(name, verbatim, true); + } } } NativeLibKind::Framework { as_needed } => { @@ -2472,6 +2518,7 @@ fn add_local_native_libraries( archive_builder_builder: &dyn ArchiveBuilderBuilder, codegen_results: &CodegenResults, tmpdir: &Path, + link_output_kind: LinkOutputKind, ) { if sess.opts.unstable_opts.link_native_libraries { // User-supplied library search paths (-L on the command line). These are the same paths @@ -2501,6 +2548,7 @@ fn add_local_native_libraries( LOCAL_CRATE, link_static, link_dynamic, + link_output_kind, ); } @@ -2511,6 +2559,7 @@ fn add_upstream_rust_crates<'a>( codegen_results: &CodegenResults, crate_type: CrateType, tmpdir: &Path, + link_output_kind: LinkOutputKind, ) { // All of the heavy lifting has previously been accomplished by the // dependency_format module of the compiler. This is just crawling the @@ -2588,6 +2637,7 @@ fn add_upstream_rust_crates<'a>( cnum, link_static, link_dynamic, + link_output_kind, ); } } @@ -2598,6 +2648,7 @@ fn add_upstream_native_libraries( archive_builder_builder: &dyn ArchiveBuilderBuilder, codegen_results: &CodegenResults, tmpdir: &Path, + link_output_kind: LinkOutputKind, ) { let search_path = OnceCell::new(); for &cnum in &codegen_results.crate_info.used_crates { @@ -2626,6 +2677,7 @@ fn add_upstream_native_libraries( cnum, link_static, link_dynamic, + link_output_kind, ); } } diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs index ad27b854d59a2..b198e35e0c18a 100644 --- a/compiler/rustc_codegen_ssa/src/back/metadata.rs +++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs @@ -6,8 +6,8 @@ use std::path::Path; use object::write::{self, StandardSegment, Symbol, SymbolSection}; use object::{ - elf, pe, Architecture, BinaryFormat, Endianness, FileFlags, Object, ObjectSection, - SectionFlags, SectionKind, SymbolFlags, SymbolKind, SymbolScope, + elf, pe, xcoff, Architecture, BinaryFormat, Endianness, FileFlags, Object, ObjectSection, + ObjectSymbol, SectionFlags, SectionKind, SymbolFlags, SymbolKind, SymbolScope, }; use snap::write::FrameEncoder; @@ -35,6 +35,8 @@ use rustc_target::spec::{RelocModel, Target}; #[derive(Debug)] pub struct DefaultMetadataLoader; +static AIX_METADATA_SYMBOL_NAME: &'static str = "__aix_rust_metadata"; + fn load_metadata_with( path: &Path, f: impl for<'a> FnOnce(&'a [u8]) -> Result<&'a [u8], String>, @@ -48,7 +50,7 @@ fn load_metadata_with( } impl MetadataLoader for DefaultMetadataLoader { - fn get_rlib_metadata(&self, _target: &Target, path: &Path) -> Result { + fn get_rlib_metadata(&self, target: &Target, path: &Path) -> Result { load_metadata_with(path, |data| { let archive = object::read::archive::ArchiveFile::parse(&*data) .map_err(|e| format!("failed to parse rlib '{}': {}", path.display(), e))?; @@ -60,7 +62,11 @@ impl MetadataLoader for DefaultMetadataLoader { let data = entry .data(data) .map_err(|e| format!("failed to parse rlib '{}': {}", path.display(), e))?; - return search_for_section(path, data, ".rmeta"); + if target.is_like_aix { + return get_metadata_xcoff(path, data); + } else { + return search_for_section(path, data, ".rmeta"); + } } } @@ -68,8 +74,12 @@ impl MetadataLoader for DefaultMetadataLoader { }) } - fn get_dylib_metadata(&self, _target: &Target, path: &Path) -> Result { - load_metadata_with(path, |data| search_for_section(path, data, ".rustc")) + fn get_dylib_metadata(&self, target: &Target, path: &Path) -> Result { + if target.is_like_aix { + load_metadata_with(path, |data| get_metadata_xcoff(path, data)) + } else { + load_metadata_with(path, |data| search_for_section(path, data, ".rustc")) + } } } @@ -141,6 +151,33 @@ fn add_gnu_property_note( file.append_section_data(section, &data, 8); } +pub(super) fn get_metadata_xcoff<'a>(path: &Path, data: &'a [u8]) -> Result<&'a [u8], String> { + let Ok(file) = object::File::parse(data) else { + return Ok(data); + }; + let info_data = search_for_section(path, data, ".info")?; + if let Some(metadata_symbol) = + file.symbols().find(|sym| sym.name() == Ok(AIX_METADATA_SYMBOL_NAME)) + { + let offset = metadata_symbol.address() as usize; + if offset < 4 { + return Err(format!("Invalid metadata symbol offset: {}", offset)); + } + // The offset specifies the location of rustc metadata in the comment section. + // The metadata is preceded by a 4-byte length field. + let len = u32::from_be_bytes(info_data[(offset - 4)..offset].try_into().unwrap()) as usize; + if offset + len > (info_data.len() as usize) { + return Err(format!( + "Metadata at offset {} with size {} is beyond .info section", + offset, len + )); + } + return Ok(&info_data[offset..(offset + len)]); + } else { + return Err(format!("Unable to find symbol {}", AIX_METADATA_SYMBOL_NAME)); + }; +} + pub(crate) fn create_object_file(sess: &Session) -> Option> { let endianness = match sess.target.options.endian { Endian::Little => Endianness::Little, @@ -183,6 +220,8 @@ pub(crate) fn create_object_file(sess: &Session) -> Option { file.section_mut(section).flags = @@ -365,6 +408,31 @@ pub fn create_wrapper_file( file.section_mut(section).flags = SectionFlags::Elf { sh_flags: elf::SHF_EXCLUDE as u64 }; } + BinaryFormat::Xcoff => { + // AIX system linker may aborts if it meets a valid XCOFF file in archive with no .text, no .data and no .bss. + file.add_section(Vec::new(), b".text".to_vec(), SectionKind::Text); + file.section_mut(section).flags = + SectionFlags::Xcoff { s_flags: xcoff::STYP_INFO as u32 }; + + let len = data.len() as u32; + let offset = file.append_section_data(section, &len.to_be_bytes(), 1); + // Add a symbol referring to the data in .info section. + file.add_symbol(Symbol { + name: AIX_METADATA_SYMBOL_NAME.into(), + value: offset + 4, + size: 0, + kind: SymbolKind::Unknown, + scope: SymbolScope::Compilation, + weak: false, + section: SymbolSection::Section(section), + flags: SymbolFlags::Xcoff { + n_sclass: xcoff::C_INFO, + x_smtyp: xcoff::C_HIDEXT, + x_smclas: xcoff::C_HIDEXT, + containing_csect: None, + }, + }); + } _ => {} }; file.append_section_data(section, data, 1); @@ -401,6 +469,9 @@ pub fn create_compressed_metadata_file( let Some(mut file) = create_object_file(sess) else { return compressed.to_vec(); }; + if file.format() == BinaryFormat::Xcoff { + return create_compressed_metadata_file_for_xcoff(file, &compressed, symbol_name); + } let section = file.add_section( file.segment_name(StandardSegment::Data).to_vec(), b".rustc".to_vec(), @@ -430,3 +501,61 @@ pub fn create_compressed_metadata_file( file.write().unwrap() } + +/// * Xcoff - On AIX, custom sections are merged into predefined sections, +/// so custom .rustc section is not preserved during linking. +/// For this reason, we store metadata in predefined .info section, and +/// define a symbol to reference the metadata. To preserve metadata during +/// linking on AIX, we have to +/// 1. Create an empty .text section, a empty .data section. +/// 2. Define an empty symbol named `symbol_name` inside .data section. +/// 3. Define an symbol named `AIX_METADATA_SYMBOL_NAME` referencing +/// data inside .info section. +/// From XCOFF's view, (2) creates a csect entry in the symbol table, the +/// symbol created by (3) is a info symbol for the preceding csect. Thus +/// two symbols are preserved during linking and we can use the second symbol +/// to reference the metadata. +pub fn create_compressed_metadata_file_for_xcoff( + mut file: write::Object<'_>, + data: &[u8], + symbol_name: &str, +) -> Vec { + assert!(file.format() == BinaryFormat::Xcoff); + // AIX system linker may aborts if it meets a valid XCOFF file in archive with no .text, no .data and no .bss. + file.add_section(Vec::new(), b".text".to_vec(), SectionKind::Text); + let data_section = file.add_section(Vec::new(), b".data".to_vec(), SectionKind::Data); + let section = file.add_section(Vec::new(), b".info".to_vec(), SectionKind::Debug); + file.add_file_symbol("lib.rmeta".into()); + file.section_mut(section).flags = SectionFlags::Xcoff { s_flags: xcoff::STYP_INFO as u32 }; + // Add a global symbol to data_section. + file.add_symbol(Symbol { + name: symbol_name.as_bytes().into(), + value: 0, + size: 0, + kind: SymbolKind::Data, + scope: SymbolScope::Dynamic, + weak: true, + section: SymbolSection::Section(data_section), + flags: SymbolFlags::None, + }); + let len = data.len() as u32; + let offset = file.append_section_data(section, &len.to_be_bytes(), 1); + // Add a symbol referring to the rustc metadata. + file.add_symbol(Symbol { + name: AIX_METADATA_SYMBOL_NAME.into(), + value: offset + 4, // The metadata is preceded by a 4-byte length field. + size: 0, + kind: SymbolKind::Unknown, + scope: SymbolScope::Dynamic, + weak: false, + section: SymbolSection::Section(section), + flags: SymbolFlags::Xcoff { + n_sclass: xcoff::C_INFO, + x_smtyp: xcoff::C_HIDEXT, + x_smclas: xcoff::C_HIDEXT, + containing_csect: None, + }, + }); + file.append_section_data(section, data, 1); + file.write().unwrap() +} diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 10e9e5588f6cc..9be69e560e888 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -9,7 +9,7 @@ use crate::{ }; use jobserver::{Acquired, Client}; use rustc_ast::attr; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_data_structures::memmap::Mmap; use rustc_data_structures::profiling::SelfProfilerRef; use rustc_data_structures::profiling::TimingGuard; @@ -23,12 +23,13 @@ use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_incremental::{ copy_cgu_workproduct_to_incr_comp_cache_dir, in_incr_comp_dir, in_incr_comp_dir_sess, }; +use rustc_metadata::fs::copy_to_stdout; use rustc_metadata::EncodedMetadata; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_middle::middle::exported_symbols::SymbolExportInfo; use rustc_middle::ty::TyCtxt; use rustc_session::cgu_reuse_tracker::CguReuseTracker; -use rustc_session::config::{self, CrateType, Lto, OutputFilenames, OutputType}; +use rustc_session::config::{self, CrateType, Lto, OutFileName, OutputFilenames, OutputType}; use rustc_session::config::{Passes, SwitchWithOptPath}; use rustc_session::Session; use rustc_span::source_map::SourceMap; @@ -498,8 +499,8 @@ pub fn start_async_codegen( fn copy_all_cgu_workproducts_to_incr_comp_cache_dir( sess: &Session, compiled_modules: &CompiledModules, -) -> FxHashMap { - let mut work_products = FxHashMap::default(); +) -> FxIndexMap { + let mut work_products = FxIndexMap::default(); if sess.opts.incremental.is_none() { return work_products; @@ -535,9 +536,16 @@ fn produce_final_output_artifacts( let mut user_wants_objects = false; // Produce final compile outputs. - let copy_gracefully = |from: &Path, to: &Path| { - if let Err(e) = fs::copy(from, to) { - sess.emit_err(errors::CopyPath::new(from, to, e)); + let copy_gracefully = |from: &Path, to: &OutFileName| match to { + OutFileName::Stdout => { + if let Err(e) = copy_to_stdout(from) { + sess.emit_err(errors::CopyPath::new(from, to.as_path(), e)); + } + } + OutFileName::Real(path) => { + if let Err(e) = fs::copy(from, path) { + sess.emit_err(errors::CopyPath::new(from, path, e)); + } } }; @@ -547,7 +555,12 @@ fn produce_final_output_artifacts( // to copy `foo.0.x` to `foo.x`. let module_name = Some(&compiled_modules.modules[0].name[..]); let path = crate_output.temp_path(output_type, module_name); - copy_gracefully(&path, &crate_output.path(output_type)); + let output = crate_output.path(output_type); + if !output_type.is_text_output() && output.is_tty() { + sess.emit_err(errors::BinaryOutputToTty { shorthand: output_type.shorthand() }); + } else { + copy_gracefully(&path, &output); + } if !sess.opts.cg.save_temps && !keep_numbered { // The user just wants `foo.x`, not `foo.#module-name#.x`. ensure_removed(sess.diagnostic(), &path); @@ -1885,7 +1898,7 @@ pub struct OngoingCodegen { } impl OngoingCodegen { - pub fn join(self, sess: &Session) -> (CodegenResults, FxHashMap) { + pub fn join(self, sess: &Session) -> (CodegenResults, FxIndexMap) { let _timer = sess.timer("finish_ongoing_codegen"); self.shared_emitter_main.check(sess, true); diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 242d209b68425..dc4a28c866ff3 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -380,7 +380,19 @@ pub fn memcpy_ty<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( return; } - bx.memcpy(dst, dst_align, src, src_align, bx.cx().const_usize(size), flags); + if flags == MemFlags::empty() + && let Some(bty) = bx.cx().scalar_copy_backend_type(layout) + { + // I look forward to only supporting opaque pointers + let pty = bx.type_ptr_to(bty); + let src = bx.pointercast(src, pty); + let dst = bx.pointercast(dst, pty); + + let temp = bx.load(bty, src, src_align); + bx.store(temp, dst, dst_align); + } else { + bx.memcpy(dst, dst_align, src, src_align, bx.cx().const_usize(size), flags); + } } pub fn codegen_instance<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>( diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index bf37ac69f2d1e..989274308fb00 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -82,6 +82,12 @@ impl IntoDiagnosticArg for DebugArgPath<'_> { } } +#[derive(Diagnostic)] +#[diag(codegen_ssa_binary_output_to_tty)] +pub struct BinaryOutputToTty { + pub shorthand: &'static str, +} + #[derive(Diagnostic)] #[diag(codegen_ssa_ignoring_emit_path)] pub struct IgnoringEmitPath { diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index 1479242f23a6c..9ac2424e76be0 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -211,8 +211,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { args[1].val.unaligned_volatile_store(bx, dst); return; } - | sym::unchecked_div - | sym::unchecked_rem | sym::unchecked_shl | sym::unchecked_shr | sym::unchecked_add @@ -229,20 +227,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.exactudiv(args[0].immediate(), args[1].immediate()) } } - sym::unchecked_div => { - if signed { - bx.sdiv(args[0].immediate(), args[1].immediate()) - } else { - bx.udiv(args[0].immediate(), args[1].immediate()) - } - } - sym::unchecked_rem => { - if signed { - bx.srem(args[0].immediate(), args[1].immediate()) - } else { - bx.urem(args[0].immediate(), args[1].immediate()) - } - } sym::unchecked_shl => bx.shl(args[0].immediate(), args[1].immediate()), sym::unchecked_shr => { if signed { diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index d83bfc74082f6..b3c9ecf8b938b 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -6,7 +6,7 @@ use crate::back::write::TargetMachineFactoryFn; use crate::{CodegenResults, ModuleCodegen}; use rustc_ast::expand::allocator::AllocatorKind; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync::{DynSend, DynSync}; use rustc_errors::ErrorGuaranteed; use rustc_metadata::EncodedMetadata; @@ -101,7 +101,7 @@ pub trait CodegenBackend { ongoing_codegen: Box, sess: &Session, outputs: &OutputFilenames, - ) -> Result<(CodegenResults, FxHashMap), ErrorGuaranteed>; + ) -> Result<(CodegenResults, FxIndexMap), ErrorGuaranteed>; /// This is called on the returned `Box` from `join_codegen` /// diff --git a/compiler/rustc_codegen_ssa/src/traits/type_.rs b/compiler/rustc_codegen_ssa/src/traits/type_.rs index 36d9864221bcb..e64417e1a4a69 100644 --- a/compiler/rustc_codegen_ssa/src/traits/type_.rs +++ b/compiler/rustc_codegen_ssa/src/traits/type_.rs @@ -126,6 +126,28 @@ pub trait LayoutTypeMethods<'tcx>: Backend<'tcx> { index: usize, immediate: bool, ) -> Self::Type; + + /// A type that can be used in a [`super::BuilderMethods::load`] + + /// [`super::BuilderMethods::store`] pair to implement a *typed* copy, + /// such as a MIR `*_0 = *_1`. + /// + /// It's always legal to return `None` here, as the provided impl does, + /// in which case callers should use [`super::BuilderMethods::memcpy`] + /// instead of the `load`+`store` pair. + /// + /// This can be helpful for things like arrays, where the LLVM backend type + /// `[3 x i16]` optimizes to three separate loads and stores, but it can + /// instead be copied via an `i48` that stays as the single `load`+`store`. + /// (As of 2023-05 LLVM cannot necessarily optimize away a `memcpy` in these + /// cases, due to `poison` handling, but in codegen we have more information + /// about the type invariants, so can emit something better instead.) + /// + /// This *should* return `None` for particularly-large types, where leaving + /// the `memcpy` may well be important to avoid code size explosion. + fn scalar_copy_backend_type(&self, layout: TyAndLayout<'tcx>) -> Option { + let _ = layout; + None + } } // For backends that support CFI using type membership (i.e., testing whether a given pointer is diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index fb24cf48a9a02..7192bbc00d556 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -77,7 +77,7 @@ pub(crate) fn eval_nullary_intrinsic<'tcx>( } sym::type_id => { ensure_monomorphic_enough(tcx, tp_ty)?; - ConstValue::from_u64(tcx.type_id_hash(tp_ty).as_u64()) + ConstValue::from_u128(tcx.type_id_hash(tp_ty).as_u128()) } sym::variant_count => match tp_ty.kind() { // Correctly handles non-monomorphic calls, so there is no need for ensure_monomorphic_enough. @@ -169,7 +169,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let ty = match intrinsic_name { sym::pref_align_of | sym::variant_count => self.tcx.types.usize, sym::needs_drop => self.tcx.types.bool, - sym::type_id => self.tcx.types.u64, + sym::type_id => self.tcx.types.u128, sym::type_name => self.tcx.mk_static_str(), _ => bug!(), }; @@ -238,9 +238,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { | sym::unchecked_shr | sym::unchecked_add | sym::unchecked_sub - | sym::unchecked_mul - | sym::unchecked_div - | sym::unchecked_rem => { + | sym::unchecked_mul => { let l = self.read_immediate(&args[0])?; let r = self.read_immediate(&args[1])?; let bin_op = match intrinsic_name { @@ -249,8 +247,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { sym::unchecked_add => BinOp::Add, sym::unchecked_sub => BinOp::Sub, sym::unchecked_mul => BinOp::Mul, - sym::unchecked_div => BinOp::Div, - sym::unchecked_rem => BinOp::Rem, _ => bug!(), }; let (val, overflowed, _ty) = self.overflowing_binary_op(bin_op, &l, &r)?; diff --git a/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs b/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs index 78c74e1892dc5..65fe164f8ec76 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs @@ -337,7 +337,7 @@ where Q: Qualif, { fn apply_statement_effect( - &self, + &mut self, state: &mut Self::Domain, statement: &mir::Statement<'tcx>, location: Location, @@ -346,7 +346,7 @@ where } fn apply_terminator_effect( - &self, + &mut self, state: &mut Self::Domain, terminator: &mir::Terminator<'tcx>, location: Location, @@ -355,7 +355,7 @@ where } fn apply_call_return_effect( - &self, + &mut self, state: &mut Self::Domain, block: BasicBlock, return_places: CallReturnPlaces<'_, 'tcx>, diff --git a/compiler/rustc_data_structures/src/intern.rs b/compiler/rustc_data_structures/src/intern.rs index ba94f3776eb90..e0f8c350c2a86 100644 --- a/compiler/rustc_data_structures/src/intern.rs +++ b/compiler/rustc_data_structures/src/intern.rs @@ -1,5 +1,6 @@ use crate::stable_hasher::{HashStable, StableHasher}; use std::cmp::Ordering; +use std::fmt::{self, Debug}; use std::hash::{Hash, Hasher}; use std::ops::Deref; use std::ptr; @@ -20,7 +21,6 @@ mod private { /// The `PrivateZst` field means you can pattern match with `Interned(v, _)` /// but you can only construct a `Interned` with `new_unchecked`, and not /// directly. -#[derive(Debug)] #[rustc_pass_by_value] pub struct Interned<'a, T>(pub &'a T, pub private::PrivateZst); @@ -108,5 +108,11 @@ where } } +impl Debug for Interned<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + #[cfg(test)] mod tests; diff --git a/compiler/rustc_data_structures/src/stable_hasher.rs b/compiler/rustc_data_structures/src/stable_hasher.rs index 6d57d81c56a4b..0c1fb7518fa3b 100644 --- a/compiler/rustc_data_structures/src/stable_hasher.rs +++ b/compiler/rustc_data_structures/src/stable_hasher.rs @@ -233,7 +233,17 @@ pub trait ToStableHashKey { /// - `DefIndex`, `CrateNum`, `LocalDefId`, because their concrete /// values depend on state that might be different between /// compilation sessions. -pub unsafe trait StableOrd: Ord {} +/// +/// The associated constant `CAN_USE_UNSTABLE_SORT` denotes whether +/// unstable sorting can be used for this type. Set to true if and +/// only if `a == b` implies `a` and `b` are fully indistinguishable. +pub unsafe trait StableOrd: Ord { + const CAN_USE_UNSTABLE_SORT: bool; +} + +unsafe impl StableOrd for &T { + const CAN_USE_UNSTABLE_SORT: bool = T::CAN_USE_UNSTABLE_SORT; +} /// Implement HashStable by just calling `Hash::hash()`. Also implement `StableOrd` for the type since /// that has the same requirements. @@ -253,7 +263,9 @@ macro_rules! impl_stable_traits_for_trivial_type { } } - unsafe impl $crate::stable_hasher::StableOrd for $t {} + unsafe impl $crate::stable_hasher::StableOrd for $t { + const CAN_USE_UNSTABLE_SORT: bool = true; + } }; } @@ -339,6 +351,10 @@ impl, T2: HashStable, CTX> HashStable for (T1, T2) } } +unsafe impl StableOrd for (T1, T2) { + const CAN_USE_UNSTABLE_SORT: bool = T1::CAN_USE_UNSTABLE_SORT && T2::CAN_USE_UNSTABLE_SORT; +} + impl HashStable for (T1, T2, T3) where T1: HashStable, @@ -353,6 +369,11 @@ where } } +unsafe impl StableOrd for (T1, T2, T3) { + const CAN_USE_UNSTABLE_SORT: bool = + T1::CAN_USE_UNSTABLE_SORT && T2::CAN_USE_UNSTABLE_SORT && T3::CAN_USE_UNSTABLE_SORT; +} + impl HashStable for (T1, T2, T3, T4) where T1: HashStable, @@ -369,6 +390,15 @@ where } } +unsafe impl StableOrd + for (T1, T2, T3, T4) +{ + const CAN_USE_UNSTABLE_SORT: bool = T1::CAN_USE_UNSTABLE_SORT + && T2::CAN_USE_UNSTABLE_SORT + && T3::CAN_USE_UNSTABLE_SORT + && T4::CAN_USE_UNSTABLE_SORT; +} + impl, CTX> HashStable for [T] { default fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { self.len().hash_stable(ctx, hasher); @@ -459,6 +489,10 @@ impl HashStable for str { } } +unsafe impl StableOrd for &str { + const CAN_USE_UNSTABLE_SORT: bool = true; +} + impl HashStable for String { #[inline] fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { @@ -468,7 +502,9 @@ impl HashStable for String { // Safety: String comparison only depends on their contents and the // contents are not changed by (de-)serialization. -unsafe impl StableOrd for String {} +unsafe impl StableOrd for String { + const CAN_USE_UNSTABLE_SORT: bool = true; +} impl ToStableHashKey for String { type KeyType = String; @@ -494,7 +530,9 @@ impl HashStable for bool { } // Safety: sort order of bools is not changed by (de-)serialization. -unsafe impl StableOrd for bool {} +unsafe impl StableOrd for bool { + const CAN_USE_UNSTABLE_SORT: bool = true; +} impl HashStable for Option where @@ -512,7 +550,9 @@ where } // Safety: the Option wrapper does not add instability to comparison. -unsafe impl StableOrd for Option {} +unsafe impl StableOrd for Option { + const CAN_USE_UNSTABLE_SORT: bool = T::CAN_USE_UNSTABLE_SORT; +} impl HashStable for Result where diff --git a/compiler/rustc_data_structures/src/temp_dir.rs b/compiler/rustc_data_structures/src/temp_dir.rs index a780d2386a63c..621d3011a2aa1 100644 --- a/compiler/rustc_data_structures/src/temp_dir.rs +++ b/compiler/rustc_data_structures/src/temp_dir.rs @@ -16,7 +16,7 @@ impl Drop for MaybeTempDir { // occur. let dir = unsafe { ManuallyDrop::take(&mut self.dir) }; if self.keep { - dir.into_path(); + let _ = dir.into_path(); } } } diff --git a/compiler/rustc_data_structures/src/unord.rs b/compiler/rustc_data_structures/src/unord.rs index 6c8d541463158..e18c7b415f6cf 100644 --- a/compiler/rustc_data_structures/src/unord.rs +++ b/compiler/rustc_data_structures/src/unord.rs @@ -140,12 +140,12 @@ impl> UnordItems { } #[inline] - pub fn into_sorted_stable_ord(self, use_stable_sort: bool) -> Vec + pub fn into_sorted_stable_ord(self) -> Vec where T: Ord + StableOrd, { let mut items: Vec = self.0.collect(); - if use_stable_sort { + if !T::CAN_USE_UNSTABLE_SORT { items.sort(); } else { items.sort_unstable() @@ -161,6 +161,10 @@ impl> UnordItems { items.sort_by_cached_key(|x| x.to_stable_hash_key(hcx)); items } + + pub fn collect>>(self) -> C { + self.into() + } } /// This is a set collection type that tries very hard to not expose diff --git a/compiler/rustc_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs index 4eabba575f42a..0cd0b51b6ad49 100644 --- a/compiler/rustc_driver/src/lib.rs +++ b/compiler/rustc_driver/src/lib.rs @@ -1,4 +1,4 @@ -// This crate is intentionally empty and a rexport of `rustc_driver_impl` to allow the code in +// This crate is intentionally empty and a re-export of `rustc_driver_impl` to allow the code in // `rustc_driver_impl` to be compiled in parallel with other crates. pub use rustc_driver_impl::*; diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 14888cf4d75c0..5b75205442ba1 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -34,7 +34,9 @@ use rustc_interface::{interface, Queries}; use rustc_lint::LintStore; use rustc_metadata::locator; use rustc_session::config::{nightly_options, CG_OPTIONS, Z_OPTIONS}; -use rustc_session::config::{ErrorOutputType, Input, OutputType, PrintRequest, TrimmedDefPaths}; +use rustc_session::config::{ + ErrorOutputType, Input, OutFileName, OutputType, PrintRequest, TrimmedDefPaths, +}; use rustc_session::cstore::MetadataLoader; use rustc_session::getopts::{self, Matches}; use rustc_session::lint::{Lint, LintId}; @@ -58,11 +60,18 @@ use std::str; use std::sync::OnceLock; use std::time::Instant; +#[allow(unused_macros)] +macro do_not_use_print($($t:tt)*) { + std::compile_error!( + "Don't use `print` or `println` here, use `safe_print` or `safe_println` instead" + ) +} + // This import blocks the use of panicking `print` and `println` in all the code // below. Please use `safe_print` and `safe_println` to avoid ICE when // encountering an I/O error during print. #[allow(unused_imports)] -use std::{compile_error as print, compile_error as println}; +use {do_not_use_print as print, do_not_use_print as println}; pub mod args; pub mod pretty; @@ -447,9 +456,12 @@ fn run_compiler( } // Extract output directory and file from matches. -fn make_output(matches: &getopts::Matches) -> (Option, Option) { +fn make_output(matches: &getopts::Matches) -> (Option, Option) { let odir = matches.opt_str("out-dir").map(|o| PathBuf::from(&o)); - let ofile = matches.opt_str("o").map(|o| PathBuf::from(&o)); + let ofile = matches.opt_str("o").map(|o| match o.as_str() { + "-" => OutFileName::Stdout, + path => OutFileName::Real(PathBuf::from(path)), + }); (odir, ofile) } @@ -695,7 +707,7 @@ fn print_crate_info( for &style in &crate_types { let fname = rustc_session::output::filename_for_input(sess, style, id, &t_outputs); - safe_println!("{}", fname.file_name().unwrap().to_string_lossy()); + safe_println!("{}", fname.as_path().file_name().unwrap().to_string_lossy()); } } Cfg => { diff --git a/compiler/rustc_driver_impl/src/pretty.rs b/compiler/rustc_driver_impl/src/pretty.rs index ee64b18d3f60c..24a5f4030b88d 100644 --- a/compiler/rustc_driver_impl/src/pretty.rs +++ b/compiler/rustc_driver_impl/src/pretty.rs @@ -9,7 +9,7 @@ use rustc_hir_pretty as pprust_hir; use rustc_middle::hir::map as hir_map; use rustc_middle::mir::{write_mir_graphviz, write_mir_pretty}; use rustc_middle::ty::{self, TyCtxt}; -use rustc_session::config::{PpAstTreeMode, PpHirMode, PpMode, PpSourceMode}; +use rustc_session::config::{OutFileName, PpAstTreeMode, PpHirMode, PpMode, PpSourceMode}; use rustc_session::Session; use rustc_span::symbol::Ident; use rustc_span::FileName; @@ -359,8 +359,8 @@ fn get_source(sess: &Session) -> (String, FileName) { fn write_or_print(out: &str, sess: &Session) { match &sess.io.output_file { - None => print!("{out}"), - Some(p) => { + None | Some(OutFileName::Stdout) => print!("{out}"), + Some(OutFileName::Real(p)) => { if let Err(e) = std::fs::write(p, out) { sess.emit_fatal(UnprettyDumpFail { path: p.display().to_string(), diff --git a/compiler/rustc_error_codes/src/error_codes/E0741.md b/compiler/rustc_error_codes/src/error_codes/E0741.md index 70d963cd41f21..0c7010526655e 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0741.md +++ b/compiler/rustc_error_codes/src/error_codes/E0741.md @@ -10,15 +10,19 @@ struct A; struct B; // error! ``` -Only structural-match types (that is, types that derive `PartialEq` and `Eq`) -may be used as the types of const generic parameters. +Only structural-match types, which are types that derive `PartialEq` and `Eq` +and implement `ConstParamTy`, may be used as the types of const generic +parameters. -To fix the previous code example, we derive `PartialEq` and `Eq`: +To fix the previous code example, we derive `PartialEq`, `Eq`, and +`ConstParamTy`: ``` #![feature(adt_const_params)] -#[derive(PartialEq, Eq)] // We derive both traits here. +use std::marker::ConstParamTy; + +#[derive(PartialEq, Eq, ConstParamTy)] // We derive both traits here. struct A; struct B; // ok! diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs index 7d9d0c76450d8..08ff2cfba5c26 100644 --- a/compiler/rustc_errors/src/diagnostic_builder.rs +++ b/compiler/rustc_errors/src/diagnostic_builder.rs @@ -115,36 +115,22 @@ pub trait EmissionGuarantee: Sized { ) -> DiagnosticBuilder<'_, Self>; } -/// Private module for sealing the `IsError` helper trait. -mod sealed_level_is_error { - use crate::Level; - - /// Sealed helper trait for statically checking that a `Level` is an error. - pub(crate) trait IsError {} - - impl IsError<{ Level::Bug }> for () {} - impl IsError<{ Level::DelayedBug }> for () {} - impl IsError<{ Level::Fatal }> for () {} - // NOTE(eddyb) `Level::Error { lint: true }` is also an error, but lints - // don't need error guarantees, as their levels are always dynamic. - impl IsError<{ Level::Error { lint: false } }> for () {} -} - impl<'a> DiagnosticBuilder<'a, ErrorGuaranteed> { /// Convenience function for internal use, clients should use one of the /// `struct_*` methods on [`Handler`]. #[track_caller] - pub(crate) fn new_guaranteeing_error, const L: Level>( + pub(crate) fn new_guaranteeing_error>( handler: &'a Handler, message: M, - ) -> Self - where - (): sealed_level_is_error::IsError, - { + ) -> Self { Self { inner: DiagnosticBuilderInner { state: DiagnosticBuilderState::Emittable(handler), - diagnostic: Box::new(Diagnostic::new_with_code(L, None, message)), + diagnostic: Box::new(Diagnostic::new_with_code( + Level::Error { lint: false }, + None, + message, + )), }, _marker: PhantomData, } @@ -203,9 +189,7 @@ impl EmissionGuarantee for ErrorGuaranteed { handler: &Handler, msg: impl Into, ) -> DiagnosticBuilder<'_, Self> { - DiagnosticBuilder::new_guaranteeing_error::<_, { Level::Error { lint: false } }>( - handler, msg, - ) + DiagnosticBuilder::new_guaranteeing_error(handler, msg) } } diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 6c5f3e62454ab..7a297ea0d5f82 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -6,7 +6,6 @@ #![feature(array_windows)] #![feature(drain_filter)] #![feature(if_let_guard)] -#![feature(adt_const_params)] #![feature(let_chains)] #![feature(never_type)] #![feature(result_option_inspect)] @@ -384,7 +383,7 @@ pub use diagnostic_builder::{DiagnosticBuilder, EmissionGuarantee, Noted}; pub use diagnostic_impls::{ DiagnosticArgFromDisplay, DiagnosticSymbolList, LabelKind, SingleLabelManySpans, }; -use std::backtrace::Backtrace; +use std::backtrace::{Backtrace, BacktraceStatus}; /// A handler deals with errors and other compiler output. /// Certain errors (fatal, bug, unimpl) may cause immediate exit, @@ -845,7 +844,7 @@ impl Handler { &self, msg: impl Into, ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { - DiagnosticBuilder::new_guaranteeing_error::<_, { Level::Error { lint: false } }>(self, msg) + DiagnosticBuilder::new_guaranteeing_error(self, msg) } /// This should only be used by `rustc_middle::lint::struct_lint_level`. Do not use it for hard errors. @@ -1332,7 +1331,7 @@ impl HandlerInner { // once *any* errors were emitted (and truncate `delayed_span_bugs` // when an error is first emitted, also), but maybe there's a case // in which that's not sound? otherwise this is really inefficient. - let backtrace = std::backtrace::Backtrace::force_capture(); + let backtrace = std::backtrace::Backtrace::capture(); self.delayed_span_bugs .push(DelayedDiagnostic::with_backtrace(diagnostic.clone(), backtrace)); @@ -1621,7 +1620,7 @@ impl HandlerInner { if self.flags.report_delayed_bugs { self.emit_diagnostic(&mut diagnostic); } - let backtrace = std::backtrace::Backtrace::force_capture(); + let backtrace = std::backtrace::Backtrace::capture(); self.delayed_good_path_bugs.push(DelayedDiagnostic::with_backtrace(diagnostic, backtrace)); } @@ -1740,7 +1739,17 @@ impl DelayedDiagnostic { } fn decorate(mut self) -> Diagnostic { - self.inner.note(format!("delayed at {}\n{}", self.inner.emitted_at, self.note)); + match self.note.status() { + BacktraceStatus::Captured => { + self.inner.note(format!("delayed at {}\n{}", self.inner.emitted_at, self.note)); + } + // Avoid the needless newline when no backtrace has been captured, + // the display impl should just be a single line. + _ => { + self.inner.note(format!("delayed at {} - {}", self.inner.emitted_at, self.note)); + } + } + self.inner } } diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 0d43b30474b06..8a251ea29d7fc 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -947,6 +947,8 @@ pub trait ResolverExpand { /// HIR proc macros items back to their harness items. fn declare_proc_macro(&mut self, id: NodeId); + fn append_stripped_cfg_item(&mut self, parent_node: NodeId, name: Ident, cfg: ast::MetaItem); + /// Tools registered with `#![register_tool]` and used by tool attributes and lints. fn registered_tools(&self) -> &RegisteredTools; } @@ -965,7 +967,7 @@ pub trait LintStoreExpand { type LintStoreExpandDyn<'a> = Option<&'a (dyn LintStoreExpand + 'a)>; -#[derive(Clone, Default)] +#[derive(Debug, Clone, Default)] pub struct ModuleData { /// Path to the module starting from the crate name, like `my_crate::foo::bar`. pub mod_path: Vec, @@ -1108,6 +1110,7 @@ impl<'a> ExtCtxt<'a> { } #[rustc_lint_diagnostics] + #[track_caller] pub fn struct_span_err>( &self, sp: S, @@ -1116,6 +1119,7 @@ impl<'a> ExtCtxt<'a> { self.sess.parse_sess.span_diagnostic.struct_span_err(sp, msg) } + #[track_caller] pub fn create_err( &self, err: impl IntoDiagnostic<'a>, @@ -1123,6 +1127,7 @@ impl<'a> ExtCtxt<'a> { self.sess.create_err(err) } + #[track_caller] pub fn emit_err(&self, err: impl IntoDiagnostic<'a>) -> ErrorGuaranteed { self.sess.emit_err(err) } @@ -1133,10 +1138,12 @@ impl<'a> ExtCtxt<'a> { /// Compilation will be stopped in the near future (at the end of /// the macro expansion phase). #[rustc_lint_diagnostics] + #[track_caller] pub fn span_err>(&self, sp: S, msg: impl Into) { self.sess.parse_sess.span_diagnostic.span_err(sp, msg); } #[rustc_lint_diagnostics] + #[track_caller] pub fn span_warn>(&self, sp: S, msg: impl Into) { self.sess.parse_sess.span_diagnostic.span_warn(sp, msg); } diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 4ff8e409d88e3..bcfa5313bde3a 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -197,9 +197,11 @@ pub fn pre_configure_attrs(sess: &Session, attrs: &[Attribute]) -> ast::AttrVec config_tokens: false, lint_node_id: ast::CRATE_NODE_ID, }; - let attrs: ast::AttrVec = - attrs.iter().flat_map(|attr| strip_unconfigured.process_cfg_attr(attr)).collect(); - if strip_unconfigured.in_cfg(&attrs) { attrs } else { ast::AttrVec::new() } + attrs + .iter() + .flat_map(|attr| strip_unconfigured.process_cfg_attr(attr)) + .take_while(|attr| !is_cfg(attr) || strip_unconfigured.cfg_true(attr).0) + .collect() } #[macro_export] @@ -416,20 +418,28 @@ impl<'a> StripUnconfigured<'a> { /// Determines if a node with the given attributes should be included in this configuration. fn in_cfg(&self, attrs: &[Attribute]) -> bool { - attrs.iter().all(|attr| !is_cfg(attr) || self.cfg_true(attr)) + attrs.iter().all(|attr| !is_cfg(attr) || self.cfg_true(attr).0) } - pub(crate) fn cfg_true(&self, attr: &Attribute) -> bool { + pub(crate) fn cfg_true(&self, attr: &Attribute) -> (bool, Option) { let meta_item = match validate_attr::parse_meta(&self.sess.parse_sess, attr) { Ok(meta_item) => meta_item, Err(mut err) => { err.emit(); - return true; + return (true, None); } }; - parse_cfg(&meta_item, &self.sess).map_or(true, |meta_item| { - attr::cfg_matches(&meta_item, &self.sess.parse_sess, self.lint_node_id, self.features) - }) + ( + parse_cfg(&meta_item, &self.sess).map_or(true, |meta_item| { + attr::cfg_matches( + &meta_item, + &self.sess.parse_sess, + self.lint_node_id, + self.features, + ) + }), + Some(meta_item), + ) } /// If attributes are not allowed on expressions, emit an error for `attr` diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index ce0093c7d4c0e..9850723a857e9 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -1039,9 +1039,20 @@ trait InvocationCollectorNode: HasAttrs + HasNodeId + Sized { ) -> Result { Ok(noop_flat_map(node, collector)) } - fn expand_cfg_false(&mut self, collector: &mut InvocationCollector<'_, '_>, span: Span) { + fn expand_cfg_false( + &mut self, + collector: &mut InvocationCollector<'_, '_>, + _pos: usize, + span: Span, + ) { collector.cx.emit_err(RemoveNodeNotSupported { span, descr: Self::descr() }); } + + /// All of the names (items) declared by this node. + /// This is an approximation and should only be used for diagnostics. + fn declared_names(&self) -> Vec { + vec![] + } } impl InvocationCollectorNode for P { @@ -1148,6 +1159,27 @@ impl InvocationCollectorNode for P { collector.cx.current_expansion.module = orig_module; res } + fn declared_names(&self) -> Vec { + if let ItemKind::Use(ut) = &self.kind { + fn collect_use_tree_leaves(ut: &ast::UseTree, idents: &mut Vec) { + match &ut.kind { + ast::UseTreeKind::Glob => {} + ast::UseTreeKind::Simple(_) => idents.push(ut.ident()), + ast::UseTreeKind::Nested(nested) => { + for (ut, _) in nested { + collect_use_tree_leaves(&ut, idents); + } + } + } + } + + let mut idents = Vec::new(); + collect_use_tree_leaves(&ut, &mut idents); + return idents; + } + + vec![self.ident] + } } struct TraitItemTag; @@ -1382,8 +1414,15 @@ impl InvocationCollectorNode for ast::Crate { fn noop_visit(&mut self, visitor: &mut V) { noop_visit_crate(self, visitor) } - fn expand_cfg_false(&mut self, collector: &mut InvocationCollector<'_, '_>, _span: Span) { - self.attrs.clear(); + fn expand_cfg_false( + &mut self, + collector: &mut InvocationCollector<'_, '_>, + pos: usize, + _span: Span, + ) { + // Attributes above `cfg(FALSE)` are left in place, because we may want to configure + // some global crate properties even on fully unconfigured crates. + self.attrs.truncate(pos); // Standard prelude imports are left in the crate for backward compatibility. self.items.truncate(collector.cx.num_standard_library_imports); } @@ -1685,8 +1724,8 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { node: &mut impl HasAttrs, attr: ast::Attribute, pos: usize, - ) -> bool { - let res = self.cfg().cfg_true(&attr); + ) -> (bool, Option) { + let (res, meta_item) = self.cfg().cfg_true(&attr); if res { // FIXME: `cfg(TRUE)` attributes do not currently remove themselves during expansion, // and some tools like rustdoc and clippy rely on that. Find a way to remove them @@ -1694,7 +1733,8 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { self.cx.expanded_inert_attrs.mark(&attr); node.visit_attrs(|attrs| attrs.insert(pos, attr)); } - res + + (res, meta_item) } fn expand_cfg_attr(&self, node: &mut impl HasAttrs, attr: &ast::Attribute, pos: usize) { @@ -1715,9 +1755,20 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { return match self.take_first_attr(&mut node) { Some((attr, pos, derives)) => match attr.name_or_empty() { sym::cfg => { - if self.expand_cfg_true(&mut node, attr, pos) { + let (res, meta_item) = self.expand_cfg_true(&mut node, attr, pos); + if res { continue; } + + if let Some(meta_item) = meta_item { + for name in node.declared_names() { + self.cx.resolver.append_stripped_cfg_item( + self.cx.current_expansion.lint_node_id, + name, + meta_item.clone(), + ) + } + } Default::default() } sym::cfg_attr => { @@ -1761,11 +1812,11 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { Some((attr, pos, derives)) => match attr.name_or_empty() { sym::cfg => { let span = attr.span; - if self.expand_cfg_true(node, attr, pos) { + if self.expand_cfg_true(node, attr, pos).0 { continue; } - node.expand_cfg_false(self, span); + node.expand_cfg_false(self, pos, span); continue; } sym::cfg_attr => { diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs index 1c222fb4a898c..f0e67cfd50e08 100644 --- a/compiler/rustc_expand/src/mbe/macro_parser.rs +++ b/compiler/rustc_expand/src/mbe/macro_parser.rs @@ -249,6 +249,7 @@ pub(super) fn compute_locs(matcher: &[TokenTree]) -> Vec { } /// A single matcher position, representing the state of matching. +#[derive(Debug)] struct MatcherPos { /// The index into `TtParser::locs`, which represents the "dot". idx: usize, diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index e4c65a2049bf6..ee9616a0f0a9d 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -628,6 +628,40 @@ fn check_lhs_nt_follows(sess: &ParseSess, def: &ast::Item, lhs: &mbe::TokenTree) // after parsing/expansion. we can report every error in every macro this way. } +fn is_empty_token_tree(sess: &ParseSess, seq: &mbe::SequenceRepetition) -> bool { + if seq.separator.is_some() { + false + } else { + let mut is_empty = true; + let mut iter = seq.tts.iter().peekable(); + while let Some(tt) = iter.next() { + match tt { + mbe::TokenTree::MetaVarDecl(_, _, Some(NonterminalKind::Vis)) => {} + mbe::TokenTree::Token(t @ Token { kind: DocComment(..), .. }) => { + let mut now = t; + while let Some(&mbe::TokenTree::Token( + next @ Token { kind: DocComment(..), .. }, + )) = iter.peek() + { + now = next; + iter.next(); + } + let span = t.span.to(now.span); + sess.span_diagnostic.span_note_without_error( + span, + "doc comments are ignored in matcher position", + ); + } + mbe::TokenTree::Sequence(_, sub_seq) + if (sub_seq.kleene.op == mbe::KleeneOp::ZeroOrMore + || sub_seq.kleene.op == mbe::KleeneOp::ZeroOrOne) => {} + _ => is_empty = false, + } + } + is_empty + } +} + /// Checks that the lhs contains no repetition which could match an empty token /// tree, because then the matcher would hang indefinitely. fn check_lhs_no_empty_seq(sess: &ParseSess, tts: &[mbe::TokenTree]) -> bool { @@ -644,16 +678,7 @@ fn check_lhs_no_empty_seq(sess: &ParseSess, tts: &[mbe::TokenTree]) -> bool { } } TokenTree::Sequence(span, seq) => { - if seq.separator.is_none() - && seq.tts.iter().all(|seq_tt| match seq_tt { - TokenTree::MetaVarDecl(_, _, Some(NonterminalKind::Vis)) => true, - TokenTree::Sequence(_, sub_seq) => { - sub_seq.kleene.op == mbe::KleeneOp::ZeroOrMore - || sub_seq.kleene.op == mbe::KleeneOp::ZeroOrOne - } - _ => false, - }) - { + if is_empty_token_tree(sess, seq) { let sp = span.entire(); sess.span_diagnostic.span_err(sp, "repetition matches empty token tree"); return false; diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index e844731091761..70fc66947df99 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1675,6 +1675,14 @@ pub struct AnonConst { pub body: BodyId, } +/// An inline constant expression `const { something }`. +#[derive(Copy, Clone, Debug, HashStable_Generic)] +pub struct ConstBlock { + pub hir_id: HirId, + pub def_id: LocalDefId, + pub body: BodyId, +} + /// An expression. #[derive(Debug, Clone, Copy, HashStable_Generic)] pub struct Expr<'hir> { @@ -1922,7 +1930,7 @@ pub fn is_range_literal(expr: &Expr<'_>) -> bool { #[derive(Debug, Clone, Copy, HashStable_Generic)] pub enum ExprKind<'hir> { /// Allow anonymous constants from an inline `const` block - ConstBlock(AnonConst), + ConstBlock(ConstBlock), /// An array (e.g., `[a, b, c, d]`). Array(&'hir [Expr<'hir>]), /// A function call. @@ -3641,6 +3649,7 @@ pub enum Node<'hir> { Variant(&'hir Variant<'hir>), Field(&'hir FieldDef<'hir>), AnonConst(&'hir AnonConst), + ConstBlock(&'hir ConstBlock), Expr(&'hir Expr<'hir>), ExprField(&'hir ExprField<'hir>), Stmt(&'hir Stmt<'hir>), @@ -3695,6 +3704,7 @@ impl<'hir> Node<'hir> { Node::TypeBinding(b) => Some(b.ident), Node::Param(..) | Node::AnonConst(..) + | Node::ConstBlock(..) | Node::Expr(..) | Node::Stmt(..) | Node::Block(..) @@ -3758,7 +3768,7 @@ impl<'hir> Node<'hir> { }) | Node::Expr(Expr { kind: - ExprKind::ConstBlock(AnonConst { body, .. }) + ExprKind::ConstBlock(ConstBlock { body, .. }) | ExprKind::Closure(Closure { body, .. }) | ExprKind::Repeat(_, ArrayLen::Body(AnonConst { body, .. })), .. @@ -3878,6 +3888,13 @@ impl<'hir> Node<'hir> { this } + /// Expect a [`Node::ConstBlock`] or panic. + #[track_caller] + pub fn expect_inline_const(self) -> &'hir ConstBlock { + let Node::ConstBlock(this) = self else { self.expect_failed("an inline constant") }; + this + } + /// Expect a [`Node::Expr`] or panic. #[track_caller] pub fn expect_expr(self) -> &'hir Expr<'hir> { diff --git a/compiler/rustc_hir/src/hir_id.rs b/compiler/rustc_hir/src/hir_id.rs index d549f52f873a9..34c6157793663 100644 --- a/compiler/rustc_hir/src/hir_id.rs +++ b/compiler/rustc_hir/src/hir_id.rs @@ -166,7 +166,9 @@ impl ItemLocalId { // Safety: Ord is implement as just comparing the ItemLocalId's numerical // values and these are not changed by (de-)serialization. -unsafe impl StableOrd for ItemLocalId {} +unsafe impl StableOrd for ItemLocalId { + const CAN_USE_UNSTABLE_SORT: bool = true; +} /// The `HirId` corresponding to `CRATE_NODE_ID` and `CRATE_DEF_ID`. pub const CRATE_HIR_ID: HirId = diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index df0047d82e12e..f84c814bd9278 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -335,6 +335,9 @@ pub trait Visitor<'v>: Sized { fn visit_anon_const(&mut self, c: &'v AnonConst) { walk_anon_const(self, c) } + fn visit_inline_const(&mut self, c: &'v ConstBlock) { + walk_inline_const(self, c) + } fn visit_expr(&mut self, ex: &'v Expr<'v>) { walk_expr(self, ex) } @@ -679,13 +682,18 @@ pub fn walk_anon_const<'v, V: Visitor<'v>>(visitor: &mut V, constant: &'v AnonCo visitor.visit_nested_body(constant.body); } +pub fn walk_inline_const<'v, V: Visitor<'v>>(visitor: &mut V, constant: &'v ConstBlock) { + visitor.visit_id(constant.hir_id); + visitor.visit_nested_body(constant.body); +} + pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) { visitor.visit_id(expression.hir_id); match expression.kind { ExprKind::Array(subexpressions) => { walk_list!(visitor, visit_expr, subexpressions); } - ExprKind::ConstBlock(ref anon_const) => visitor.visit_anon_const(anon_const), + ExprKind::ConstBlock(ref const_block) => visitor.visit_inline_const(const_block), ExprKind::Repeat(ref element, ref count) => { visitor.visit_expr(element); visitor.visit_array_length(count) diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 02d1dfcd1134c..cd6cf36baa44d 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -195,6 +195,13 @@ hir_analysis_return_type_notation_conflicting_bound = hir_analysis_return_type_notation_equality_bound = return type notation is not allowed to use type equality +hir_analysis_return_type_notation_illegal_param_const = + return type notation is not allowed for functions that have const parameters + .label = const parameter declared here +hir_analysis_return_type_notation_illegal_param_type = + return type notation is not allowed for functions that have type parameters + .label = type parameter declared here + hir_analysis_return_type_notation_missing_method = cannot find associated function `{$assoc_name}` for `{$ty_name}` diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 3d78ea9aa9ba1..a9e8afe434ed3 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -26,10 +26,8 @@ use rustc_hir::def::{CtorOf, DefKind, Namespace, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{walk_generics, Visitor as _}; use rustc_hir::{GenericArg, GenericArgs, OpaqueTyOrigin}; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::ObligationCause; -use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; use rustc_middle::middle::stability::AllowUnstable; use rustc_middle::ty::fold::FnMutDelegate; use rustc_middle::ty::subst::{self, GenericArgKind, InternalSubsts, SubstsRef}; @@ -1215,6 +1213,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } let projection_ty = if return_type_notation { + let mut emitted_bad_param_err = false; // If we have an method return type bound, then we need to substitute // the method's early bound params with suitable late-bound params. let mut num_bound_vars = candidate.bound_vars().len(); @@ -1230,16 +1229,35 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { }, ) .into(), - GenericParamDefKind::Type { .. } => tcx - .mk_bound( + GenericParamDefKind::Type { .. } => { + if !emitted_bad_param_err { + tcx.sess.emit_err( + crate::errors::ReturnTypeNotationIllegalParam::Type { + span: path_span, + param_span: tcx.def_span(param.def_id), + }, + ); + emitted_bad_param_err = true; + } + tcx.mk_bound( ty::INNERMOST, ty::BoundTy { var: ty::BoundVar::from_usize(num_bound_vars), kind: ty::BoundTyKind::Param(param.def_id, param.name), }, ) - .into(), + .into() + } GenericParamDefKind::Const { .. } => { + if !emitted_bad_param_err { + tcx.sess.emit_err( + crate::errors::ReturnTypeNotationIllegalParam::Const { + span: path_span, + param_span: tcx.def_span(param.def_id), + }, + ); + emitted_bad_param_err = true; + } let ty = tcx .type_of(param.def_id) .no_bound_vars() @@ -1601,7 +1619,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { tcx.associated_items(pred.def_id()) .in_definition_order() .filter(|item| item.kind == ty::AssocKind::Type) - .filter(|item| tcx.opt_rpitit_info(item.def_id).is_none()) + .filter(|item| item.opt_rpitit_info.is_none()) .map(|item| item.def_id), ); } @@ -1643,6 +1661,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } } + // `dyn Trait` desugars to (not Rust syntax) `dyn Trait where ::Assoc = Foo`. + // So every `Projection` clause is an `Assoc = Foo` bound. `associated_types` contains all associated + // types's `DefId`, so the following loop removes all the `DefIds` of the associated types that have a + // corresponding `Projection` clause for (projection_bound, _) in &projection_bounds { for def_ids in associated_types.values_mut() { def_ids.remove(&projection_bound.projection_def_id()); @@ -2468,7 +2490,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { infcx.probe(|_| { let ocx = ObligationCtxt::new_in_snapshot(&infcx); - let impl_substs = infcx.fresh_item_substs(impl_); + let impl_substs = infcx.fresh_substs_for_item(span, impl_); let impl_ty = tcx.type_of(impl_).subst(tcx, impl_substs); let impl_ty = ocx.normalize(&cause, param_env, impl_ty); @@ -3755,36 +3777,3 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } } } - -pub trait InferCtxtExt<'tcx> { - fn fresh_item_substs(&self, def_id: DefId) -> SubstsRef<'tcx>; -} - -impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { - fn fresh_item_substs(&self, def_id: DefId) -> SubstsRef<'tcx> { - InternalSubsts::for_item(self.tcx, def_id, |param, _| match param.kind { - GenericParamDefKind::Lifetime => self.tcx.lifetimes.re_erased.into(), - GenericParamDefKind::Type { .. } => self - .next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::SubstitutionPlaceholder, - span: self.tcx.def_span(def_id), - }) - .into(), - GenericParamDefKind::Const { .. } => { - let span = self.tcx.def_span(def_id); - let origin = ConstVariableOrigin { - kind: ConstVariableOriginKind::SubstitutionPlaceholder, - span, - }; - self.next_const_var( - self.tcx - .type_of(param.def_id) - .no_bound_vars() - .expect("const parameter types cannot be generic"), - origin, - ) - .into() - } - }) - } -} diff --git a/compiler/rustc_hir_analysis/src/autoderef.rs b/compiler/rustc_hir_analysis/src/autoderef.rs index d6d1498d708ed..6eb18aecd653f 100644 --- a/compiler/rustc_hir_analysis/src/autoderef.rs +++ b/compiler/rustc_hir_analysis/src/autoderef.rs @@ -73,7 +73,7 @@ impl<'a, 'tcx> Iterator for Autoderef<'a, 'tcx> { // NOTE: we may still need to normalize the built-in deref in case // we have some type like `&::Assoc`, since users of // autoderef expect this type to have been structurally normalized. - if self.infcx.tcx.trait_solver_next() + if self.infcx.next_trait_solver() && let ty::Alias(ty::Projection, _) = ty.kind() { let (normalized_ty, obligations) = self.structurally_normalize(ty)?; @@ -161,8 +161,7 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> { &self, ty: Ty<'tcx>, ) -> Option<(Ty<'tcx>, Vec>)> { - let tcx = self.infcx.tcx; - let mut fulfill_cx = >::new_in_snapshot(tcx); + let mut fulfill_cx = >::new_in_snapshot(self.infcx); let cause = traits::ObligationCause::misc(self.span, self.body_id); let normalized_ty = match self diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 89f96695a2f69..17be5fe66cf62 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -704,7 +704,7 @@ pub(super) fn check_specialization_validity<'tcx>( // grandparent. In that case, if parent is a `default impl`, inherited items use the // "defaultness" from the grandparent, else they are final. None => { - if tcx.impl_defaultness(parent_impl.def_id()).is_default() { + if tcx.defaultness(parent_impl.def_id()).is_default() { None } else { Some(Err(parent_impl.def_id())) @@ -803,7 +803,7 @@ fn check_impl_items_against_trait<'tcx>( .as_ref() .is_some_and(|node_item| node_item.item.defaultness(tcx).has_value()); - if !is_implemented && tcx.impl_defaultness(impl_id).is_final() { + if !is_implemented && tcx.defaultness(impl_id).is_final() { missing_items.push(tcx.associated_item(trait_item_id)); } @@ -1549,7 +1549,7 @@ pub(super) fn check_generator_obligations(tcx: TyCtxt<'_>, def_id: LocalDefId) { .with_opaque_type_inference(DefiningAnchor::Bind(def_id)) .build(); - let mut fulfillment_cx = >::new(infcx.tcx); + let mut fulfillment_cx = >::new(&infcx); for (predicate, cause) in generator_interior_predicates { let obligation = Obligation::new(tcx, cause.clone(), param_env, *predicate); fulfillment_cx.register_predicate_obligation(&infcx, obligation); diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index 31b89525f15d4..dce31975dbc5d 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -302,7 +302,7 @@ fn compare_method_predicate_entailment<'tcx>( return Err(emitted); } - if check_implied_wf == CheckImpliedWfMode::Check { + if check_implied_wf == CheckImpliedWfMode::Check && !(impl_sig, trait_sig).references_error() { // We need to check that the impl's args are well-formed given // the hybrid param-env (impl + trait method where-clauses). ocx.register_obligation(traits::Obligation::new( @@ -1216,7 +1216,7 @@ fn compare_number_of_generics<'tcx>( // has mismatched type or const generic arguments, then the method that it's // inheriting the generics from will also have mismatched arguments, and // we'll report an error for that instead. Delay a bug for safety, though. - if tcx.opt_rpitit_info(trait_.def_id).is_some() { + if trait_.opt_rpitit_info.is_some() { return Err(tcx.sess.delay_span_bug( rustc_span::DUMMY_SP, "errors comparing numbers of generics of trait/impl functions were not emitted", @@ -2006,7 +2006,7 @@ pub(super) fn check_type_bounds<'tcx>( // A synthetic impl Trait for RPITIT desugaring has no HIR, which we currently use to get the // span for an impl's associated type. Instead, for these, use the def_span for the synthesized // associated type. - let impl_ty_span = if tcx.opt_rpitit_info(impl_ty.def_id).is_some() { + let impl_ty_span = if impl_ty.opt_rpitit_info.is_some() { tcx.def_span(impl_ty_def_id) } else { match tcx.hir().get_by_def_id(impl_ty_def_id) { diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 1f18017f00b9d..36c468e778986 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -217,7 +217,7 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { sym::needs_drop => (1, Vec::new(), tcx.types.bool), sym::type_name => (1, Vec::new(), tcx.mk_static_str()), - sym::type_id => (1, Vec::new(), tcx.types.u64), + sym::type_id => (1, Vec::new(), tcx.types.u128), sym::offset => (2, vec![param(0), param(1)], param(0)), sym::arith_offset => ( 1, diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 3971a4c01d661..c9e74896ac08e 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -188,7 +188,7 @@ fn missing_items_err( full_impl_span: Span, ) { let missing_items = - missing_items.iter().filter(|trait_item| tcx.opt_rpitit_info(trait_item.def_id).is_none()); + missing_items.iter().filter(|trait_item| trait_item.opt_rpitit_info.is_none()); let missing_items_msg = missing_items .clone() diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index 6ab5556e951d9..5bd6fcb9612dd 100644 --- a/compiler/rustc_hir_analysis/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -392,7 +392,7 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h // Manually recurse over closures and inline consts, because they are the only // case of nested bodies that share the parent environment. hir::ExprKind::Closure(&hir::Closure { body, .. }) - | hir::ExprKind::ConstBlock(hir::AnonConst { body, .. }) => { + | hir::ExprKind::ConstBlock(hir::ConstBlock { body, .. }) => { let body = visitor.tcx.hir().body(body); visitor.visit_body(body); } diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index fff417fcb2909..a269a7a1d8b1e 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -829,83 +829,20 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) { let ty = tcx.type_of(param.def_id).subst_identity(); if tcx.features().adt_const_params { - if let Some(non_structural_match_ty) = - traits::search_for_adt_const_param_violation(param.span, tcx, ty) - { - // We use the same error code in both branches, because this is really the same - // issue: we just special-case the message for type parameters to make it - // clearer. - match non_structural_match_ty.kind() { - ty::Param(_) => { - // Const parameters may not have type parameters as their types, - // because we cannot be sure that the type parameter derives `PartialEq` - // and `Eq` (just implementing them is not enough for `structural_match`). - struct_span_err!( - tcx.sess, - hir_ty.span, - E0741, - "`{ty}` is not guaranteed to `#[derive(PartialEq, Eq)]`, so may not be \ - used as the type of a const parameter", - ) - .span_label( - hir_ty.span, - format!("`{ty}` may not derive both `PartialEq` and `Eq`"), - ) - .note( - "it is not currently possible to use a type parameter as the type of a \ - const parameter", - ) - .emit(); - } - ty::Float(_) => { - struct_span_err!( - tcx.sess, - hir_ty.span, - E0741, - "`{ty}` is forbidden as the type of a const generic parameter", - ) - .note("floats do not derive `Eq` or `Ord`, which are required for const parameters") - .emit(); - } - ty::FnPtr(_) => { - struct_span_err!( - tcx.sess, - hir_ty.span, - E0741, - "using function pointers as const generic parameters is forbidden", - ) - .emit(); - } - ty::RawPtr(_) => { - struct_span_err!( - tcx.sess, - hir_ty.span, - E0741, - "using raw pointers as const generic parameters is forbidden", - ) - .emit(); - } - _ => { - let mut diag = struct_span_err!( - tcx.sess, - hir_ty.span, - E0741, - "`{}` must be annotated with `#[derive(PartialEq, Eq)]` to be used as \ - the type of a const parameter", - non_structural_match_ty, - ); - - if ty == non_structural_match_ty { - diag.span_label( - hir_ty.span, - format!("`{ty}` doesn't derive both `PartialEq` and `Eq`"), - ); - } - - diag.emit(); - } - } - } + enter_wf_checking_ctxt(tcx, hir_ty.span, param.def_id, |wfcx| { + let trait_def_id = + tcx.require_lang_item(LangItem::ConstParamTy, Some(hir_ty.span)); + wfcx.register_bound( + ObligationCause::new( + hir_ty.span, + param.def_id, + ObligationCauseCode::ConstParam(ty), + ), + wfcx.param_env, + ty, + trait_def_id, + ); + }); } else { let err_ty_str; let mut is_ptr = true; @@ -1808,9 +1745,11 @@ fn check_variances_for_type_defn<'tcx>( item: &hir::Item<'tcx>, hir_generics: &hir::Generics<'_>, ) { - let ty = tcx.type_of(item.owner_id).subst_identity(); - if tcx.has_error_field(ty) { - return; + let identity_substs = ty::InternalSubsts::identity_for_item(tcx, item.owner_id); + for field in tcx.adt_def(item.owner_id).all_fields() { + if field.ty(tcx, identity_substs).references_error() { + return; + } } let ty_predicates = tcx.predicates_of(item.owner_id); diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 2f7d465839c4c..c7b9fc9a697f4 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -941,7 +941,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef { match item { Some(item) if matches!(item.kind, hir::AssocItemKind::Fn { .. }) => { - if !tcx.impl_defaultness(item.id.owner_id).has_value() { + if !tcx.defaultness(item.id.owner_id).has_value() { tcx.sess.emit_err(errors::FunctionNotHaveDefaultImplementation { span: item.span, note_span: attr_span, diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index ed60998ec8dcf..e92a0dcc1fb1f 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -50,7 +50,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { // We do not allow generic parameters in anon consts if we are inside // of a const parameter type, e.g. `struct Foo` is not allowed. None - } else if tcx.lazy_normalization() { + } else if tcx.features().generic_const_exprs { let parent_node = tcx.hir().get_parent(hir_id); if let Node::Variant(Variant { disr_expr: Some(constant), .. }) = parent_node && constant.hir_id == hir_id @@ -123,9 +123,6 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { { Some(parent_def_id.to_def_id()) } - Node::Expr(&Expr { kind: ExprKind::ConstBlock(_), .. }) => { - Some(tcx.typeck_root_def_id(def_id.to_def_id())) - } // Exclude `GlobalAsm` here which cannot have generics. Node::Expr(&Expr { kind: ExprKind::InlineAsm(asm), .. }) if asm.operands.iter().any(|(op, _op_sp)| match op { @@ -142,7 +139,8 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { } } } - Node::Expr(&hir::Expr { kind: hir::ExprKind::Closure { .. }, .. }) => { + Node::ConstBlock(_) + | Node::Expr(&hir::Expr { kind: hir::ExprKind::Closure { .. }, .. }) => { Some(tcx.typeck_root_def_id(def_id.to_def_id())) } Node::Item(item) => match item.kind { @@ -339,17 +337,14 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { } // provide junk type parameter defs for const blocks. - if let Node::AnonConst(_) = node { - let parent_node = tcx.hir().get_parent(hir_id); - if let Node::Expr(&Expr { kind: ExprKind::ConstBlock(_), .. }) = parent_node { - params.push(ty::GenericParamDef { - index: next_index(), - name: Symbol::intern(""), - def_id: def_id.to_def_id(), - pure_wrt_drop: false, - kind: ty::GenericParamDefKind::Type { has_default: false, synthetic: false }, - }); - } + if let Node::ConstBlock(_) = node { + params.push(ty::GenericParamDef { + index: next_index(), + name: Symbol::intern(""), + def_id: def_id.to_def_id(), + pure_wrt_drop: false, + kind: ty::GenericParamDefKind::Type { has_default: false, synthetic: false }, + }); } let param_def_id_to_index = params.iter().map(|param| (param.def_id, param.index)).collect(); diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index 70d950eddd8a7..dcb5790292879 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -463,7 +463,7 @@ pub(super) fn explicit_predicates_of<'tcx>( } } } else { - if matches!(def_kind, DefKind::AnonConst) && tcx.lazy_normalization() { + if matches!(def_kind, DefKind::AnonConst) && tcx.features().generic_const_exprs { let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); let parent_def_id = tcx.hir().get_parent_item(hir_id); diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 65ab00fda817a..c2b837fcfa670 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -34,12 +34,6 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> { Node::Ty(&Ty { kind: TyKind::Typeof(ref e), .. }) if e.hir_id == hir_id => { return tcx.typeck(def_id).node_type(e.hir_id) } - Node::Expr(&Expr { kind: ExprKind::ConstBlock(ref anon_const), .. }) - if anon_const.hir_id == hir_id => - { - let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id()); - return substs.as_inline_const().ty() - } Node::Expr(&Expr { kind: ExprKind::InlineAsm(asm), .. }) | Node::Item(&Item { kind: ItemKind::GlobalAsm(asm), .. }) if asm.operands.iter().any(|(op, _op_sp)| match op { @@ -435,7 +429,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder { - if in_trait && !tcx.impl_defaultness(owner).has_value() { + if in_trait && !tcx.defaultness(owner).has_value() { span_bug!( tcx.def_span(def_id), "tried to get type of this RPITIT with no definition" @@ -487,6 +481,11 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder anon_const_type_of(tcx, def_id), + Node::ConstBlock(_) => { + let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id()); + substs.as_inline_const().ty() + } + Node::GenericParam(param) => match ¶m.kind { GenericParamKind::Type { default: Some(ty), .. } | GenericParamKind::Const { ty, .. } => icx.to_ty(ty), diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 6e7eb4f6cdcd8..7dce1272f96df 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -857,3 +857,21 @@ pub(crate) enum DropImplPolarity { span: Span, }, } + +#[derive(Diagnostic)] +pub(crate) enum ReturnTypeNotationIllegalParam { + #[diag(hir_analysis_return_type_notation_illegal_param_type)] + Type { + #[primary_span] + span: Span, + #[label] + param_span: Span, + }, + #[diag(hir_analysis_return_type_notation_illegal_param_const)] + Const { + #[primary_span] + span: Span, + #[label] + param_span: Span, + }, +} diff --git a/compiler/rustc_hir_analysis/src/outlives/mod.rs b/compiler/rustc_hir_analysis/src/outlives/mod.rs index 2106d6ff07dcc..fdbb890ce3d47 100644 --- a/compiler/rustc_hir_analysis/src/outlives/mod.rs +++ b/compiler/rustc_hir_analysis/src/outlives/mod.rs @@ -20,7 +20,8 @@ pub fn provide(providers: &mut Providers) { fn inferred_outlives_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[(ty::Clause<'_>, Span)] { let id = tcx.hir().local_def_id_to_hir_id(item_def_id); - if matches!(tcx.def_kind(item_def_id), hir::def::DefKind::AnonConst) && tcx.lazy_normalization() + if matches!(tcx.def_kind(item_def_id), hir::def::DefKind::AnonConst) + && tcx.features().generic_const_exprs { if tcx.hir().opt_const_param_default_param_def_id(id).is_some() { // In `generics_of` we set the generics' parent to be our parent's parent which means that diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index d93e8efc1b59a..ced46fe426c47 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -84,6 +84,7 @@ impl<'a> State<'a> { Node::ImplItem(a) => self.print_impl_item(a), Node::Variant(a) => self.print_variant(a), Node::AnonConst(a) => self.print_anon_const(a), + Node::ConstBlock(a) => self.print_inline_const(a), Node::Expr(a) => self.print_expr(a), Node::ExprField(a) => self.print_expr_field(&a), Node::Stmt(a) => self.print_stmt(a), @@ -1095,10 +1096,10 @@ impl<'a> State<'a> { self.end() } - fn print_expr_anon_const(&mut self, anon_const: &hir::AnonConst) { + fn print_inline_const(&mut self, constant: &hir::ConstBlock) { self.ibox(INDENT_UNIT); self.word_space("const"); - self.print_anon_const(anon_const); + self.ann.nested(self, Nested::Body(constant.body)); self.end() } @@ -1370,7 +1371,7 @@ impl<'a> State<'a> { self.print_expr_vec(exprs); } hir::ExprKind::ConstBlock(ref anon_const) => { - self.print_expr_anon_const(anon_const); + self.print_inline_const(anon_const); } hir::ExprKind::Repeat(element, ref count) => { self.print_expr_repeat(element, count); diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl index aab432eee571b..4728edd837a6c 100644 --- a/compiler/rustc_hir_typeck/messages.ftl +++ b/compiler/rustc_hir_typeck/messages.ftl @@ -25,6 +25,8 @@ hir_typeck_const_select_must_be_fn = this argument must be a function item hir_typeck_convert_to_str = try converting the passed type into a `&str` +hir_typeck_convert_using_method = try using `{$sugg}` to convert `{$found}` to `{$expected}` + hir_typeck_ctor_is_private = tuple struct constructor `{$def}` is private hir_typeck_expected_default_return_type = expected `()` because of default return type diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 98c683f0200a3..7a1e830073a72 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -689,8 +689,6 @@ impl<'a, 'tcx> CastCheck<'tcx> { fn trivial_cast_lint(&self, fcx: &FnCtxt<'a, 'tcx>) { let t_cast = self.cast_ty; let t_expr = self.expr_ty; - let type_asc_or = - if fcx.tcx.features().type_ascription { "type ascription or " } else { "" }; let (adjective, lint) = if t_cast.is_numeric() && t_expr.is_numeric() { ("numeric ", lint::builtin::TRIVIAL_NUMERIC_CASTS) } else { @@ -711,7 +709,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { |lint| { lint.help(format!( "cast can be replaced by coercion; this might \ - require {type_asc_or}a temporary variable" + require a temporary variable" )) }, ); diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs index bfabd44bb5792..69ccbf0b58ff3 100644 --- a/compiler/rustc_hir_typeck/src/check.rs +++ b/compiler/rustc_hir_typeck/src/check.rs @@ -96,7 +96,19 @@ pub(super) fn check_fn<'a, 'tcx>( // for simple cases like `fn foo(x: Trait)`, // where we would error once on the parameter as a whole, and once on the binding `x`. if param.pat.simple_ident().is_none() && !params_can_be_unsized { - fcx.require_type_is_sized(param_ty, param.pat.span, traits::SizedArgumentType(ty_span)); + fcx.require_type_is_sized( + param_ty, + param.pat.span, + // ty_span == binding_span iff this is a closure parameter with no type ascription, + // or if it's an implicit `self` parameter + traits::SizedArgumentType( + if ty_span == Some(param.span) && tcx.is_closure(fn_def_id.into()) { + None + } else { + ty_span + }, + ), + ); } fcx.write_ty(param.hir_id, param_ty); diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 905781ec8f594..81231e8fe068b 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -156,7 +156,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // In the new solver, lazy norm may allow us to shallowly equate // more types, but we emit possibly impossible-to-satisfy obligations. // Filter these cases out to make sure our coercion is more accurate. - if self.tcx.trait_solver_next() { + if self.next_trait_solver() { if let Ok(res) = &res { for obligation in &res.obligations { if !self.predicate_may_hold(&obligation) { @@ -1595,7 +1595,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { Some(blk_id), ); if !fcx.tcx.features().unsized_locals { - unsized_return = self.is_return_ty_unsized(fcx, blk_id); + unsized_return = self.is_return_ty_definitely_unsized(fcx); } if let Some(expression) = expression && let hir::ExprKind::Loop(loop_blk, ..) = expression.kind { @@ -1614,8 +1614,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { None, ); if !fcx.tcx.features().unsized_locals { - let id = fcx.tcx.hir().parent_id(id); - unsized_return = self.is_return_ty_unsized(fcx, id); + unsized_return = self.is_return_ty_definitely_unsized(fcx); } } _ => { @@ -1896,15 +1895,24 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { err.help("you could instead create a new `enum` with a variant for each returned type"); } - fn is_return_ty_unsized<'a>(&self, fcx: &FnCtxt<'a, 'tcx>, blk_id: hir::HirId) -> bool { - if let Some((_, fn_decl, _)) = fcx.get_fn_decl(blk_id) - && let hir::FnRetTy::Return(ty) = fn_decl.output - && let ty = fcx.astconv().ast_ty_to_ty( ty) - && let ty::Dynamic(..) = ty.kind() - { - return true; + /// Checks whether the return type is unsized via an obligation, which makes + /// sure we consider `dyn Trait: Sized` where clauses, which are trivially + /// false but technically valid for typeck. + fn is_return_ty_definitely_unsized(&self, fcx: &FnCtxt<'_, 'tcx>) -> bool { + if let Some(sig) = fcx.body_fn_sig() { + !fcx.predicate_may_hold(&Obligation::new( + fcx.tcx, + ObligationCause::dummy(), + fcx.param_env, + ty::TraitRef::new( + fcx.tcx, + fcx.tcx.require_lang_item(hir::LangItem::Sized, None), + [sig.output()], + ), + )) + } else { + false } - false } pub fn complete<'a>(self, fcx: &FnCtxt<'a, 'tcx>) -> Ty<'tcx> { diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index b50630e636b2e..b62f689ec6b89 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -3,7 +3,7 @@ use rustc_ast::util::parser::PREC_POSTFIX; use rustc_errors::MultiSpan; use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed}; use rustc_hir as hir; -use rustc_hir::def::CtorKind; +use rustc_hir::def::{CtorKind, Res}; use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; use rustc_hir::{is_range_literal, Node}; @@ -91,6 +91,56 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.note_wrong_return_ty_due_to_generic_arg(err, expr, expr_ty); } + /// Really hacky heuristic to remap an `assert_eq!` error to the user + /// expressions provided to the macro. + fn adjust_expr_for_assert_eq_macro( + &self, + found_expr: &mut &'tcx hir::Expr<'tcx>, + expected_expr: &mut Option<&'tcx hir::Expr<'tcx>>, + ) { + let Some(expected_expr) = expected_expr else { return; }; + + if !found_expr.span.eq_ctxt(expected_expr.span) { + return; + } + + if !found_expr + .span + .ctxt() + .outer_expn_data() + .macro_def_id + .is_some_and(|def_id| self.tcx.is_diagnostic_item(sym::assert_eq_macro, def_id)) + { + return; + } + + let hir::ExprKind::Unary( + hir::UnOp::Deref, + hir::Expr { kind: hir::ExprKind::Path(found_path), .. }, + ) = found_expr.kind else { return; }; + let hir::ExprKind::Unary( + hir::UnOp::Deref, + hir::Expr { kind: hir::ExprKind::Path(expected_path), .. }, + ) = expected_expr.kind else { return; }; + + for (path, name, idx, var) in [ + (expected_path, "left_val", 0, expected_expr), + (found_path, "right_val", 1, found_expr), + ] { + if let hir::QPath::Resolved(_, path) = path + && let [segment] = path.segments + && segment.ident.name.as_str() == name + && let Res::Local(hir_id) = path.res + && let Some((_, hir::Node::Expr(match_expr))) = self.tcx.hir().parent_iter(hir_id).nth(2) + && let hir::ExprKind::Match(scrutinee, _, _) = match_expr.kind + && let hir::ExprKind::Tup(exprs) = scrutinee.kind + && let hir::ExprKind::AddrOf(_, _, macro_arg) = exprs[idx].kind + { + *var = macro_arg; + } + } + } + /// Requires that the two types unify, and prints an error message if /// they don't. pub fn demand_suptype(&self, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>) { @@ -156,7 +206,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn demand_coerce( &self, - expr: &hir::Expr<'tcx>, + expr: &'tcx hir::Expr<'tcx>, checked_ty: Ty<'tcx>, expected: Ty<'tcx>, expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, @@ -177,10 +227,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { #[instrument(level = "debug", skip(self, expr, expected_ty_expr, allow_two_phase))] pub fn demand_coerce_diag( &self, - expr: &hir::Expr<'tcx>, + mut expr: &'tcx hir::Expr<'tcx>, checked_ty: Ty<'tcx>, expected: Ty<'tcx>, - expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, + mut expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, allow_two_phase: AllowTwoPhase, ) -> (Ty<'tcx>, Option>) { let expected = self.resolve_vars_with_obligations(expected); @@ -190,6 +240,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Err(e) => e, }; + self.adjust_expr_for_assert_eq_macro(&mut expr, &mut expected_ty_expr); + self.set_tainted_by_errors(self.tcx.sess.delay_span_bug( expr.span, "`TypeError` when attempting coercion but no error emitted", diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 4222205c84106..5161a366ae7d3 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -327,3 +327,19 @@ pub struct CtorIsPrivate { pub span: Span, pub def: String, } + +#[derive(Subdiagnostic)] +#[multipart_suggestion( + hir_typeck_convert_using_method, + applicability = "machine-applicable", + style = "verbose" +)] +pub struct SuggestConvertViaMethod<'tcx> { + #[suggestion_part(code = "{sugg}")] + pub span: Span, + #[suggestion_part(code = "")] + pub borrow_removal_span: Option, + pub sugg: &'static str, + pub expected: Ty<'tcx>, + pub found: Ty<'tcx>, +} diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 19ff77d8349aa..f03c7ca44ba5f 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -348,9 +348,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } ExprKind::DropTemps(e) => self.check_expr_with_expectation(e, expected), ExprKind::Array(args) => self.check_expr_array(args, expected, expr), - ExprKind::ConstBlock(ref anon_const) => { - self.check_expr_const_block(anon_const, expected, expr) - } + ExprKind::ConstBlock(ref block) => self.check_expr_const_block(block, expected, expr), ExprKind::Repeat(element, ref count) => { self.check_expr_repeat(element, count, expected, expr) } @@ -1368,20 +1366,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn check_expr_const_block( &self, - anon_const: &'tcx hir::AnonConst, + block: &'tcx hir::ConstBlock, expected: Expectation<'tcx>, _expr: &'tcx hir::Expr<'tcx>, ) -> Ty<'tcx> { - let body = self.tcx.hir().body(anon_const.body); + let body = self.tcx.hir().body(block.body); // Create a new function context. - let def_id = anon_const.def_id; + let def_id = block.def_id; let fcx = FnCtxt::new(self, self.param_env.with_const(), def_id); crate::GatherLocalsVisitor::new(&fcx).visit_body(body); let ty = fcx.check_expr_with_expectation(&body.value, expected); fcx.require_type_is_sized(ty, body.value.span, traits::ConstSized); - fcx.write_ty(anon_const.hir_id, ty); + fcx.write_ty(block.hir_id, ty); ty } @@ -2083,13 +2081,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }, _ => { // prevent all specified fields from being suggested - let skip_fields = skip_fields.iter().map(|x| x.ident.name); - if let Some(field_name) = self.suggest_field_name( - variant, - field.ident.name, - skip_fields.collect(), - expr_span, - ) { + let skip_fields: Vec<_> = skip_fields.iter().map(|x| x.ident.name).collect(); + if let Some(field_name) = + self.suggest_field_name(variant, field.ident.name, &skip_fields, expr_span) + { err.span_suggestion( field.ident.span, "a field with a similar name exists", @@ -2110,9 +2105,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { format!("`{ty}` does not have this field"), ); } - let available_field_names = + let mut available_field_names = self.available_field_names(variant, expr_span); - if !available_field_names.is_empty() { + available_field_names + .retain(|name| skip_fields.iter().all(|skip| name != skip)); + if available_field_names.is_empty() { + err.note("all struct fields are already assigned"); + } else { err.note(format!( "available fields are: {}", self.name_series_display(available_field_names) @@ -2132,7 +2131,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, variant: &'tcx ty::VariantDef, field: Symbol, - skip: Vec, + skip: &[Symbol], // The span where stability will be checked span: Span, ) -> Option { @@ -2584,7 +2583,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { access_span: Span, ) { if let Some(suggested_field_name) = - self.suggest_field_name(def.non_enum_variant(), field.name, vec![], access_span) + self.suggest_field_name(def.non_enum_variant(), field.name, &[], access_span) { err.span_suggestion( field.span, @@ -3117,16 +3116,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } ty::Tuple(tys) => { - let fstr = field.as_str(); - - if let Ok(index) = fstr.parse::() { - if fstr == index.to_string() { - if let Some(&field_ty) = tys.get(index) { - field_indices.push(index.into()); - current_container = field_ty; + if let Ok(index) = field.as_str().parse::() + && field.name == sym::integer(index) + { + for ty in tys.iter().take(index + 1) { + self.require_type_is_sized(ty, expr.span, traits::MiscObligation); + } + if let Some(&field_ty) = tys.get(index) { + field_indices.push(index.into()); + current_container = field_ty; - continue; - } + continue; } } } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 38ddb7e760410..fb56b7e74cb54 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -1476,7 +1476,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn structurally_resolved_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> { let mut ty = self.resolve_vars_with_obligations(ty); - if self.tcx.trait_solver_next() + if self.next_trait_solver() && let ty::Alias(ty::Projection, _) = ty.kind() { match self diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 72c42f8e78952..48c3d6f08dee1 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -362,7 +362,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { continue; } - let is_closure = matches!(arg.kind, ExprKind::Closure { .. }); + // For this check, we do *not* want to treat async generator closures (async blocks) + // as proper closures. Doing so would regress type inference when feeding + // the return value of an argument-position async block to an argument-position + // closure wrapped in a block. + // See . + let is_closure = if let ExprKind::Closure(closure) = arg.kind { + !tcx.generator_is_async(closure.def_id.to_def_id()) + } else { + false + }; if is_closure != check_closures { continue; } @@ -1395,7 +1404,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // type of the place it is referencing, and not some // supertype thereof. let init_ty = self.check_expr_with_needs(init, Needs::maybe_mut_place(m)); - self.demand_eqtype(init.span, local_ty, init_ty); + if let Some(mut diag) = self.demand_eqtype_diag(init.span, local_ty, init_ty) { + self.emit_type_mismatch_suggestions( + &mut diag, + init.peel_drop_temps(), + init_ty, + local_ty, + None, + None, + ); + diag.emit(); + } init_ty } else { self.check_expr_coercible_to_type(init, local_ty, None) @@ -1631,7 +1650,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir::Stmt { kind: hir::StmtKind::Expr(hir::Expr { - kind: hir::ExprKind::Assign(..), + kind: hir::ExprKind::Assign(lhs, ..), .. }), .. @@ -1641,7 +1660,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } = blk { self.comes_from_while_condition(blk.hir_id, |_| { - err.downgrade_to_delayed_bug(); + // We cannot suppress the error if the LHS of assignment + // is a syntactic place expression because E0070 would + // not be emitted by `check_lhs_assignable`. + let res = self.typeck_results.borrow().expr_ty_opt(lhs); + + if !lhs.is_syntactic_place_expr() + || res.references_error() + { + err.downgrade_to_delayed_bug(); + } }) } } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index e19b0664461a9..3a4fe334f888a 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -1,6 +1,8 @@ use super::FnCtxt; -use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel, SuggestBoxing}; +use crate::errors::{ + AddReturnTypeSuggestion, ExpectedReturnTypeLabel, SuggestBoxing, SuggestConvertViaMethod, +}; use crate::fluent_generated as fluent; use crate::method::probe::{IsSuggestion, Mode, ProbeScope}; use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX}; @@ -275,6 +277,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, ) -> bool { let expr = expr.peel_blocks(); + let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id); + if let Some((suggestion, msg, applicability, verbose, annotation)) = self.suggest_deref_or_ref(expr, found, expected) { @@ -325,9 +329,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } return true; - } else if self.suggest_else_fn_with_closure(err, expr, found, expected) { + } + + if self.suggest_else_fn_with_closure(err, expr, found, expected) { return true; - } else if self.suggest_fn_call(err, expr, found, |output| self.can_coerce(output, expected)) + } + + if self.suggest_fn_call(err, expr, found, |output| self.can_coerce(output, expected)) && let ty::FnDef(def_id, ..) = *found.kind() && let Some(sp) = self.tcx.hir().span_if_local(def_id) { @@ -343,97 +351,156 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_label(sp, format!("{descr} `{name}` defined here")); } return true; - } else if self.suggest_cast(err, expr, found, expected, expected_ty_expr) { + } + + if self.suggest_cast(err, expr, found, expected, expected_ty_expr) { return true; - } else { - let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id); - if !methods.is_empty() { - let mut suggestions = methods.iter() - .filter_map(|conversion_method| { - let receiver_method_ident = expr.method_ident(); - if let Some(method_ident) = receiver_method_ident - && method_ident.name == conversion_method.name - { - return None // do not suggest code that is already there (#53348) - } + } - let method_call_list = [sym::to_vec, sym::to_string]; - let mut sugg = if let ExprKind::MethodCall(receiver_method, ..) = expr.kind - && receiver_method.ident.name == sym::clone - && method_call_list.contains(&conversion_method.name) - // If receiver is `.clone()` and found type has one of those methods, - // we guess that the user wants to convert from a slice type (`&[]` or `&str`) - // to an owned type (`Vec` or `String`). These conversions clone internally, - // so we remove the user's `clone` call. - { - vec![( - receiver_method.ident.span, - conversion_method.name.to_string() - )] - } else if expr.precedence().order() - < ExprPrecedence::MethodCall.order() - { - vec![ - (expr.span.shrink_to_lo(), "(".to_string()), - (expr.span.shrink_to_hi(), format!(").{}()", conversion_method.name)), - ] - } else { - vec![(expr.span.shrink_to_hi(), format!(".{}()", conversion_method.name))] - }; - let struct_pat_shorthand_field = self.maybe_get_struct_pattern_shorthand_field(expr); - if let Some(name) = struct_pat_shorthand_field { - sugg.insert( - 0, - (expr.span.shrink_to_lo(), format!("{}: ", name)), - ); - } - Some(sugg) - }) - .peekable(); - if suggestions.peek().is_some() { - err.multipart_suggestions( - "try using a conversion method", - suggestions, - Applicability::MaybeIncorrect, - ); - return true; - } - } else if let ty::Adt(found_adt, found_substs) = found.kind() - && self.tcx.is_diagnostic_item(sym::Option, found_adt.did()) - && let ty::Adt(expected_adt, expected_substs) = expected.kind() - && self.tcx.is_diagnostic_item(sym::Option, expected_adt.did()) - && let ty::Ref(_, inner_ty, _) = expected_substs.type_at(0).kind() - && inner_ty.is_str() - { - let ty = found_substs.type_at(0); - let mut peeled = ty; - let mut ref_cnt = 0; - while let ty::Ref(_, inner, _) = peeled.kind() { - peeled = *inner; - ref_cnt += 1; - } - if let ty::Adt(adt, _) = peeled.kind() - && Some(adt.did()) == self.tcx.lang_items().string() - { - let sugg = if ref_cnt == 0 { - ".as_deref()" + if !methods.is_empty() { + let mut suggestions = methods + .iter() + .filter_map(|conversion_method| { + let receiver_method_ident = expr.method_ident(); + if let Some(method_ident) = receiver_method_ident + && method_ident.name == conversion_method.name + { + return None // do not suggest code that is already there (#53348) + } + + let method_call_list = [sym::to_vec, sym::to_string]; + let mut sugg = if let ExprKind::MethodCall(receiver_method, ..) = expr.kind + && receiver_method.ident.name == sym::clone + && method_call_list.contains(&conversion_method.name) + // If receiver is `.clone()` and found type has one of those methods, + // we guess that the user wants to convert from a slice type (`&[]` or `&str`) + // to an owned type (`Vec` or `String`). These conversions clone internally, + // so we remove the user's `clone` call. + { + vec![( + receiver_method.ident.span, + conversion_method.name.to_string() + )] + } else if expr.precedence().order() + < ExprPrecedence::MethodCall.order() + { + vec![ + (expr.span.shrink_to_lo(), "(".to_string()), + (expr.span.shrink_to_hi(), format!(").{}()", conversion_method.name)), + ] } else { - ".map(|x| x.as_str())" + vec![(expr.span.shrink_to_hi(), format!(".{}()", conversion_method.name))] }; - err.span_suggestion_verbose( - expr.span.shrink_to_hi(), - fluent::hir_typeck_convert_to_str, - sugg, - Applicability::MachineApplicable, - ); - return true; - } + let struct_pat_shorthand_field = + self.maybe_get_struct_pattern_shorthand_field(expr); + if let Some(name) = struct_pat_shorthand_field { + sugg.insert(0, (expr.span.shrink_to_lo(), format!("{}: ", name))); + } + Some(sugg) + }) + .peekable(); + if suggestions.peek().is_some() { + err.multipart_suggestions( + "try using a conversion method", + suggestions, + Applicability::MaybeIncorrect, + ); + return true; + } + } + + if let Some((found_ty_inner, expected_ty_inner, error_tys)) = + self.deconstruct_option_or_result(found, expected) + && let ty::Ref(_, peeled, hir::Mutability::Not) = *expected_ty_inner.kind() + { + // Suggest removing any stray borrows (unless there's macro shenanigans involved). + let inner_expr = expr.peel_borrows(); + if !inner_expr.span.eq_ctxt(expr.span) { + return false; + } + let borrow_removal_span = if inner_expr.hir_id == expr.hir_id { + None + } else { + Some(expr.span.shrink_to_lo().until(inner_expr.span)) + }; + // Given `Result<_, E>`, check our expected ty is `Result<_, &E>` for + // `as_ref` and `as_deref` compatibility. + let error_tys_equate_as_ref = error_tys.map_or(true, |(found, expected)| { + self.can_eq(self.param_env, self.tcx.mk_imm_ref(self.tcx.lifetimes.re_erased, found), expected) + }); + // FIXME: This could/should be extended to suggest `as_mut` and `as_deref_mut`, + // but those checks need to be a bit more delicate and the benefit is diminishing. + if self.can_eq(self.param_env, found_ty_inner, peeled) && error_tys_equate_as_ref { + err.subdiagnostic(SuggestConvertViaMethod { + span: expr.span.shrink_to_hi(), + sugg: ".as_ref()", + expected, + found, + borrow_removal_span, + }); + return true; + } else if let Some((deref_ty, _)) = + self.autoderef(expr.span, found_ty_inner).silence_errors().nth(1) + && self.can_eq(self.param_env, deref_ty, peeled) + && error_tys_equate_as_ref + { + err.subdiagnostic(SuggestConvertViaMethod { + span: expr.span.shrink_to_hi(), + sugg: ".as_deref()", + expected, + found, + borrow_removal_span, + }); + return true; + } else if let ty::Adt(adt, _) = found_ty_inner.peel_refs().kind() + && Some(adt.did()) == self.tcx.lang_items().string() + && peeled.is_str() + // `Result::map`, conversely, does not take ref of the error type. + && error_tys.map_or(true, |(found, expected)| { + self.can_eq(self.param_env, found, expected) + }) + { + err.span_suggestion_verbose( + expr.span.shrink_to_hi(), + fluent::hir_typeck_convert_to_str, + ".map(|x| x.as_str())", + Applicability::MachineApplicable, + ); + return true; } } false } + fn deconstruct_option_or_result( + &self, + found_ty: Ty<'tcx>, + expected_ty: Ty<'tcx>, + ) -> Option<(Ty<'tcx>, Ty<'tcx>, Option<(Ty<'tcx>, Ty<'tcx>)>)> { + let ty::Adt(found_adt, found_substs) = found_ty.peel_refs().kind() else { + return None; + }; + let ty::Adt(expected_adt, expected_substs) = expected_ty.kind() else { + return None; + }; + if self.tcx.is_diagnostic_item(sym::Option, found_adt.did()) + && self.tcx.is_diagnostic_item(sym::Option, expected_adt.did()) + { + Some((found_substs.type_at(0), expected_substs.type_at(0), None)) + } else if self.tcx.is_diagnostic_item(sym::Result, found_adt.did()) + && self.tcx.is_diagnostic_item(sym::Result, expected_adt.did()) + { + Some(( + found_substs.type_at(0), + expected_substs.type_at(0), + Some((found_substs.type_at(1), expected_substs.type_at(1))), + )) + } else { + None + } + } + /// When encountering the expected boxed value allocated in the stack, suggest allocating it /// in the heap by calling `Box::new()`. pub(in super::super) fn suggest_boxing_when_appropriate( diff --git a/compiler/rustc_hir_typeck/src/gather_locals.rs b/compiler/rustc_hir_typeck/src/gather_locals.rs index 38445f2844052..d9b9b34ba583b 100644 --- a/compiler/rustc_hir_typeck/src/gather_locals.rs +++ b/compiler/rustc_hir_typeck/src/gather_locals.rs @@ -129,7 +129,17 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> { self.fcx.require_type_is_sized( var_ty, p.span, - traits::SizedArgumentType(Some(ty_span)), + // ty_span == ident.span iff this is a closure parameter with no type + // ascription, or if it's an implicit `self` parameter + traits::SizedArgumentType( + if ty_span == ident.span + && self.fcx.tcx.is_closure(self.fcx.body_id.into()) + { + None + } else { + Some(ty_span) + }, + ), ); } } else { diff --git a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs index e4a62ec05aef1..786a8c28f998b 100644 --- a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs +++ b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs @@ -270,6 +270,7 @@ impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> { | hir::Node::Variant(..) | hir::Node::Field(..) | hir::Node::AnonConst(..) + | hir::Node::ConstBlock(..) | hir::Node::Stmt(..) | hir::Node::PathSegment(..) | hir::Node::Ty(..) diff --git a/compiler/rustc_hir_typeck/src/inherited.rs b/compiler/rustc_hir_typeck/src/inherited.rs index 294c3bb78a5ba..aa4f90b4ad8a0 100644 --- a/compiler/rustc_hir_typeck/src/inherited.rs +++ b/compiler/rustc_hir_typeck/src/inherited.rs @@ -86,8 +86,8 @@ impl<'tcx> Inherited<'tcx> { Inherited { typeck_results, + fulfillment_cx: RefCell::new(>::new(&infcx)), infcx, - fulfillment_cx: RefCell::new(>::new(tcx)), locals: RefCell::new(Default::default()), deferred_sized_obligations: RefCell::new(Vec::new()), deferred_call_resolutions: RefCell::new(Default::default()), diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 9f3d35a77dc75..b7b11ff8942f9 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -9,7 +9,6 @@ use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::DefKind; -use rustc_hir_analysis::astconv::InferCtxtExt as _; use rustc_hir_analysis::autoderef::{self, Autoderef}; use rustc_infer::infer::canonical::OriginalQueryValues; use rustc_infer::infer::canonical::{Canonical, QueryResponse}; @@ -954,7 +953,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { trait_def_id: DefId, ) { debug!("assemble_extension_candidates_for_trait(trait_def_id={:?})", trait_def_id); - let trait_substs = self.fresh_item_substs(trait_def_id); + let trait_substs = self.fresh_substs_for_item(self.span, trait_def_id); let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, trait_substs); if self.tcx.is_trait_alias(trait_def_id) { @@ -1899,7 +1898,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { &self, impl_def_id: DefId, ) -> (ty::EarlyBinder>, SubstsRef<'tcx>) { - (self.tcx.type_of(impl_def_id), self.fresh_item_substs(impl_def_id)) + (self.tcx.type_of(impl_def_id), self.fresh_substs_for_item(self.span, impl_def_id)) } /// Replaces late-bound-regions bound by `value` with `'static` using diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 9ee967dc7a97e..7a484e7ddc51e 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -2110,20 +2110,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | sym::Hash | sym::Debug => true, _ => false, - } && match trait_pred.trait_ref.substs.as_slice() { - // Only suggest deriving if lhs == rhs... - [lhs, rhs] => { - if let Some(lhs) = lhs.as_type() - && let Some(rhs) = rhs.as_type() - { - self.can_eq(self.param_env, lhs, rhs) - } else { - false - } - }, - // Unary ops can always be derived - [_] => true, - _ => false, }; if can_derive { let self_name = trait_pred.self_ty().to_string(); diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 5af955d313482..2f9871a103a83 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -393,9 +393,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // They can denote both statically and dynamically-sized byte arrays. let mut pat_ty = ty; if let hir::ExprKind::Lit(Spanned { node: ast::LitKind::ByteStr(..), .. }) = lt.kind { - let expected = self.structurally_resolved_type(span, expected); - if let ty::Ref(_, inner_ty, _) = expected.kind() - && matches!(inner_ty.kind(), ty::Slice(_)) + if let ty::Ref(_, inner_ty, _) = *self.structurally_resolved_type(span, expected).kind() + && self.structurally_resolved_type(span, inner_ty).is_slice() { let tcx = self.tcx; trace!(?lt.hir_id.local_id, "polymorphic byte string lit"); diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 964acc4eb77ef..a395858262f04 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -14,7 +14,6 @@ use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCast}; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt}; -use rustc_middle::ty::TypeckResults; use rustc_middle::ty::{self, ClosureSizeProfileData, Ty, TyCtxt}; use rustc_span::symbol::sym; use rustc_span::Span; @@ -148,31 +147,25 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { fn fix_scalar_builtin_expr(&mut self, e: &hir::Expr<'_>) { match e.kind { hir::ExprKind::Unary(hir::UnOp::Neg | hir::UnOp::Not, inner) => { - let inner_ty = self.fcx.node_ty(inner.hir_id); - let inner_ty = self.fcx.resolve_vars_if_possible(inner_ty); + let inner_ty = self.typeck_results.node_type(inner.hir_id); if inner_ty.is_scalar() { - let mut typeck_results = self.fcx.typeck_results.borrow_mut(); - typeck_results.type_dependent_defs_mut().remove(e.hir_id); - typeck_results.node_substs_mut().remove(e.hir_id); + self.typeck_results.type_dependent_defs_mut().remove(e.hir_id); + self.typeck_results.node_substs_mut().remove(e.hir_id); } } hir::ExprKind::Binary(ref op, lhs, rhs) | hir::ExprKind::AssignOp(ref op, lhs, rhs) => { - let lhs_ty = self.fcx.node_ty(lhs.hir_id); - let lhs_ty = self.fcx.resolve_vars_if_possible(lhs_ty); - - let rhs_ty = self.fcx.node_ty(rhs.hir_id); - let rhs_ty = self.fcx.resolve_vars_if_possible(rhs_ty); + let lhs_ty = self.typeck_results.node_type(lhs.hir_id); + let rhs_ty = self.typeck_results.node_type(rhs.hir_id); if lhs_ty.is_scalar() && rhs_ty.is_scalar() { - let mut typeck_results = self.fcx.typeck_results.borrow_mut(); - typeck_results.type_dependent_defs_mut().remove(e.hir_id); - typeck_results.node_substs_mut().remove(e.hir_id); + self.typeck_results.type_dependent_defs_mut().remove(e.hir_id); + self.typeck_results.node_substs_mut().remove(e.hir_id); match e.kind { hir::ExprKind::Binary(..) => { if !op.node.is_by_value() { - let mut adjustments = typeck_results.adjustments_mut(); + let mut adjustments = self.typeck_results.adjustments_mut(); if let Some(a) = adjustments.get_mut(lhs.hir_id) { a.pop(); } @@ -182,7 +175,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { } } hir::ExprKind::AssignOp(..) - if let Some(a) = typeck_results.adjustments_mut().get_mut(lhs.hir_id) => + if let Some(a) = self.typeck_results.adjustments_mut().get_mut(lhs.hir_id) => { a.pop(); } @@ -200,16 +193,14 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { // if they are not we don't modify the expr, hence we bypass the ICE fn is_builtin_index( &mut self, - typeck_results: &TypeckResults<'tcx>, e: &hir::Expr<'_>, base_ty: Ty<'tcx>, index_ty: Ty<'tcx>, ) -> bool { - if let Some(elem_ty) = base_ty.builtin_index() { - let Some(exp_ty) = typeck_results.expr_ty_opt(e) else {return false;}; - let resolved_exp_ty = self.resolve(exp_ty, &e.span); - - elem_ty == resolved_exp_ty && index_ty == self.fcx.tcx.types.usize + if let Some(elem_ty) = base_ty.builtin_index() + && let Some(exp_ty) = self.typeck_results.expr_ty_opt(e) + { + elem_ty == exp_ty && index_ty == self.fcx.tcx.types.usize } else { false } @@ -221,38 +212,34 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { // usize-ish fn fix_index_builtin_expr(&mut self, e: &hir::Expr<'_>) { if let hir::ExprKind::Index(ref base, ref index) = e.kind { - let mut typeck_results = self.fcx.typeck_results.borrow_mut(); - // All valid indexing looks like this; might encounter non-valid indexes at this point. - let base_ty = typeck_results - .expr_ty_adjusted_opt(base) - .map(|t| self.fcx.resolve_vars_if_possible(t).kind()); + let base_ty = self.typeck_results.expr_ty_adjusted_opt(base); if base_ty.is_none() { // When encountering `return [0][0]` outside of a `fn` body we can encounter a base // that isn't in the type table. We assume more relevant errors have already been // emitted, so we delay an ICE if none have. (#64638) self.tcx().sess.delay_span_bug(e.span, format!("bad base: `{:?}`", base)); } - if let Some(ty::Ref(_, base_ty, _)) = base_ty { - let index_ty = typeck_results.expr_ty_adjusted_opt(index).unwrap_or_else(|| { - // When encountering `return [0][0]` outside of a `fn` body we would attempt - // to access an nonexistent index. We assume that more relevant errors will - // already have been emitted, so we only gate on this with an ICE if no - // error has been emitted. (#64638) - self.fcx.tcx.ty_error_with_message( - e.span, - format!("bad index {:?} for base: `{:?}`", index, base), - ) - }); - let index_ty = self.fcx.resolve_vars_if_possible(index_ty); - let resolved_base_ty = self.resolve(*base_ty, &base.span); - - if self.is_builtin_index(&typeck_results, e, resolved_base_ty, index_ty) { + if let Some(base_ty) = base_ty + && let ty::Ref(_, base_ty_inner, _) = *base_ty.kind() + { + let index_ty = + self.typeck_results.expr_ty_adjusted_opt(index).unwrap_or_else(|| { + // When encountering `return [0][0]` outside of a `fn` body we would attempt + // to access an nonexistent index. We assume that more relevant errors will + // already have been emitted, so we only gate on this with an ICE if no + // error has been emitted. (#64638) + self.fcx.tcx.ty_error_with_message( + e.span, + format!("bad index {:?} for base: `{:?}`", index, base), + ) + }); + if self.is_builtin_index(e, base_ty_inner, index_ty) { // Remove the method call record - typeck_results.type_dependent_defs_mut().remove(e.hir_id); - typeck_results.node_substs_mut().remove(e.hir_id); + self.typeck_results.type_dependent_defs_mut().remove(e.hir_id); + self.typeck_results.node_substs_mut().remove(e.hir_id); - if let Some(a) = typeck_results.adjustments_mut().get_mut(base.hir_id) { + if let Some(a) = self.typeck_results.adjustments_mut().get_mut(base.hir_id) { // Discard the need for a mutable borrow // Extra adjustment made when indexing causes a drop @@ -283,9 +270,6 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> { fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) { - self.fix_scalar_builtin_expr(e); - self.fix_index_builtin_expr(e); - match e.kind { hir::ExprKind::Closure(&hir::Closure { body, .. }) => { let body = self.fcx.tcx.hir().body(body); @@ -314,6 +298,9 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> { self.visit_node_id(e.span, e.hir_id); intravisit::walk_expr(self, e); + + self.fix_scalar_builtin_expr(e); + self.fix_index_builtin_expr(e); } fn visit_generic_param(&mut self, p: &'tcx hir::GenericParam<'tcx>) { @@ -591,7 +578,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { .insert(opaque_type_key, hidden_type) && last_opaque_ty.ty != hidden_type.ty { - assert!(!self.tcx().trait_solver_next()); + assert!(!self.fcx.next_trait_solver()); hidden_type .report_mismatch(&last_opaque_ty, opaque_type_key.def_id, self.tcx()) .stash( @@ -812,7 +799,7 @@ impl<'cx, 'tcx> TypeFolder> for Resolver<'cx, 'tcx> { fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { match self.fcx.fully_resolve(t) { - Ok(t) if self.fcx.tcx.trait_solver_next() => { + Ok(t) if self.fcx.next_trait_solver() => { // We must normalize erasing regions here, since later lints // expect that types that show up in the typeck are fully // normalized. diff --git a/compiler/rustc_incremental/src/assert_dep_graph.rs b/compiler/rustc_incremental/src/assert_dep_graph.rs index 22bd12f2e6361..52a84b204d00b 100644 --- a/compiler/rustc_incremental/src/assert_dep_graph.rs +++ b/compiler/rustc_incremental/src/assert_dep_graph.rs @@ -35,7 +35,7 @@ use crate::errors; use rustc_ast as ast; -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::graph::implementation::{Direction, NodeIndex, INCOMING, OUTGOING}; use rustc_graphviz as dot; use rustc_hir as hir; @@ -258,7 +258,7 @@ fn dump_graph(query: &DepGraphQuery) { } #[allow(missing_docs)] -pub struct GraphvizDepGraph(FxHashSet, Vec<(DepKind, DepKind)>); +pub struct GraphvizDepGraph(FxIndexSet, Vec<(DepKind, DepKind)>); impl<'a> dot::GraphWalk<'a> for GraphvizDepGraph { type Node = DepKind; @@ -303,7 +303,7 @@ impl<'a> dot::Labeller<'a> for GraphvizDepGraph { fn node_set<'q>( query: &'q DepGraphQuery, filter: &DepNodeFilter, -) -> Option> { +) -> Option> { debug!("node_set(filter={:?})", filter); if filter.accepts_all() { @@ -315,9 +315,9 @@ fn node_set<'q>( fn filter_nodes<'q>( query: &'q DepGraphQuery, - sources: &Option>, - targets: &Option>, -) -> FxHashSet { + sources: &Option>, + targets: &Option>, +) -> FxIndexSet { if let Some(sources) = sources { if let Some(targets) = targets { walk_between(query, sources, targets) @@ -333,10 +333,10 @@ fn filter_nodes<'q>( fn walk_nodes<'q>( query: &'q DepGraphQuery, - starts: &FxHashSet<&'q DepNode>, + starts: &FxIndexSet<&'q DepNode>, direction: Direction, -) -> FxHashSet { - let mut set = FxHashSet::default(); +) -> FxIndexSet { + let mut set = FxIndexSet::default(); for &start in starts { debug!("walk_nodes: start={:?} outgoing?={:?}", start, direction == OUTGOING); if set.insert(start.kind) { @@ -357,9 +357,9 @@ fn walk_nodes<'q>( fn walk_between<'q>( query: &'q DepGraphQuery, - sources: &FxHashSet<&'q DepNode>, - targets: &FxHashSet<&'q DepNode>, -) -> FxHashSet { + sources: &FxIndexSet<&'q DepNode>, + targets: &FxIndexSet<&'q DepNode>, +) -> FxIndexSet { // This is a bit tricky. We want to include a node only if it is: // (a) reachable from a source and (b) will reach a target. And we // have to be careful about cycles etc. Luckily efficiency is not @@ -426,8 +426,8 @@ fn walk_between<'q>( } } -fn filter_edges(query: &DepGraphQuery, nodes: &FxHashSet) -> Vec<(DepKind, DepKind)> { - let uniq: FxHashSet<_> = query +fn filter_edges(query: &DepGraphQuery, nodes: &FxIndexSet) -> Vec<(DepKind, DepKind)> { + let uniq: FxIndexSet<_> = query .edges() .into_iter() .map(|(s, t)| (s.kind, t.kind)) diff --git a/compiler/rustc_incremental/src/assert_module_sources.rs b/compiler/rustc_incremental/src/assert_module_sources.rs index c550e553bb032..0111a6d302d4a 100644 --- a/compiler/rustc_incremental/src/assert_module_sources.rs +++ b/compiler/rustc_incremental/src/assert_module_sources.rs @@ -24,7 +24,7 @@ use crate::errors; use rustc_ast as ast; -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::unord::UnordSet; use rustc_hir::def_id::LOCAL_CRATE; use rustc_middle::mir::mono::CodegenUnitNameBuilder; use rustc_middle::ty::TyCtxt; @@ -52,7 +52,7 @@ pub fn assert_module_sources(tcx: TyCtxt<'_>) { struct AssertModuleSource<'tcx> { tcx: TyCtxt<'tcx>, - available_cgus: FxHashSet, + available_cgus: UnordSet, } impl<'tcx> AssertModuleSource<'tcx> { @@ -118,9 +118,8 @@ impl<'tcx> AssertModuleSource<'tcx> { debug!("mapping '{}' to cgu name '{}'", self.field(attr, sym::module), cgu_name); if !self.available_cgus.contains(&cgu_name) { - let mut cgu_names: Vec<&str> = - self.available_cgus.iter().map(|cgu| cgu.as_str()).collect(); - cgu_names.sort(); + let cgu_names: Vec<&str> = + self.available_cgus.items().map(|cgu| cgu.as_str()).into_sorted_stable_ord(); self.tcx.sess.emit_err(errors::NoModuleNamed { span: attr.span, user_path, diff --git a/compiler/rustc_incremental/src/lib.rs b/compiler/rustc_incremental/src/lib.rs index 11710c368cefe..b9171fad55ba3 100644 --- a/compiler/rustc_incremental/src/lib.rs +++ b/compiler/rustc_incremental/src/lib.rs @@ -4,7 +4,6 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(never_type)] #![recursion_limit = "256"] -#![allow(rustc::potential_query_instability)] #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] diff --git a/compiler/rustc_incremental/src/persist/dirty_clean.rs b/compiler/rustc_incremental/src/persist/dirty_clean.rs index 43274091cb873..cbe77e7b16de8 100644 --- a/compiler/rustc_incremental/src/persist/dirty_clean.rs +++ b/compiler/rustc_incremental/src/persist/dirty_clean.rs @@ -22,6 +22,7 @@ use crate::errors; use rustc_ast::{self as ast, Attribute, NestedMetaItem}; use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::unord::UnordSet; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit; use rustc_hir::Node as HirNode; @@ -125,7 +126,7 @@ const LABELS_ADT: &[&[&str]] = &[BASE_HIR, BASE_STRUCT]; // // type_of for these. -type Labels = FxHashSet; +type Labels = UnordSet; /// Represents the requested configuration by rustc_clean/dirty struct Assertion { @@ -197,7 +198,7 @@ impl<'tcx> DirtyCleanVisitor<'tcx> { let (name, mut auto) = self.auto_labels(item_id, attr); let except = self.except(attr); let loaded_from_disk = self.loaded_from_disk(attr); - for e in except.iter() { + for e in except.items().map(|x| x.as_str()).into_sorted_stable_ord() { if !auto.remove(e) { self.tcx.sess.emit_fatal(errors::AssertionAuto { span: attr.span, name, e }); } @@ -376,15 +377,17 @@ impl<'tcx> DirtyCleanVisitor<'tcx> { continue; }; self.checked_attrs.insert(attr.id); - for label in assertion.clean { + for label in assertion.clean.items().map(|x| x.as_str()).into_sorted_stable_ord() { let dep_node = DepNode::from_label_string(self.tcx, &label, def_path_hash).unwrap(); self.assert_clean(item_span, dep_node); } - for label in assertion.dirty { + for label in assertion.dirty.items().map(|x| x.as_str()).into_sorted_stable_ord() { let dep_node = DepNode::from_label_string(self.tcx, &label, def_path_hash).unwrap(); self.assert_dirty(item_span, dep_node); } - for label in assertion.loaded_from_disk { + for label in + assertion.loaded_from_disk.items().map(|x| x.as_str()).into_sorted_stable_ord() + { let dep_node = DepNode::from_label_string(self.tcx, &label, def_path_hash).unwrap(); self.assert_loaded_from_disk(item_span, dep_node); } diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs index e3c688b3e98cb..243057b99bca2 100644 --- a/compiler/rustc_incremental/src/persist/fs.rs +++ b/compiler/rustc_incremental/src/persist/fs.rs @@ -104,8 +104,9 @@ //! implemented. use crate::errors; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_data_structures::svh::Svh; +use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_data_structures::{base_n, flock}; use rustc_errors::ErrorGuaranteed; use rustc_fs_util::{link_or_copy, try_canonicalize, LinkOrCopy}; @@ -635,8 +636,8 @@ pub fn garbage_collect_session_directories(sess: &Session) -> io::Result<()> { // First do a pass over the crate directory, collecting lock files and // session directories - let mut session_directories = FxHashSet::default(); - let mut lock_files = FxHashSet::default(); + let mut session_directories = FxIndexSet::default(); + let mut lock_files = UnordSet::default(); for dir_entry in crate_directory.read_dir()? { let Ok(dir_entry) = dir_entry else { @@ -657,10 +658,11 @@ pub fn garbage_collect_session_directories(sess: &Session) -> io::Result<()> { // This is something we don't know, leave it alone } } + session_directories.sort(); // Now map from lock files to session directories - let lock_file_to_session_dir: FxHashMap> = lock_files - .into_iter() + let lock_file_to_session_dir: UnordMap> = lock_files + .into_items() .map(|lock_file_name| { assert!(lock_file_name.ends_with(LOCK_FILE_EXT)); let dir_prefix_end = lock_file_name.len() - LOCK_FILE_EXT.len(); @@ -670,11 +672,13 @@ pub fn garbage_collect_session_directories(sess: &Session) -> io::Result<()> { }; (lock_file_name, session_dir.map(String::clone)) }) - .collect(); + .into(); // Delete all lock files, that don't have an associated directory. They must // be some kind of leftover - for (lock_file_name, directory_name) in &lock_file_to_session_dir { + for (lock_file_name, directory_name) in + lock_file_to_session_dir.items().into_sorted_stable_ord() + { if directory_name.is_none() { let Ok(timestamp) = extract_timestamp_from_session_dir(lock_file_name) else { debug!( @@ -685,19 +689,19 @@ pub fn garbage_collect_session_directories(sess: &Session) -> io::Result<()> { continue; }; - let lock_file_path = crate_directory.join(&**lock_file_name); + let lock_file_path = crate_directory.join(&*lock_file_name); if is_old_enough_to_be_collected(timestamp) { debug!( "garbage_collect_session_directories() - deleting \ - garbage lock file: {}", + garbage lock file: {}", lock_file_path.display() ); delete_session_dir_lock_file(sess, &lock_file_path); } else { debug!( "garbage_collect_session_directories() - lock file with \ - no session dir not old enough to be collected: {}", + no session dir not old enough to be collected: {}", lock_file_path.display() ); } @@ -705,14 +709,14 @@ pub fn garbage_collect_session_directories(sess: &Session) -> io::Result<()> { } // Filter out `None` directories - let lock_file_to_session_dir: FxHashMap = lock_file_to_session_dir - .into_iter() + let lock_file_to_session_dir: UnordMap = lock_file_to_session_dir + .into_items() .filter_map(|(lock_file_name, directory_name)| directory_name.map(|n| (lock_file_name, n))) - .collect(); + .into(); // Delete all session directories that don't have a lock file. for directory_name in session_directories { - if !lock_file_to_session_dir.values().any(|dir| *dir == directory_name) { + if !lock_file_to_session_dir.items().any(|(_, dir)| *dir == directory_name) { let path = crate_directory.join(directory_name); if let Err(err) = safe_remove_dir_all(&path) { sess.emit_warning(errors::InvalidGcFailed { path: &path, err }); @@ -721,103 +725,103 @@ pub fn garbage_collect_session_directories(sess: &Session) -> io::Result<()> { } // Now garbage collect the valid session directories. - let mut deletion_candidates = vec![]; + let deletion_candidates = + lock_file_to_session_dir.items().filter_map(|(lock_file_name, directory_name)| { + debug!("garbage_collect_session_directories() - inspecting: {}", directory_name); - for (lock_file_name, directory_name) in &lock_file_to_session_dir { - debug!("garbage_collect_session_directories() - inspecting: {}", directory_name); - - let Ok(timestamp) = extract_timestamp_from_session_dir(directory_name) else { + let Ok(timestamp) = extract_timestamp_from_session_dir(directory_name) else { debug!( "found session-dir with malformed timestamp: {}", crate_directory.join(directory_name).display() ); // Ignore it - continue; + return None; }; - if is_finalized(directory_name) { - let lock_file_path = crate_directory.join(lock_file_name); - match flock::Lock::new( - &lock_file_path, - false, // don't wait - false, // don't create the lock-file - true, - ) { - // get an exclusive lock - Ok(lock) => { - debug!( - "garbage_collect_session_directories() - \ + if is_finalized(directory_name) { + let lock_file_path = crate_directory.join(lock_file_name); + match flock::Lock::new( + &lock_file_path, + false, // don't wait + false, // don't create the lock-file + true, + ) { + // get an exclusive lock + Ok(lock) => { + debug!( + "garbage_collect_session_directories() - \ successfully acquired lock" - ); - debug!( - "garbage_collect_session_directories() - adding \ + ); + debug!( + "garbage_collect_session_directories() - adding \ deletion candidate: {}", - directory_name - ); - - // Note that we are holding on to the lock - deletion_candidates.push(( - timestamp, - crate_directory.join(directory_name), - Some(lock), - )); - } - Err(_) => { - debug!( - "garbage_collect_session_directories() - \ + directory_name + ); + + // Note that we are holding on to the lock + return Some(( + (timestamp, crate_directory.join(directory_name)), + Some(lock), + )); + } + Err(_) => { + debug!( + "garbage_collect_session_directories() - \ not collecting, still in use" - ); + ); + } } - } - } else if is_old_enough_to_be_collected(timestamp) { - // When cleaning out "-working" session directories, i.e. - // session directories that might still be in use by another - // compiler instance, we only look a directories that are - // at least ten seconds old. This is supposed to reduce the - // chance of deleting a directory in the time window where - // the process has allocated the directory but has not yet - // acquired the file-lock on it. - - // Try to acquire the directory lock. If we can't, it - // means that the owning process is still alive and we - // leave this directory alone. - let lock_file_path = crate_directory.join(lock_file_name); - match flock::Lock::new( - &lock_file_path, - false, // don't wait - false, // don't create the lock-file - true, - ) { - // get an exclusive lock - Ok(lock) => { - debug!( - "garbage_collect_session_directories() - \ + } else if is_old_enough_to_be_collected(timestamp) { + // When cleaning out "-working" session directories, i.e. + // session directories that might still be in use by another + // compiler instance, we only look a directories that are + // at least ten seconds old. This is supposed to reduce the + // chance of deleting a directory in the time window where + // the process has allocated the directory but has not yet + // acquired the file-lock on it. + + // Try to acquire the directory lock. If we can't, it + // means that the owning process is still alive and we + // leave this directory alone. + let lock_file_path = crate_directory.join(lock_file_name); + match flock::Lock::new( + &lock_file_path, + false, // don't wait + false, // don't create the lock-file + true, + ) { + // get an exclusive lock + Ok(lock) => { + debug!( + "garbage_collect_session_directories() - \ successfully acquired lock" - ); + ); - delete_old(sess, &crate_directory.join(directory_name)); + delete_old(sess, &crate_directory.join(directory_name)); - // Let's make it explicit that the file lock is released at this point, - // or rather, that we held on to it until here - drop(lock); - } - Err(_) => { - debug!( - "garbage_collect_session_directories() - \ + // Let's make it explicit that the file lock is released at this point, + // or rather, that we held on to it until here + drop(lock); + } + Err(_) => { + debug!( + "garbage_collect_session_directories() - \ not collecting, still in use" - ); + ); + } } - } - } else { - debug!( - "garbage_collect_session_directories() - not finalized, not \ + } else { + debug!( + "garbage_collect_session_directories() - not finalized, not \ old enough" - ); - } - } + ); + } + None + }); + let deletion_candidates = deletion_candidates.into(); // Delete all but the most recent of the candidates - for (path, lock) in all_except_most_recent(deletion_candidates) { + all_except_most_recent(deletion_candidates).into_items().all(|(path, lock)| { debug!("garbage_collect_session_directories() - deleting `{}`", path.display()); if let Err(err) = safe_remove_dir_all(&path) { @@ -829,7 +833,8 @@ pub fn garbage_collect_session_directories(sess: &Session) -> io::Result<()> { // Let's make it explicit that the file lock is released at this point, // or rather, that we held on to it until here drop(lock); - } + true + }); Ok(()) } @@ -845,18 +850,19 @@ fn delete_old(sess: &Session, path: &Path) { } fn all_except_most_recent( - deletion_candidates: Vec<(SystemTime, PathBuf, Option)>, -) -> FxHashMap> { - let most_recent = deletion_candidates.iter().map(|&(timestamp, ..)| timestamp).max(); + deletion_candidates: UnordMap<(SystemTime, PathBuf), Option>, +) -> UnordMap> { + let most_recent = deletion_candidates.items().map(|(&(timestamp, _), _)| timestamp).max(); if let Some(most_recent) = most_recent { - deletion_candidates - .into_iter() - .filter(|&(timestamp, ..)| timestamp != most_recent) - .map(|(_, path, lock)| (path, lock)) - .collect() + UnordMap::from( + deletion_candidates + .into_items() + .filter(|&((timestamp, _), _)| timestamp != most_recent) + .map(|((_, path), lock)| (path, lock)), + ) } else { - FxHashMap::default() + UnordMap::default() } } diff --git a/compiler/rustc_incremental/src/persist/fs/tests.rs b/compiler/rustc_incremental/src/persist/fs/tests.rs index 184796948b67d..644b8187621c9 100644 --- a/compiler/rustc_incremental/src/persist/fs/tests.rs +++ b/compiler/rustc_incremental/src/persist/fs/tests.rs @@ -2,26 +2,19 @@ use super::*; #[test] fn test_all_except_most_recent() { + let input: UnordMap<_, Option> = UnordMap::from_iter([ + ((UNIX_EPOCH + Duration::new(4, 0), PathBuf::from("4")), None), + ((UNIX_EPOCH + Duration::new(1, 0), PathBuf::from("1")), None), + ((UNIX_EPOCH + Duration::new(5, 0), PathBuf::from("5")), None), + ((UNIX_EPOCH + Duration::new(3, 0), PathBuf::from("3")), None), + ((UNIX_EPOCH + Duration::new(2, 0), PathBuf::from("2")), None), + ]); assert_eq!( - all_except_most_recent(vec![ - (UNIX_EPOCH + Duration::new(4, 0), PathBuf::from("4"), None), - (UNIX_EPOCH + Duration::new(1, 0), PathBuf::from("1"), None), - (UNIX_EPOCH + Duration::new(5, 0), PathBuf::from("5"), None), - (UNIX_EPOCH + Duration::new(3, 0), PathBuf::from("3"), None), - (UNIX_EPOCH + Duration::new(2, 0), PathBuf::from("2"), None), - ]) - .keys() - .cloned() - .collect::>(), - [PathBuf::from("1"), PathBuf::from("2"), PathBuf::from("3"), PathBuf::from("4"),] - .into_iter() - .collect::>() + all_except_most_recent(input).into_items().map(|(path, _)| path).into_sorted_stable_ord(), + vec![PathBuf::from("1"), PathBuf::from("2"), PathBuf::from("3"), PathBuf::from("4")] ); - assert_eq!( - all_except_most_recent(vec![]).keys().cloned().collect::>(), - FxHashSet::default() - ); + assert!(all_except_most_recent(UnordMap::default()).is_empty()); } #[test] diff --git a/compiler/rustc_incremental/src/persist/load.rs b/compiler/rustc_incremental/src/persist/load.rs index a4407a93ff3ba..bb479b5bdccda 100644 --- a/compiler/rustc_incremental/src/persist/load.rs +++ b/compiler/rustc_incremental/src/persist/load.rs @@ -1,8 +1,8 @@ //! Code to save/load the dep-graph from files. use crate::errors; -use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::memmap::Mmap; +use rustc_data_structures::unord::UnordMap; use rustc_middle::dep_graph::{SerializedDepGraph, WorkProduct, WorkProductId}; use rustc_middle::query::on_disk_cache::OnDiskCache; use rustc_serialize::opaque::MemDecoder; @@ -16,7 +16,7 @@ use super::file_format; use super::fs::*; use super::work_product; -type WorkProductMap = FxHashMap; +type WorkProductMap = UnordMap; #[derive(Debug)] /// Represents the result of an attempt to load incremental compilation data. @@ -147,7 +147,7 @@ pub fn load_dep_graph(sess: &Session) -> DepGraphFuture { let report_incremental_info = sess.opts.unstable_opts.incremental_info; let expected_hash = sess.opts.dep_tracking_hash(false); - let mut prev_work_products = FxHashMap::default(); + let mut prev_work_products = UnordMap::default(); // If we are only building with -Zquery-dep-graph but without an actual // incr. comp. session directory, we skip this. Otherwise we'd fail @@ -163,7 +163,7 @@ pub fn load_dep_graph(sess: &Session) -> DepGraphFuture { Decodable::decode(&mut work_product_decoder); for swp in work_products { - let all_files_exist = swp.work_product.saved_files.iter().all(|(_, path)| { + let all_files_exist = swp.work_product.saved_files.items().all(|(_, path)| { let exists = in_incr_comp_dir_sess(sess, path).exists(); if !exists && sess.opts.unstable_opts.incremental_info { eprintln!("incremental: could not find file for work product: {path}",); diff --git a/compiler/rustc_incremental/src/persist/save.rs b/compiler/rustc_incremental/src/persist/save.rs index 7376be6be8b8a..bfaa52f9c8134 100644 --- a/compiler/rustc_incremental/src/persist/save.rs +++ b/compiler/rustc_incremental/src/persist/save.rs @@ -1,5 +1,5 @@ use crate::errors; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync::join; use rustc_middle::dep_graph::{DepGraph, SerializedDepGraph, WorkProduct, WorkProductId}; use rustc_middle::ty::TyCtxt; @@ -79,7 +79,7 @@ pub fn save_dep_graph(tcx: TyCtxt<'_>) { pub fn save_work_product_index( sess: &Session, dep_graph: &DepGraph, - new_work_products: FxHashMap, + new_work_products: FxIndexMap, ) { if sess.opts.incremental.is_none() { return; @@ -105,7 +105,7 @@ pub fn save_work_product_index( if !new_work_products.contains_key(id) { work_product::delete_workproduct_files(sess, wp); debug_assert!( - !wp.saved_files.iter().all(|(_, path)| in_incr_comp_dir_sess(sess, path).exists()) + !wp.saved_files.items().all(|(_, path)| in_incr_comp_dir_sess(sess, path).exists()) ); } } @@ -113,13 +113,13 @@ pub fn save_work_product_index( // Check that we did not delete one of the current work-products: debug_assert!({ new_work_products.iter().all(|(_, wp)| { - wp.saved_files.iter().all(|(_, path)| in_incr_comp_dir_sess(sess, path).exists()) + wp.saved_files.items().all(|(_, path)| in_incr_comp_dir_sess(sess, path).exists()) }) }); } fn encode_work_product_index( - work_products: &FxHashMap, + work_products: &FxIndexMap, encoder: &mut FileEncoder, ) { let serialized_products: Vec<_> = work_products @@ -146,7 +146,7 @@ fn encode_query_cache(tcx: TyCtxt<'_>, encoder: FileEncoder) -> FileEncodeResult pub fn build_dep_graph( sess: &Session, prev_graph: SerializedDepGraph, - prev_work_products: FxHashMap, + prev_work_products: FxIndexMap, ) -> Option { if sess.opts.incremental.is_none() { // No incremental compilation. diff --git a/compiler/rustc_incremental/src/persist/work_product.rs b/compiler/rustc_incremental/src/persist/work_product.rs index dc98fbeb0d166..bce5ca1e16bd1 100644 --- a/compiler/rustc_incremental/src/persist/work_product.rs +++ b/compiler/rustc_incremental/src/persist/work_product.rs @@ -4,7 +4,7 @@ use crate::errors; use crate::persist::fs::*; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::unord::UnordMap; use rustc_fs_util::link_or_copy; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_session::Session; @@ -20,7 +20,7 @@ pub fn copy_cgu_workproduct_to_incr_comp_cache_dir( debug!(?cgu_name, ?files); sess.opts.incremental.as_ref()?; - let mut saved_files = FxHashMap::default(); + let mut saved_files = UnordMap::default(); for (ext, path) in files { let file_name = format!("{cgu_name}.{ext}"); let path_in_incr_dir = in_incr_comp_dir_sess(sess, &file_name); @@ -46,7 +46,7 @@ pub fn copy_cgu_workproduct_to_incr_comp_cache_dir( /// Removes files for a given work product. pub fn delete_workproduct_files(sess: &Session, work_product: &WorkProduct) { - for (_, path) in &work_product.saved_files { + for (_, path) in work_product.saved_files.items().into_sorted_stable_ord() { let path = in_incr_comp_dir_sess(sess, path); if let Err(err) = std_fs::remove_file(&path) { sess.emit_warning(errors::DeleteWorkProduct { path: &path, err }); diff --git a/compiler/rustc_infer/messages.ftl b/compiler/rustc_infer/messages.ftl index f44c4a7c1e33c..4d0e770636721 100644 --- a/compiler/rustc_infer/messages.ftl +++ b/compiler/rustc_infer/messages.ftl @@ -278,9 +278,6 @@ infer_ril_introduced_by = requirement introduced by this return type infer_ril_introduced_here = `'static` requirement introduced here infer_ril_static_introduced_by = "`'static` lifetime requirement introduced by the return type -infer_sarwa_option = you can convert from `&Option` to `Option<&T>` using `.as_ref()` -infer_sarwa_result = you can convert from `&Result` to `Result<&T, &E>` using `.as_ref()` - infer_sbfrit_box_return_expr = if you change the return type to expect trait objects, box the returned expressions infer_sbfrit_change_return_type = you could change the return type to be a boxed trait object diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs index b1e819e83f19b..7e1fa08f23ae2 100644 --- a/compiler/rustc_infer/src/errors/mod.rs +++ b/compiler/rustc_infer/src/errors/mod.rs @@ -1246,30 +1246,6 @@ pub struct FnConsiderCasting { pub casting: String, } -#[derive(Subdiagnostic)] -pub enum SuggestAsRefWhereAppropriate<'a> { - #[suggestion( - infer_sarwa_option, - code = "{snippet}.as_ref()", - applicability = "machine-applicable" - )] - Option { - #[primary_span] - span: Span, - snippet: &'a str, - }, - #[suggestion( - infer_sarwa_result, - code = "{snippet}.as_ref()", - applicability = "machine-applicable" - )] - Result { - #[primary_span] - span: Span, - snippet: &'a str, - }, -} - #[derive(Subdiagnostic)] pub enum SuggestAccessingField<'a> { #[suggestion( diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs index 6b2dd0a2b4fed..1f0bf4f9887c3 100644 --- a/compiler/rustc_infer/src/infer/at.rs +++ b/compiler/rustc_infer/src/infer/at.rs @@ -82,6 +82,7 @@ impl<'tcx> InferCtxt<'tcx> { in_snapshot: self.in_snapshot.clone(), universe: self.universe.clone(), intercrate: self.intercrate, + next_trait_solver: self.next_trait_solver, } } } diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs index b6b935de68c84..7a80ee98cb3c1 100644 --- a/compiler/rustc_infer/src/infer/combine.rs +++ b/compiler/rustc_infer/src/infer/combine.rs @@ -109,11 +109,11 @@ impl<'tcx> InferCtxt<'tcx> { | ( ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)), ty::Alias(AliasKind::Projection, _), - ) if self.tcx.trait_solver_next() => { + ) if self.next_trait_solver() => { bug!() } - (_, ty::Alias(..)) | (ty::Alias(..), _) if self.tcx.trait_solver_next() => { + (_, ty::Alias(..)) | (ty::Alias(..), _) if self.next_trait_solver() => { relation.register_type_relate_obligation(a, b); Ok(a) } @@ -227,9 +227,20 @@ impl<'tcx> InferCtxt<'tcx> { return self.unify_const_variable(vid, a, relation.param_env()); } (ty::ConstKind::Unevaluated(..), _) | (_, ty::ConstKind::Unevaluated(..)) - if self.tcx.lazy_normalization() => + if self.tcx.features().generic_const_exprs || self.next_trait_solver() => { - relation.register_const_equate_obligation(a, b); + let (a, b) = if relation.a_is_expected() { (a, b) } else { (b, a) }; + + relation.register_predicates([ty::Binder::dummy(if self.next_trait_solver() { + ty::PredicateKind::AliasRelate( + a.into(), + b.into(), + ty::AliasRelationDirection::Equate, + ) + } else { + ty::PredicateKind::ConstEquate(a, b) + })]); + return Ok(b); } _ => {} @@ -453,19 +464,6 @@ pub trait ObligationEmittingRelation<'tcx>: TypeRelation<'tcx> { /// be used if control over the obligation causes is required. fn register_predicates(&mut self, obligations: impl IntoIterator>); - /// Register an obligation that both constants must be equal to each other. - /// - /// If they aren't equal then the relation doesn't hold. - fn register_const_equate_obligation(&mut self, a: ty::Const<'tcx>, b: ty::Const<'tcx>) { - let (a, b) = if self.a_is_expected() { (a, b) } else { (b, a) }; - - self.register_predicates([ty::Binder::dummy(if self.tcx().trait_solver_next() { - ty::PredicateKind::AliasRelate(a.into(), b.into(), ty::AliasRelationDirection::Equate) - } else { - ty::PredicateKind::ConstEquate(a, b) - })]); - } - /// Register an obligation that both types must be related to each other according to /// the [`ty::AliasRelationDirection`] given by [`ObligationEmittingRelation::alias_relate_direction`] fn register_type_relate_obligation(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) { diff --git a/compiler/rustc_infer/src/infer/equate.rs b/compiler/rustc_infer/src/infer/equate.rs index 42dfe4f6bb812..495c250a77d07 100644 --- a/compiler/rustc_infer/src/infer/equate.rs +++ b/compiler/rustc_infer/src/infer/equate.rs @@ -105,7 +105,7 @@ impl<'tcx> TypeRelation<'tcx> for Equate<'_, '_, 'tcx> { | (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. })) if self.fields.define_opaque_types == DefineOpaqueTypes::Yes && def_id.is_local() - && !self.tcx().trait_solver_next() => + && !self.fields.infcx.next_trait_solver() => { self.fields.obligations.extend( infcx diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index f8a253c894991..ce5b5882e3b26 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -1897,7 +1897,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { if should_suggest_fixes { self.suggest_tuple_pattern(cause, &exp_found, diag); - self.suggest_as_ref_where_appropriate(span, &exp_found, diag); self.suggest_accessing_field_where_appropriate(cause, &exp_found, diag); self.suggest_await_on_expect_found(cause, span, &exp_found, diag); self.suggest_function_pointers(cause, span, &exp_found, diag); diff --git a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs index d1f110472c927..a723dc3f079d7 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs @@ -374,12 +374,18 @@ impl Trait for X { ) { let tcx = self.tcx; + // Don't suggest constraining a projection to something containing itself + if self.tcx.erase_regions(values.found).contains(self.tcx.erase_regions(values.expected)) { + return; + } + let msg = || { format!( "consider constraining the associated type `{}` to `{}`", values.expected, values.found ) }; + let body_owner = tcx.hir().get_if_local(body_owner_def_id); let current_method_ident = body_owner.and_then(|n| n.ident()).map(|i| i.name); @@ -586,7 +592,7 @@ fn foo(&self) -> Self::T { String::new() } // FIXME: account for returning some type in a trait fn impl that has // an assoc type as a return type (#72076). if let hir::Defaultness::Default { has_value: true } = - tcx.impl_defaultness(item.id.owner_id) + tcx.defaultness(item.id.owner_id) { let assoc_ty = tcx.type_of(item.id.owner_id).subst_identity(); if self.infcx.can_eq(param_env, assoc_ty, found) { diff --git a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs index d885d040707e4..1422bdc9ea282 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs @@ -13,9 +13,9 @@ use rustc_span::{sym, BytePos, Span}; use crate::errors::{ ConsiderAddingAwait, FnConsiderCasting, FnItemsAreDistinct, FnUniqTypes, - FunctionPointerSuggestion, SuggestAccessingField, SuggestAsRefWhereAppropriate, - SuggestBoxingForReturnImplTrait, SuggestRemoveSemiOrReturnBinding, SuggestTuplePatternMany, - SuggestTuplePatternOne, TypeErrorAdditionalDiags, + FunctionPointerSuggestion, SuggestAccessingField, SuggestBoxingForReturnImplTrait, + SuggestRemoveSemiOrReturnBinding, SuggestTuplePatternMany, SuggestTuplePatternOne, + TypeErrorAdditionalDiags, }; use super::TypeErrCtxt; @@ -289,27 +289,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } } - /// When encountering a case where `.as_ref()` on a `Result` or `Option` would be appropriate, - /// suggests it. - pub(super) fn suggest_as_ref_where_appropriate( - &self, - span: Span, - exp_found: &ty::error::ExpectedFound>, - diag: &mut Diagnostic, - ) { - if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) - && let Some(msg) = self.should_suggest_as_ref_kind(exp_found.expected, exp_found.found) - { - // HACK: fix issue# 100605, suggesting convert from &Option to Option<&T>, remove the extra `&` - let snippet = snippet.trim_start_matches('&'); - let subdiag = match msg { - SuggestAsRefKind::Option => SuggestAsRefWhereAppropriate::Option { span, snippet }, - SuggestAsRefKind::Result => SuggestAsRefWhereAppropriate::Result { span, snippet }, - }; - diag.subdiagnostic(subdiag); - } - } - pub(super) fn suggest_function_pointers( &self, cause: &ObligationCause<'tcx>, diff --git a/compiler/rustc_infer/src/infer/lattice.rs b/compiler/rustc_infer/src/infer/lattice.rs index 7190d33d299b7..9ef35429fe32c 100644 --- a/compiler/rustc_infer/src/infer/lattice.rs +++ b/compiler/rustc_infer/src/infer/lattice.rs @@ -113,7 +113,7 @@ where | (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. })) if this.define_opaque_types() == DefineOpaqueTypes::Yes && def_id.is_local() - && !this.tcx().trait_solver_next() => + && !this.infcx().next_trait_solver() => { this.register_obligations( infcx diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 447d4c9f84bc0..9526ed144c349 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -330,6 +330,8 @@ pub struct InferCtxt<'tcx> { /// there is no type that the user could *actually name* that /// would satisfy it. This avoids crippling inference, basically. pub intercrate: bool, + + next_trait_solver: bool, } /// See the `error_reporting` module for more details. @@ -545,6 +547,9 @@ pub struct InferCtxtBuilder<'tcx> { skip_leak_check: bool, /// Whether we are in coherence mode. intercrate: bool, + /// Whether we should use the new trait solver in the local inference context, + /// which affects things like which solver is used in `predicate_may_hold`. + next_trait_solver: bool, } pub trait TyCtxtInferExt<'tcx> { @@ -559,6 +564,7 @@ impl<'tcx> TyCtxtInferExt<'tcx> for TyCtxt<'tcx> { considering_regions: true, skip_leak_check: false, intercrate: false, + next_trait_solver: self.next_trait_solver_globally(), } } } @@ -575,6 +581,11 @@ impl<'tcx> InferCtxtBuilder<'tcx> { self } + pub fn with_next_trait_solver(mut self, next_trait_solver: bool) -> Self { + self.next_trait_solver = next_trait_solver; + self + } + pub fn intercrate(mut self, intercrate: bool) -> Self { self.intercrate = intercrate; self @@ -617,6 +628,7 @@ impl<'tcx> InferCtxtBuilder<'tcx> { considering_regions, skip_leak_check, intercrate, + next_trait_solver, } = *self; InferCtxt { tcx, @@ -634,6 +646,7 @@ impl<'tcx> InferCtxtBuilder<'tcx> { in_snapshot: Cell::new(false), universe: Cell::new(ty::UniverseIndex::ROOT), intercrate, + next_trait_solver, } } } @@ -670,6 +683,10 @@ pub struct CombinedSnapshot<'tcx> { } impl<'tcx> InferCtxt<'tcx> { + pub fn next_trait_solver(&self) -> bool { + self.next_trait_solver + } + /// Creates a `TypeErrCtxt` for emitting various inference errors. /// During typeck, use `FnCtxt::err_ctxt` instead. pub fn err_ctxt(&self) -> TypeErrCtxt<'_, 'tcx> { @@ -1195,15 +1212,15 @@ impl<'tcx> InferCtxt<'tcx> { .var_origin(vid) } - /// Takes ownership of the list of variable regions. This implies - /// that all the region constraints have already been taken, and - /// hence that `resolve_regions_and_report_errors` can never be - /// called. This is used only during NLL processing to "hand off" ownership - /// of the set of region variables into the NLL region context. + /// Clone the list of variable regions. This is used only during NLL processing + /// to put the set of region variables into the NLL region context. pub fn get_region_var_origins(&self) -> VarInfos { let mut inner = self.inner.borrow_mut(); let (var_infos, data) = inner .region_constraint_storage + // We clone instead of taking because borrowck still wants to use + // the inference context after calling this for diagnostics + // and the new trait solver. .clone() .expect("regions already resolved") .with_log(&mut inner.undo_log) diff --git a/compiler/rustc_infer/src/infer/nll_relate/mod.rs b/compiler/rustc_infer/src/infer/nll_relate/mod.rs index d3fd01b964255..71c07f31bc952 100644 --- a/compiler/rustc_infer/src/infer/nll_relate/mod.rs +++ b/compiler/rustc_infer/src/infer/nll_relate/mod.rs @@ -491,12 +491,12 @@ where ( &ty::Alias(ty::Opaque, ty::AliasTy { def_id: a_def_id, .. }), &ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }), - ) if a_def_id == b_def_id || infcx.tcx.trait_solver_next() => { + ) if a_def_id == b_def_id || infcx.next_trait_solver() => { infcx.super_combine_tys(self, a, b).or_else(|err| { // This behavior is only there for the old solver, the new solver // shouldn't ever fail. Instead, it unconditionally emits an // alias-relate goal. - assert!(!self.tcx().trait_solver_next()); + assert!(!self.infcx.next_trait_solver()); self.tcx().sess.delay_span_bug( self.delegate.span(), "failure to relate an opaque to itself should result in an error later on", @@ -506,7 +506,7 @@ where } (&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _) | (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. })) - if def_id.is_local() && !self.tcx().trait_solver_next() => + if def_id.is_local() && !self.infcx.next_trait_solver() => { self.relate_opaques(a, b) } diff --git a/compiler/rustc_infer/src/infer/opaque_types.rs b/compiler/rustc_infer/src/infer/opaque_types.rs index 105a3f08c8205..a9ead429f4c71 100644 --- a/compiler/rustc_infer/src/infer/opaque_types.rs +++ b/compiler/rustc_infer/src/infer/opaque_types.rs @@ -49,7 +49,7 @@ impl<'tcx> InferCtxt<'tcx> { param_env: ty::ParamEnv<'tcx>, ) -> InferOk<'tcx, T> { // We handle opaque types differently in the new solver. - if self.tcx.trait_solver_next() { + if self.next_trait_solver() { return InferOk { value, obligations: vec![] }; } @@ -578,7 +578,7 @@ impl<'tcx> InferCtxt<'tcx> { param_env: ty::ParamEnv<'tcx>, hidden_ty: Ty<'tcx>, ) -> InferResult<'tcx, ()> { - assert!(self.tcx.trait_solver_next()); + assert!(self.next_trait_solver()); let origin = self .opaque_type_origin(opaque_type_key.def_id) .expect("should be called for defining usages only"); @@ -614,7 +614,7 @@ impl<'tcx> InferCtxt<'tcx> { ty::Alias(ty::Projection, projection_ty) if !projection_ty.has_escaping_bound_vars() && !tcx.is_impl_trait_in_trait(projection_ty.def_id) - && !tcx.trait_solver_next() => + && !self.next_trait_solver() => { self.infer_projection( param_env, diff --git a/compiler/rustc_infer/src/infer/projection.rs b/compiler/rustc_infer/src/infer/projection.rs index fa6529dfa93ec..4f8c9188cf85b 100644 --- a/compiler/rustc_infer/src/infer/projection.rs +++ b/compiler/rustc_infer/src/infer/projection.rs @@ -21,7 +21,7 @@ impl<'tcx> InferCtxt<'tcx> { recursion_depth: usize, obligations: &mut Vec>, ) -> Ty<'tcx> { - if self.tcx.trait_solver_next() { + if self.next_trait_solver() { // FIXME(-Ztrait-solver=next): Instead of branching here, // completely change the normalization routine with the new solver. // diff --git a/compiler/rustc_infer/src/infer/sub.rs b/compiler/rustc_infer/src/infer/sub.rs index ceafafb5582cd..d9f9d2aabdbf7 100644 --- a/compiler/rustc_infer/src/infer/sub.rs +++ b/compiler/rustc_infer/src/infer/sub.rs @@ -132,7 +132,7 @@ impl<'tcx> TypeRelation<'tcx> for Sub<'_, '_, 'tcx> { | (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. })) if self.fields.define_opaque_types == DefineOpaqueTypes::Yes && def_id.is_local() - && !self.tcx().trait_solver_next() => + && !self.fields.infcx.next_trait_solver() => { self.fields.obligations.extend( infcx diff --git a/compiler/rustc_infer/src/infer/type_variable.rs b/compiler/rustc_infer/src/infer/type_variable.rs index f7ab05b2d498a..9f85f9207e8f2 100644 --- a/compiler/rustc_infer/src/infer/type_variable.rs +++ b/compiler/rustc_infer/src/infer/type_variable.rs @@ -129,7 +129,6 @@ pub enum TypeVariableOriginKind { /// (before it has been determined). // FIXME(eddyb) distinguish upvar inference variables from the rest. ClosureSynthetic, - SubstitutionPlaceholder, AutoDeref, AdjustmentType, diff --git a/compiler/rustc_interface/Cargo.toml b/compiler/rustc_interface/Cargo.toml index 2c7438ed9db43..7826d42dcb2d7 100644 --- a/compiler/rustc_interface/Cargo.toml +++ b/compiler/rustc_interface/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [lib] [dependencies] +atty = "0.2.13" libloading = "0.7.1" tracing = "0.1" rustc-rayon-core = { version = "0.5.0", optional = true } diff --git a/compiler/rustc_interface/messages.ftl b/compiler/rustc_interface/messages.ftl index be1a75f020b4d..bd9fad8b04296 100644 --- a/compiler/rustc_interface/messages.ftl +++ b/compiler/rustc_interface/messages.ftl @@ -33,6 +33,7 @@ interface_mixed_proc_macro_crate = interface_multiple_output_types_adaption = due to multiple output types requested, the explicitly specified output file name will be adapted for each output type +interface_multiple_output_types_to_stdout = can't use option `-o` or `--emit` to write multiple output types to stdout interface_out_dir_error = failed to find or create the directory specified by `--out-dir` diff --git a/compiler/rustc_interface/src/errors.rs b/compiler/rustc_interface/src/errors.rs index 0eedee250269e..a9ab2720d89a5 100644 --- a/compiler/rustc_interface/src/errors.rs +++ b/compiler/rustc_interface/src/errors.rs @@ -108,3 +108,7 @@ pub struct IgnoringExtraFilename; #[derive(Diagnostic)] #[diag(interface_ignoring_out_dir)] pub struct IgnoringOutDir; + +#[derive(Diagnostic)] +#[diag(interface_multiple_output_types_to_stdout)] +pub struct MultipleOutputTypesToStdout; diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 39d56897999d9..2edc72ba72ea1 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -14,7 +14,7 @@ use rustc_middle::{bug, ty}; use rustc_parse::maybe_new_parser_from_source_str; use rustc_query_impl::QueryCtxt; use rustc_query_system::query::print_query_stack; -use rustc_session::config::{self, ErrorOutputType, Input, OutputFilenames}; +use rustc_session::config::{self, ErrorOutputType, Input, OutFileName, OutputFilenames}; use rustc_session::config::{CheckCfg, ExpectedValues}; use rustc_session::lint; use rustc_session::parse::{CrateConfig, ParseSess}; @@ -252,7 +252,7 @@ pub struct Config { pub input: Input, pub output_dir: Option, - pub output_file: Option, + pub output_file: Option, pub file_loader: Option>, pub locale_resources: &'static [&'static str], diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 42d8d22809178..83a74742f5b76 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -24,7 +24,7 @@ use rustc_parse::{parse_crate_from_file, parse_crate_from_source_str, validate_a use rustc_passes::{self, hir_stats, layout_test}; use rustc_plugin_impl as plugin; use rustc_resolve::Resolver; -use rustc_session::config::{CrateType, Input, OutputFilenames, OutputType}; +use rustc_session::config::{CrateType, Input, OutFileName, OutputFilenames, OutputType}; use rustc_session::cstore::{MetadataLoader, Untracked}; use rustc_session::output::filename_for_input; use rustc_session::search_paths::PathKind; @@ -373,19 +373,23 @@ fn generated_output_paths( ) -> Vec { let mut out_filenames = Vec::new(); for output_type in sess.opts.output_types.keys() { - let file = outputs.path(*output_type); + let out_filename = outputs.path(*output_type); + let file = out_filename.as_path().to_path_buf(); match *output_type { // If the filename has been overridden using `-o`, it will not be modified // by appending `.rlib`, `.exe`, etc., so we can skip this transformation. OutputType::Exe if !exact_name => { for crate_type in sess.crate_types().iter() { let p = filename_for_input(sess, *crate_type, crate_name, outputs); - out_filenames.push(p); + out_filenames.push(p.as_path().to_path_buf()); } } OutputType::DepInfo if sess.opts.unstable_opts.dep_info_omit_d_target => { // Don't add the dep-info output when omitting it from dep-info targets } + OutputType::DepInfo if out_filename.is_stdout() => { + // Don't add the dep-info output when it goes to stdout + } _ => { out_filenames.push(file); } @@ -452,7 +456,8 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P if !sess.opts.output_types.contains_key(&OutputType::DepInfo) { return; } - let deps_filename = outputs.path(OutputType::DepInfo); + let deps_output = outputs.path(OutputType::DepInfo); + let deps_filename = deps_output.as_path(); let result: io::Result<()> = try { // Build a list of files used to compile the output and @@ -515,33 +520,47 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P } } - let mut file = BufWriter::new(fs::File::create(&deps_filename)?); - for path in out_filenames { - writeln!(file, "{}: {}\n", path.display(), files.join(" "))?; - } + let write_deps_to_file = |file: &mut dyn Write| -> io::Result<()> { + for path in out_filenames { + writeln!(file, "{}: {}\n", path.display(), files.join(" "))?; + } - // Emit a fake target for each input file to the compilation. This - // prevents `make` from spitting out an error if a file is later - // deleted. For more info see #28735 - for path in files { - writeln!(file, "{path}:")?; - } + // Emit a fake target for each input file to the compilation. This + // prevents `make` from spitting out an error if a file is later + // deleted. For more info see #28735 + for path in files { + writeln!(file, "{path}:")?; + } - // Emit special comments with information about accessed environment variables. - let env_depinfo = sess.parse_sess.env_depinfo.borrow(); - if !env_depinfo.is_empty() { - let mut envs: Vec<_> = env_depinfo - .iter() - .map(|(k, v)| (escape_dep_env(*k), v.map(escape_dep_env))) - .collect(); - envs.sort_unstable(); - writeln!(file)?; - for (k, v) in envs { - write!(file, "# env-dep:{k}")?; - if let Some(v) = v { - write!(file, "={v}")?; - } + // Emit special comments with information about accessed environment variables. + let env_depinfo = sess.parse_sess.env_depinfo.borrow(); + if !env_depinfo.is_empty() { + let mut envs: Vec<_> = env_depinfo + .iter() + .map(|(k, v)| (escape_dep_env(*k), v.map(escape_dep_env))) + .collect(); + envs.sort_unstable(); writeln!(file)?; + for (k, v) in envs { + write!(file, "# env-dep:{k}")?; + if let Some(v) = v { + write!(file, "={v}")?; + } + writeln!(file)?; + } + } + + Ok(()) + }; + + match deps_output { + OutFileName::Stdout => { + let mut file = BufWriter::new(io::stdout()); + write_deps_to_file(&mut file)?; + } + OutFileName::Real(ref path) => { + let mut file = BufWriter::new(fs::File::create(path)?); + write_deps_to_file(&mut file)?; } } }; diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index c441a8ffd6f94..455a8129656d4 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -5,6 +5,7 @@ use crate::passes; use rustc_ast as ast; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_codegen_ssa::CodegenResults; +use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::steal::Steal; use rustc_data_structures::svh::Svh; use rustc_data_structures::sync::{AppendOnlyIndexVec, Lrc, OnceCell, RwLock, WorkerLocal}; @@ -193,9 +194,15 @@ impl<'tcx> Queries<'tcx> { let future_opt = self.dep_graph_future()?.steal(); let dep_graph = future_opt .and_then(|future| { - let (prev_graph, prev_work_products) = + let (prev_graph, mut prev_work_products) = sess.time("blocked_on_dep_graph_loading", || future.open().open(sess)); - + // Convert from UnordMap to FxIndexMap by sorting + let prev_work_product_ids = + prev_work_products.items().map(|x| *x.0).into_sorted_stable_ord(); + let prev_work_products = prev_work_product_ids + .into_iter() + .map(|x| (x, prev_work_products.remove(&x).unwrap())) + .collect::>(); rustc_incremental::build_dep_graph(sess, prev_graph, prev_work_products) }) .unwrap_or_else(DepGraph::new_disabled); diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 28e719a40e565..77ee2b40e37c1 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -11,7 +11,7 @@ use rustc_session::config::InstrumentXRay; use rustc_session::config::TraitSolver; use rustc_session::config::{build_configuration, build_session_options, to_crate_config}; use rustc_session::config::{ - BranchProtection, Externs, OomStrategy, OutputType, OutputTypes, PAuthKey, PacRet, + BranchProtection, Externs, OomStrategy, OutFileName, OutputType, OutputTypes, PAuthKey, PacRet, ProcMacroExecutionStrategy, SymbolManglingVersion, WasiExecModel, }; use rustc_session::config::{CFGuard, ExternEntry, LinkerPluginLto, LtoCli, SwitchWithOptPath}; @@ -167,8 +167,14 @@ fn test_output_types_tracking_hash_different_paths() { let mut v2 = Options::default(); let mut v3 = Options::default(); - v1.output_types = OutputTypes::new(&[(OutputType::Exe, Some(PathBuf::from("./some/thing")))]); - v2.output_types = OutputTypes::new(&[(OutputType::Exe, Some(PathBuf::from("/some/thing")))]); + v1.output_types = OutputTypes::new(&[( + OutputType::Exe, + Some(OutFileName::Real(PathBuf::from("./some/thing"))), + )]); + v2.output_types = OutputTypes::new(&[( + OutputType::Exe, + Some(OutFileName::Real(PathBuf::from("/some/thing"))), + )]); v3.output_types = OutputTypes::new(&[(OutputType::Exe, None)]); assert_non_crate_hash_different(&v1, &v2); @@ -182,13 +188,13 @@ fn test_output_types_tracking_hash_different_construction_order() { let mut v2 = Options::default(); v1.output_types = OutputTypes::new(&[ - (OutputType::Exe, Some(PathBuf::from("./some/thing"))), - (OutputType::Bitcode, Some(PathBuf::from("./some/thing.bc"))), + (OutputType::Exe, Some(OutFileName::Real(PathBuf::from("./some/thing")))), + (OutputType::Bitcode, Some(OutFileName::Real(PathBuf::from("./some/thing.bc")))), ]); v2.output_types = OutputTypes::new(&[ - (OutputType::Bitcode, Some(PathBuf::from("./some/thing.bc"))), - (OutputType::Exe, Some(PathBuf::from("./some/thing"))), + (OutputType::Bitcode, Some(OutFileName::Real(PathBuf::from("./some/thing.bc")))), + (OutputType::Exe, Some(OutFileName::Real(PathBuf::from("./some/thing")))), ]); assert_same_hash(&v1, &v2); diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index cb19750203e85..87252fefb1e2c 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -11,7 +11,7 @@ use rustc_parse::validate_attr; use rustc_session as session; use rustc_session::config::CheckCfg; use rustc_session::config::{self, CrateType}; -use rustc_session::config::{ErrorOutputType, OutputFilenames}; +use rustc_session::config::{ErrorOutputType, OutFileName, OutputFilenames, OutputTypes}; use rustc_session::filesearch::sysroot_candidates; use rustc_session::lint::{self, BuiltinLintDiagnostics, LintBuffer}; use rustc_session::parse::CrateConfig; @@ -500,7 +500,36 @@ pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec bool { + if atty::is(atty::Stream::Stdout) { + // If stdout is a tty, check if multiple text output types are + // specified by `--emit foo=- --emit bar=-` or `-o - --emit foo,bar` + let named_text_types = output_types + .iter() + .filter(|(f, o)| f.is_text_output() && *o == &Some(OutFileName::Stdout)) + .count(); + let unnamed_text_types = + output_types.iter().filter(|(f, o)| f.is_text_output() && o.is_none()).count(); + named_text_types > 1 || unnamed_text_types > 1 && single_output_file_is_stdout + } else { + // Otherwise, all the output types should be checked + let named_types = + output_types.values().filter(|o| *o == &Some(OutFileName::Stdout)).count(); + let unnamed_types = output_types.values().filter(|o| o.is_none()).count(); + named_types > 1 || unnamed_types > 1 && single_output_file_is_stdout + } +} + pub fn build_output_filenames(attrs: &[ast::Attribute], sess: &Session) -> OutputFilenames { + if multiple_output_types_to_stdout( + &sess.opts.output_types, + sess.io.output_file == Some(OutFileName::Stdout), + ) { + sess.emit_fatal(errors::MultipleOutputTypesToStdout); + } match sess.io.output_file { None => { // "-" as input file will cause the parser to read from stdin so we @@ -544,7 +573,7 @@ pub fn build_output_filenames(attrs: &[ast::Attribute], sess: &Session) -> Outpu OutputFilenames::new( out_file.parent().unwrap_or_else(|| Path::new("")).to_path_buf(), - out_file.file_stem().unwrap_or_default().to_str().unwrap().to_string(), + out_file.filestem().unwrap_or_default().to_str().unwrap().to_string(), ofile, sess.io.temps_dir.clone(), sess.opts.cg.extra_filename.clone(), diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 98fe3821947d5..5e13ee7b8a41d 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -314,6 +314,11 @@ lint_invalid_from_utf8_checked = calls to `{$method}` with a invalid literal alw lint_invalid_from_utf8_unchecked = calls to `{$method}` with a invalid literal are undefined behavior .label = the literal was valid UTF-8 up to the {$valid_up_to} bytes +lint_invalid_nan_comparisons_eq_ne = incorrect NaN comparison, NaN cannot be directly compared to itself + .suggestion = use `f32::is_nan()` or `f64::is_nan()` instead + +lint_invalid_nan_comparisons_lt_le_gt_ge = incorrect NaN comparison, NaN is not orderable + lint_lintpass_by_hand = implementing `LintPass` by hand .help = try using `declare_lint_pass!` or `impl_lint_pass!` instead @@ -492,6 +497,10 @@ lint_tykind = usage of `ty::TyKind` lint_tykind_kind = usage of `ty::TyKind::` .suggestion = try using `ty::` directly +lint_undropped_manually_drops = calls to `std::mem::drop` with `std::mem::ManuallyDrop` instead of the inner value does nothing + .label = argument has type `{$arg_ty}` + .suggestion = use `std::mem::ManuallyDrop::into_inner` to get the inner value + lint_ungated_async_fn_track_caller = `#[track_caller]` on async functions is a no-op .label = this function will not propagate the caller location diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 213e8db66a056..ff2989112af1a 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -286,7 +286,9 @@ impl<'tcx> LateLintPass<'tcx> for NonShorthandFieldPatterns { } declare_lint! { - /// The `unsafe_code` lint catches usage of `unsafe` code. + /// The `unsafe_code` lint catches usage of `unsafe` code and other + /// potentially unsound constructs like `no_mangle`, `export_name`, + /// and `link_section`. /// /// ### Example /// @@ -297,17 +299,29 @@ declare_lint! { /// /// } /// } + /// + /// #[no_mangle] + /// fn func_0() { } + /// + /// #[export_name = "exported_symbol_name"] + /// pub fn name_in_rust() { } + /// + /// #[no_mangle] + /// #[link_section = ".example_section"] + /// pub static VAR1: u32 = 1; /// ``` /// /// {{produces}} /// /// ### Explanation /// - /// This lint is intended to restrict the usage of `unsafe`, which can be - /// difficult to use correctly. + /// This lint is intended to restrict the usage of `unsafe` blocks and other + /// constructs (including, but not limited to `no_mangle`, `link_section` + /// and `export_name` attributes) wrong usage of which causes undefined + /// behavior. UNSAFE_CODE, Allow, - "usage of `unsafe` code" + "usage of `unsafe` code and other potentially unsound constructs" } declare_lint_pass!(UnsafeCode => [UNSAFE_CODE]); @@ -548,8 +562,12 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) { // Previously the Impl and Use types have been excluded from missing docs, - // so we will continue to exclude them for compatibility - if let hir::ItemKind::Impl(..) | hir::ItemKind::Use(..) = it.kind { + // so we will continue to exclude them for compatibility. + // + // The documentation on `ExternCrate` is not used at the moment so no need to warn for it. + if let hir::ItemKind::Impl(..) | hir::ItemKind::Use(..) | hir::ItemKind::ExternCrate(_) = + it.kind + { return; } diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 947530a1b65a9..13164b0b339a3 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -953,8 +953,8 @@ pub trait LintContext: Sized { db.span_label(duplicate_reexport_span, format!("but the name `{}` in the {} namespace is also re-exported here", name, namespace)); } BuiltinLintDiagnostics::HiddenGlobReexports { name, namespace, glob_reexport_span, private_item_span } => { - db.span_label(glob_reexport_span, format!("the name `{}` in the {} namespace is supposed to be publicly re-exported here", name, namespace)); - db.span_label(private_item_span, "but the private item here shadows it"); + db.span_note(glob_reexport_span, format!("the name `{}` in the {} namespace is supposed to be publicly re-exported here", name, namespace)); + db.span_note(private_item_span, "but the private item here shadows it".to_owned()); } } // Rewrap `db`, and pass control to the user. diff --git a/compiler/rustc_lint/src/drop_forget_useless.rs b/compiler/rustc_lint/src/drop_forget_useless.rs index ed2b384805e05..4cea6169dc3d9 100644 --- a/compiler/rustc_lint/src/drop_forget_useless.rs +++ b/compiler/rustc_lint/src/drop_forget_useless.rs @@ -1,8 +1,12 @@ use rustc_hir::{Arm, Expr, ExprKind, Node}; +use rustc_middle::ty; use rustc_span::sym; use crate::{ - lints::{DropCopyDiag, DropRefDiag, ForgetCopyDiag, ForgetRefDiag}, + lints::{ + DropCopyDiag, DropRefDiag, ForgetCopyDiag, ForgetRefDiag, UndroppedManuallyDropsDiag, + UndroppedManuallyDropsSuggestion, + }, LateContext, LateLintPass, LintContext, }; @@ -109,7 +113,29 @@ declare_lint! { "calls to `std::mem::forget` with a value that implements Copy" } -declare_lint_pass!(DropForgetUseless => [DROPPING_REFERENCES, FORGETTING_REFERENCES, DROPPING_COPY_TYPES, FORGETTING_COPY_TYPES]); +declare_lint! { + /// The `undropped_manually_drops` lint check for calls to `std::mem::drop` with + /// a value of `std::mem::ManuallyDrop` which doesn't drop. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// struct S; + /// drop(std::mem::ManuallyDrop::new(S)); + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// `ManuallyDrop` does not drop it's inner value so calling `std::mem::drop` will + /// not drop the inner value of the `ManuallyDrop` either. + pub UNDROPPED_MANUALLY_DROPS, + Deny, + "calls to `std::mem::drop` with `std::mem::ManuallyDrop` instead of it's inner value" +} + +declare_lint_pass!(DropForgetUseless => [DROPPING_REFERENCES, FORGETTING_REFERENCES, DROPPING_COPY_TYPES, FORGETTING_COPY_TYPES, UNDROPPED_MANUALLY_DROPS]); impl<'tcx> LateLintPass<'tcx> for DropForgetUseless { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { @@ -134,6 +160,20 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetUseless { sym::mem_forget if is_copy => { cx.emit_spanned_lint(FORGETTING_COPY_TYPES, expr.span, ForgetCopyDiag { arg_ty, label: arg.span }); } + sym::mem_drop if let ty::Adt(adt, _) = arg_ty.kind() && adt.is_manually_drop() => { + cx.emit_spanned_lint( + UNDROPPED_MANUALLY_DROPS, + expr.span, + UndroppedManuallyDropsDiag { + arg_ty, + label: arg.span, + suggestion: UndroppedManuallyDropsSuggestion { + start_span: arg.span.shrink_to_lo(), + end_span: arg.span.shrink_to_hi() + } + } + ); + } _ => return, }; } diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index fd15f7952023a..e990c771bdf50 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -699,6 +699,25 @@ pub struct ForgetCopyDiag<'a> { pub label: Span, } +#[derive(LintDiagnostic)] +#[diag(lint_undropped_manually_drops)] +pub struct UndroppedManuallyDropsDiag<'a> { + pub arg_ty: Ty<'a>, + #[label] + pub label: Span, + #[subdiagnostic] + pub suggestion: UndroppedManuallyDropsSuggestion, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion(lint_suggestion, applicability = "machine-applicable")] +pub struct UndroppedManuallyDropsSuggestion { + #[suggestion_part(code = "std::mem::ManuallyDrop::into_inner(")] + pub start_span: Span, + #[suggestion_part(code = ")")] + pub end_span: Span, +} + // invalid_from_utf8.rs #[derive(LintDiagnostic)] pub enum InvalidFromUtf8Diag { @@ -1415,6 +1434,36 @@ pub struct OverflowingLiteral<'a> { #[diag(lint_unused_comparisons)] pub struct UnusedComparisons; +#[derive(LintDiagnostic)] +pub enum InvalidNanComparisons { + #[diag(lint_invalid_nan_comparisons_eq_ne)] + EqNe { + #[subdiagnostic] + suggestion: InvalidNanComparisonsSuggestion, + }, + #[diag(lint_invalid_nan_comparisons_lt_le_gt_ge)] + LtLeGtGe, +} + +#[derive(Subdiagnostic)] +pub enum InvalidNanComparisonsSuggestion { + #[multipart_suggestion( + lint_suggestion, + style = "verbose", + applicability = "machine-applicable" + )] + Spanful { + #[suggestion_part(code = "!")] + neg: Option, + #[suggestion_part(code = ".is_nan()")] + float: Span, + #[suggestion_part(code = "")] + nan_plus_binop: Span, + }, + #[help(lint_suggestion)] + Spanless, +} + pub struct ImproperCTypes<'a> { pub ty: Ty<'a>, pub desc: &'a str, diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 4bf4fda8292b6..264a59c5585a0 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -2,10 +2,10 @@ use crate::{ fluent_generated as fluent, lints::{ AtomicOrderingFence, AtomicOrderingLoad, AtomicOrderingStore, ImproperCTypes, - InvalidAtomicOrderingDiag, OnlyCastu8ToChar, OverflowingBinHex, OverflowingBinHexSign, - OverflowingBinHexSub, OverflowingInt, OverflowingIntHelp, OverflowingLiteral, - OverflowingUInt, RangeEndpointOutOfRange, UnusedComparisons, UseInclusiveRange, - VariantSizeDifferencesDiag, + InvalidAtomicOrderingDiag, InvalidNanComparisons, InvalidNanComparisonsSuggestion, + OnlyCastu8ToChar, OverflowingBinHex, OverflowingBinHexSign, OverflowingBinHexSub, + OverflowingInt, OverflowingIntHelp, OverflowingLiteral, OverflowingUInt, + RangeEndpointOutOfRange, UnusedComparisons, UseInclusiveRange, VariantSizeDifferencesDiag, }, }; use crate::{LateContext, LateLintPass, LintContext}; @@ -113,13 +113,35 @@ declare_lint! { "detects enums with widely varying variant sizes" } +declare_lint! { + /// The `invalid_nan_comparisons` lint checks comparison with `f32::NAN` or `f64::NAN` + /// as one of the operand. + /// + /// ### Example + /// + /// ```rust + /// let a = 2.3f32; + /// if a == f32::NAN {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// NaN does not compare meaningfully to anything – not + /// even itself – so those comparisons are always false. + INVALID_NAN_COMPARISONS, + Warn, + "detects invalid floating point NaN comparisons" +} + #[derive(Copy, Clone)] pub struct TypeLimits { /// Id of the last visited negated expression negated_expr_id: Option, } -impl_lint_pass!(TypeLimits => [UNUSED_COMPARISONS, OVERFLOWING_LITERALS]); +impl_lint_pass!(TypeLimits => [UNUSED_COMPARISONS, OVERFLOWING_LITERALS, INVALID_NAN_COMPARISONS]); impl TypeLimits { pub fn new() -> TypeLimits { @@ -486,6 +508,68 @@ fn lint_literal<'tcx>( } } +fn lint_nan<'tcx>( + cx: &LateContext<'tcx>, + e: &'tcx hir::Expr<'tcx>, + binop: hir::BinOp, + l: &'tcx hir::Expr<'tcx>, + r: &'tcx hir::Expr<'tcx>, +) { + fn is_nan(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { + let expr = expr.peel_blocks().peel_borrows(); + match expr.kind { + ExprKind::Path(qpath) => { + let Some(def_id) = cx.typeck_results().qpath_res(&qpath, expr.hir_id).opt_def_id() else { return false; }; + + matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::f32_nan | sym::f64_nan)) + } + _ => false, + } + } + + fn eq_ne( + e: &hir::Expr<'_>, + l: &hir::Expr<'_>, + r: &hir::Expr<'_>, + f: impl FnOnce(Span, Span) -> InvalidNanComparisonsSuggestion, + ) -> InvalidNanComparisons { + let suggestion = + if let Some(l_span) = l.span.find_ancestor_inside(e.span) && + let Some(r_span) = r.span.find_ancestor_inside(e.span) { + f(l_span, r_span) + } else { + InvalidNanComparisonsSuggestion::Spanless + }; + + InvalidNanComparisons::EqNe { suggestion } + } + + let lint = match binop.node { + hir::BinOpKind::Eq | hir::BinOpKind::Ne if is_nan(cx, l) => { + eq_ne(e, l, r, |l_span, r_span| InvalidNanComparisonsSuggestion::Spanful { + nan_plus_binop: l_span.until(r_span), + float: r_span.shrink_to_hi(), + neg: (binop.node == hir::BinOpKind::Ne).then(|| r_span.shrink_to_lo()), + }) + } + hir::BinOpKind::Eq | hir::BinOpKind::Ne if is_nan(cx, r) => { + eq_ne(e, l, r, |l_span, r_span| InvalidNanComparisonsSuggestion::Spanful { + nan_plus_binop: l_span.shrink_to_hi().to(r_span), + float: l_span.shrink_to_hi(), + neg: (binop.node == hir::BinOpKind::Ne).then(|| l_span.shrink_to_lo()), + }) + } + hir::BinOpKind::Lt | hir::BinOpKind::Le | hir::BinOpKind::Gt | hir::BinOpKind::Ge + if is_nan(cx, l) || is_nan(cx, r) => + { + InvalidNanComparisons::LtLeGtGe + } + _ => return, + }; + + cx.emit_spanned_lint(INVALID_NAN_COMPARISONS, e.span, lint); +} + impl<'tcx> LateLintPass<'tcx> for TypeLimits { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx hir::Expr<'tcx>) { match e.kind { @@ -496,8 +580,12 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits { } } hir::ExprKind::Binary(binop, ref l, ref r) => { - if is_comparison(binop) && !check_limits(cx, binop, &l, &r) { - cx.emit_spanned_lint(UNUSED_COMPARISONS, e.span, UnusedComparisons); + if is_comparison(binop) { + if !check_limits(cx, binop, &l, &r) { + cx.emit_spanned_lint(UNUSED_COMPARISONS, e.span, UnusedComparisons); + } else { + lint_nan(cx, e, binop, l, r); + } } } hir::ExprKind::Lit(ref lit) => lint_literal(cx, self, e, lit), diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 8f75fa11dd9a2..04df23c736b0d 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -556,6 +556,7 @@ trait UnusedDelimLint { followed_by_block: bool, left_pos: Option, right_pos: Option, + is_kw: bool, ); fn is_expr_delims_necessary( @@ -624,6 +625,7 @@ trait UnusedDelimLint { ctx: UnusedDelimsCtx, left_pos: Option, right_pos: Option, + is_kw: bool, ) { // If `value` has `ExprKind::Err`, unused delim lint can be broken. // For example, the following code caused ICE. @@ -667,7 +669,7 @@ trait UnusedDelimLint { left_pos.is_some_and(|s| s >= value.span.lo()), right_pos.is_some_and(|s| s <= value.span.hi()), ); - self.emit_unused_delims(cx, value.span, spans, ctx.into(), keep_space); + self.emit_unused_delims(cx, value.span, spans, ctx.into(), keep_space, is_kw); } fn emit_unused_delims( @@ -677,6 +679,7 @@ trait UnusedDelimLint { spans: Option<(Span, Span)>, msg: &str, keep_space: (bool, bool), + is_kw: bool, ) { let primary_span = if let Some((lo, hi)) = spans { if hi.is_empty() { @@ -690,7 +693,7 @@ trait UnusedDelimLint { let suggestion = spans.map(|(lo, hi)| { let sm = cx.sess().source_map(); let lo_replace = - if keep_space.0 && + if (keep_space.0 || is_kw) && let Ok(snip) = sm.span_to_prev_source(lo) && !snip.ends_with(' ') { " " } else { @@ -720,7 +723,7 @@ trait UnusedDelimLint { fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { use rustc_ast::ExprKind::*; - let (value, ctx, followed_by_block, left_pos, right_pos) = match e.kind { + let (value, ctx, followed_by_block, left_pos, right_pos, is_kw) = match e.kind { // Do not lint `unused_braces` in `if let` expressions. If(ref cond, ref block, _) if !matches!(cond.kind, Let(_, _, _)) @@ -728,7 +731,7 @@ trait UnusedDelimLint { { let left = e.span.lo() + rustc_span::BytePos(2); let right = block.span.lo(); - (cond, UnusedDelimsCtx::IfCond, true, Some(left), Some(right)) + (cond, UnusedDelimsCtx::IfCond, true, Some(left), Some(right), true) } // Do not lint `unused_braces` in `while let` expressions. @@ -738,27 +741,27 @@ trait UnusedDelimLint { { let left = e.span.lo() + rustc_span::BytePos(5); let right = block.span.lo(); - (cond, UnusedDelimsCtx::WhileCond, true, Some(left), Some(right)) + (cond, UnusedDelimsCtx::WhileCond, true, Some(left), Some(right), true) } ForLoop(_, ref cond, ref block, ..) => { - (cond, UnusedDelimsCtx::ForIterExpr, true, None, Some(block.span.lo())) + (cond, UnusedDelimsCtx::ForIterExpr, true, None, Some(block.span.lo()), true) } Match(ref head, _) if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => { let left = e.span.lo() + rustc_span::BytePos(5); - (head, UnusedDelimsCtx::MatchScrutineeExpr, true, Some(left), None) + (head, UnusedDelimsCtx::MatchScrutineeExpr, true, Some(left), None, true) } Ret(Some(ref value)) => { let left = e.span.lo() + rustc_span::BytePos(3); - (value, UnusedDelimsCtx::ReturnValue, false, Some(left), None) + (value, UnusedDelimsCtx::ReturnValue, false, Some(left), None, true) } - Index(_, ref value) => (value, UnusedDelimsCtx::IndexExpr, false, None, None), + Index(_, ref value) => (value, UnusedDelimsCtx::IndexExpr, false, None, None, false), Assign(_, ref value, _) | AssignOp(.., ref value) => { - (value, UnusedDelimsCtx::AssignedValue, false, None, None) + (value, UnusedDelimsCtx::AssignedValue, false, None, None, false) } // either function/method call, or something this lint doesn't care about ref call_or_other => { @@ -778,12 +781,20 @@ trait UnusedDelimLint { return; } for arg in args_to_check { - self.check_unused_delims_expr(cx, arg, ctx, false, None, None); + self.check_unused_delims_expr(cx, arg, ctx, false, None, None, false); } return; } }; - self.check_unused_delims_expr(cx, &value, ctx, followed_by_block, left_pos, right_pos); + self.check_unused_delims_expr( + cx, + &value, + ctx, + followed_by_block, + left_pos, + right_pos, + is_kw, + ); } fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) { @@ -794,7 +805,7 @@ trait UnusedDelimLint { None => UnusedDelimsCtx::AssignedValue, Some(_) => UnusedDelimsCtx::AssignedValueLetElse, }; - self.check_unused_delims_expr(cx, init, ctx, false, None, None); + self.check_unused_delims_expr(cx, init, ctx, false, None, None, false); } } StmtKind::Expr(ref expr) => { @@ -805,6 +816,7 @@ trait UnusedDelimLint { false, None, None, + false, ); } _ => {} @@ -824,6 +836,7 @@ trait UnusedDelimLint { false, None, None, + false, ); } } @@ -879,6 +892,7 @@ impl UnusedDelimLint for UnusedParens { followed_by_block: bool, left_pos: Option, right_pos: Option, + is_kw: bool, ) { match value.kind { ast::ExprKind::Paren(ref inner) => { @@ -893,7 +907,7 @@ impl UnusedDelimLint for UnusedParens { _, ) if node.lazy())) { - self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos) + self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos, is_kw) } } ast::ExprKind::Let(_, ref expr, _) => { @@ -904,6 +918,7 @@ impl UnusedDelimLint for UnusedParens { followed_by_block, None, None, + false, ); } _ => {} @@ -942,7 +957,7 @@ impl UnusedParens { .span .find_ancestor_inside(value.span) .map(|inner| (value.span.with_hi(inner.lo()), value.span.with_lo(inner.hi()))); - self.emit_unused_delims(cx, value.span, spans, "pattern", keep_space); + self.emit_unused_delims(cx, value.span, spans, "pattern", keep_space, false); } } } @@ -967,6 +982,7 @@ impl EarlyLintPass for UnusedParens { true, None, None, + true, ); for stmt in &block.stmts { ::check_stmt(self, cx, stmt); @@ -985,6 +1001,7 @@ impl EarlyLintPass for UnusedParens { false, None, None, + true, ); } } @@ -1043,6 +1060,7 @@ impl EarlyLintPass for UnusedParens { false, None, None, + false, ); } ast::TyKind::Paren(r) => { @@ -1057,7 +1075,7 @@ impl EarlyLintPass for UnusedParens { .find_ancestor_inside(ty.span) .map(|r| (ty.span.with_hi(r.lo()), ty.span.with_lo(r.hi()))); - self.emit_unused_delims(cx, ty.span, spans, "type", (false, false)); + self.emit_unused_delims(cx, ty.span, spans, "type", (false, false), false); } } self.with_self_ty_parens = false; @@ -1130,6 +1148,7 @@ impl UnusedDelimLint for UnusedBraces { followed_by_block: bool, left_pos: Option, right_pos: Option, + is_kw: bool, ) { match value.kind { ast::ExprKind::Block(ref inner, None) @@ -1170,7 +1189,7 @@ impl UnusedDelimLint for UnusedBraces { && !value.span.from_expansion() && !inner.span.from_expansion() { - self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos) + self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos, is_kw) } } } @@ -1183,6 +1202,7 @@ impl UnusedDelimLint for UnusedBraces { followed_by_block, None, None, + false, ); } _ => {} @@ -1207,6 +1227,7 @@ impl EarlyLintPass for UnusedBraces { false, None, None, + false, ); } } @@ -1220,6 +1241,7 @@ impl EarlyLintPass for UnusedBraces { false, None, None, + false, ); } } @@ -1233,6 +1255,7 @@ impl EarlyLintPass for UnusedBraces { false, None, None, + false, ); } } @@ -1247,6 +1270,7 @@ impl EarlyLintPass for UnusedBraces { false, None, None, + false, ); } @@ -1258,6 +1282,7 @@ impl EarlyLintPass for UnusedBraces { false, None, None, + false, ); } diff --git a/compiler/rustc_metadata/messages.ftl b/compiler/rustc_metadata/messages.ftl index 6d8601b9e2bc5..13b3dac85d109 100644 --- a/compiler/rustc_metadata/messages.ftl +++ b/compiler/rustc_metadata/messages.ftl @@ -4,6 +4,9 @@ metadata_as_needed_compatibility = metadata_bad_panic_strategy = the linked panic runtime `{$runtime}` is not compiled with this crate's panic strategy `{$strategy}` +metadata_binary_output_to_tty = + option `-o` or `--emit` is used to write binary output type `metadata` to stdout, but stdout is a tty + metadata_bundle_needs_static = linking modifier `bundle` is only compatible with `static` linking kind @@ -63,6 +66,9 @@ metadata_fail_seek_file = metadata_fail_write_file = failed to write to the file: {$err} +metadata_failed_copy_to_stdout = + failed to copy {$filename} to stdout: {$err} + metadata_failed_create_encoded_metadata = failed to create encoded metadata from file: {$err} diff --git a/compiler/rustc_metadata/src/errors.rs b/compiler/rustc_metadata/src/errors.rs index a44c1dd582ed3..fca06c0f47c22 100644 --- a/compiler/rustc_metadata/src/errors.rs +++ b/compiler/rustc_metadata/src/errors.rs @@ -395,6 +395,17 @@ pub struct FailedWriteError { pub err: Error, } +#[derive(Diagnostic)] +#[diag(metadata_failed_copy_to_stdout)] +pub struct FailedCopyToStdout { + pub filename: PathBuf, + pub err: Error, +} + +#[derive(Diagnostic)] +#[diag(metadata_binary_output_to_tty)] +pub struct BinaryOutputToTty; + #[derive(Diagnostic)] #[diag(metadata_missing_native_library)] pub struct MissingNativeLibrary<'a> { diff --git a/compiler/rustc_metadata/src/fs.rs b/compiler/rustc_metadata/src/fs.rs index 08de828fbdba2..238f963ed4630 100644 --- a/compiler/rustc_metadata/src/fs.rs +++ b/compiler/rustc_metadata/src/fs.rs @@ -1,18 +1,19 @@ use crate::errors::{ - FailedCreateEncodedMetadata, FailedCreateFile, FailedCreateTempdir, FailedWriteError, + BinaryOutputToTty, FailedCopyToStdout, FailedCreateEncodedMetadata, FailedCreateFile, + FailedCreateTempdir, FailedWriteError, }; use crate::{encode_metadata, EncodedMetadata}; use rustc_data_structures::temp_dir::MaybeTempDir; use rustc_hir::def_id::LOCAL_CRATE; use rustc_middle::ty::TyCtxt; -use rustc_session::config::OutputType; +use rustc_session::config::{OutFileName, OutputType}; use rustc_session::output::filename_for_metadata; use rustc_session::{MetadataKind, Session}; use tempfile::Builder as TempFileBuilder; -use std::fs; use std::path::{Path, PathBuf}; +use std::{fs, io}; // FIXME(eddyb) maybe include the crate name in this? pub const METADATA_FILENAME: &str = "lib.rmeta"; @@ -74,23 +75,37 @@ pub fn encode_and_write_metadata(tcx: TyCtxt<'_>) -> (EncodedMetadata, bool) { // this file always exists. let need_metadata_file = tcx.sess.opts.output_types.contains_key(&OutputType::Metadata); let (metadata_filename, metadata_tmpdir) = if need_metadata_file { - if let Err(err) = non_durable_rename(&metadata_filename, &out_filename) { - tcx.sess.emit_fatal(FailedWriteError { filename: out_filename, err }); - } + let filename = match out_filename { + OutFileName::Real(ref path) => { + if let Err(err) = non_durable_rename(&metadata_filename, path) { + tcx.sess.emit_fatal(FailedWriteError { filename: path.to_path_buf(), err }); + } + path.clone() + } + OutFileName::Stdout => { + if out_filename.is_tty() { + tcx.sess.emit_err(BinaryOutputToTty); + } else if let Err(err) = copy_to_stdout(&metadata_filename) { + tcx.sess + .emit_err(FailedCopyToStdout { filename: metadata_filename.clone(), err }); + } + metadata_filename + } + }; if tcx.sess.opts.json_artifact_notifications { tcx.sess .parse_sess .span_diagnostic - .emit_artifact_notification(&out_filename, "metadata"); + .emit_artifact_notification(&out_filename.as_path(), "metadata"); } - (out_filename, None) + (filename, None) } else { (metadata_filename, Some(metadata_tmpdir)) }; // Load metadata back to memory: codegen may need to include it in object files. - let metadata = - EncodedMetadata::from_path(metadata_filename, metadata_tmpdir).unwrap_or_else(|err| { + let metadata = EncodedMetadata::from_path(metadata_filename.clone(), metadata_tmpdir) + .unwrap_or_else(|err| { tcx.sess.emit_fatal(FailedCreateEncodedMetadata { err }); }); @@ -116,3 +131,11 @@ pub fn non_durable_rename(src: &Path, dst: &Path) -> std::io::Result<()> { let _ = std::fs::remove_file(dst); std::fs::rename(src, dst) } + +pub fn copy_to_stdout(from: &Path) -> io::Result<()> { + let file = fs::File::open(from)?; + let mut reader = io::BufReader::new(file); + let mut stdout = io::stdout(); + io::copy(&mut reader, &mut stdout)?; + Ok(()) +} diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 8f883bdcf12b8..21cbab542933c 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -995,6 +995,15 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { ) } + fn get_stripped_cfg_items(self, cnum: CrateNum, tcx: TyCtxt<'tcx>) -> &'tcx [StrippedCfgItem] { + let item_names = self + .root + .stripped_cfg_items + .decode((self, tcx)) + .map(|item| item.map_mod_id(|index| DefId { krate: cnum, index })); + tcx.arena.alloc_from_iter(item_names) + } + /// Iterates over the diagnostic items in the given crate. fn get_diagnostic_items(self) -> DiagnosticItems { let mut id_to_name = FxHashMap::default(); diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index a15307e4345c1..77f9fcfc5e6b4 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -231,7 +231,7 @@ provide! { tcx, def_id, other, cdata, opt_def_kind => { table_direct } impl_parent => { table } impl_polarity => { table_direct } - impl_defaultness => { table_direct } + defaultness => { table_direct } constness => { table_direct } coerce_unsized_info => { table } mir_const_qualif => { table } @@ -345,6 +345,7 @@ provide! { tcx, def_id, other, cdata, stability_implications => { cdata.get_stability_implications(tcx).iter().copied().collect() } + stripped_cfg_items => { cdata.get_stripped_cfg_items(cdata.cnum, tcx) } is_intrinsic => { cdata.get_is_intrinsic(def_id.index) } defined_lang_items => { cdata.get_lang_items(tcx) } diagnostic_items => { cdata.get_diagnostic_items() } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 6ceb61e793e53..9c3b8780d97aa 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -3,6 +3,7 @@ use crate::rmeta::def_path_hash_map::DefPathHashMapRef; use crate::rmeta::table::TableBuilder; use crate::rmeta::*; +use rustc_ast::expand::StrippedCfgItem; use rustc_ast::Attribute; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; @@ -584,6 +585,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { (self.encode_lang_items(), self.encode_lang_items_missing()) }); + let stripped_cfg_items = stat!("stripped-cfg-items", || self.encode_stripped_cfg_items()); + let diagnostic_items = stat!("diagnostic-items", || self.encode_diagnostic_items()); let native_libraries = stat!("native-libs", || self.encode_native_libraries()); @@ -694,6 +697,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { lang_items, diagnostic_items, lang_items_missing, + stripped_cfg_items, native_libraries, foreign_modules, source_map, @@ -1191,7 +1195,7 @@ fn should_encode_type(tcx: TyCtxt<'_>, def_id: LocalDefId, def_kind: DefKind) -> fn should_encode_const(def_kind: DefKind) -> bool { match def_kind { - DefKind::Const | DefKind::AssocConst | DefKind::AnonConst => true, + DefKind::Const | DefKind::AssocConst | DefKind::AnonConst | DefKind::InlineConst => true, DefKind::Struct | DefKind::Union @@ -1210,7 +1214,6 @@ fn should_encode_const(def_kind: DefKind) -> bool { | DefKind::Closure | DefKind::Generator | DefKind::ConstParam - | DefKind::InlineConst | DefKind::AssocTy | DefKind::TyParam | DefKind::Trait @@ -1437,8 +1440,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { debug!("EncodeContext::encode_info_for_trait_item({:?})", def_id); let tcx = self.tcx; - let impl_defaultness = tcx.impl_defaultness(def_id.expect_local()); - self.tables.impl_defaultness.set_some(def_id.index, impl_defaultness); + let defaultness = tcx.defaultness(def_id.expect_local()); + self.tables.defaultness.set_some(def_id.index, defaultness); let trait_item = tcx.associated_item(def_id); self.tables.assoc_container.set_some(def_id.index, trait_item.container); @@ -1466,8 +1469,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { debug!("EncodeContext::encode_info_for_impl_item({:?})", def_id); let tcx = self.tcx; - let defaultness = self.tcx.impl_defaultness(def_id.expect_local()); - self.tables.impl_defaultness.set_some(def_id.index, defaultness); + let defaultness = self.tcx.defaultness(def_id.expect_local()); + self.tables.defaultness.set_some(def_id.index, defaultness); let impl_item = self.tcx.associated_item(def_id); self.tables.assoc_container.set_some(def_id.index, impl_item.container); @@ -1653,7 +1656,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { ); } hir::ItemKind::Impl(hir::Impl { defaultness, constness, .. }) => { - self.tables.impl_defaultness.set_some(def_id.index, *defaultness); + self.tables.defaultness.set_some(def_id.index, *defaultness); self.tables.constness.set_some(def_id.index, *constness); self.tables.impl_polarity.set_some(def_id.index, self.tcx.impl_polarity(def_id)); @@ -1940,6 +1943,15 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { self.lazy_array(&tcx.lang_items().missing) } + fn encode_stripped_cfg_items(&mut self) -> LazyArray> { + self.lazy_array( + self.tcx + .stripped_cfg_items(LOCAL_CRATE) + .into_iter() + .map(|item| item.clone().map_mod_id(|def_id| def_id.index)), + ) + } + fn encode_traits(&mut self) -> LazyArray { empty_proc_macro!(self); self.lazy_array(self.tcx.traits(LOCAL_CRATE).iter().map(|def_id| def_id.index)) diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 2da888f4468c0..66048f3ece747 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -6,6 +6,7 @@ use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use table::TableBuilder; use rustc_ast as ast; +use rustc_ast::expand::StrippedCfgItem; use rustc_attr as attr; use rustc_data_structures::svh::Svh; use rustc_hir as hir; @@ -256,6 +257,7 @@ pub(crate) struct CrateRoot { stability_implications: LazyArray<(Symbol, Symbol)>, lang_items: LazyArray<(DefIndex, LangItem)>, lang_items_missing: LazyArray, + stripped_cfg_items: LazyArray>, diagnostic_items: LazyArray<(Symbol, DefIndex)>, native_libraries: LazyArray, foreign_modules: LazyArray, @@ -420,7 +422,7 @@ define_tables! { impl_parent: Table, impl_polarity: Table, constness: Table, - impl_defaultness: Table, + defaultness: Table, // FIXME(eddyb) perhaps compute this on the fly if cheap enough? coerce_unsized_info: Table>, mir_const_qualif: Table>, diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index a149a61ec136e..6c404fbb7c684 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -124,6 +124,7 @@ macro_rules! arena_types { [] predefined_opaques_in_body: rustc_middle::traits::solve::PredefinedOpaquesData<'tcx>, [decode] doc_link_resolutions: rustc_hir::def::DocLinkResMap, [] closure_kind_origin: (rustc_span::Span, rustc_middle::hir::place::Place<'tcx>), + [] stripped_cfg_items: rustc_ast::expand::StrippedCfgItem, [] mod_child: rustc_middle::metadata::ModChild, ]); ) diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index d1ddc8fc1fd2e..5f2eb890c4eba 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -44,6 +44,7 @@ pub fn associated_body(node: Node<'_>) -> Option<(LocalDefId, BodyId)> { } Node::AnonConst(constant) => Some((constant.def_id, constant.body)), + Node::ConstBlock(constant) => Some((constant.def_id, constant.body)), _ => None, } @@ -240,15 +241,8 @@ impl<'hir> Map<'hir> { None => bug!("constructor node without a constructor"), } } - Node::AnonConst(_) => { - let inline = match self.find_parent(hir_id) { - Some(Node::Expr(&Expr { - kind: ExprKind::ConstBlock(ref anon_const), .. - })) if anon_const.hir_id == hir_id => true, - _ => false, - }; - if inline { DefKind::InlineConst } else { DefKind::AnonConst } - } + Node::AnonConst(_) => DefKind::AnonConst, + Node::ConstBlock(_) => DefKind::InlineConst, Node::Field(_) => DefKind::Field, Node::Expr(expr) => match expr.kind { ExprKind::Closure(Closure { movability: None, .. }) => DefKind::Closure, @@ -1060,6 +1054,7 @@ impl<'hir> Map<'hir> { Node::Variant(variant) => variant.span, Node::Field(field) => field.span, Node::AnonConst(constant) => self.body(constant.body).value.span, + Node::ConstBlock(constant) => self.body(constant.body).value.span, Node::Expr(expr) => expr.span, Node::ExprField(field) => field.span, Node::Stmt(stmt) => stmt.span, @@ -1289,6 +1284,7 @@ fn hir_id_to_string(map: Map<'_>, id: HirId) -> String { format!("{id} (field `{}` in {})", field.ident, path_str(field.def_id)) } Some(Node::AnonConst(_)) => node_str("const"), + Some(Node::ConstBlock(_)) => node_str("const"), Some(Node::Expr(_)) => node_str("expr"), Some(Node::ExprField(_)) => node_str("expr field"), Some(Node::Stmt(_)) => node_str("stmt"), @@ -1434,6 +1430,11 @@ impl<'hir> Visitor<'hir> for ItemCollector<'hir> { intravisit::walk_anon_const(self, c) } + fn visit_inline_const(&mut self, c: &'hir ConstBlock) { + self.body_owners.push(c.def_id); + intravisit::walk_inline_const(self, c) + } + fn visit_expr(&mut self, ex: &'hir Expr<'hir>) { if let ExprKind::Closure(closure) = ex.kind { self.body_owners.push(closure.def_id); diff --git a/compiler/rustc_middle/src/infer/unify_key.rs b/compiler/rustc_middle/src/infer/unify_key.rs index a873854f0686a..991b9f01985ca 100644 --- a/compiler/rustc_middle/src/infer/unify_key.rs +++ b/compiler/rustc_middle/src/infer/unify_key.rs @@ -116,7 +116,6 @@ pub enum ConstVariableOriginKind { MiscVariable, ConstInference, ConstParameterDefinition(Symbol, DefId), - SubstitutionPlaceholder, } #[derive(Copy, Clone, Debug)] diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs index 91caf9db336cf..0416411dfe14a 100644 --- a/compiler/rustc_middle/src/mir/interpret/value.rs +++ b/compiler/rustc_middle/src/mir/interpret/value.rs @@ -97,6 +97,10 @@ impl<'tcx> ConstValue<'tcx> { ConstValue::Scalar(Scalar::from_u64(i)) } + pub fn from_u128(i: u128) -> Self { + ConstValue::Scalar(Scalar::from_u128(i)) + } + pub fn from_target_usize(i: u64, cx: &impl HasDataLayout) -> Self { ConstValue::Scalar(Scalar::from_target_usize(i, cx)) } @@ -240,6 +244,11 @@ impl Scalar { Scalar::Int(i.into()) } + #[inline] + pub fn from_u128(i: u128) -> Self { + Scalar::Int(i.into()) + } + #[inline] pub fn from_target_usize(i: u64, cx: &impl HasDataLayout) -> Self { Self::from_uint(i, cx.data_layout().pointer_size) diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 83f8f00d72ce2..1a65f74f4fe22 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -1273,13 +1273,18 @@ pub enum BinOp { Mul, /// The `/` operator (division) /// - /// Division by zero is UB, because the compiler should have inserted checks - /// prior to this. + /// For integer types, division by zero is UB, as is `MIN / -1` for signed. + /// The compiler should have inserted checks prior to this. + /// + /// Floating-point division by zero is safe, and does not need guards. Div, /// The `%` operator (modulus) /// - /// Using zero as the modulus (second operand) is UB, because the compiler - /// should have inserted checks prior to this. + /// For integer types, using zero as the modulus (second operand) is UB, + /// as is `MIN % -1` for signed. + /// The compiler should have inserted checks prior to this. + /// + /// Floating-point remainder by zero is safe, and does not need guards. Rem, /// The `^` operator (bitwise xor) BitXor, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 0b31c9bbf8149..a6c8d41e925f1 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -54,7 +54,7 @@ use crate::ty::{ }; use rustc_arena::TypedArena; use rustc_ast as ast; -use rustc_ast::expand::allocator::AllocatorKind; +use rustc_ast::expand::{allocator::AllocatorKind, StrippedCfgItem}; use rustc_attr as attr; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet}; @@ -1475,8 +1475,9 @@ rustc_queries! { desc { "getting traits in scope at a block" } } - query impl_defaultness(def_id: DefId) -> hir::Defaultness { - desc { |tcx| "looking up whether `{}` is a default impl", tcx.def_path_str(def_id) } + /// Returns whether the impl or associated function has the `default` keyword. + query defaultness(def_id: DefId) -> hir::Defaultness { + desc { |tcx| "looking up whether `{}` has `default`", tcx.def_path_str(def_id) } separate_provide_extern feedable } @@ -2173,6 +2174,15 @@ rustc_queries! { query check_tys_might_be_eq(arg: Canonical<'tcx, (ty::ParamEnv<'tcx>, Ty<'tcx>, Ty<'tcx>)>) -> Result<(), NoSolution> { desc { "check whether two const param are definitely not equal to eachother"} } + + /// Get all item paths that were stripped by a `#[cfg]` in a particular crate. + /// Should not be called for the local crate before the resolver outputs are created, as it + /// is only fed there. + query stripped_cfg_items(cnum: CrateNum) -> &'tcx [StrippedCfgItem] { + feedable + desc { "getting cfg-ed out item names" } + separate_provide_extern + } } rustc_query_append! { define_callbacks! } diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 0a903a76974da..bf3872e81d4a7 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -445,6 +445,9 @@ pub enum ObligationCauseCode<'tcx> { /// Obligations to prove that a `std::ops::Drop` impl is not stronger than /// the ADT it's being implemented for. DropImpl, + + /// Requirement for a `const N: Ty` to implement `Ty: ConstParamTy` + ConstParam(Ty<'tcx>), } /// The 'location' at which we try to perform HIR-based wf checking. diff --git a/compiler/rustc_middle/src/traits/specialization_graph.rs b/compiler/rustc_middle/src/traits/specialization_graph.rs index c016f72275099..dc2cd20356094 100644 --- a/compiler/rustc_middle/src/traits/specialization_graph.rs +++ b/compiler/rustc_middle/src/traits/specialization_graph.rs @@ -228,7 +228,7 @@ impl<'tcx> Ancestors<'tcx> { if let Some(item) = node.item(tcx, trait_item_def_id) { if finalizing_node.is_none() { let is_specializable = item.defaultness(tcx).is_default() - || tcx.impl_defaultness(node.def_id()).is_default(); + || tcx.defaultness(node.def_id()).is_default(); if !is_specializable { finalizing_node = Some(node); diff --git a/compiler/rustc_middle/src/ty/assoc.rs b/compiler/rustc_middle/src/ty/assoc.rs index 090b769323add..cce609c261e8d 100644 --- a/compiler/rustc_middle/src/ty/assoc.rs +++ b/compiler/rustc_middle/src/ty/assoc.rs @@ -48,7 +48,7 @@ impl AssocItem { /// /// [`type_of`]: crate::ty::TyCtxt::type_of pub fn defaultness(&self, tcx: TyCtxt<'_>) -> hir::Defaultness { - tcx.impl_defaultness(self.def_id) + tcx.defaultness(self.def_id) } #[inline] diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index b05e791211d10..4ee543bbfb6dc 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -478,6 +478,17 @@ impl<'tcx> TyCtxtFeed<'tcx, LocalDefId> { /// [rustc dev guide] for more details. /// /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/ty.html +/// +/// An implementation detail: `TyCtxt` is a wrapper type for [GlobalCtxt], +/// which is the struct that actually holds all the data. `TyCtxt` derefs to +/// `GlobalCtxt`, and in practice `TyCtxt` is passed around everywhere, and all +/// operations are done via `TyCtxt`. A `TyCtxt` is obtained for a `GlobalCtxt` +/// by calling `enter` with a closure `f`. That function creates both the +/// `TyCtxt`, and an `ImplicitCtxt` around it that is put into TLS. Within `f`: +/// - The `ImplicitCtxt` is available implicitly via TLS. +/// - The `TyCtxt` is available explicitly via the `tcx` parameter, and also +/// implicitly within the `ImplicitCtxt`. Explicit access is preferred when +/// possible. #[derive(Copy, Clone)] #[rustc_diagnostic_item = "TyCtxt"] #[rustc_pass_by_value] @@ -493,6 +504,7 @@ impl<'tcx> Deref for TyCtxt<'tcx> { } } +/// See [TyCtxt] for details about this type. pub struct GlobalCtxt<'tcx> { pub arena: &'tcx WorkerLocal>, pub hir_arena: &'tcx WorkerLocal>, @@ -1003,15 +1015,6 @@ impl<'tcx> TyCtxt<'tcx> { self.query_system.on_disk_cache.as_ref().map_or(Ok(0), |c| c.serialize(self, encoder)) } - /// If `true`, we should use lazy normalization for constants, otherwise - /// we still evaluate them eagerly. - #[inline] - pub fn lazy_normalization(self) -> bool { - let features = self.features(); - // Note: We only use lazy normalization for generic const expressions. - features.generic_const_exprs - } - #[inline] pub fn local_crate_exports_generics(self) -> bool { debug_assert!(self.sess.opts.share_generics()); @@ -1872,18 +1875,28 @@ impl<'tcx> TyCtxt<'tcx> { } #[inline] - pub fn mk_closure(self, closure_id: DefId, closure_substs: SubstsRef<'tcx>) -> Ty<'tcx> { - self.mk_ty_from_kind(Closure(closure_id, closure_substs)) + pub fn mk_closure(self, def_id: DefId, closure_substs: SubstsRef<'tcx>) -> Ty<'tcx> { + debug_assert_eq!( + closure_substs.len(), + self.generics_of(self.typeck_root_def_id(def_id)).count() + 3, + "closure constructed with incorrect substitutions" + ); + self.mk_ty_from_kind(Closure(def_id, closure_substs)) } #[inline] pub fn mk_generator( self, - id: DefId, + def_id: DefId, generator_substs: SubstsRef<'tcx>, movability: hir::Movability, ) -> Ty<'tcx> { - self.mk_ty_from_kind(Generator(id, generator_substs, movability)) + debug_assert_eq!( + generator_substs.len(), + self.generics_of(self.typeck_root_def_id(def_id)).count() + 5, + "generator constructed with incorrect number of substitutions" + ); + self.mk_ty_from_kind(Generator(def_id, generator_substs, movability)) } #[inline] @@ -2320,10 +2333,18 @@ impl<'tcx> TyCtxt<'tcx> { self.opt_local_def_id_to_hir_id(local_def_id).unwrap() } - pub fn trait_solver_next(self) -> bool { + pub fn next_trait_solver_globally(self) -> bool { self.sess.opts.unstable_opts.trait_solver == rustc_session::config::TraitSolver::Next } + pub fn next_trait_solver_in_coherence(self) -> bool { + matches!( + self.sess.opts.unstable_opts.trait_solver, + rustc_session::config::TraitSolver::Next + | rustc_session::config::TraitSolver::NextCoherence + ) + } + pub fn lower_impl_trait_in_trait_to_assoc_ty(self) -> bool { self.sess.opts.unstable_opts.lower_impl_trait_in_trait_to_assoc_ty } diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index e641d1ef1be6f..c0d591430f7fc 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -586,7 +586,7 @@ impl<'tcx> Instance<'tcx> { if let Some(substs) = self.substs_for_mir_body() { v.subst(tcx, substs) } else { - v.skip_binder() + v.subst_identity() } } diff --git a/compiler/rustc_middle/src/ty/parameterized.rs b/compiler/rustc_middle/src/ty/parameterized.rs index a2e77d9cdfe38..13be15269f4c2 100644 --- a/compiler/rustc_middle/src/ty/parameterized.rs +++ b/compiler/rustc_middle/src/ty/parameterized.rs @@ -73,6 +73,7 @@ trivially_parameterized_over_tcx! { ty::fast_reject::SimplifiedType, rustc_ast::Attribute, rustc_ast::DelimArgs, + rustc_ast::expand::StrippedCfgItem, rustc_attr::ConstStability, rustc_attr::DefaultBodyStability, rustc_attr::Deprecation, diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index dce2f5545f51d..c9f69c37782e3 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -11,7 +11,7 @@ use crate::ty::{ use crate::ty::{GenericArgKind, SubstsRef}; use rustc_apfloat::Float as _; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_data_structures::stable_hasher::{Hash64, HashStable, StableHasher}; +use rustc_data_structures::stable_hasher::{Hash128, HashStable, StableHasher}; use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; @@ -129,7 +129,7 @@ impl IntTypeExt for IntegerType { impl<'tcx> TyCtxt<'tcx> { /// Creates a hash of the type `Ty` which will be the same no matter what crate /// context it's calculated within. This is used by the `type_id` intrinsic. - pub fn type_id_hash(self, ty: Ty<'tcx>) -> Hash64 { + pub fn type_id_hash(self, ty: Ty<'tcx>) -> Hash128 { // We want the type_id be independent of the types free regions, so we // erase them. The erase_regions() call will also anonymize bound // regions, which is desirable too. @@ -173,18 +173,6 @@ impl<'tcx> TyCtxt<'tcx> { } } - pub fn has_error_field(self, ty: Ty<'tcx>) -> bool { - if let ty::Adt(def, substs) = *ty.kind() { - for field in def.all_fields() { - let field_ty = field.ty(self, substs); - if let ty::Error(_) = field_ty.kind() { - return true; - } - } - } - false - } - /// Attempts to returns the deeply last field of nested structures, but /// does not apply any normalization in its search. Returns the same type /// if input `ty` is not a structure at all. diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs index 8f6a069a7db54..7f0c1d53f729a 100644 --- a/compiler/rustc_mir_build/src/build/mod.rs +++ b/compiler/rustc_mir_build/src/build/mod.rs @@ -556,7 +556,7 @@ fn construct_const<'a, 'tcx>( span, .. }) => (*span, ty.span), - Node::AnonConst(_) => { + Node::AnonConst(_) | Node::ConstBlock(_) => { let span = tcx.def_span(def); (span, span) } diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 1e51cb9aa96e7..e7e88351bbd1e 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -443,7 +443,12 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { let mut let_suggestion = None; let mut misc_suggestion = None; let mut interpreted_as_const = None; - if let PatKind::Constant { .. } = pat.kind + + if let PatKind::Constant { .. } + | PatKind::AscribeUserType { + subpattern: box Pat { kind: PatKind::Constant { .. }, .. }, + .. + } = pat.kind && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(pat.span) { // If the pattern to match is an integer literal: diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 1bbe7b45c1ed2..8eeb055ed82d0 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -582,22 +582,15 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { /// Converts inline const patterns. fn lower_inline_const( &mut self, - anon_const: &'tcx hir::AnonConst, + block: &'tcx hir::ConstBlock, id: hir::HirId, span: Span, ) -> PatKind<'tcx> { let tcx = self.tcx; - let def_id = anon_const.def_id; - let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); - let body_id = match tcx.hir().get(hir_id) { - hir::Node::AnonConst(ac) => ac.body, - _ => span_bug!( - tcx.def_span(def_id.to_def_id()), - "from_inline_const can only process anonymous constants" - ), - }; + let def_id = block.def_id; + let body_id = block.body; let expr = &tcx.hir().body(body_id).value; - let ty = tcx.typeck(def_id).node_type(hir_id); + let ty = tcx.typeck(def_id).node_type(block.hir_id); // Special case inline consts that are just literals. This is solely // a performance optimization, as we could also just go through the regular diff --git a/compiler/rustc_mir_dataflow/src/framework/cursor.rs b/compiler/rustc_mir_dataflow/src/framework/cursor.rs index f3b5544aa8b9d..c978bddfef540 100644 --- a/compiler/rustc_mir_dataflow/src/framework/cursor.rs +++ b/compiler/rustc_mir_dataflow/src/framework/cursor.rs @@ -1,18 +1,60 @@ //! Random access inspection of the results of a dataflow analysis. -use crate::framework::BitSetExt; +use crate::{framework::BitSetExt, CloneAnalysis}; -use std::borrow::Borrow; +use std::borrow::{Borrow, BorrowMut}; use std::cmp::Ordering; #[cfg(debug_assertions)] use rustc_index::bit_set::BitSet; use rustc_middle::mir::{self, BasicBlock, Location}; -use super::{Analysis, Direction, Effect, EffectIndex, Results}; +use super::{Analysis, Direction, Effect, EffectIndex, EntrySets, Results, ResultsCloned}; + +// `AnalysisResults` is needed as an impl such as the following has an unconstrained type +// parameter: +// ``` +// impl<'tcx, A, E, R> ResultsCursor<'_, 'tcx, A, R> +// where +// A: Analysis<'tcx>, +// E: Borrow>, +// R: Results<'tcx, A, E>, +// {} +// ``` + +/// A type representing the analysis results consumed by a `ResultsCursor`. +pub trait AnalysisResults<'tcx, A>: BorrowMut> +where + A: Analysis<'tcx>, +{ + /// The type containing the entry sets for this `Results` type. + /// + /// Should be either `EntrySets<'tcx, A>` or `&EntrySets<'tcx, A>`. + type EntrySets: Borrow>; +} +impl<'tcx, A, E> AnalysisResults<'tcx, A> for Results<'tcx, A, E> +where + A: Analysis<'tcx>, + E: Borrow>, +{ + type EntrySets = E; +} +impl<'a, 'tcx, A, E> AnalysisResults<'tcx, A> for &'a mut Results<'tcx, A, E> +where + A: Analysis<'tcx>, + E: Borrow>, +{ + type EntrySets = E; +} /// A `ResultsCursor` that borrows the underlying `Results`. -pub type ResultsRefCursor<'a, 'mir, 'tcx, A> = ResultsCursor<'mir, 'tcx, A, &'a Results<'tcx, A>>; +pub type ResultsRefCursor<'res, 'mir, 'tcx, A> = + ResultsCursor<'mir, 'tcx, A, &'res mut Results<'tcx, A>>; + +/// A `ResultsCursor` which uses a cloned `Analysis` while borrowing the underlying `Results`. This +/// allows multiple cursors over the same `Results`. +pub type ResultsClonedCursor<'res, 'mir, 'tcx, A> = + ResultsCursor<'mir, 'tcx, A, ResultsCloned<'res, 'tcx, A>>; /// Allows random access inspection of the results of a dataflow analysis. /// @@ -45,7 +87,38 @@ where impl<'mir, 'tcx, A, R> ResultsCursor<'mir, 'tcx, A, R> where A: Analysis<'tcx>, - R: Borrow>, +{ + /// Returns the dataflow state at the current location. + pub fn get(&self) -> &A::Domain { + &self.state + } + + /// Returns the body this analysis was run on. + pub fn body(&self) -> &'mir mir::Body<'tcx> { + self.body + } + + /// Unwraps this cursor, returning the underlying `Results`. + pub fn into_results(self) -> R { + self.results + } +} + +impl<'res, 'mir, 'tcx, A> ResultsCursor<'mir, 'tcx, A, ResultsCloned<'res, 'tcx, A>> +where + A: Analysis<'tcx> + CloneAnalysis, +{ + /// Creates a new cursor over the same `Results`. Note that the cursor's position is *not* + /// copied. + pub fn new_cursor(&self) -> Self { + Self::new(self.body, self.results.reclone_analysis()) + } +} + +impl<'mir, 'tcx, A, R> ResultsCursor<'mir, 'tcx, A, R> +where + A: Analysis<'tcx>, + R: AnalysisResults<'tcx, A>, { /// Returns a new cursor that can inspect `results`. pub fn new(body: &'mir mir::Body<'tcx>, results: R) -> Self { @@ -74,8 +147,13 @@ where } /// Returns the underlying `Results`. - pub fn results(&self) -> &Results<'tcx, A> { - &self.results.borrow() + pub fn results(&mut self) -> &Results<'tcx, A, R::EntrySets> { + self.results.borrow() + } + + /// Returns the underlying `Results`. + pub fn mut_results(&mut self) -> &mut Results<'tcx, A, R::EntrySets> { + self.results.borrow_mut() } /// Returns the `Analysis` used to generate the underlying `Results`. @@ -83,9 +161,14 @@ where &self.results.borrow().analysis } - /// Returns the dataflow state at the current location. - pub fn get(&self) -> &A::Domain { - &self.state + /// Returns the `Analysis` used to generate the underlying `Results`. + pub fn mut_analysis(&mut self) -> &mut A { + &mut self.results.borrow_mut().analysis + } + + /// Returns both the dataflow state at the current location and the `Analysis`. + pub fn get_with_analysis(&mut self) -> (&A::Domain, &mut A) { + (&self.state, &mut self.results.borrow_mut().analysis) } /// Resets the cursor to hold the entry set for the given basic block. @@ -97,7 +180,7 @@ where #[cfg(debug_assertions)] assert!(self.reachable_blocks.contains(block)); - self.state.clone_from(&self.results.borrow().entry_set_for_block(block)); + self.state.clone_from(self.results.borrow().entry_set_for_block(block)); self.pos = CursorPosition::block_entry(block); self.state_needs_reset = false; } @@ -186,7 +269,7 @@ where ) }; - let analysis = &self.results.borrow().analysis; + let analysis = &mut self.results.borrow_mut().analysis; let target_effect_index = effect.at_index(target.statement_index); A::Direction::apply_effects_in_range( @@ -205,8 +288,8 @@ where /// /// This can be used, e.g., to apply the call return effect directly to the cursor without /// creating an extra copy of the dataflow state. - pub fn apply_custom_effect(&mut self, f: impl FnOnce(&A, &mut A::Domain)) { - f(&self.results.borrow().analysis, &mut self.state); + pub fn apply_custom_effect(&mut self, f: impl FnOnce(&mut A, &mut A::Domain)) { + f(&mut self.results.borrow_mut().analysis, &mut self.state); self.state_needs_reset = true; } } @@ -215,7 +298,6 @@ impl<'mir, 'tcx, A, R> ResultsCursor<'mir, 'tcx, A, R> where A: crate::GenKillAnalysis<'tcx>, A::Domain: BitSetExt, - R: Borrow>, { pub fn contains(&self, elem: A::Idx) -> bool { self.get().contains(elem) diff --git a/compiler/rustc_mir_dataflow/src/framework/direction.rs b/compiler/rustc_mir_dataflow/src/framework/direction.rs index ba328e78040a5..0c379288a0962 100644 --- a/compiler/rustc_mir_dataflow/src/framework/direction.rs +++ b/compiler/rustc_mir_dataflow/src/framework/direction.rs @@ -16,7 +16,7 @@ pub trait Direction { /// /// `effects.start()` must precede or equal `effects.end()` in this direction. fn apply_effects_in_range<'tcx, A>( - analysis: &A, + analysis: &mut A, state: &mut A::Domain, block: BasicBlock, block_data: &mir::BasicBlockData<'tcx>, @@ -25,7 +25,7 @@ pub trait Direction { A: Analysis<'tcx>; fn apply_effects_in_block<'tcx, A>( - analysis: &A, + analysis: &mut A, state: &mut A::Domain, block: BasicBlock, block_data: &mir::BasicBlockData<'tcx>, @@ -33,7 +33,7 @@ pub trait Direction { A: Analysis<'tcx>; fn gen_kill_effects_in_block<'tcx, A>( - analysis: &A, + analysis: &mut A, trans: &mut GenKillSet, block: BasicBlock, block_data: &mir::BasicBlockData<'tcx>, @@ -44,13 +44,13 @@ pub trait Direction { state: &mut F, block: BasicBlock, block_data: &'mir mir::BasicBlockData<'tcx>, - results: &R, - vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = F>, + results: &mut R, + vis: &mut impl ResultsVisitor<'mir, 'tcx, R, FlowState = F>, ) where R: ResultsVisitable<'tcx, FlowState = F>; fn join_state_into_successors_of<'tcx, A>( - analysis: &A, + analysis: &mut A, tcx: TyCtxt<'tcx>, body: &mir::Body<'tcx>, exit_state: &mut A::Domain, @@ -67,7 +67,7 @@ impl Direction for Backward { const IS_FORWARD: bool = false; fn apply_effects_in_block<'tcx, A>( - analysis: &A, + analysis: &mut A, state: &mut A::Domain, block: BasicBlock, block_data: &mir::BasicBlockData<'tcx>, @@ -87,7 +87,7 @@ impl Direction for Backward { } fn gen_kill_effects_in_block<'tcx, A>( - analysis: &A, + analysis: &mut A, trans: &mut GenKillSet, block: BasicBlock, block_data: &mir::BasicBlockData<'tcx>, @@ -107,7 +107,7 @@ impl Direction for Backward { } fn apply_effects_in_range<'tcx, A>( - analysis: &A, + analysis: &mut A, state: &mut A::Domain, block: BasicBlock, block_data: &mir::BasicBlockData<'tcx>, @@ -187,36 +187,36 @@ impl Direction for Backward { state: &mut F, block: BasicBlock, block_data: &'mir mir::BasicBlockData<'tcx>, - results: &R, - vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = F>, + results: &mut R, + vis: &mut impl ResultsVisitor<'mir, 'tcx, R, FlowState = F>, ) where R: ResultsVisitable<'tcx, FlowState = F>, { results.reset_to_block_entry(state, block); - vis.visit_block_end(&state, block_data, block); + vis.visit_block_end(results, &state, block_data, block); // Terminator let loc = Location { block, statement_index: block_data.statements.len() }; let term = block_data.terminator(); results.reconstruct_before_terminator_effect(state, term, loc); - vis.visit_terminator_before_primary_effect(state, term, loc); + vis.visit_terminator_before_primary_effect(results, state, term, loc); results.reconstruct_terminator_effect(state, term, loc); - vis.visit_terminator_after_primary_effect(state, term, loc); + vis.visit_terminator_after_primary_effect(results, state, term, loc); for (statement_index, stmt) in block_data.statements.iter().enumerate().rev() { let loc = Location { block, statement_index }; results.reconstruct_before_statement_effect(state, stmt, loc); - vis.visit_statement_before_primary_effect(state, stmt, loc); + vis.visit_statement_before_primary_effect(results, state, stmt, loc); results.reconstruct_statement_effect(state, stmt, loc); - vis.visit_statement_after_primary_effect(state, stmt, loc); + vis.visit_statement_after_primary_effect(results, state, stmt, loc); } - vis.visit_block_start(state, block_data, block); + vis.visit_block_start(results, state, block_data, block); } fn join_state_into_successors_of<'tcx, A>( - analysis: &A, + analysis: &mut A, _tcx: TyCtxt<'tcx>, body: &mir::Body<'tcx>, exit_state: &mut A::Domain, @@ -319,7 +319,7 @@ impl Direction for Forward { const IS_FORWARD: bool = true; fn apply_effects_in_block<'tcx, A>( - analysis: &A, + analysis: &mut A, state: &mut A::Domain, block: BasicBlock, block_data: &mir::BasicBlockData<'tcx>, @@ -339,7 +339,7 @@ impl Direction for Forward { } fn gen_kill_effects_in_block<'tcx, A>( - analysis: &A, + analysis: &mut A, trans: &mut GenKillSet, block: BasicBlock, block_data: &mir::BasicBlockData<'tcx>, @@ -359,7 +359,7 @@ impl Direction for Forward { } fn apply_effects_in_range<'tcx, A>( - analysis: &A, + analysis: &mut A, state: &mut A::Domain, block: BasicBlock, block_data: &mir::BasicBlockData<'tcx>, @@ -435,35 +435,35 @@ impl Direction for Forward { state: &mut F, block: BasicBlock, block_data: &'mir mir::BasicBlockData<'tcx>, - results: &R, - vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = F>, + results: &mut R, + vis: &mut impl ResultsVisitor<'mir, 'tcx, R, FlowState = F>, ) where R: ResultsVisitable<'tcx, FlowState = F>, { results.reset_to_block_entry(state, block); - vis.visit_block_start(state, block_data, block); + vis.visit_block_start(results, state, block_data, block); for (statement_index, stmt) in block_data.statements.iter().enumerate() { let loc = Location { block, statement_index }; results.reconstruct_before_statement_effect(state, stmt, loc); - vis.visit_statement_before_primary_effect(state, stmt, loc); + vis.visit_statement_before_primary_effect(results, state, stmt, loc); results.reconstruct_statement_effect(state, stmt, loc); - vis.visit_statement_after_primary_effect(state, stmt, loc); + vis.visit_statement_after_primary_effect(results, state, stmt, loc); } let loc = Location { block, statement_index: block_data.statements.len() }; let term = block_data.terminator(); results.reconstruct_before_terminator_effect(state, term, loc); - vis.visit_terminator_before_primary_effect(state, term, loc); + vis.visit_terminator_before_primary_effect(results, state, term, loc); results.reconstruct_terminator_effect(state, term, loc); - vis.visit_terminator_after_primary_effect(state, term, loc); + vis.visit_terminator_after_primary_effect(results, state, term, loc); - vis.visit_block_end(state, block_data, block); + vis.visit_block_end(results, state, block_data, block); } fn join_state_into_successors_of<'tcx, A>( - analysis: &A, + analysis: &mut A, _tcx: TyCtxt<'tcx>, _body: &mir::Body<'tcx>, exit_state: &mut A::Domain, diff --git a/compiler/rustc_mir_dataflow/src/framework/engine.rs b/compiler/rustc_mir_dataflow/src/framework/engine.rs index 3e8f792e63442..4bdfa35fc708b 100644 --- a/compiler/rustc_mir_dataflow/src/framework/engine.rs +++ b/compiler/rustc_mir_dataflow/src/framework/engine.rs @@ -5,7 +5,9 @@ use crate::errors::{ }; use crate::framework::BitSetExt; +use std::borrow::Borrow; use std::ffi::OsString; +use std::marker::PhantomData; use std::path::PathBuf; use rustc_ast as ast; @@ -22,54 +24,108 @@ use rustc_span::symbol::{sym, Symbol}; use super::fmt::DebugWithContext; use super::graphviz; use super::{ - visit_results, Analysis, Direction, GenKill, GenKillAnalysis, GenKillSet, JoinSemiLattice, - ResultsCursor, ResultsVisitor, + visit_results, Analysis, AnalysisDomain, CloneAnalysis, Direction, GenKill, GenKillAnalysis, + GenKillSet, JoinSemiLattice, ResultsClonedCursor, ResultsCursor, ResultsRefCursor, + ResultsVisitor, }; +pub type EntrySets<'tcx, A> = IndexVec>::Domain>; + /// A dataflow analysis that has converged to fixpoint. -pub struct Results<'tcx, A> +pub struct Results<'tcx, A, E = EntrySets<'tcx, A>> where A: Analysis<'tcx>, { pub analysis: A, - pub(super) entry_sets: IndexVec, + pub(super) entry_sets: E, + pub(super) _marker: PhantomData<&'tcx ()>, } -impl<'tcx, A> Results<'tcx, A> +/// `Results` type with a cloned `Analysis` and borrowed entry sets. +pub type ResultsCloned<'res, 'tcx, A> = Results<'tcx, A, &'res EntrySets<'tcx, A>>; + +impl<'tcx, A, E> Results<'tcx, A, E> where A: Analysis<'tcx>, + E: Borrow>, { /// Creates a `ResultsCursor` that can inspect these `Results`. pub fn into_results_cursor<'mir>( self, body: &'mir mir::Body<'tcx>, - ) -> ResultsCursor<'mir, 'tcx, A> { + ) -> ResultsCursor<'mir, 'tcx, A, Self> { ResultsCursor::new(body, self) } /// Gets the dataflow state for the given block. pub fn entry_set_for_block(&self, block: BasicBlock) -> &A::Domain { - &self.entry_sets[block] + &self.entry_sets.borrow()[block] } pub fn visit_with<'mir>( - &self, + &mut self, body: &'mir mir::Body<'tcx>, blocks: impl IntoIterator, - vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = A::Domain>, + vis: &mut impl ResultsVisitor<'mir, 'tcx, Self, FlowState = A::Domain>, ) { visit_results(body, blocks, self, vis) } pub fn visit_reachable_with<'mir>( - &self, + &mut self, body: &'mir mir::Body<'tcx>, - vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = A::Domain>, + vis: &mut impl ResultsVisitor<'mir, 'tcx, Self, FlowState = A::Domain>, ) { let blocks = mir::traversal::reachable(body); visit_results(body, blocks.map(|(bb, _)| bb), self, vis) } } +impl<'tcx, A> Results<'tcx, A> +where + A: Analysis<'tcx>, +{ + /// Creates a `ResultsCursor` that can inspect these `Results`. + pub fn as_results_cursor<'a, 'mir>( + &'a mut self, + body: &'mir mir::Body<'tcx>, + ) -> ResultsRefCursor<'a, 'mir, 'tcx, A> { + ResultsCursor::new(body, self) + } +} +impl<'tcx, A> Results<'tcx, A> +where + A: Analysis<'tcx> + CloneAnalysis, +{ + /// Creates a new `Results` type with a cloned `Analysis` and borrowed entry sets. + pub fn clone_analysis(&self) -> ResultsCloned<'_, 'tcx, A> { + Results { + analysis: self.analysis.clone_analysis(), + entry_sets: &self.entry_sets, + _marker: PhantomData, + } + } + + /// Creates a `ResultsCursor` that can inspect these `Results`. + pub fn cloned_results_cursor<'mir>( + &self, + body: &'mir mir::Body<'tcx>, + ) -> ResultsClonedCursor<'_, 'mir, 'tcx, A> { + self.clone_analysis().into_results_cursor(body) + } +} +impl<'res, 'tcx, A> Results<'tcx, A, &'res EntrySets<'tcx, A>> +where + A: Analysis<'tcx> + CloneAnalysis, +{ + /// Creates a new `Results` type with a cloned `Analysis` and borrowed entry sets. + pub fn reclone_analysis(&self) -> Self { + Results { + analysis: self.analysis.clone_analysis(), + entry_sets: self.entry_sets, + _marker: PhantomData, + } + } +} /// A solver for dataflow problems. pub struct Engine<'a, 'tcx, A> @@ -98,7 +154,7 @@ where T: Idx, { /// Creates a new `Engine` to solve a gen-kill dataflow problem. - pub fn new_gen_kill(tcx: TyCtxt<'tcx>, body: &'a mir::Body<'tcx>, analysis: A) -> Self { + pub fn new_gen_kill(tcx: TyCtxt<'tcx>, body: &'a mir::Body<'tcx>, mut analysis: A) -> Self { // If there are no back-edges in the control-flow graph, we only ever need to apply the // transfer function for each block exactly once (assuming that we process blocks in RPO). // @@ -114,7 +170,7 @@ where for (block, block_data) in body.basic_blocks.iter_enumerated() { let trans = &mut trans_for_block[block]; - A::Direction::gen_kill_effects_in_block(&analysis, trans, block, block_data); + A::Direction::gen_kill_effects_in_block(&mut analysis, trans, block, block_data); } let apply_trans = Box::new(move |bb: BasicBlock, state: &mut A::Domain| { @@ -171,7 +227,13 @@ where A::Domain: DebugWithContext, { let Engine { - analysis, body, mut entry_sets, tcx, apply_trans_for_block, pass_name, .. + mut analysis, + body, + mut entry_sets, + tcx, + apply_trans_for_block, + pass_name, + .. } = self; let mut dirty_queue: WorkQueue = WorkQueue::with_none(body.basic_blocks.len()); @@ -203,11 +265,13 @@ where // Apply the block transfer function, using the cached one if it exists. match &apply_trans_for_block { Some(apply) => apply(bb, &mut state), - None => A::Direction::apply_effects_in_block(&analysis, &mut state, bb, bb_data), + None => { + A::Direction::apply_effects_in_block(&mut analysis, &mut state, bb, bb_data) + } } A::Direction::join_state_into_successors_of( - &analysis, + &mut analysis, tcx, body, &mut state, @@ -221,9 +285,9 @@ where ); } - let results = Results { analysis, entry_sets }; + let mut results = Results { analysis, entry_sets, _marker: PhantomData }; - let res = write_graphviz_results(tcx, &body, &results, pass_name); + let res = write_graphviz_results(tcx, body, &mut results, pass_name); if let Err(e) = res { error!("Failed to write graphviz dataflow results: {}", e); } @@ -239,7 +303,7 @@ where fn write_graphviz_results<'tcx, A>( tcx: TyCtxt<'tcx>, body: &mir::Body<'tcx>, - results: &Results<'tcx, A>, + results: &mut Results<'tcx, A>, pass_name: Option<&'static str>, ) -> std::io::Result<()> where diff --git a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs index 707729f8f21b7..e331533c37103 100644 --- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs +++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs @@ -1,6 +1,7 @@ //! A helpful diagram for debugging dataflow problems. use std::borrow::Cow; +use std::cell::RefCell; use std::sync::OnceLock; use std::{io, ops, str}; @@ -28,23 +29,27 @@ impl OutputStyle { } } -pub struct Formatter<'a, 'tcx, A> +pub struct Formatter<'res, 'mir, 'tcx, A> where A: Analysis<'tcx>, { - body: &'a Body<'tcx>, - results: &'a Results<'tcx, A>, + body: &'mir Body<'tcx>, + results: RefCell<&'res mut Results<'tcx, A>>, style: OutputStyle, reachable: BitSet, } -impl<'a, 'tcx, A> Formatter<'a, 'tcx, A> +impl<'res, 'mir, 'tcx, A> Formatter<'res, 'mir, 'tcx, A> where A: Analysis<'tcx>, { - pub fn new(body: &'a Body<'tcx>, results: &'a Results<'tcx, A>, style: OutputStyle) -> Self { + pub fn new( + body: &'mir Body<'tcx>, + results: &'res mut Results<'tcx, A>, + style: OutputStyle, + ) -> Self { let reachable = mir::traversal::reachable_as_bitset(body); - Formatter { body, results, style, reachable } + Formatter { body, results: results.into(), style, reachable } } } @@ -64,7 +69,7 @@ fn dataflow_successors(body: &Body<'_>, bb: BasicBlock) -> Vec { .collect() } -impl<'tcx, A> dot::Labeller<'_> for Formatter<'_, 'tcx, A> +impl<'tcx, A> dot::Labeller<'_> for Formatter<'_, '_, 'tcx, A> where A: Analysis<'tcx>, A::Domain: DebugWithContext, @@ -83,13 +88,14 @@ where fn node_label(&self, block: &Self::Node) -> dot::LabelText<'_> { let mut label = Vec::new(); + let mut results = self.results.borrow_mut(); let mut fmt = BlockFormatter { - results: ResultsRefCursor::new(self.body, self.results), + results: results.as_results_cursor(self.body), style: self.style, bg: Background::Light, }; - fmt.write_node_label(&mut label, self.body, *block).unwrap(); + fmt.write_node_label(&mut label, *block).unwrap(); dot::LabelText::html(String::from_utf8(label).unwrap()) } @@ -103,7 +109,7 @@ where } } -impl<'a, 'tcx, A> dot::GraphWalk<'a> for Formatter<'a, 'tcx, A> +impl<'mir, 'tcx, A> dot::GraphWalk<'mir> for Formatter<'_, 'mir, 'tcx, A> where A: Analysis<'tcx>, { @@ -137,16 +143,16 @@ where } } -struct BlockFormatter<'a, 'tcx, A> +struct BlockFormatter<'res, 'mir, 'tcx, A> where A: Analysis<'tcx>, { - results: ResultsRefCursor<'a, 'a, 'tcx, A>, + results: ResultsRefCursor<'res, 'mir, 'tcx, A>, bg: Background, style: OutputStyle, } -impl<'a, 'tcx, A> BlockFormatter<'a, 'tcx, A> +impl<'res, 'mir, 'tcx, A> BlockFormatter<'res, 'mir, 'tcx, A> where A: Analysis<'tcx>, A::Domain: DebugWithContext, @@ -159,12 +165,7 @@ where bg } - fn write_node_label( - &mut self, - w: &mut impl io::Write, - body: &'a Body<'tcx>, - block: BasicBlock, - ) -> io::Result<()> { + fn write_node_label(&mut self, w: &mut impl io::Write, block: BasicBlock) -> io::Result<()> { // Sample output: // +-+-----------------------------------------------+ // A | bb4 | @@ -215,11 +216,11 @@ where self.write_row_with_full_state(w, "", "(on start)")?; // D + E: Statement and terminator transfer functions - self.write_statements_and_terminator(w, body, block)?; + self.write_statements_and_terminator(w, block)?; // F: State at end of block - let terminator = body[block].terminator(); + let terminator = self.results.body()[block].terminator(); // Write the full dataflow state immediately after the terminator if it differs from the // state at block entry. @@ -389,10 +390,14 @@ where fn write_statements_and_terminator( &mut self, w: &mut impl io::Write, - body: &'a Body<'tcx>, block: BasicBlock, ) -> io::Result<()> { - let diffs = StateDiffCollector::run(body, block, self.results.results(), self.style); + let diffs = StateDiffCollector::run( + self.results.body(), + block, + self.results.mut_results(), + self.style, + ); let mut diffs_before = diffs.before.map(|v| v.into_iter()); let mut diffs_after = diffs.after.into_iter(); @@ -401,7 +406,7 @@ where if A::Direction::IS_FORWARD { it.next().unwrap() } else { it.next_back().unwrap() } }; - for (i, statement) in body[block].statements.iter().enumerate() { + for (i, statement) in self.results.body()[block].statements.iter().enumerate() { let statement_str = format!("{statement:?}"); let index_str = format!("{i}"); @@ -423,7 +428,7 @@ where assert!(diffs_after.is_empty()); assert!(diffs_before.as_ref().map_or(true, ExactSizeIterator::is_empty)); - let terminator = body[block].terminator(); + let terminator = self.results.body()[block].terminator(); let mut terminator_str = String::new(); terminator.kind.fmt_head(&mut terminator_str).unwrap(); @@ -492,29 +497,24 @@ where } } -struct StateDiffCollector<'a, 'tcx, A> -where - A: Analysis<'tcx>, -{ - analysis: &'a A, - prev_state: A::Domain, +struct StateDiffCollector { + prev_state: D, before: Option>, after: Vec, } -impl<'a, 'tcx, A> StateDiffCollector<'a, 'tcx, A> -where - A: Analysis<'tcx>, - A::Domain: DebugWithContext, -{ - fn run( - body: &'a mir::Body<'tcx>, +impl StateDiffCollector { + fn run<'tcx, A>( + body: &mir::Body<'tcx>, block: BasicBlock, - results: &'a Results<'tcx, A>, + results: &mut Results<'tcx, A>, style: OutputStyle, - ) -> Self { + ) -> Self + where + A: Analysis<'tcx, Domain = D>, + D: DebugWithContext, + { let mut collector = StateDiffCollector { - analysis: &results.analysis, prev_state: results.analysis.bottom_value(body), after: vec![], before: (style == OutputStyle::BeforeAndAfter).then_some(vec![]), @@ -525,7 +525,7 @@ where } } -impl<'a, 'tcx, A> ResultsVisitor<'a, 'tcx> for StateDiffCollector<'a, 'tcx, A> +impl<'tcx, A> ResultsVisitor<'_, 'tcx, Results<'tcx, A>> for StateDiffCollector where A: Analysis<'tcx>, A::Domain: DebugWithContext, @@ -534,6 +534,7 @@ where fn visit_block_start( &mut self, + _results: &Results<'tcx, A>, state: &Self::FlowState, _block_data: &mir::BasicBlockData<'tcx>, _block: BasicBlock, @@ -545,6 +546,7 @@ where fn visit_block_end( &mut self, + _results: &Results<'tcx, A>, state: &Self::FlowState, _block_data: &mir::BasicBlockData<'tcx>, _block: BasicBlock, @@ -556,45 +558,49 @@ where fn visit_statement_before_primary_effect( &mut self, + results: &Results<'tcx, A>, state: &Self::FlowState, _statement: &mir::Statement<'tcx>, _location: Location, ) { if let Some(before) = self.before.as_mut() { - before.push(diff_pretty(state, &self.prev_state, self.analysis)); + before.push(diff_pretty(state, &self.prev_state, &results.analysis)); self.prev_state.clone_from(state) } } fn visit_statement_after_primary_effect( &mut self, + results: &Results<'tcx, A>, state: &Self::FlowState, _statement: &mir::Statement<'tcx>, _location: Location, ) { - self.after.push(diff_pretty(state, &self.prev_state, self.analysis)); + self.after.push(diff_pretty(state, &self.prev_state, &results.analysis)); self.prev_state.clone_from(state) } fn visit_terminator_before_primary_effect( &mut self, + results: &Results<'tcx, A>, state: &Self::FlowState, _terminator: &mir::Terminator<'tcx>, _location: Location, ) { if let Some(before) = self.before.as_mut() { - before.push(diff_pretty(state, &self.prev_state, self.analysis)); + before.push(diff_pretty(state, &self.prev_state, &results.analysis)); self.prev_state.clone_from(state) } } fn visit_terminator_after_primary_effect( &mut self, + results: &Results<'tcx, A>, state: &Self::FlowState, _terminator: &mir::Terminator<'tcx>, _location: Location, ) { - self.after.push(diff_pretty(state, &self.prev_state, self.analysis)); + self.after.push(diff_pretty(state, &self.prev_state, &results.analysis)); self.prev_state.clone_from(state) } } diff --git a/compiler/rustc_mir_dataflow/src/framework/mod.rs b/compiler/rustc_mir_dataflow/src/framework/mod.rs index f2263007f6fe9..e5e9c1507ba8b 100644 --- a/compiler/rustc_mir_dataflow/src/framework/mod.rs +++ b/compiler/rustc_mir_dataflow/src/framework/mod.rs @@ -45,9 +45,9 @@ pub mod graphviz; pub mod lattice; mod visitor; -pub use self::cursor::{ResultsCursor, ResultsRefCursor}; +pub use self::cursor::{ResultsClonedCursor, ResultsCursor, ResultsRefCursor}; pub use self::direction::{Backward, Direction, Forward}; -pub use self::engine::{Engine, Results}; +pub use self::engine::{Engine, EntrySets, Results, ResultsCloned}; pub use self::lattice::{JoinSemiLattice, MeetSemiLattice}; pub use self::visitor::{visit_results, ResultsVisitable, ResultsVisitor}; @@ -146,7 +146,7 @@ pub trait AnalysisDomain<'tcx> { pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { /// Updates the current dataflow state with the effect of evaluating a statement. fn apply_statement_effect( - &self, + &mut self, state: &mut Self::Domain, statement: &mir::Statement<'tcx>, location: Location, @@ -159,7 +159,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { /// *part* of the effect of a statement (e.g. for two-phase borrows). As a general rule, /// analyses should not implement this without also implementing `apply_statement_effect`. fn apply_before_statement_effect( - &self, + &mut self, _state: &mut Self::Domain, _statement: &mir::Statement<'tcx>, _location: Location, @@ -173,7 +173,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { /// `InitializedPlaces` analyses, the return place for a function call is not marked as /// initialized here. fn apply_terminator_effect( - &self, + &mut self, state: &mut Self::Domain, terminator: &mir::Terminator<'tcx>, location: Location, @@ -186,7 +186,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { /// *part* of the effect of a terminator (e.g. for two-phase borrows). As a general rule, /// analyses should not implement this without also implementing `apply_terminator_effect`. fn apply_before_terminator_effect( - &self, + &mut self, _state: &mut Self::Domain, _terminator: &mir::Terminator<'tcx>, _location: Location, @@ -201,7 +201,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { /// This is separate from `apply_terminator_effect` to properly track state across unwind /// edges. fn apply_call_return_effect( - &self, + &mut self, state: &mut Self::Domain, block: BasicBlock, return_places: CallReturnPlaces<'_, 'tcx>, @@ -214,7 +214,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { /// /// By default, no effects happen. fn apply_yield_resume_effect( - &self, + &mut self, _state: &mut Self::Domain, _resume_block: BasicBlock, _resume_place: mir::Place<'tcx>, @@ -235,7 +235,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { /// engine doesn't need to clone the exit state for a block unless /// `SwitchIntEdgeEffects::apply` is actually called. fn apply_switch_int_edge_effects( - &self, + &mut self, _block: BasicBlock, _discr: &mir::Operand<'tcx>, _apply_edge_effects: &mut impl SwitchIntEdgeEffects, @@ -269,6 +269,21 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { } } +/// Defines an `Analysis` which can be cloned for use in multiple `ResultsCursor`s or +/// `ResultsVisitor`s. Note this need not be a full clone, only enough of one to be used with a new +/// `ResultsCursor` or `ResultsVisitor` +pub trait CloneAnalysis { + fn clone_analysis(&self) -> Self; +} +impl<'tcx, A> CloneAnalysis for A +where + A: Analysis<'tcx> + Copy, +{ + fn clone_analysis(&self) -> Self { + *self + } +} + /// A gen/kill dataflow problem. /// /// Each method in this trait has a corresponding one in `Analysis`. However, these methods only @@ -282,7 +297,7 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> { /// See `Analysis::apply_statement_effect`. fn statement_effect( - &self, + &mut self, trans: &mut impl GenKill, statement: &mir::Statement<'tcx>, location: Location, @@ -290,7 +305,7 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> { /// See `Analysis::apply_before_statement_effect`. fn before_statement_effect( - &self, + &mut self, _trans: &mut impl GenKill, _statement: &mir::Statement<'tcx>, _location: Location, @@ -299,7 +314,7 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> { /// See `Analysis::apply_terminator_effect`. fn terminator_effect( - &self, + &mut self, trans: &mut impl GenKill, terminator: &mir::Terminator<'tcx>, location: Location, @@ -307,7 +322,7 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> { /// See `Analysis::apply_before_terminator_effect`. fn before_terminator_effect( - &self, + &mut self, _trans: &mut impl GenKill, _terminator: &mir::Terminator<'tcx>, _location: Location, @@ -318,7 +333,7 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> { /// See `Analysis::apply_call_return_effect`. fn call_return_effect( - &self, + &mut self, trans: &mut impl GenKill, block: BasicBlock, return_places: CallReturnPlaces<'_, 'tcx>, @@ -326,7 +341,7 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> { /// See `Analysis::apply_yield_resume_effect`. fn yield_resume_effect( - &self, + &mut self, _trans: &mut impl GenKill, _resume_block: BasicBlock, _resume_place: mir::Place<'tcx>, @@ -335,7 +350,7 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> { /// See `Analysis::apply_switch_int_edge_effects`. fn switch_int_edge_effects>( - &self, + &mut self, _block: BasicBlock, _discr: &mir::Operand<'tcx>, _edge_effects: &mut impl SwitchIntEdgeEffects, @@ -349,7 +364,7 @@ where A::Domain: GenKill + BitSetExt, { fn apply_statement_effect( - &self, + &mut self, state: &mut A::Domain, statement: &mir::Statement<'tcx>, location: Location, @@ -358,7 +373,7 @@ where } fn apply_before_statement_effect( - &self, + &mut self, state: &mut A::Domain, statement: &mir::Statement<'tcx>, location: Location, @@ -367,7 +382,7 @@ where } fn apply_terminator_effect( - &self, + &mut self, state: &mut A::Domain, terminator: &mir::Terminator<'tcx>, location: Location, @@ -376,7 +391,7 @@ where } fn apply_before_terminator_effect( - &self, + &mut self, state: &mut A::Domain, terminator: &mir::Terminator<'tcx>, location: Location, @@ -387,7 +402,7 @@ where /* Edge-specific effects */ fn apply_call_return_effect( - &self, + &mut self, state: &mut A::Domain, block: BasicBlock, return_places: CallReturnPlaces<'_, 'tcx>, @@ -396,7 +411,7 @@ where } fn apply_yield_resume_effect( - &self, + &mut self, state: &mut A::Domain, resume_block: BasicBlock, resume_place: mir::Place<'tcx>, @@ -405,7 +420,7 @@ where } fn apply_switch_int_edge_effects( - &self, + &mut self, block: BasicBlock, discr: &mir::Operand<'tcx>, edge_effects: &mut impl SwitchIntEdgeEffects, diff --git a/compiler/rustc_mir_dataflow/src/framework/tests.rs b/compiler/rustc_mir_dataflow/src/framework/tests.rs index 0fed305b930c4..45c2fe55aca9f 100644 --- a/compiler/rustc_mir_dataflow/src/framework/tests.rs +++ b/compiler/rustc_mir_dataflow/src/framework/tests.rs @@ -179,7 +179,7 @@ impl<'tcx, D: Direction> AnalysisDomain<'tcx> for MockAnalysis<'tcx, D> { impl<'tcx, D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> { fn apply_statement_effect( - &self, + &mut self, state: &mut Self::Domain, _statement: &mir::Statement<'tcx>, location: Location, @@ -189,7 +189,7 @@ impl<'tcx, D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> { } fn apply_before_statement_effect( - &self, + &mut self, state: &mut Self::Domain, _statement: &mir::Statement<'tcx>, location: Location, @@ -199,7 +199,7 @@ impl<'tcx, D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> { } fn apply_terminator_effect( - &self, + &mut self, state: &mut Self::Domain, _terminator: &mir::Terminator<'tcx>, location: Location, @@ -209,7 +209,7 @@ impl<'tcx, D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> { } fn apply_before_terminator_effect( - &self, + &mut self, state: &mut Self::Domain, _terminator: &mir::Terminator<'tcx>, location: Location, @@ -219,7 +219,7 @@ impl<'tcx, D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> { } fn apply_call_return_effect( - &self, + &mut self, _state: &mut Self::Domain, _block: BasicBlock, _return_places: CallReturnPlaces<'_, 'tcx>, @@ -266,7 +266,8 @@ fn test_cursor(analysis: MockAnalysis<'_, D>) { let body = analysis.body; let mut cursor = - Results { entry_sets: analysis.mock_entry_sets(), analysis }.into_results_cursor(body); + Results { entry_sets: analysis.mock_entry_sets(), analysis, _marker: PhantomData } + .into_results_cursor(body); cursor.allow_unreachable(); diff --git a/compiler/rustc_mir_dataflow/src/framework/visitor.rs b/compiler/rustc_mir_dataflow/src/framework/visitor.rs index 75b4e150a8a33..76a729827813e 100644 --- a/compiler/rustc_mir_dataflow/src/framework/visitor.rs +++ b/compiler/rustc_mir_dataflow/src/framework/visitor.rs @@ -1,16 +1,18 @@ +use std::borrow::Borrow; + use rustc_middle::mir::{self, BasicBlock, Location}; -use super::{Analysis, Direction, Results}; +use super::{Analysis, Direction, EntrySets, Results}; /// Calls the corresponding method in `ResultsVisitor` for every location in a `mir::Body` with the /// dataflow state at that location. -pub fn visit_results<'mir, 'tcx, F, V>( +pub fn visit_results<'mir, 'tcx, F, R>( body: &'mir mir::Body<'tcx>, blocks: impl IntoIterator, - results: &V, - vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = F>, + results: &mut R, + vis: &mut impl ResultsVisitor<'mir, 'tcx, R, FlowState = F>, ) where - V: ResultsVisitable<'tcx, FlowState = F>, + R: ResultsVisitable<'tcx, FlowState = F>, { let mut state = results.new_flow_state(body); @@ -22,15 +24,18 @@ pub fn visit_results<'mir, 'tcx, F, V>( assert!(reachable_blocks.contains(block)); let block_data = &body[block]; - V::Direction::visit_results_in_block(&mut state, block, block_data, results, vis); + R::Direction::visit_results_in_block(&mut state, block, block_data, results, vis); } } -pub trait ResultsVisitor<'mir, 'tcx> { +/// A visitor over the results of an `Analysis`. The type parameter `R` is the results type being +/// visited. +pub trait ResultsVisitor<'mir, 'tcx, R> { type FlowState; fn visit_block_start( &mut self, + _results: &R, _state: &Self::FlowState, _block_data: &'mir mir::BasicBlockData<'tcx>, _block: BasicBlock, @@ -41,6 +46,7 @@ pub trait ResultsVisitor<'mir, 'tcx> { /// its `statement_effect`. fn visit_statement_before_primary_effect( &mut self, + _results: &R, _state: &Self::FlowState, _statement: &'mir mir::Statement<'tcx>, _location: Location, @@ -51,6 +57,7 @@ pub trait ResultsVisitor<'mir, 'tcx> { /// statement applied to `state`. fn visit_statement_after_primary_effect( &mut self, + _results: &R, _state: &Self::FlowState, _statement: &'mir mir::Statement<'tcx>, _location: Location, @@ -61,6 +68,7 @@ pub trait ResultsVisitor<'mir, 'tcx> { /// its `terminator_effect`. fn visit_terminator_before_primary_effect( &mut self, + _results: &R, _state: &Self::FlowState, _terminator: &'mir mir::Terminator<'tcx>, _location: Location, @@ -73,6 +81,7 @@ pub trait ResultsVisitor<'mir, 'tcx> { /// The `call_return_effect` (if one exists) will *not* be applied to `state`. fn visit_terminator_after_primary_effect( &mut self, + _results: &R, _state: &Self::FlowState, _terminator: &'mir mir::Terminator<'tcx>, _location: Location, @@ -81,6 +90,7 @@ pub trait ResultsVisitor<'mir, 'tcx> { fn visit_block_end( &mut self, + _results: &R, _state: &Self::FlowState, _block_data: &'mir mir::BasicBlockData<'tcx>, _block: BasicBlock, @@ -105,37 +115,38 @@ pub trait ResultsVisitable<'tcx> { fn reset_to_block_entry(&self, state: &mut Self::FlowState, block: BasicBlock); fn reconstruct_before_statement_effect( - &self, + &mut self, state: &mut Self::FlowState, statement: &mir::Statement<'tcx>, location: Location, ); fn reconstruct_statement_effect( - &self, + &mut self, state: &mut Self::FlowState, statement: &mir::Statement<'tcx>, location: Location, ); fn reconstruct_before_terminator_effect( - &self, + &mut self, state: &mut Self::FlowState, terminator: &mir::Terminator<'tcx>, location: Location, ); fn reconstruct_terminator_effect( - &self, + &mut self, state: &mut Self::FlowState, terminator: &mir::Terminator<'tcx>, location: Location, ); } -impl<'tcx, A> ResultsVisitable<'tcx> for Results<'tcx, A> +impl<'tcx, A, E> ResultsVisitable<'tcx> for Results<'tcx, A, E> where A: Analysis<'tcx>, + E: Borrow>, { type FlowState = A::Domain; @@ -146,11 +157,11 @@ where } fn reset_to_block_entry(&self, state: &mut Self::FlowState, block: BasicBlock) { - state.clone_from(&self.entry_set_for_block(block)); + state.clone_from(self.entry_set_for_block(block)); } fn reconstruct_before_statement_effect( - &self, + &mut self, state: &mut Self::FlowState, stmt: &mir::Statement<'tcx>, loc: Location, @@ -159,7 +170,7 @@ where } fn reconstruct_statement_effect( - &self, + &mut self, state: &mut Self::FlowState, stmt: &mir::Statement<'tcx>, loc: Location, @@ -168,7 +179,7 @@ where } fn reconstruct_before_terminator_effect( - &self, + &mut self, state: &mut Self::FlowState, term: &mir::Terminator<'tcx>, loc: Location, @@ -177,7 +188,7 @@ where } fn reconstruct_terminator_effect( - &self, + &mut self, state: &mut Self::FlowState, term: &mir::Terminator<'tcx>, loc: Location, diff --git a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs index 92d30f254a6af..b88ed32b687f6 100644 --- a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs +++ b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs @@ -10,6 +10,7 @@ use rustc_middle::mir::*; /// At present, this is used as a very limited form of alias analysis. For example, /// `MaybeBorrowedLocals` is used to compute which locals are live during a yield expression for /// immovable generators. +#[derive(Clone, Copy)] pub struct MaybeBorrowedLocals; impl MaybeBorrowedLocals { @@ -36,7 +37,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeBorrowedLocals { type Idx = Local; fn statement_effect( - &self, + &mut self, trans: &mut impl GenKill, statement: &mir::Statement<'tcx>, location: Location, @@ -45,7 +46,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeBorrowedLocals { } fn terminator_effect( - &self, + &mut self, trans: &mut impl GenKill, terminator: &mir::Terminator<'tcx>, location: Location, @@ -54,7 +55,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeBorrowedLocals { } fn call_return_effect( - &self, + &mut self, _trans: &mut impl GenKill, _block: mir::BasicBlock, _return_places: CallReturnPlaces<'_, 'tcx>, diff --git a/compiler/rustc_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs index 6ae6bdc17d501..9662c19777fe0 100644 --- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs @@ -21,6 +21,7 @@ use crate::{Analysis, AnalysisDomain, Backward, CallReturnPlaces, GenKill, GenKi /// [`MaybeBorrowedLocals`]: super::MaybeBorrowedLocals /// [flow-test]: https://github.com/rust-lang/rust/blob/a08c47310c7d49cbdc5d7afb38408ba519967ecd/src/test/ui/mir-dataflow/liveness-ptr.rs /// [liveness]: https://en.wikipedia.org/wiki/Live_variable_analysis +#[derive(Clone, Copy)] pub struct MaybeLiveLocals; impl<'tcx> AnalysisDomain<'tcx> for MaybeLiveLocals { @@ -43,7 +44,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeLiveLocals { type Idx = Local; fn statement_effect( - &self, + &mut self, trans: &mut impl GenKill, statement: &mir::Statement<'tcx>, location: Location, @@ -52,7 +53,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeLiveLocals { } fn terminator_effect( - &self, + &mut self, trans: &mut impl GenKill, terminator: &mir::Terminator<'tcx>, location: Location, @@ -61,7 +62,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeLiveLocals { } fn call_return_effect( - &self, + &mut self, trans: &mut impl GenKill, _block: mir::BasicBlock, return_places: CallReturnPlaces<'_, 'tcx>, @@ -74,7 +75,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeLiveLocals { } fn yield_resume_effect( - &self, + &mut self, trans: &mut impl GenKill, _resume_block: mir::BasicBlock, resume_place: mir::Place<'tcx>, @@ -215,6 +216,7 @@ impl DefUse { /// This is basically written for dead store elimination and nothing else. /// /// All of the caveats of `MaybeLiveLocals` apply. +#[derive(Clone, Copy)] pub struct MaybeTransitiveLiveLocals<'a> { always_live: &'a BitSet, } @@ -247,7 +249,7 @@ impl<'a, 'tcx> AnalysisDomain<'tcx> for MaybeTransitiveLiveLocals<'a> { impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> { fn apply_statement_effect( - &self, + &mut self, trans: &mut Self::Domain, statement: &mir::Statement<'tcx>, location: Location, @@ -282,7 +284,7 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> { } fn apply_terminator_effect( - &self, + &mut self, trans: &mut Self::Domain, terminator: &mir::Terminator<'tcx>, location: Location, @@ -291,7 +293,7 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> { } fn apply_call_return_effect( - &self, + &mut self, trans: &mut Self::Domain, _block: mir::BasicBlock, return_places: CallReturnPlaces<'_, 'tcx>, @@ -304,7 +306,7 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> { } fn apply_yield_resume_effect( - &self, + &mut self, trans: &mut Self::Domain, _resume_block: mir::BasicBlock, resume_place: mir::Place<'tcx>, diff --git a/compiler/rustc_mir_dataflow/src/impls/mod.rs b/compiler/rustc_mir_dataflow/src/impls/mod.rs index 171db6965ac18..98cec1c676022 100644 --- a/compiler/rustc_mir_dataflow/src/impls/mod.rs +++ b/compiler/rustc_mir_dataflow/src/impls/mod.rs @@ -306,7 +306,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { type Idx = MovePathIndex; fn statement_effect( - &self, + &mut self, trans: &mut impl GenKill, statement: &mir::Statement<'tcx>, location: Location, @@ -329,7 +329,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { } fn terminator_effect( - &self, + &mut self, trans: &mut impl GenKill, terminator: &mir::Terminator<'tcx>, location: Location, @@ -351,7 +351,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { } fn call_return_effect( - &self, + &mut self, trans: &mut impl GenKill, _block: mir::BasicBlock, return_places: CallReturnPlaces<'_, 'tcx>, @@ -372,7 +372,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { } fn switch_int_edge_effects>( - &self, + &mut self, block: mir::BasicBlock, discr: &mir::Operand<'tcx>, edge_effects: &mut impl SwitchIntEdgeEffects, @@ -442,7 +442,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { type Idx = MovePathIndex; fn statement_effect( - &self, + &mut self, trans: &mut impl GenKill, _statement: &mir::Statement<'tcx>, location: Location, @@ -456,7 +456,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { } fn terminator_effect( - &self, + &mut self, trans: &mut impl GenKill, _terminator: &mir::Terminator<'tcx>, location: Location, @@ -467,7 +467,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { } fn call_return_effect( - &self, + &mut self, trans: &mut impl GenKill, _block: mir::BasicBlock, return_places: CallReturnPlaces<'_, 'tcx>, @@ -488,7 +488,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { } fn switch_int_edge_effects>( - &self, + &mut self, block: mir::BasicBlock, discr: &mir::Operand<'tcx>, edge_effects: &mut impl SwitchIntEdgeEffects, @@ -562,7 +562,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for DefinitelyInitializedPlaces<'_, 'tcx> { type Idx = MovePathIndex; fn statement_effect( - &self, + &mut self, trans: &mut impl GenKill, _statement: &mir::Statement<'tcx>, location: Location, @@ -573,7 +573,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for DefinitelyInitializedPlaces<'_, 'tcx> { } fn terminator_effect( - &self, + &mut self, trans: &mut impl GenKill, _terminator: &mir::Terminator<'tcx>, location: Location, @@ -584,7 +584,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for DefinitelyInitializedPlaces<'_, 'tcx> { } fn call_return_effect( - &self, + &mut self, trans: &mut impl GenKill, _block: mir::BasicBlock, return_places: CallReturnPlaces<'_, 'tcx>, @@ -627,7 +627,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> { #[instrument(skip(self, trans), level = "debug")] fn statement_effect( - &self, + &mut self, trans: &mut impl GenKill, stmt: &mir::Statement<'tcx>, location: Location, @@ -651,7 +651,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> { #[instrument(skip(self, trans, _terminator), level = "debug")] fn terminator_effect( - &self, + &mut self, trans: &mut impl GenKill, _terminator: &mir::Terminator<'tcx>, location: Location, @@ -672,7 +672,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> { } fn call_return_effect( - &self, + &mut self, trans: &mut impl GenKill, block: mir::BasicBlock, _return_places: CallReturnPlaces<'_, 'tcx>, diff --git a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs index 463ce083a64fd..666c8d50a8a8c 100644 --- a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs @@ -1,10 +1,9 @@ pub use super::*; -use crate::{CallReturnPlaces, GenKill, Results, ResultsRefCursor}; +use crate::{CallReturnPlaces, GenKill, ResultsClonedCursor}; use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; use std::borrow::Cow; -use std::cell::RefCell; #[derive(Clone)] pub struct MaybeStorageLive<'a> { @@ -17,6 +16,12 @@ impl<'a> MaybeStorageLive<'a> { } } +impl crate::CloneAnalysis for MaybeStorageLive<'_> { + fn clone_analysis(&self) -> Self { + self.clone() + } +} + impl<'tcx, 'a> crate::AnalysisDomain<'tcx> for MaybeStorageLive<'a> { type Domain = BitSet; @@ -43,7 +48,7 @@ impl<'tcx, 'a> crate::GenKillAnalysis<'tcx> for MaybeStorageLive<'a> { type Idx = Local; fn statement_effect( - &self, + &mut self, trans: &mut impl GenKill, stmt: &mir::Statement<'tcx>, _: Location, @@ -56,7 +61,7 @@ impl<'tcx, 'a> crate::GenKillAnalysis<'tcx> for MaybeStorageLive<'a> { } fn terminator_effect( - &self, + &mut self, _trans: &mut impl GenKill, _: &mir::Terminator<'tcx>, _: Location, @@ -65,7 +70,7 @@ impl<'tcx, 'a> crate::GenKillAnalysis<'tcx> for MaybeStorageLive<'a> { } fn call_return_effect( - &self, + &mut self, _trans: &mut impl GenKill, _block: BasicBlock, _return_places: CallReturnPlaces<'_, 'tcx>, @@ -110,7 +115,7 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeStorageDead { type Idx = Local; fn statement_effect( - &self, + &mut self, trans: &mut impl GenKill, stmt: &mir::Statement<'tcx>, _: Location, @@ -123,7 +128,7 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeStorageDead { } fn terminator_effect( - &self, + &mut self, _trans: &mut impl GenKill, _: &mir::Terminator<'tcx>, _: Location, @@ -132,7 +137,7 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeStorageDead { } fn call_return_effect( - &self, + &mut self, _trans: &mut impl GenKill, _block: BasicBlock, _return_places: CallReturnPlaces<'_, 'tcx>, @@ -141,28 +146,28 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeStorageDead { } } -type BorrowedLocalsResults<'a, 'tcx> = ResultsRefCursor<'a, 'a, 'tcx, MaybeBorrowedLocals>; +type BorrowedLocalsResults<'res, 'mir, 'tcx> = + ResultsClonedCursor<'res, 'mir, 'tcx, MaybeBorrowedLocals>; /// Dataflow analysis that determines whether each local requires storage at a /// given location; i.e. whether its storage can go away without being observed. -pub struct MaybeRequiresStorage<'mir, 'tcx> { - body: &'mir Body<'tcx>, - borrowed_locals: RefCell>, +pub struct MaybeRequiresStorage<'res, 'mir, 'tcx> { + borrowed_locals: BorrowedLocalsResults<'res, 'mir, 'tcx>, } -impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> { - pub fn new( - body: &'mir Body<'tcx>, - borrowed_locals: &'mir Results<'tcx, MaybeBorrowedLocals>, - ) -> Self { - MaybeRequiresStorage { - body, - borrowed_locals: RefCell::new(ResultsRefCursor::new(&body, borrowed_locals)), - } +impl<'res, 'mir, 'tcx> MaybeRequiresStorage<'res, 'mir, 'tcx> { + pub fn new(borrowed_locals: BorrowedLocalsResults<'res, 'mir, 'tcx>) -> Self { + MaybeRequiresStorage { borrowed_locals } + } +} + +impl crate::CloneAnalysis for MaybeRequiresStorage<'_, '_, '_> { + fn clone_analysis(&self) -> Self { + Self { borrowed_locals: self.borrowed_locals.new_cursor() } } } -impl<'mir, 'tcx> crate::AnalysisDomain<'tcx> for MaybeRequiresStorage<'mir, 'tcx> { +impl<'tcx> crate::AnalysisDomain<'tcx> for MaybeRequiresStorage<'_, '_, 'tcx> { type Domain = BitSet; const NAME: &'static str = "requires_storage"; @@ -181,17 +186,17 @@ impl<'mir, 'tcx> crate::AnalysisDomain<'tcx> for MaybeRequiresStorage<'mir, 'tcx } } -impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tcx> { +impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, '_, 'tcx> { type Idx = Local; fn before_statement_effect( - &self, + &mut self, trans: &mut impl GenKill, stmt: &mir::Statement<'tcx>, loc: Location, ) { // If a place is borrowed in a statement, it needs storage for that statement. - self.borrowed_locals.borrow().analysis().statement_effect(trans, stmt, loc); + self.borrowed_locals.mut_analysis().statement_effect(trans, stmt, loc); match &stmt.kind { StatementKind::StorageDead(l) => trans.kill(*l), @@ -218,7 +223,7 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc } fn statement_effect( - &self, + &mut self, trans: &mut impl GenKill, _: &mir::Statement<'tcx>, loc: Location, @@ -229,13 +234,13 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc } fn before_terminator_effect( - &self, + &mut self, trans: &mut impl GenKill, terminator: &mir::Terminator<'tcx>, loc: Location, ) { // If a place is borrowed in a terminator, it needs storage for that terminator. - self.borrowed_locals.borrow().analysis().terminator_effect(trans, terminator, loc); + self.borrowed_locals.mut_analysis().terminator_effect(trans, terminator, loc); match &terminator.kind { TerminatorKind::Call { destination, .. } => { @@ -282,7 +287,7 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc } fn terminator_effect( - &self, + &mut self, trans: &mut impl GenKill, terminator: &mir::Terminator<'tcx>, loc: Location, @@ -321,7 +326,7 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc } fn call_return_effect( - &self, + &mut self, trans: &mut impl GenKill, _block: BasicBlock, return_places: CallReturnPlaces<'_, 'tcx>, @@ -330,7 +335,7 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc } fn yield_resume_effect( - &self, + &mut self, trans: &mut impl GenKill, _resume_block: BasicBlock, resume_place: mir::Place<'tcx>, @@ -339,28 +344,28 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc } } -impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> { +impl<'tcx> MaybeRequiresStorage<'_, '_, 'tcx> { /// Kill locals that are fully moved and have not been borrowed. - fn check_for_move(&self, trans: &mut impl GenKill, loc: Location) { - let mut visitor = MoveVisitor { trans, borrowed_locals: &self.borrowed_locals }; - visitor.visit_location(&self.body, loc); + fn check_for_move(&mut self, trans: &mut impl GenKill, loc: Location) { + let body = self.borrowed_locals.body(); + let mut visitor = MoveVisitor { trans, borrowed_locals: &mut self.borrowed_locals }; + visitor.visit_location(body, loc); } } -struct MoveVisitor<'a, 'mir, 'tcx, T> { - borrowed_locals: &'a RefCell>, +struct MoveVisitor<'a, 'res, 'mir, 'tcx, T> { + borrowed_locals: &'a mut BorrowedLocalsResults<'res, 'mir, 'tcx>, trans: &'a mut T, } -impl<'a, 'mir, 'tcx, T> Visitor<'tcx> for MoveVisitor<'a, 'mir, 'tcx, T> +impl<'tcx, T> Visitor<'tcx> for MoveVisitor<'_, '_, '_, 'tcx, T> where T: GenKill, { fn visit_local(&mut self, local: Local, context: PlaceContext, loc: Location) { if PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) == context { - let mut borrowed_locals = self.borrowed_locals.borrow_mut(); - borrowed_locals.seek_before_primary_effect(loc); - if !borrowed_locals.contains(local) { + self.borrowed_locals.seek_before_primary_effect(loc); + if !self.borrowed_locals.contains(local) { self.trans.kill(local); } } diff --git a/compiler/rustc_mir_dataflow/src/lib.rs b/compiler/rustc_mir_dataflow/src/lib.rs index fc4efb943e6b7..3494a37c3cf2d 100644 --- a/compiler/rustc_mir_dataflow/src/lib.rs +++ b/compiler/rustc_mir_dataflow/src/lib.rs @@ -28,8 +28,9 @@ pub use self::drop_flag_effects::{ }; pub use self::framework::{ fmt, graphviz, lattice, visit_results, Analysis, AnalysisDomain, Backward, CallReturnPlaces, - Direction, Engine, Forward, GenKill, GenKillAnalysis, JoinSemiLattice, Results, ResultsCursor, - ResultsRefCursor, ResultsVisitable, ResultsVisitor, SwitchIntEdgeEffects, + CloneAnalysis, Direction, Engine, Forward, GenKill, GenKillAnalysis, JoinSemiLattice, Results, + ResultsCloned, ResultsClonedCursor, ResultsCursor, ResultsRefCursor, ResultsVisitable, + ResultsVisitor, SwitchIntEdgeEffects, }; use self::move_paths::MoveData; diff --git a/compiler/rustc_mir_dataflow/src/rustc_peek.rs b/compiler/rustc_mir_dataflow/src/rustc_peek.rs index 7cae68efbecc3..0cbc7442cf1fb 100644 --- a/compiler/rustc_mir_dataflow/src/rustc_peek.rs +++ b/compiler/rustc_mir_dataflow/src/rustc_peek.rs @@ -17,7 +17,7 @@ use crate::impls::{ use crate::move_paths::{HasMoveData, MoveData}; use crate::move_paths::{LookupResult, MovePathIndex}; use crate::MoveDataParamEnv; -use crate::{Analysis, JoinSemiLattice, Results, ResultsCursor}; +use crate::{Analysis, JoinSemiLattice, ResultsCursor}; pub struct SanityCheck; @@ -42,7 +42,7 @@ impl<'tcx> MirPass<'tcx> for SanityCheck { .into_engine(tcx, body) .iterate_to_fixpoint(); - sanity_check_via_rustc_peek(tcx, body, &flow_inits); + sanity_check_via_rustc_peek(tcx, flow_inits.into_results_cursor(body)); } if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_maybe_uninit).is_some() { @@ -50,7 +50,7 @@ impl<'tcx> MirPass<'tcx> for SanityCheck { .into_engine(tcx, body) .iterate_to_fixpoint(); - sanity_check_via_rustc_peek(tcx, body, &flow_uninits); + sanity_check_via_rustc_peek(tcx, flow_uninits.into_results_cursor(body)); } if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_definite_init).is_some() { @@ -58,13 +58,13 @@ impl<'tcx> MirPass<'tcx> for SanityCheck { .into_engine(tcx, body) .iterate_to_fixpoint(); - sanity_check_via_rustc_peek(tcx, body, &flow_def_inits); + sanity_check_via_rustc_peek(tcx, flow_def_inits.into_results_cursor(body)); } if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_liveness).is_some() { let flow_liveness = MaybeLiveLocals.into_engine(tcx, body).iterate_to_fixpoint(); - sanity_check_via_rustc_peek(tcx, body, &flow_liveness); + sanity_check_via_rustc_peek(tcx, flow_liveness.into_results_cursor(body)); } if has_rustc_mir_with(tcx, def_id, sym::stop_after_dataflow).is_some() { @@ -91,17 +91,14 @@ impl<'tcx> MirPass<'tcx> for SanityCheck { /// errors are not intended to be used for unit tests.) pub fn sanity_check_via_rustc_peek<'tcx, A>( tcx: TyCtxt<'tcx>, - body: &Body<'tcx>, - results: &Results<'tcx, A>, + mut cursor: ResultsCursor<'_, 'tcx, A>, ) where A: RustcPeekAt<'tcx>, { - let def_id = body.source.def_id(); + let def_id = cursor.body().source.def_id(); debug!("sanity_check_via_rustc_peek def_id: {:?}", def_id); - let mut cursor = ResultsCursor::new(body, results); - - let peek_calls = body.basic_blocks.iter_enumerated().filter_map(|(bb, block_data)| { + let peek_calls = cursor.body().basic_blocks.iter_enumerated().filter_map(|(bb, block_data)| { PeekCall::from_terminator(tcx, block_data.terminator()).map(|call| (bb, block_data, call)) }); @@ -132,8 +129,8 @@ pub fn sanity_check_via_rustc_peek<'tcx, A>( ) => { let loc = Location { block: bb, statement_index }; cursor.seek_before_primary_effect(loc); - let state = cursor.get(); - results.analysis.peek_at(tcx, *place, state, call); + let (state, analysis) = cursor.get_with_analysis(); + analysis.peek_at(tcx, *place, state, call); } _ => { diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs index b74d06e5ae8dd..5693e5a4a712a 100644 --- a/compiler/rustc_mir_dataflow/src/value_analysis.rs +++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs @@ -343,7 +343,7 @@ where T: ValueAnalysis<'tcx>, { fn apply_statement_effect( - &self, + &mut self, state: &mut Self::Domain, statement: &Statement<'tcx>, _location: Location, @@ -354,7 +354,7 @@ where } fn apply_terminator_effect( - &self, + &mut self, state: &mut Self::Domain, terminator: &Terminator<'tcx>, _location: Location, @@ -365,7 +365,7 @@ where } fn apply_call_return_effect( - &self, + &mut self, state: &mut Self::Domain, _block: BasicBlock, return_places: crate::CallReturnPlaces<'_, 'tcx>, @@ -376,7 +376,7 @@ where } fn apply_switch_int_edge_effects( - &self, + &mut self, _block: BasicBlock, discr: &Operand<'tcx>, apply_edge_effects: &mut impl SwitchIntEdgeEffects, diff --git a/compiler/rustc_mir_transform/src/check_unsafety.rs b/compiler/rustc_mir_transform/src/check_unsafety.rs index 069514d8a3bf3..2f851cd1eb5f7 100644 --- a/compiler/rustc_mir_transform/src/check_unsafety.rs +++ b/compiler/rustc_mir_transform/src/check_unsafety.rs @@ -421,10 +421,8 @@ impl<'tcx> intravisit::Visitor<'tcx> for UnusedUnsafeVisitor<'_, 'tcx> { intravisit::walk_block(self, block); } - fn visit_anon_const(&mut self, c: &'tcx hir::AnonConst) { - if matches!(self.tcx.def_kind(c.def_id), DefKind::InlineConst) { - self.visit_body(self.tcx.hir().body(c.body)) - } + fn visit_inline_const(&mut self, c: &'tcx hir::ConstBlock) { + self.visit_body(self.tcx.hir().body(c.body)) } fn visit_fn( diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index 7adfc9dff2ae9..78fb196358faf 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -10,8 +10,12 @@ use rustc_middle::mir::visit::{MutVisitor, Visitor}; use rustc_middle::mir::*; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_mir_dataflow::value_analysis::{Map, State, TrackElem, ValueAnalysis, ValueOrPlace}; -use rustc_mir_dataflow::{lattice::FlatSet, Analysis, ResultsVisitor, SwitchIntEdgeEffects}; +use rustc_mir_dataflow::value_analysis::{ + Map, State, TrackElem, ValueAnalysis, ValueAnalysisWrapper, ValueOrPlace, +}; +use rustc_mir_dataflow::{ + lattice::FlatSet, Analysis, Results, ResultsVisitor, SwitchIntEdgeEffects, +}; use rustc_span::DUMMY_SP; use rustc_target::abi::{Align, FieldIdx, VariantIdx}; @@ -52,11 +56,11 @@ impl<'tcx> MirPass<'tcx> for DataflowConstProp { // Perform the actual dataflow analysis. let analysis = ConstAnalysis::new(tcx, body, map); - let results = debug_span!("analyze") + let mut results = debug_span!("analyze") .in_scope(|| analysis.wrap().into_engine(tcx, body).iterate_to_fixpoint()); // Collect results and patch the body afterwards. - let mut visitor = CollectAndPatch::new(tcx, &results.analysis.0.map); + let mut visitor = CollectAndPatch::new(tcx); debug_span!("collect").in_scope(|| results.visit_reachable_with(body, &mut visitor)); debug_span!("patch").in_scope(|| visitor.visit_body(body)); } @@ -387,9 +391,8 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { } } -struct CollectAndPatch<'tcx, 'map> { +struct CollectAndPatch<'tcx> { tcx: TyCtxt<'tcx>, - map: &'map Map, /// For a given MIR location, this stores the values of the operands used by that location. In /// particular, this is before the effect, such that the operands of `_1 = _1 + _2` are @@ -400,9 +403,9 @@ struct CollectAndPatch<'tcx, 'map> { assignments: FxHashMap>, } -impl<'tcx, 'map> CollectAndPatch<'tcx, 'map> { - fn new(tcx: TyCtxt<'tcx>, map: &'map Map) -> Self { - Self { tcx, map, before_effect: FxHashMap::default(), assignments: FxHashMap::default() } +impl<'tcx> CollectAndPatch<'tcx> { + fn new(tcx: TyCtxt<'tcx>) -> Self { + Self { tcx, before_effect: FxHashMap::default(), assignments: FxHashMap::default() } } fn make_operand(&self, scalar: ScalarTy<'tcx>) -> Operand<'tcx> { @@ -414,18 +417,23 @@ impl<'tcx, 'map> CollectAndPatch<'tcx, 'map> { } } -impl<'mir, 'tcx, 'map> ResultsVisitor<'mir, 'tcx> for CollectAndPatch<'tcx, 'map> { +impl<'mir, 'tcx> + ResultsVisitor<'mir, 'tcx, Results<'tcx, ValueAnalysisWrapper>>> + for CollectAndPatch<'tcx> +{ type FlowState = State>>; fn visit_statement_before_primary_effect( &mut self, + results: &Results<'tcx, ValueAnalysisWrapper>>, state: &Self::FlowState, statement: &'mir Statement<'tcx>, location: Location, ) { match &statement.kind { StatementKind::Assign(box (_, rvalue)) => { - OperandCollector { state, visitor: self }.visit_rvalue(rvalue, location); + OperandCollector { state, visitor: self, map: &results.analysis.0.map } + .visit_rvalue(rvalue, location); } _ => (), } @@ -433,6 +441,7 @@ impl<'mir, 'tcx, 'map> ResultsVisitor<'mir, 'tcx> for CollectAndPatch<'tcx, 'map fn visit_statement_after_primary_effect( &mut self, + results: &Results<'tcx, ValueAnalysisWrapper>>, state: &Self::FlowState, statement: &'mir Statement<'tcx>, location: Location, @@ -441,30 +450,34 @@ impl<'mir, 'tcx, 'map> ResultsVisitor<'mir, 'tcx> for CollectAndPatch<'tcx, 'map StatementKind::Assign(box (_, Rvalue::Use(Operand::Constant(_)))) => { // Don't overwrite the assignment if it already uses a constant (to keep the span). } - StatementKind::Assign(box (place, _)) => match state.get(place.as_ref(), self.map) { - FlatSet::Top => (), - FlatSet::Elem(value) => { - self.assignments.insert(location, value); - } - FlatSet::Bottom => { - // This assignment is either unreachable, or an uninitialized value is assigned. + StatementKind::Assign(box (place, _)) => { + match state.get(place.as_ref(), &results.analysis.0.map) { + FlatSet::Top => (), + FlatSet::Elem(value) => { + self.assignments.insert(location, value); + } + FlatSet::Bottom => { + // This assignment is either unreachable, or an uninitialized value is assigned. + } } - }, + } _ => (), } } fn visit_terminator_before_primary_effect( &mut self, + results: &Results<'tcx, ValueAnalysisWrapper>>, state: &Self::FlowState, terminator: &'mir Terminator<'tcx>, location: Location, ) { - OperandCollector { state, visitor: self }.visit_terminator(terminator, location); + OperandCollector { state, visitor: self, map: &results.analysis.0.map } + .visit_terminator(terminator, location); } } -impl<'tcx, 'map> MutVisitor<'tcx> for CollectAndPatch<'tcx, 'map> { +impl<'tcx> MutVisitor<'tcx> for CollectAndPatch<'tcx> { fn tcx<'a>(&'a self) -> TyCtxt<'tcx> { self.tcx } @@ -496,14 +509,15 @@ impl<'tcx, 'map> MutVisitor<'tcx> for CollectAndPatch<'tcx, 'map> { struct OperandCollector<'tcx, 'map, 'a> { state: &'a State>>, - visitor: &'a mut CollectAndPatch<'tcx, 'map>, + visitor: &'a mut CollectAndPatch<'tcx>, + map: &'map Map, } impl<'tcx, 'map, 'a> Visitor<'tcx> for OperandCollector<'tcx, 'map, 'a> { fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) { match operand { Operand::Copy(place) | Operand::Move(place) => { - match self.state.get(place.as_ref(), self.visitor.map) { + match self.state.get(place.as_ref(), self.map) { FlatSet::Top => (), FlatSet::Elem(value) => { self.visitor.before_effect.insert((location, *place), value); diff --git a/compiler/rustc_mir_transform/src/dump_mir.rs b/compiler/rustc_mir_transform/src/dump_mir.rs index 746e3d9652db6..13841be494cf0 100644 --- a/compiler/rustc_mir_transform/src/dump_mir.rs +++ b/compiler/rustc_mir_transform/src/dump_mir.rs @@ -7,7 +7,7 @@ use crate::MirPass; use rustc_middle::mir::write_mir_pretty; use rustc_middle::mir::Body; use rustc_middle::ty::TyCtxt; -use rustc_session::config::OutputType; +use rustc_session::config::{OutFileName, OutputType}; pub struct Marker(pub &'static str); @@ -20,8 +20,15 @@ impl<'tcx> MirPass<'tcx> for Marker { } pub fn emit_mir(tcx: TyCtxt<'_>) -> io::Result<()> { - let path = tcx.output_filenames(()).path(OutputType::Mir); - let mut f = io::BufWriter::new(File::create(&path)?); - write_mir_pretty(tcx, None, &mut f)?; + match tcx.output_filenames(()).path(OutputType::Mir) { + OutFileName::Stdout => { + let mut f = io::stdout(); + write_mir_pretty(tcx, None, &mut f)?; + } + OutFileName::Real(path) => { + let mut f = io::BufWriter::new(File::create(&path)?); + write_mir_pretty(tcx, None, &mut f)?; + } + } Ok(()) } diff --git a/compiler/rustc_mir_transform/src/generator.rs b/compiler/rustc_mir_transform/src/generator.rs index 89567ed0ab882..fe3f8ed047a70 100644 --- a/compiler/rustc_mir_transform/src/generator.rs +++ b/compiler/rustc_mir_transform/src/generator.rs @@ -597,16 +597,15 @@ fn locals_live_across_suspend_points<'tcx>( let borrowed_locals_results = MaybeBorrowedLocals.into_engine(tcx, body_ref).pass_name("generator").iterate_to_fixpoint(); - let mut borrowed_locals_cursor = - rustc_mir_dataflow::ResultsCursor::new(body_ref, &borrowed_locals_results); + let mut borrowed_locals_cursor = borrowed_locals_results.cloned_results_cursor(body_ref); // Calculate the MIR locals that we actually need to keep storage around // for. - let requires_storage_results = MaybeRequiresStorage::new(body, &borrowed_locals_results) - .into_engine(tcx, body_ref) - .iterate_to_fixpoint(); - let mut requires_storage_cursor = - rustc_mir_dataflow::ResultsCursor::new(body_ref, &requires_storage_results); + let mut requires_storage_results = + MaybeRequiresStorage::new(borrowed_locals_results.cloned_results_cursor(body)) + .into_engine(tcx, body_ref) + .iterate_to_fixpoint(); + let mut requires_storage_cursor = requires_storage_results.as_results_cursor(body_ref); // Calculate the liveness of MIR locals ignoring borrows. let mut liveness = MaybeLiveLocals @@ -747,7 +746,7 @@ fn compute_storage_conflicts<'mir, 'tcx>( body: &'mir Body<'tcx>, saved_locals: &GeneratorSavedLocals, always_live_locals: BitSet, - requires_storage: rustc_mir_dataflow::Results<'tcx, MaybeRequiresStorage<'mir, 'tcx>>, + mut requires_storage: rustc_mir_dataflow::Results<'tcx, MaybeRequiresStorage<'_, 'mir, 'tcx>>, ) -> BitMatrix { assert_eq!(body.local_decls.len(), saved_locals.domain_size()); @@ -802,13 +801,14 @@ struct StorageConflictVisitor<'mir, 'tcx, 's> { local_conflicts: BitMatrix, } -impl<'mir, 'tcx> rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx> +impl<'mir, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx, R> for StorageConflictVisitor<'mir, 'tcx, '_> { type FlowState = BitSet; fn visit_statement_before_primary_effect( &mut self, + _results: &R, state: &Self::FlowState, _statement: &'mir Statement<'tcx>, loc: Location, @@ -818,6 +818,7 @@ impl<'mir, 'tcx> rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx> fn visit_terminator_before_primary_effect( &mut self, + _results: &R, state: &Self::FlowState, _terminator: &'mir Terminator<'tcx>, loc: Location, diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 55b9f084c39f7..5487b5987e001 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -145,13 +145,16 @@ impl<'tcx> Inliner<'tcx> { Ok(new_blocks) => { debug!("inlined {}", callsite.callee); self.changed = true; + + self.history.push(callsite.callee.def_id()); + self.process_blocks(caller_body, new_blocks); + self.history.pop(); + inlined_count += 1; if inlined_count == inline_limit { + debug!("inline count reached"); return; } - self.history.push(callsite.callee.def_id()); - self.process_blocks(caller_body, new_blocks); - self.history.pop(); } } } diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs index dae01e41e5f3d..3a7d58f712568 100644 --- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs +++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs @@ -82,30 +82,35 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics { drop(args); terminator.kind = TerminatorKind::Goto { target }; } - sym::wrapping_add | sym::wrapping_sub | sym::wrapping_mul => { - if let Some(target) = *target { - let lhs; - let rhs; - { - let mut args = args.drain(..); - lhs = args.next().unwrap(); - rhs = args.next().unwrap(); - } - let bin_op = match intrinsic_name { - sym::wrapping_add => BinOp::Add, - sym::wrapping_sub => BinOp::Sub, - sym::wrapping_mul => BinOp::Mul, - _ => bug!("unexpected intrinsic"), - }; - block.statements.push(Statement { - source_info: terminator.source_info, - kind: StatementKind::Assign(Box::new(( - *destination, - Rvalue::BinaryOp(bin_op, Box::new((lhs, rhs))), - ))), - }); - terminator.kind = TerminatorKind::Goto { target }; + sym::wrapping_add + | sym::wrapping_sub + | sym::wrapping_mul + | sym::unchecked_div + | sym::unchecked_rem => { + let target = target.unwrap(); + let lhs; + let rhs; + { + let mut args = args.drain(..); + lhs = args.next().unwrap(); + rhs = args.next().unwrap(); } + let bin_op = match intrinsic_name { + sym::wrapping_add => BinOp::Add, + sym::wrapping_sub => BinOp::Sub, + sym::wrapping_mul => BinOp::Mul, + sym::unchecked_div => BinOp::Div, + sym::unchecked_rem => BinOp::Rem, + _ => bug!("unexpected intrinsic"), + }; + block.statements.push(Statement { + source_info: terminator.source_info, + kind: StatementKind::Assign(Box::new(( + *destination, + Rvalue::BinaryOp(bin_op, Box::new((lhs, rhs))), + ))), + }); + terminator.kind = TerminatorKind::Goto { target }; } sym::add_with_overflow | sym::sub_with_overflow | sym::mul_with_overflow => { if let Some(target) = *target { diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index ae726dea94426..5f12f1937c06c 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -647,7 +647,7 @@ fn build_call_shim<'tcx>( let mut sig = if let Some(sig_substs) = sig_substs { sig.subst(tcx, &sig_substs) } else { - sig.skip_binder() + sig.subst_identity() }; if let CallKind::Indirect(fnty) = call_kind { diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 09025c1ee3319..f4ee7b7587587 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -35,15 +35,15 @@ //! //! - A "mono item" is something that results in a function or global in //! the LLVM IR of a codegen unit. Mono items do not stand on their -//! own, they can reference other mono items. For example, if function +//! own, they can use other mono items. For example, if function //! `foo()` calls function `bar()` then the mono item for `foo()` -//! references the mono item for function `bar()`. In general, the -//! definition for mono item A referencing a mono item B is that -//! the LLVM artifact produced for A references the LLVM artifact produced +//! uses the mono item for function `bar()`. In general, the +//! definition for mono item A using a mono item B is that +//! the LLVM artifact produced for A uses the LLVM artifact produced //! for B. //! -//! - Mono items and the references between them form a directed graph, -//! where the mono items are the nodes and references form the edges. +//! - Mono items and the uses between them form a directed graph, +//! where the mono items are the nodes and uses form the edges. //! Let's call this graph the "mono item graph". //! //! - The mono item graph for a program contains all mono items @@ -53,12 +53,11 @@ //! mono item graph for the current crate. It runs in two phases: //! //! 1. Discover the roots of the graph by traversing the HIR of the crate. -//! 2. Starting from the roots, find neighboring nodes by inspecting the MIR +//! 2. Starting from the roots, find uses by inspecting the MIR //! representation of the item corresponding to a given node, until no more //! new nodes are found. //! //! ### Discovering roots -//! //! The roots of the mono item graph correspond to the public non-generic //! syntactic items in the source code. We find them by walking the HIR of the //! crate, and whenever we hit upon a public function, method, or static item, @@ -69,25 +68,23 @@ //! specified. Functions marked `#[no_mangle]` and functions called by inlinable //! functions also always act as roots.) //! -//! ### Finding neighbor nodes -//! Given a mono item node, we can discover neighbors by inspecting its -//! MIR. We walk the MIR and any time we hit upon something that signifies a -//! reference to another mono item, we have found a neighbor. Since the -//! mono item we are currently at is always monomorphic, we also know the -//! concrete type arguments of its neighbors, and so all neighbors again will be -//! monomorphic. The specific forms a reference to a neighboring node can take -//! in MIR are quite diverse. Here is an overview: +//! ### Finding uses +//! Given a mono item node, we can discover uses by inspecting its MIR. We walk +//! the MIR to find other mono items used by each mono item. Since the mono +//! item we are currently at is always monomorphic, we also know the concrete +//! type arguments of its used mono items. The specific forms a use can take in +//! MIR are quite diverse. Here is an overview: //! //! #### Calling Functions/Methods -//! The most obvious form of one mono item referencing another is a +//! The most obvious way for one mono item to use another is a //! function or method call (represented by a CALL terminator in MIR). But -//! calls are not the only thing that might introduce a reference between two +//! calls are not the only thing that might introduce a use between two //! function mono items, and as we will see below, they are just a //! specialization of the form described next, and consequently will not get any //! special treatment in the algorithm. //! //! #### Taking a reference to a function or method -//! A function does not need to actually be called in order to be a neighbor of +//! A function does not need to actually be called in order to be used by //! another function. It suffices to just take a reference in order to introduce //! an edge. Consider the following example: //! @@ -109,18 +106,18 @@ //! The MIR of none of these functions will contain an explicit call to //! `print_val::`. Nonetheless, in order to mono this program, we need //! an instance of this function. Thus, whenever we encounter a function or -//! method in operand position, we treat it as a neighbor of the current +//! method in operand position, we treat it as a use of the current //! mono item. Calls are just a special case of that. //! //! #### Drop glue //! Drop glue mono items are introduced by MIR drop-statements. The -//! generated mono item will again have drop-glue item neighbors if the +//! generated mono item will have additional drop-glue item uses if the //! type to be dropped contains nested values that also need to be dropped. It -//! might also have a function item neighbor for the explicit `Drop::drop` +//! might also have a function item use for the explicit `Drop::drop` //! implementation of its type. //! //! #### Unsizing Casts -//! A subtle way of introducing neighbor edges is by casting to a trait object. +//! A subtle way of introducing use edges is by casting to a trait object. //! Since the resulting fat-pointer contains a reference to a vtable, we need to //! instantiate all object-safe methods of the trait, as we need to store //! pointers to these functions even if they never get called anywhere. This can @@ -151,7 +148,7 @@ //! Mono item collection can be performed in one of two modes: //! //! - Lazy mode means that items will only be instantiated when actually -//! referenced. The goal is to produce the least amount of machine code +//! used. The goal is to produce the least amount of machine code //! possible. //! //! - Eager mode is meant to be used in conjunction with incremental compilation @@ -198,7 +195,6 @@ use rustc_session::lint::builtin::LARGE_ASSIGNMENTS; use rustc_session::Limit; use rustc_span::source_map::{dummy_spanned, respan, Span, Spanned, DUMMY_SP}; use rustc_target::abi::Size; -use std::ops::Range; use std::path::PathBuf; use crate::errors::{ @@ -211,66 +207,51 @@ pub enum MonoItemCollectionMode { Lazy, } -/// Maps every mono item to all mono items it references in its -/// body. -pub struct InliningMap<'tcx> { - // Maps a source mono item to the range of mono items - // accessed by it. - // The range selects elements within the `targets` vecs. - index: FxHashMap, Range>, - targets: Vec>, +pub struct UsageMap<'tcx> { + // Maps every mono item to the mono items used by it. + used_map: FxHashMap, Vec>>, + + // Maps every mono item to the mono items that use it. + user_map: FxHashMap, Vec>>, } type MonoItems<'tcx> = Vec>>; -impl<'tcx> InliningMap<'tcx> { - fn new() -> InliningMap<'tcx> { - InliningMap { index: FxHashMap::default(), targets: Vec::new() } +impl<'tcx> UsageMap<'tcx> { + fn new() -> UsageMap<'tcx> { + UsageMap { used_map: FxHashMap::default(), user_map: FxHashMap::default() } } - fn record_accesses<'a>( + fn record_used<'a>( &mut self, - source: MonoItem<'tcx>, - new_targets: &'a [Spanned>], + user_item: MonoItem<'tcx>, + used_items: &'a [Spanned>], ) where 'tcx: 'a, { - let start_index = self.targets.len(); - let new_items_count = new_targets.len(); - - self.targets.reserve(new_items_count); - - for Spanned { node: mono_item, .. } in new_targets.into_iter() { - self.targets.push(*mono_item); + let used_items: Vec<_> = used_items.iter().map(|item| item.node).collect(); + for &used_item in used_items.iter() { + self.user_map.entry(used_item).or_default().push(user_item); } - let end_index = self.targets.len(); - assert!(self.index.insert(source, start_index..end_index).is_none()); + assert!(self.used_map.insert(user_item, used_items).is_none()); } - /// Internally iterate over all items referenced by `source` which will be - /// made available for inlining. - pub fn with_inlining_candidates(&self, tcx: TyCtxt<'tcx>, source: MonoItem<'tcx>, mut f: F) - where - F: FnMut(MonoItem<'tcx>), - { - if let Some(range) = self.index.get(&source) { - for candidate in self.targets[range.clone()].iter() { - let is_inlined = candidate.instantiation_mode(tcx) == InstantiationMode::LocalCopy; - if is_inlined { - f(*candidate); - } - } - } + pub fn get_user_items(&self, item: MonoItem<'tcx>) -> Option<&[MonoItem<'tcx>]> { + self.user_map.get(&item).map(|items| items.as_slice()) } - /// Internally iterate over all items and the things each accesses. - pub fn iter_accesses(&self, mut f: F) + /// Internally iterate over all inlined items used by `item`. + pub fn for_each_inlined_used_item(&self, tcx: TyCtxt<'tcx>, item: MonoItem<'tcx>, mut f: F) where - F: FnMut(MonoItem<'tcx>, &[MonoItem<'tcx>]), + F: FnMut(MonoItem<'tcx>), { - for (&accessor, range) in &self.index { - f(accessor, &self.targets[range.clone()]) + let used_items = self.used_map.get(&item).unwrap(); + for used_item in used_items.iter() { + let is_inlined = used_item.instantiation_mode(tcx) == InstantiationMode::LocalCopy; + if is_inlined { + f(*used_item); + } } } } @@ -279,7 +260,7 @@ impl<'tcx> InliningMap<'tcx> { pub fn collect_crate_mono_items( tcx: TyCtxt<'_>, mode: MonoItemCollectionMode, -) -> (FxHashSet>, InliningMap<'_>) { +) -> (FxHashSet>, UsageMap<'_>) { let _prof_timer = tcx.prof.generic_activity("monomorphization_collector"); let roots = @@ -288,12 +269,12 @@ pub fn collect_crate_mono_items( debug!("building mono item graph, beginning at roots"); let mut visited = MTLock::new(FxHashSet::default()); - let mut inlining_map = MTLock::new(InliningMap::new()); + let mut usage_map = MTLock::new(UsageMap::new()); let recursion_limit = tcx.recursion_limit(); { let visited: MTLockRef<'_, _> = &mut visited; - let inlining_map: MTLockRef<'_, _> = &mut inlining_map; + let usage_map: MTLockRef<'_, _> = &mut usage_map; tcx.sess.time("monomorphization_collector_graph_walk", || { par_for_each_in(roots, |root| { @@ -304,13 +285,13 @@ pub fn collect_crate_mono_items( visited, &mut recursion_depths, recursion_limit, - inlining_map, + usage_map, ); }); }); } - (visited.into_inner(), inlining_map.into_inner()) + (visited.into_inner(), usage_map.into_inner()) } // Find all non-generic items by walking the HIR. These items serve as roots to @@ -353,24 +334,23 @@ fn collect_roots(tcx: TyCtxt<'_>, mode: MonoItemCollectionMode) -> Vec( tcx: TyCtxt<'tcx>, - starting_point: Spanned>, + starting_item: Spanned>, visited: MTLockRef<'_, FxHashSet>>, recursion_depths: &mut DefIdMap, recursion_limit: Limit, - inlining_map: MTLockRef<'_, InliningMap<'tcx>>, + usage_map: MTLockRef<'_, UsageMap<'tcx>>, ) { - if !visited.lock_mut().insert(starting_point.node) { + if !visited.lock_mut().insert(starting_item.node) { // We've been here already, no need to search again. return; } - let mut neighbors = Vec::new(); + let mut used_items = Vec::new(); let recursion_depth_reset; - // // Post-monomorphization errors MVP // // We can encounter errors while monomorphizing an item, but we don't have a good way of @@ -396,7 +376,7 @@ fn collect_items_rec<'tcx>( // FIXME: don't rely on global state, instead bubble up errors. Note: this is very hard to do. let error_count = tcx.sess.diagnostic().err_count(); - match starting_point.node { + match starting_item.node { MonoItem::Static(def_id) => { let instance = Instance::mono(tcx, def_id); @@ -404,19 +384,19 @@ fn collect_items_rec<'tcx>( debug_assert!(should_codegen_locally(tcx, &instance)); let ty = instance.ty(tcx, ty::ParamEnv::reveal_all()); - visit_drop_use(tcx, ty, true, starting_point.span, &mut neighbors); + visit_drop_use(tcx, ty, true, starting_item.span, &mut used_items); recursion_depth_reset = None; if let Ok(alloc) = tcx.eval_static_initializer(def_id) { for &id in alloc.inner().provenance().ptrs().values() { - collect_miri(tcx, id, &mut neighbors); + collect_miri(tcx, id, &mut used_items); } } if tcx.needs_thread_local_shim(def_id) { - neighbors.push(respan( - starting_point.span, + used_items.push(respan( + starting_item.span, MonoItem::Fn(Instance { def: InstanceDef::ThreadLocalShim(def_id), substs: InternalSubsts::empty(), @@ -432,14 +412,14 @@ fn collect_items_rec<'tcx>( recursion_depth_reset = Some(check_recursion_limit( tcx, instance, - starting_point.span, + starting_item.span, recursion_depths, recursion_limit, )); check_type_length_limit(tcx, instance); rustc_data_structures::stack::ensure_sufficient_stack(|| { - collect_neighbours(tcx, instance, &mut neighbors); + collect_used_items(tcx, instance, &mut used_items); }); } MonoItem::GlobalAsm(item_id) => { @@ -457,13 +437,13 @@ fn collect_items_rec<'tcx>( hir::InlineAsmOperand::SymFn { anon_const } => { let fn_ty = tcx.typeck_body(anon_const.body).node_type(anon_const.hir_id); - visit_fn_use(tcx, fn_ty, false, *op_sp, &mut neighbors); + visit_fn_use(tcx, fn_ty, false, *op_sp, &mut used_items); } hir::InlineAsmOperand::SymStatic { path: _, def_id } => { let instance = Instance::mono(tcx, *def_id); if should_codegen_locally(tcx, &instance) { trace!("collecting static {:?}", def_id); - neighbors.push(dummy_spanned(MonoItem::Static(*def_id))); + used_items.push(dummy_spanned(MonoItem::Static(*def_id))); } } hir::InlineAsmOperand::In { .. } @@ -483,19 +463,19 @@ fn collect_items_rec<'tcx>( // Check for PMEs and emit a diagnostic if one happened. To try to show relevant edges of the // mono item graph. if tcx.sess.diagnostic().err_count() > error_count - && starting_point.node.is_generic_fn() - && starting_point.node.is_user_defined() + && starting_item.node.is_generic_fn() + && starting_item.node.is_user_defined() { - let formatted_item = with_no_trimmed_paths!(starting_point.node.to_string()); + let formatted_item = with_no_trimmed_paths!(starting_item.node.to_string()); tcx.sess.emit_note(EncounteredErrorWhileInstantiating { - span: starting_point.span, + span: starting_item.span, formatted_item, }); } - inlining_map.lock_mut().record_accesses(starting_point.node, &neighbors); + usage_map.lock_mut().record_used(starting_item.node, &used_items); - for neighbour in neighbors { - collect_items_rec(tcx, neighbour, visited, recursion_depths, recursion_limit, inlining_map); + for used_item in used_items { + collect_items_rec(tcx, used_item, visited, recursion_depths, recursion_limit, usage_map); } if let Some((def_id, depth)) = recursion_depth_reset { @@ -611,14 +591,14 @@ fn check_type_length_limit<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) { } } -struct MirNeighborCollector<'a, 'tcx> { +struct MirUsedCollector<'a, 'tcx> { tcx: TyCtxt<'tcx>, body: &'a mir::Body<'tcx>, output: &'a mut MonoItems<'tcx>, instance: Instance<'tcx>, } -impl<'a, 'tcx> MirNeighborCollector<'a, 'tcx> { +impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> { pub fn monomorphize(&self, value: T) -> T where T: TypeFoldable>, @@ -632,7 +612,7 @@ impl<'a, 'tcx> MirNeighborCollector<'a, 'tcx> { } } -impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { +impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) { debug!("visiting rvalue {:?}", *rvalue); @@ -1392,13 +1372,13 @@ fn collect_miri<'tcx>(tcx: TyCtxt<'tcx>, alloc_id: AllocId, output: &mut MonoIte /// Scans the MIR in order to find function calls, closures, and drop-glue. #[instrument(skip(tcx, output), level = "debug")] -fn collect_neighbours<'tcx>( +fn collect_used_items<'tcx>( tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, output: &mut MonoItems<'tcx>, ) { let body = tcx.instance_mir(instance.def); - MirNeighborCollector { tcx, body: &body, output, instance }.visit_body(&body); + MirUsedCollector { tcx, body: &body, output, instance }.visit_body(&body); } #[instrument(skip(tcx, output), level = "debug")] diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index 015361f8ad5b7..1d9c8ded349c0 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -115,42 +115,41 @@ use rustc_middle::ty::{self, visit::TypeVisitableExt, InstanceDef, TyCtxt}; use rustc_session::config::{DumpMonoStatsFormat, SwitchWithOptPath}; use rustc_span::symbol::Symbol; -use crate::collector::InliningMap; +use crate::collector::UsageMap; use crate::collector::{self, MonoItemCollectionMode}; use crate::errors::{CouldntDumpMonoStats, SymbolAlreadyDefined, UnknownCguCollectionMode}; struct PartitioningCx<'a, 'tcx> { tcx: TyCtxt<'tcx>, target_cgu_count: usize, - inlining_map: &'a InliningMap<'tcx>, + usage_map: &'a UsageMap<'tcx>, } struct PlacedRootMonoItems<'tcx> { /// The codegen units, sorted by name to make things deterministic. codegen_units: Vec>, - roots: FxHashSet>, internalization_candidates: FxHashSet>, } // The output CGUs are sorted by name. fn partition<'tcx, I>( tcx: TyCtxt<'tcx>, - mono_items: &mut I, + mono_items: I, max_cgu_count: usize, - inlining_map: &InliningMap<'tcx>, + usage_map: &UsageMap<'tcx>, ) -> Vec> where I: Iterator>, { let _prof_timer = tcx.prof.generic_activity("cgu_partitioning"); - let cx = &PartitioningCx { tcx, target_cgu_count: max_cgu_count, inlining_map }; + let cx = &PartitioningCx { tcx, target_cgu_count: max_cgu_count, usage_map }; // In the first step, we place all regular monomorphizations into their // respective 'home' codegen unit. Regular monomorphizations are all // functions and statics defined in the local crate. - let PlacedRootMonoItems { mut codegen_units, roots, internalization_candidates } = { + let PlacedRootMonoItems { mut codegen_units, internalization_candidates } = { let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_place_roots"); place_root_mono_items(cx, mono_items) }; @@ -174,9 +173,9 @@ where // monomorphizations have to go into each codegen unit. These additional // monomorphizations can be drop-glue, functions from external crates, and // local functions the definition of which is marked with `#[inline]`. - let mono_item_placements = { + { let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_place_inline_items"); - place_inlined_mono_items(cx, &mut codegen_units, roots) + place_inlined_mono_items(cx, &mut codegen_units) }; for cgu in &mut codegen_units { @@ -189,12 +188,7 @@ where // more freedom to optimize. if !tcx.sess.link_dead_code() { let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_internalize_symbols"); - internalize_symbols( - cx, - &mut codegen_units, - mono_item_placements, - internalization_candidates, - ); + internalize_symbols(cx, &mut codegen_units, internalization_candidates); } let instrument_dead_code = @@ -239,12 +233,11 @@ where fn place_root_mono_items<'tcx, I>( cx: &PartitioningCx<'_, 'tcx>, - mono_items: &mut I, + mono_items: I, ) -> PlacedRootMonoItems<'tcx> where I: Iterator>, { - let mut roots = FxHashSet::default(); let mut codegen_units = FxHashMap::default(); let is_incremental_build = cx.tcx.sess.opts.incremental.is_some(); let mut internalization_candidates = FxHashSet::default(); @@ -295,7 +288,6 @@ where } codegen_unit.items_mut().insert(mono_item, (linkage, visibility)); - roots.insert(mono_item); } // Always ensure we have at least one CGU; otherwise, if we have a @@ -308,7 +300,7 @@ where let mut codegen_units: Vec<_> = codegen_units.into_values().collect(); codegen_units.sort_by(|a, b| a.name().as_str().cmp(b.name().as_str())); - PlacedRootMonoItems { codegen_units, roots, internalization_candidates } + PlacedRootMonoItems { codegen_units, internalization_candidates } } // This function requires the CGUs to be sorted by name on input, and ensures @@ -404,93 +396,39 @@ fn merge_codegen_units<'tcx>( codegen_units.sort_by(|a, b| a.name().as_str().cmp(b.name().as_str())); } -/// For symbol internalization, we need to know whether a symbol/mono-item is -/// accessed from outside the codegen unit it is defined in. This type is used -/// to keep track of that. -#[derive(Clone, PartialEq, Eq, Debug)] -enum MonoItemPlacement { - SingleCgu { cgu_name: Symbol }, - MultipleCgus, -} - fn place_inlined_mono_items<'tcx>( cx: &PartitioningCx<'_, 'tcx>, codegen_units: &mut [CodegenUnit<'tcx>], - roots: FxHashSet>, -) -> FxHashMap, MonoItemPlacement> { - let mut mono_item_placements = FxHashMap::default(); - - let single_codegen_unit = codegen_units.len() == 1; - - for old_codegen_unit in codegen_units.iter_mut() { - // Collect all items that need to be available in this codegen unit. - let mut reachable = FxHashSet::default(); - for root in old_codegen_unit.items().keys() { - follow_inlining(cx.tcx, *root, cx.inlining_map, &mut reachable); +) { + for cgu in codegen_units.iter_mut() { + // Collect all inlined items that need to be available in this codegen unit. + let mut reachable_inlined_items = FxHashSet::default(); + for root in cgu.items().keys() { + // Get all inlined items that are reachable from it without going + // via another root item. + get_reachable_inlined_items(cx.tcx, *root, cx.usage_map, &mut reachable_inlined_items); } - let mut new_codegen_unit = CodegenUnit::new(old_codegen_unit.name()); - // Add all monomorphizations that are not already there. - for mono_item in reachable { - if let Some(linkage) = old_codegen_unit.items().get(&mono_item) { - // This is a root, just copy it over. - new_codegen_unit.items_mut().insert(mono_item, *linkage); - } else { - if roots.contains(&mono_item) { - bug!( - "GloballyShared mono-item inlined into other CGU: \ - {:?}", - mono_item - ); - } - - // This is a CGU-private copy. - new_codegen_unit - .items_mut() - .insert(mono_item, (Linkage::Internal, Visibility::Default)); - } + for inlined_item in reachable_inlined_items { + assert!(!cgu.items().contains_key(&inlined_item)); - if !single_codegen_unit { - // If there is more than one codegen unit, we need to keep track - // in which codegen units each monomorphization is placed. - match mono_item_placements.entry(mono_item) { - Entry::Occupied(e) => { - let placement = e.into_mut(); - debug_assert!(match *placement { - MonoItemPlacement::SingleCgu { cgu_name } => { - cgu_name != new_codegen_unit.name() - } - MonoItemPlacement::MultipleCgus => true, - }); - *placement = MonoItemPlacement::MultipleCgus; - } - Entry::Vacant(e) => { - e.insert(MonoItemPlacement::SingleCgu { - cgu_name: new_codegen_unit.name(), - }); - } - } - } + // This is a CGU-private copy. + cgu.items_mut().insert(inlined_item, (Linkage::Internal, Visibility::Default)); } - - *old_codegen_unit = new_codegen_unit; } - return mono_item_placements; - - fn follow_inlining<'tcx>( + fn get_reachable_inlined_items<'tcx>( tcx: TyCtxt<'tcx>, - mono_item: MonoItem<'tcx>, - inlining_map: &InliningMap<'tcx>, + item: MonoItem<'tcx>, + usage_map: &UsageMap<'tcx>, visited: &mut FxHashSet>, ) { - if !visited.insert(mono_item) { - return; - } - - inlining_map.with_inlining_candidates(tcx, mono_item, |target| { - follow_inlining(tcx, target, inlining_map, visited); + usage_map.for_each_inlined_used_item(tcx, item, |inlined_item| { + let is_new = visited.insert(inlined_item); + if is_new { + get_reachable_inlined_items(tcx, inlined_item, usage_map, visited); + } }); } } @@ -498,61 +436,75 @@ fn place_inlined_mono_items<'tcx>( fn internalize_symbols<'tcx>( cx: &PartitioningCx<'_, 'tcx>, codegen_units: &mut [CodegenUnit<'tcx>], - mono_item_placements: FxHashMap, MonoItemPlacement>, internalization_candidates: FxHashSet>, ) { - if codegen_units.len() == 1 { - // Fast path for when there is only one codegen unit. In this case we - // can internalize all candidates, since there is nowhere else they - // could be accessed from. - for cgu in codegen_units { - for candidate in &internalization_candidates { - cgu.items_mut().insert(*candidate, (Linkage::Internal, Visibility::Default)); - } - } - - return; + /// For symbol internalization, we need to know whether a symbol/mono-item + /// is used from outside the codegen unit it is defined in. This type is + /// used to keep track of that. + #[derive(Clone, PartialEq, Eq, Debug)] + enum MonoItemPlacement { + SingleCgu { cgu_name: Symbol }, + MultipleCgus, } - // Build a map from every monomorphization to all the monomorphizations that - // reference it. - let mut accessor_map: FxHashMap, Vec>> = Default::default(); - cx.inlining_map.iter_accesses(|accessor, accessees| { - for accessee in accessees { - accessor_map.entry(*accessee).or_default().push(accessor); + let mut mono_item_placements = FxHashMap::default(); + let single_codegen_unit = codegen_units.len() == 1; + + if !single_codegen_unit { + for cgu in codegen_units.iter_mut() { + for item in cgu.items().keys() { + // If there is more than one codegen unit, we need to keep track + // in which codegen units each monomorphization is placed. + match mono_item_placements.entry(*item) { + Entry::Occupied(e) => { + let placement = e.into_mut(); + debug_assert!(match *placement { + MonoItemPlacement::SingleCgu { cgu_name } => cgu_name != cgu.name(), + MonoItemPlacement::MultipleCgus => true, + }); + *placement = MonoItemPlacement::MultipleCgus; + } + Entry::Vacant(e) => { + e.insert(MonoItemPlacement::SingleCgu { cgu_name: cgu.name() }); + } + } + } } - }); + } // For each internalization candidates in each codegen unit, check if it is - // accessed from outside its defining codegen unit. + // used from outside its defining codegen unit. for cgu in codegen_units { let home_cgu = MonoItemPlacement::SingleCgu { cgu_name: cgu.name() }; - for (accessee, linkage_and_visibility) in cgu.items_mut() { - if !internalization_candidates.contains(accessee) { + for (item, linkage_and_visibility) in cgu.items_mut() { + if !internalization_candidates.contains(item) { // This item is no candidate for internalizing, so skip it. continue; } - debug_assert_eq!(mono_item_placements[accessee], home_cgu); - - if let Some(accessors) = accessor_map.get(accessee) { - if accessors - .iter() - .filter_map(|accessor| { - // Some accessors might not have been - // instantiated. We can safely ignore those. - mono_item_placements.get(accessor) - }) - .any(|placement| *placement != home_cgu) - { - // Found an accessor from another CGU, so skip to the next - // item without marking this one as internal. - continue; + + if !single_codegen_unit { + debug_assert_eq!(mono_item_placements[item], home_cgu); + + if let Some(user_items) = cx.usage_map.get_user_items(*item) { + if user_items + .iter() + .filter_map(|user_item| { + // Some user mono items might not have been + // instantiated. We can safely ignore those. + mono_item_placements.get(user_item) + }) + .any(|placement| *placement != home_cgu) + { + // Found a user from another CGU, so skip to the next item + // without marking this one as internal. + continue; + } } } - // If we got here, we did not find any accesses from other CGUs, - // so it's fine to make this monomorphization internal. + // If we got here, we did not find any uses from other CGUs, so + // it's fine to make this monomorphization internal. *linkage_and_visibility = (Linkage::Internal, Visibility::Default); } } @@ -788,7 +740,7 @@ fn mono_item_visibility<'tcx>( } else { // If this isn't a generic function then we mark this a `Default` if // this is a reachable item, meaning that it's a symbol other crates may - // access when they link to us. + // use when they link to us. if tcx.is_reachable_non_generic(def_id.to_def_id()) { *can_be_internalized = false; debug_assert!(!is_generic); @@ -888,15 +840,10 @@ fn debug_dump<'a, 'tcx: 'a>(tcx: TyCtxt<'tcx>, label: &str, cgus: &[CodegenUnit< cgu.size_estimate() ); - // The order of `cgu.items()` is non-deterministic; sort it by name - // to give deterministic output. - let mut items: Vec<_> = cgu.items().iter().collect(); - items.sort_by_key(|(item, _)| item.symbol_name(tcx).name); - for (item, linkage) in items { + for (item, linkage) in cgu.items_in_deterministic_order(tcx) { let symbol_name = item.symbol_name(tcx).name; let symbol_hash_start = symbol_name.rfind('h'); let symbol_hash = symbol_hash_start.map_or("", |i| &symbol_name[i..]); - let size = item.size_estimate(tcx); let _ = with_no_trimmed_paths!(writeln!( s, @@ -968,19 +915,15 @@ fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> (&DefIdSet, &[Co } }; - let (items, inlining_map) = collector::collect_crate_mono_items(tcx, collection_mode); + let (items, usage_map) = collector::collect_crate_mono_items(tcx, collection_mode); tcx.sess.abort_if_errors(); let (codegen_units, _) = tcx.sess.time("partition_and_assert_distinct_symbols", || { sync::join( || { - let mut codegen_units = partition( - tcx, - &mut items.iter().copied(), - tcx.sess.codegen_units(), - &inlining_map, - ); + let mut codegen_units = + partition(tcx, items.iter().copied(), tcx.sess.codegen_units(), &usage_map); codegen_units[0].make_primary(); &*tcx.arena.alloc_from_iter(codegen_units) }, diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 1b28f3c97e8f6..d81b389920148 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -91,6 +91,18 @@ impl From> for LhsExpr { } } +#[derive(Debug)] +enum DestructuredFloat { + /// 1e2 + Single(Symbol, Span), + /// 1. + TrailingDot(Symbol, Span, Span), + /// 1.2 | 1.2e3 + MiddleDot(Symbol, Span, Span, Symbol, Span), + /// Invalid + Error, +} + impl<'a> Parser<'a> { /// Parses an expression. #[inline] @@ -1001,9 +1013,15 @@ impl<'a> Parser<'a> { } fn error_unexpected_after_dot(&self) { - // FIXME Could factor this out into non_fatal_unexpected or something. let actual = pprust::token_to_string(&self.token); - self.sess.emit_err(errors::UnexpectedTokenAfterDot { span: self.token.span, actual }); + let span = self.token.span; + let sm = self.sess.source_map(); + let (span, actual) = match (&self.token.kind, self.subparser_name) { + (token::Eof, Some(_)) if let Ok(actual) = sm.span_to_snippet(sm.next_point(span)) => + (span.shrink_to_hi(), actual.into()), + _ => (span, actual), + }; + self.sess.emit_err(errors::UnexpectedTokenAfterDot { span, actual }); } // We need an identifier or integer, but the next token is a float. @@ -1013,13 +1031,8 @@ impl<'a> Parser<'a> { // support pushing "future tokens" (would be also helpful to `break_and_eat`), or // we should break everything including floats into more basic proc-macro style // tokens in the lexer (probably preferable). - fn parse_expr_tuple_field_access_float( - &mut self, - lo: Span, - base: P, - float: Symbol, - suffix: Option, - ) -> P { + // See also `TokenKind::break_two_token_op` which does similar splitting of `>>` into `>`. + fn break_up_float(&mut self, float: Symbol) -> DestructuredFloat { #[derive(Debug)] enum FloatComponent { IdentLike(String), @@ -1056,7 +1069,7 @@ impl<'a> Parser<'a> { match &*components { // 1e2 [IdentLike(i)] => { - self.parse_expr_tuple_field_access(lo, base, Symbol::intern(&i), suffix, None) + DestructuredFloat::Single(Symbol::intern(&i), span) } // 1. [IdentLike(i), Punct('.')] => { @@ -1068,11 +1081,8 @@ impl<'a> Parser<'a> { } else { (span, span) }; - assert!(suffix.is_none()); let symbol = Symbol::intern(&i); - self.token = Token::new(token::Ident(symbol, false), ident_span); - let next_token = (Token::new(token::Dot, dot_span), self.token_spacing); - self.parse_expr_tuple_field_access(lo, base, symbol, None, Some(next_token)) + DestructuredFloat::TrailingDot(symbol, ident_span, dot_span) } // 1.2 | 1.2e3 [IdentLike(i1), Punct('.'), IdentLike(i2)] => { @@ -1088,16 +1098,8 @@ impl<'a> Parser<'a> { (span, span, span) }; let symbol1 = Symbol::intern(&i1); - self.token = Token::new(token::Ident(symbol1, false), ident1_span); - // This needs to be `Spacing::Alone` to prevent regressions. - // See issue #76399 and PR #76285 for more details - let next_token1 = (Token::new(token::Dot, dot_span), Spacing::Alone); - let base1 = - self.parse_expr_tuple_field_access(lo, base, symbol1, None, Some(next_token1)); let symbol2 = Symbol::intern(&i2); - let next_token2 = Token::new(token::Ident(symbol2, false), ident2_span); - self.bump_with((next_token2, self.token_spacing)); // `.` - self.parse_expr_tuple_field_access(lo, base1, symbol2, suffix, None) + DestructuredFloat::MiddleDot(symbol1, ident1_span, dot_span, symbol2, ident2_span) } // 1e+ | 1e- (recovered) [IdentLike(_), Punct('+' | '-')] | @@ -1109,12 +1111,83 @@ impl<'a> Parser<'a> { [IdentLike(_), Punct('.'), IdentLike(_), Punct('+' | '-'), IdentLike(_)] => { // See the FIXME about `TokenCursor` above. self.error_unexpected_after_dot(); - base + DestructuredFloat::Error } _ => panic!("unexpected components in a float token: {:?}", components), } } + fn parse_expr_tuple_field_access_float( + &mut self, + lo: Span, + base: P, + float: Symbol, + suffix: Option, + ) -> P { + match self.break_up_float(float) { + // 1e2 + DestructuredFloat::Single(sym, _sp) => { + self.parse_expr_tuple_field_access(lo, base, sym, suffix, None) + } + // 1. + DestructuredFloat::TrailingDot(sym, ident_span, dot_span) => { + assert!(suffix.is_none()); + self.token = Token::new(token::Ident(sym, false), ident_span); + let next_token = (Token::new(token::Dot, dot_span), self.token_spacing); + self.parse_expr_tuple_field_access(lo, base, sym, None, Some(next_token)) + } + // 1.2 | 1.2e3 + DestructuredFloat::MiddleDot(symbol1, ident1_span, dot_span, symbol2, ident2_span) => { + self.token = Token::new(token::Ident(symbol1, false), ident1_span); + // This needs to be `Spacing::Alone` to prevent regressions. + // See issue #76399 and PR #76285 for more details + let next_token1 = (Token::new(token::Dot, dot_span), Spacing::Alone); + let base1 = + self.parse_expr_tuple_field_access(lo, base, symbol1, None, Some(next_token1)); + let next_token2 = Token::new(token::Ident(symbol2, false), ident2_span); + self.bump_with((next_token2, self.token_spacing)); // `.` + self.parse_expr_tuple_field_access(lo, base1, symbol2, suffix, None) + } + DestructuredFloat::Error => base, + } + } + + fn parse_field_name_maybe_tuple(&mut self) -> PResult<'a, ThinVec> { + let token::Literal(token::Lit { kind: token::Float, symbol, suffix }) = self.token.kind + else { + return Ok(thin_vec![self.parse_field_name()?]); + }; + Ok(match self.break_up_float(symbol) { + // 1e2 + DestructuredFloat::Single(sym, sp) => { + self.bump(); + thin_vec![Ident::new(sym, sp)] + } + // 1. + DestructuredFloat::TrailingDot(sym, sym_span, dot_span) => { + assert!(suffix.is_none()); + // Analogous to `Self::break_and_eat` + self.token_cursor.break_last_token = true; + // This might work, in cases like `1. 2`, and might not, + // in cases like `offset_of!(Ty, 1.)`. It depends on what comes + // after the float-like token, and therefore we have to make + // the other parts of the parser think that there is a dot literal. + self.token = Token::new(token::Ident(sym, false), sym_span); + self.bump_with((Token::new(token::Dot, dot_span), self.token_spacing)); + thin_vec![Ident::new(sym, sym_span)] + } + // 1.2 | 1.2e3 + DestructuredFloat::MiddleDot(symbol1, ident1_span, _dot_span, symbol2, ident2_span) => { + self.bump(); + thin_vec![Ident::new(symbol1, ident1_span), Ident::new(symbol2, ident2_span)] + } + DestructuredFloat::Error => { + self.bump(); + thin_vec![Ident::new(symbol, self.prev_token.span)] + } + }) + } + fn parse_expr_tuple_field_access( &mut self, lo: Span, @@ -1821,10 +1894,11 @@ impl<'a> Parser<'a> { let (fields, _trailing, _recovered) = self.parse_seq_to_before_end( &TokenKind::CloseDelim(Delimiter::Parenthesis), seq_sep, - Parser::parse_field_name, + Parser::parse_field_name_maybe_tuple, )?; + let fields = fields.into_iter().flatten().collect::>(); let span = lo.to(self.token.span); - Ok(self.mk_expr(span, ExprKind::OffsetOf(container, fields.to_vec().into()))) + Ok(self.mk_expr(span, ExprKind::OffsetOf(container, fields.into()))) } /// Returns a string literal if the next token is a string literal. diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index c317d96368ee6..fdf365178474d 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -938,7 +938,8 @@ impl<'a> Parser<'a> { let mut etc = false; let mut ate_comma = true; let mut delayed_err: Option> = None; - let mut etc_span = None; + let mut first_etc_and_maybe_comma_span = None; + let mut last_non_comma_dotdot_span = None; while self.token != token::CloseDelim(Delimiter::Brace) { let attrs = match self.parse_outer_attributes() { @@ -969,12 +970,27 @@ impl<'a> Parser<'a> { { etc = true; let mut etc_sp = self.token.span; + if first_etc_and_maybe_comma_span.is_none() { + if let Some(comma_tok) = self + .look_ahead(1, |t| if *t == token::Comma { Some(t.clone()) } else { None }) + { + let nw_span = self + .sess + .source_map() + .span_extend_to_line(comma_tok.span) + .trim_start(comma_tok.span.shrink_to_lo()) + .map(|s| self.sess.source_map().span_until_non_whitespace(s)); + first_etc_and_maybe_comma_span = nw_span.map(|s| etc_sp.to(s)); + } else { + first_etc_and_maybe_comma_span = + Some(self.sess.source_map().span_until_non_whitespace(etc_sp)); + } + } self.recover_bad_dot_dot(); self.bump(); // `..` || `...` || `_` if self.token == token::CloseDelim(Delimiter::Brace) { - etc_span = Some(etc_sp); break; } let token_str = super::token_descr(&self.token); @@ -996,7 +1012,6 @@ impl<'a> Parser<'a> { ate_comma = true; } - etc_span = Some(etc_sp.until(self.token.span)); if self.token == token::CloseDelim(Delimiter::Brace) { // If the struct looks otherwise well formed, recover and continue. if let Some(sp) = comma_sp { @@ -1040,6 +1055,9 @@ impl<'a> Parser<'a> { } }?; ate_comma = this.eat(&token::Comma); + + last_non_comma_dotdot_span = Some(this.prev_token.span); + // We just ate a comma, so there's no need to use // `TrailingToken::Comma` Ok((field, TrailingToken::None)) @@ -1049,15 +1067,30 @@ impl<'a> Parser<'a> { } if let Some(mut err) = delayed_err { - if let Some(etc_span) = etc_span { - err.multipart_suggestion( - "move the `..` to the end of the field list", - vec![ - (etc_span, String::new()), - (self.token.span, format!("{}.. }}", if ate_comma { "" } else { ", " })), - ], - Applicability::MachineApplicable, - ); + if let Some(first_etc_span) = first_etc_and_maybe_comma_span { + if self.prev_token == token::DotDot { + // We have `.., x, ..`. + err.multipart_suggestion( + "remove the starting `..`", + vec![(first_etc_span, String::new())], + Applicability::MachineApplicable, + ); + } else { + if let Some(last_non_comma_dotdot_span) = last_non_comma_dotdot_span { + // We have `.., x`. + err.multipart_suggestion( + "move the `..` to the end of the field list", + vec![ + (first_etc_span, String::new()), + ( + self.token.span.to(last_non_comma_dotdot_span.shrink_to_hi()), + format!("{} .. }}", if ate_comma { "" } else { "," }), + ), + ], + Applicability::MachineApplicable, + ); + } + } } err.emit(); } diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 7f9222dac6c4c..e76f1614b9334 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -211,6 +211,8 @@ passes_doc_keyword_not_mod = passes_doc_keyword_only_impl = `#[doc(keyword = "...")]` should be used on impl blocks +passes_doc_test_literal = `#![doc(test(...)]` does not take a literal + passes_doc_test_takes_list = `#[doc(test(...)]` takes a list of attributes diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index c3189d1fefe40..c35c7da266429 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -944,21 +944,28 @@ impl CheckAttrVisitor<'_> { let mut is_valid = true; if let Some(metas) = meta.meta_item_list() { for i_meta in metas { - match i_meta.name_or_empty() { - sym::attr | sym::no_crate_inject => {} - _ => { + match (i_meta.name_or_empty(), i_meta.meta_item()) { + (sym::attr | sym::no_crate_inject, _) => {} + (_, Some(m)) => { self.tcx.emit_spanned_lint( INVALID_DOC_ATTRIBUTES, hir_id, i_meta.span(), errors::DocTestUnknown { - path: rustc_ast_pretty::pprust::path_to_string( - &i_meta.meta_item().unwrap().path, - ), + path: rustc_ast_pretty::pprust::path_to_string(&m.path), }, ); is_valid = false; } + (_, None) => { + self.tcx.emit_spanned_lint( + INVALID_DOC_ATTRIBUTES, + hir_id, + i_meta.span(), + errors::DocTestLiteral, + ); + is_valid = false; + } } } } else { diff --git a/compiler/rustc_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs index 2357b0aadefbb..fc437c429fbeb 100644 --- a/compiler/rustc_passes/src/check_const.rs +++ b/compiler/rustc_passes/src/check_const.rs @@ -199,6 +199,11 @@ impl<'tcx> Visitor<'tcx> for CheckConstVisitor<'tcx> { self.recurse_into(kind, None, |this| intravisit::walk_anon_const(this, anon)); } + fn visit_inline_const(&mut self, block: &'tcx hir::ConstBlock) { + let kind = Some(hir::ConstContext::Const); + self.recurse_into(kind, None, |this| intravisit::walk_inline_const(this, block)); + } + fn visit_body(&mut self, body: &'tcx hir::Body<'tcx>) { let owner = self.tcx.hir().body_owner_def_id(body.id()); let kind = self.tcx.hir().body_const_context(owner); diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 7812dcde44c83..d5ac1cd9ce335 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -500,6 +500,17 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> { self.in_pat = in_pat; } + + fn visit_inline_const(&mut self, c: &'tcx hir::ConstBlock) { + // When inline const blocks are used in pattern position, paths + // referenced by it should be considered as used. + let in_pat = mem::replace(&mut self.in_pat, false); + + self.live_symbols.insert(c.def_id); + intravisit::walk_inline_const(self, c); + + self.in_pat = in_pat; + } } fn has_allow_dead_code_or_lang_attr(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 99fc69d1bec7b..ae624dbc9c953 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -281,6 +281,10 @@ pub struct DocTestUnknown { pub path: String, } +#[derive(LintDiagnostic)] +#[diag(passes_doc_test_literal)] +pub struct DocTestLiteral; + #[derive(LintDiagnostic)] #[diag(passes_doc_test_takes_list)] pub struct DocTestTakesList; diff --git a/compiler/rustc_passes/src/loops.rs b/compiler/rustc_passes/src/loops.rs index 73cfe68e7f214..7c64df6a50ecf 100644 --- a/compiler/rustc_passes/src/loops.rs +++ b/compiler/rustc_passes/src/loops.rs @@ -24,7 +24,7 @@ enum Context { Closure(Span), AsyncClosure(Span), LabeledBlock, - AnonConst, + Constant, } #[derive(Copy, Clone)] @@ -53,7 +53,11 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> { } fn visit_anon_const(&mut self, c: &'hir hir::AnonConst) { - self.with_context(AnonConst, |v| intravisit::walk_anon_const(v, c)); + self.with_context(Constant, |v| intravisit::walk_anon_const(v, c)); + } + + fn visit_inline_const(&mut self, c: &'hir hir::ConstBlock) { + self.with_context(Constant, |v| intravisit::walk_inline_const(v, c)); } fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) { @@ -192,7 +196,7 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> { AsyncClosure(closure_span) => { self.sess.emit_err(BreakInsideAsyncBlock { span, closure_span, name }); } - Normal | AnonConst => { + Normal | Constant => { self.sess.emit_err(OutsideLoop { span, name, is_break: name == "break" }); } } diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 65dfdf31e5457..afd32e38d5b87 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -409,8 +409,8 @@ impl VisibilityLike for ty::Visibility { } } -impl VisibilityLike for Option { - const MAX: Self = Some(EffectiveVisibility::from_vis(ty::Visibility::Public)); +impl VisibilityLike for EffectiveVisibility { + const MAX: Self = EffectiveVisibility::from_vis(ty::Visibility::Public); // Type inference is very smart sometimes. // It can make an impl reachable even some components of its type or trait are unreachable. // E.g. methods of `impl ReachableTrait for ReachableTy { ... }` @@ -422,13 +422,14 @@ impl VisibilityLike for Option { // (which require reaching the `DefId`s in them). const SHALLOW: bool = true; fn new_min(find: &FindMin<'_, '_, Self>, def_id: LocalDefId) -> Self { - if let Some(min) = find.min { - return find - .effective_visibilities - .effective_vis(def_id) - .map(|eff_vis| min.min(*eff_vis, find.tcx)); - } - None + let effective_vis = + find.effective_visibilities.effective_vis(def_id).cloned().unwrap_or_else(|| { + let private_vis = + ty::Visibility::Restricted(find.tcx.parent_module_from_def_id(def_id)); + EffectiveVisibility::from_vis(private_vis) + }); + + effective_vis.min(find.min, find.tcx) } } @@ -751,7 +752,7 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> { reach.generics().predicates(); if trait_item_ref.kind == AssocItemKind::Type - && !tcx.impl_defaultness(trait_item_ref.id.owner_id).has_value() + && !tcx.defaultness(trait_item_ref.id.owner_id).has_value() { // No type to visit. } else { @@ -766,28 +767,23 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> { } } hir::ItemKind::Impl(ref impl_) => { - if let Some(item_ev) = Option::::of_impl( + let item_ev = EffectiveVisibility::of_impl( item.owner_id.def_id, self.tcx, &self.effective_visibilities, - ) { - self.update_eff_vis(item.owner_id.def_id, item_ev, None, Level::Direct); + ); + self.update_eff_vis(item.owner_id.def_id, item_ev, None, Level::Direct); - self.reach(item.owner_id.def_id, item_ev) - .generics() - .predicates() - .ty() - .trait_ref(); + self.reach(item.owner_id.def_id, item_ev).generics().predicates().ty().trait_ref(); - for impl_item_ref in impl_.items { - let def_id = impl_item_ref.id.owner_id.def_id; - let nominal_vis = - impl_.of_trait.is_none().then(|| self.tcx.local_visibility(def_id)); - self.update_eff_vis(def_id, item_ev, nominal_vis, Level::Direct); + for impl_item_ref in impl_.items { + let def_id = impl_item_ref.id.owner_id.def_id; + let nominal_vis = + impl_.of_trait.is_none().then(|| self.tcx.local_visibility(def_id)); + self.update_eff_vis(def_id, item_ev, nominal_vis, Level::Direct); - if let Some(impl_item_ev) = self.get(def_id) { - self.reach(def_id, impl_item_ev).generics().predicates().ty(); - } + if let Some(impl_item_ev) = self.get(def_id) { + self.reach(def_id, impl_item_ev).generics().predicates().ty(); } } } @@ -1927,7 +1923,7 @@ impl<'tcx> PrivateItemsInPublicInterfacesChecker<'tcx> { let (check_ty, is_assoc_ty) = match assoc_item_kind { AssocItemKind::Const | AssocItemKind::Fn { .. } => (true, false), - AssocItemKind::Type => (self.tcx.impl_defaultness(def_id).has_value(), true), + AssocItemKind::Type => (self.tcx.defaultness(def_id).has_value(), true), }; check.in_assoc_ty = is_assoc_ty; check.generics().predicates(); diff --git a/compiler/rustc_query_impl/Cargo.toml b/compiler/rustc_query_impl/Cargo.toml index c9353b6fc9fb9..ac697a3ae3e99 100644 --- a/compiler/rustc_query_impl/Cargo.toml +++ b/compiler/rustc_query_impl/Cargo.toml @@ -7,7 +7,6 @@ edition = "2021" [dependencies] -memoffset = { version = "0.8.0", features = ["unstable_const"] } field-offset = "0.3.5" measureme = "10.0.0" rustc_ast = { path = "../rustc_ast" } @@ -25,5 +24,8 @@ rustc_span = { path = "../rustc_span" } thin-vec = "0.2.12" tracing = "0.1" +# Not used directly, but included to enable the unstable_offset_of feature +memoffset = { version = "0.9.0", features = ["unstable_offset_of"] } + [features] rustc_use_parallel_compiler = ["rustc-rayon-core", "rustc_query_system/rustc_use_parallel_compiler"] diff --git a/compiler/rustc_query_system/src/dep_graph/dep_node.rs b/compiler/rustc_query_system/src/dep_graph/dep_node.rs index 9e1ca6ab515d8..39a4cb1b179b4 100644 --- a/compiler/rustc_query_system/src/dep_graph/dep_node.rs +++ b/compiler/rustc_query_system/src/dep_graph/dep_node.rs @@ -46,7 +46,7 @@ use super::{DepContext, DepKind, FingerprintStyle}; use crate::ich::StableHashingContext; use rustc_data_structures::fingerprint::{Fingerprint, PackedFingerprint}; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableOrd, ToStableHashKey}; use rustc_hir::definitions::DefPathHash; use std::fmt; use std::hash::Hash; @@ -247,3 +247,14 @@ impl HashStable for WorkProductId { self.hash.hash_stable(hcx, hasher) } } +impl ToStableHashKey for WorkProductId { + type KeyType = Fingerprint; + #[inline] + fn to_stable_hash_key(&self, _: &HCX) -> Self::KeyType { + self.hash + } +} +unsafe impl StableOrd for WorkProductId { + // Fingerprint can use unstable (just a tuple of `u64`s), so WorkProductId can as well + const CAN_USE_UNSTABLE_SORT: bool = true; +} diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index c0d7386dd6adb..c9e80a6d9bc13 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -1,11 +1,12 @@ use parking_lot::Mutex; use rustc_data_structures::fingerprint::Fingerprint; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_data_structures::profiling::{EventId, QueryInvocationId, SelfProfilerRef}; use rustc_data_structures::sharded::{self, Sharded}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::steal::Steal; use rustc_data_structures::sync::{AtomicU32, AtomicU64, Lock, Lrc, Ordering}; +use rustc_data_structures::unord::UnordMap; use rustc_index::IndexVec; use rustc_serialize::opaque::{FileEncodeResult, FileEncoder}; use smallvec::{smallvec, SmallVec}; @@ -93,7 +94,7 @@ pub struct DepGraphData { /// things available to us. If we find that they are not dirty, we /// load the path to the file storing those work-products here into /// this map. We can later look for and extract that data. - previous_work_products: FxHashMap, + previous_work_products: FxIndexMap, dep_node_debug: Lock, String>>, @@ -116,7 +117,7 @@ impl DepGraph { pub fn new( profiler: &SelfProfilerRef, prev_graph: SerializedDepGraph, - prev_work_products: FxHashMap, + prev_work_products: FxIndexMap, encoder: FileEncoder, record_graph: bool, record_stats: bool, @@ -688,7 +689,7 @@ impl DepGraph { /// Access the map of work-products created during the cached run. Only /// used during saving of the dep-graph. - pub fn previous_work_products(&self) -> &FxHashMap { + pub fn previous_work_products(&self) -> &FxIndexMap { &self.data.as_ref().unwrap().previous_work_products } @@ -1048,7 +1049,7 @@ pub struct WorkProduct { /// /// By convention, file extensions are currently used as identifiers, i.e. the key "o" maps to /// the object file's path, and "dwo" to the dwarf object file's path. - pub saved_files: FxHashMap, + pub saved_files: UnordMap, } // Index type for `DepNodeData`'s edges. diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index 730e4c8d30db3..b2bc33c7e0de0 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -69,6 +69,8 @@ where make_query: fn(Qcx, K) -> QueryStackFrame, jobs: &mut QueryMap, ) -> Option<()> { + let mut active = Vec::new(); + #[cfg(parallel_compiler)] { // We use try_lock_shards here since we are called from the @@ -77,8 +79,7 @@ where for shard in shards.iter() { for (k, v) in shard.iter() { if let QueryResult::Started(ref job) = *v { - let query = make_query(qcx, *k); - jobs.insert(job.id, QueryJobInfo { query, job: job.clone() }); + active.push((*k, job.clone())); } } } @@ -91,12 +92,18 @@ where // really hurt much.) for (k, v) in self.active.try_lock()?.iter() { if let QueryResult::Started(ref job) = *v { - let query = make_query(qcx, *k); - jobs.insert(job.id, QueryJobInfo { query, job: job.clone() }); + active.push((*k, job.clone())); } } } + // Call `make_query` while we're not holding a `self.active` lock as `make_query` may call + // queries leading to a deadlock. + for (key, job) in active { + let query = make_query(qcx, key); + jobs.insert(job.id, QueryJobInfo { query, job }); + } + Some(()) } } diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 377652ce71bb2..ca811c9ed7d98 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1,8 +1,10 @@ use std::ptr; +use rustc_ast::expand::StrippedCfgItem; use rustc_ast::ptr::P; use rustc_ast::visit::{self, Visitor}; use rustc_ast::{self as ast, Crate, ItemKind, ModKind, NodeId, Path, CRATE_NODE_ID}; +use rustc_ast::{MetaItemKind, NestedMetaItem}; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{ @@ -776,7 +778,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { .tcx .sess .create_err(errs::SelfImportOnlyInImportListWithNonEmptyPrefix { span }), - ResolutionError::FailedToResolve { label, suggestion } => { + ResolutionError::FailedToResolve { last_segment, label, suggestion, module } => { let mut err = struct_span_err!(self.tcx.sess, span, E0433, "failed to resolve: {}", &label); err.span_label(span, label); @@ -789,6 +791,13 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { err.multipart_suggestion(msg, suggestions, applicability); } + if let Some(ModuleOrUniformRoot::Module(module)) = module + && let Some(module) = module.opt_def_id() + && let Some(last_segment) = last_segment + { + self.find_cfg_stripped(&mut err, &last_segment, module); + } + err } ResolutionError::CannotCaptureDynamicEnvironmentInFnItem => { @@ -971,9 +980,15 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { VisResolutionError::AncestorOnly(span) => { self.tcx.sess.create_err(errs::AncestorOnly(span)) } - VisResolutionError::FailedToResolve(span, label, suggestion) => { - self.into_struct_error(span, ResolutionError::FailedToResolve { label, suggestion }) - } + VisResolutionError::FailedToResolve(span, label, suggestion) => self.into_struct_error( + span, + ResolutionError::FailedToResolve { + last_segment: None, + label, + suggestion, + module: None, + }, + ), VisResolutionError::ExpectedFound(span, path_str, res) => { self.tcx.sess.create_err(errs::ExpectedFound { span, res, path_str }) } @@ -1337,6 +1352,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { macro_kind: MacroKind, parent_scope: &ParentScope<'a>, ident: Ident, + krate: &Crate, ) { let is_expected = &|res: Res| res.macro_kind() == Some(macro_kind); let suggestion = self.early_lookup_typo_candidate( @@ -1349,13 +1365,17 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let import_suggestions = self.lookup_import_candidates(ident, Namespace::MacroNS, parent_scope, is_expected); + let (span, found_use) = match parent_scope.module.nearest_parent_mod().as_local() { + Some(def_id) => UsePlacementFinder::check(krate, self.def_id_to_node_id[def_id]), + None => (None, FoundUse::No), + }; show_candidates( self.tcx, err, - None, + span, &import_suggestions, Instead::No, - FoundUse::Yes, + found_use, DiagnosticMode::Normal, vec![], "", @@ -1721,10 +1741,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { ribs: Option<&PerNS>>>, ignore_binding: Option<&'a NameBinding<'a>>, module: Option>, - i: usize, + failed_segment_idx: usize, ident: Ident, ) -> (String, Option) { - let is_last = i == path.len() - 1; + let is_last = failed_segment_idx == path.len() - 1; let ns = if is_last { opt_ns.unwrap_or(TypeNS) } else { TypeNS }; let module_res = match module { Some(ModuleOrUniformRoot::Module(module)) => module.res(), @@ -1758,8 +1778,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } else { (format!("could not find `{ident}` in the crate root"), None) } - } else if i > 0 { - let parent = path[i - 1].ident.name; + } else if failed_segment_idx > 0 { + let parent = path[failed_segment_idx - 1].ident.name; let parent = match parent { // ::foo is mounted at the crate root for 2015, and is the extern // prelude for 2018+ @@ -2207,6 +2227,44 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { None } } + + /// Finds a cfg-ed out item inside `module` with the matching name. + pub(crate) fn find_cfg_stripped( + &mut self, + err: &mut Diagnostic, + last_segment: &Symbol, + module: DefId, + ) { + let local_items; + let symbols = if module.is_local() { + local_items = self + .stripped_cfg_items + .iter() + .filter_map(|item| { + let parent_module = self.opt_local_def_id(item.parent_module)?.to_def_id(); + Some(StrippedCfgItem { parent_module, name: item.name, cfg: item.cfg.clone() }) + }) + .collect::>(); + local_items.as_slice() + } else { + self.tcx.stripped_cfg_items(module.krate) + }; + + for &StrippedCfgItem { parent_module, name, ref cfg } in symbols { + if parent_module != module || name.name != *last_segment { + continue; + } + + err.span_note(name.span, "found an item that was configured out"); + + if let MetaItemKind::List(nested) = &cfg.kind + && let NestedMetaItem::MetaItem(meta_item) = &nested[0] + && let MetaItemKind::NameValue(feature_name) = &meta_item.kind + { + err.note(format!("the item is gated behind the `{}` feature", feature_name.symbol)); + } + } + } } /// Given a `binding_span` of a binding within a use statement: diff --git a/compiler/rustc_resolve/src/effective_visibilities.rs b/compiler/rustc_resolve/src/effective_visibilities.rs index 7393bdb388a56..4863c9f479015 100644 --- a/compiler/rustc_resolve/src/effective_visibilities.rs +++ b/compiler/rustc_resolve/src/effective_visibilities.rs @@ -81,7 +81,7 @@ impl<'r, 'a, 'tcx> EffectiveVisibilitiesVisitor<'r, 'a, 'tcx> { def_effective_visibilities: Default::default(), import_effective_visibilities: Default::default(), current_private_vis: Visibility::Restricted(CRATE_DEF_ID), - changed: false, + changed: true, }; visitor.def_effective_visibilities.update_root(); diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs index 2ab55f12637c8..e88cbb955b556 100644 --- a/compiler/rustc_resolve/src/errors.rs +++ b/compiler/rustc_resolve/src/errors.rs @@ -330,6 +330,7 @@ pub(crate) struct ParamInTyOfConstParam { pub(crate) param_kind: Option, } +#[derive(Debug)] #[derive(Subdiagnostic)] pub(crate) enum ParamKindInTyOfConstParam { #[note(resolve_type_param_in_ty_of_const_param)] @@ -365,6 +366,7 @@ pub(crate) struct ParamInNonTrivialAnonConst { #[help(resolve_param_in_non_trivial_anon_const_help)] pub(crate) struct ParamInNonTrivialAnonConstHelp; +#[derive(Debug)] #[derive(Subdiagnostic)] pub(crate) enum ParamKindInNonTrivialAnonConst { #[note(resolve_type_param_in_non_trivial_anon_const)] @@ -562,6 +564,7 @@ pub(crate) struct CfgAccessibleUnsure { pub(crate) span: Span, } +#[derive(Debug)] #[derive(Diagnostic)] #[diag(resolve_param_in_enum_discriminant)] pub(crate) struct ParamInEnumDiscriminant { @@ -573,6 +576,7 @@ pub(crate) struct ParamInEnumDiscriminant { pub(crate) param_kind: ParamKindInEnumDiscriminant, } +#[derive(Debug)] #[derive(Subdiagnostic)] pub(crate) enum ParamKindInEnumDiscriminant { #[note(resolve_type_param_in_enum_discriminant)] diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 945c7ce3a9b32..ec0a8535e7180 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -1365,20 +1365,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { ribs: Option<&PerNS>>>, ignore_binding: Option<&'a NameBinding<'a>>, ) -> PathResult<'a> { - debug!( - "resolve_path(path={:?}, opt_ns={:?}, finalize={:?}) path_len: {}", - path, - opt_ns, - finalize, - path.len() - ); - let mut module = None; let mut allow_super = true; let mut second_binding = None; - for (i, &Segment { ident, id, .. }) in path.iter().enumerate() { - debug!("resolve_path ident {} {:?} {:?}", i, ident, id); + for (segment_idx, &Segment { ident, id, .. }) in path.iter().enumerate() { + debug!("resolve_path ident {} {:?} {:?}", segment_idx, ident, id); let record_segment_res = |this: &mut Self, res| { if finalize.is_some() { if let Some(id) = id { @@ -1390,7 +1382,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } }; - let is_last = i + 1 == path.len(); + let is_last = segment_idx + 1 == path.len(); let ns = if is_last { opt_ns.unwrap_or(TypeNS) } else { TypeNS }; let name = ident.name; @@ -1399,7 +1391,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { if ns == TypeNS { if allow_super && name == kw::Super { let mut ctxt = ident.span.ctxt().normalize_to_macros_2_0(); - let self_module = match i { + let self_module = match segment_idx { 0 => Some(self.resolve_self(&mut ctxt, parent_scope.module)), _ => match module { Some(ModuleOrUniformRoot::Module(module)) => Some(module), @@ -1414,11 +1406,15 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { continue; } } - return PathResult::failed(ident.span, false, finalize.is_some(), || { - ("there are too many leading `super` keywords".to_string(), None) - }); + return PathResult::failed( + ident.span, + false, + finalize.is_some(), + module, + || ("there are too many leading `super` keywords".to_string(), None), + ); } - if i == 0 { + if segment_idx == 0 { if name == kw::SelfLower { let mut ctxt = ident.span.ctxt().normalize_to_macros_2_0(); module = Some(ModuleOrUniformRoot::Module( @@ -1447,14 +1443,14 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } // Report special messages for path segment keywords in wrong positions. - if ident.is_path_segment_keyword() && i != 0 { - return PathResult::failed(ident.span, false, finalize.is_some(), || { + if ident.is_path_segment_keyword() && segment_idx != 0 { + return PathResult::failed(ident.span, false, finalize.is_some(), module, || { let name_str = if name == kw::PathRoot { "crate root".to_string() } else { format!("`{}`", name) }; - let label = if i == 1 && path[0].ident.name == kw::PathRoot { + let label = if segment_idx == 1 && path[0].ident.name == kw::PathRoot { format!("global paths cannot start with {}", name_str) } else { format!("{} in paths can only be used in start position", name_str) @@ -1519,7 +1515,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { }; match binding { Ok(binding) => { - if i == 1 { + if segment_idx == 1 { second_binding = Some(binding); } let res = binding.res(); @@ -1543,17 +1539,23 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { record_segment_res(self, res); return PathResult::NonModule(PartialRes::with_unresolved_segments( res, - path.len() - i - 1, + path.len() - segment_idx - 1, )); } else { - return PathResult::failed(ident.span, is_last, finalize.is_some(), || { - let label = format!( - "`{ident}` is {} {}, not a module", - res.article(), - res.descr() - ); - (label, None) - }); + return PathResult::failed( + ident.span, + is_last, + finalize.is_some(), + module, + || { + let label = format!( + "`{ident}` is {} {}, not a module", + res.article(), + res.descr() + ); + (label, None) + }, + ); } } Err(Undetermined) => return PathResult::Indeterminate, @@ -1562,23 +1564,29 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { if opt_ns.is_some() && !module.is_normal() { return PathResult::NonModule(PartialRes::with_unresolved_segments( module.res().unwrap(), - path.len() - i, + path.len() - segment_idx, )); } } - return PathResult::failed(ident.span, is_last, finalize.is_some(), || { - self.report_path_resolution_error( - path, - opt_ns, - parent_scope, - ribs, - ignore_binding, - module, - i, - ident, - ) - }); + return PathResult::failed( + ident.span, + is_last, + finalize.is_some(), + module, + || { + self.report_path_resolution_error( + path, + opt_ns, + parent_scope, + ribs, + ignore_binding, + module, + segment_idx, + ident, + ) + }, + ); } } } diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index c1bb262c0d407..7f944fb574596 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -803,14 +803,34 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { module } - PathResult::Failed { is_error_from_last_segment: false, span, label, suggestion } => { + PathResult::Failed { + is_error_from_last_segment: false, + span, + label, + suggestion, + module, + } => { if no_ambiguity { assert!(import.imported_module.get().is_none()); - self.report_error(span, ResolutionError::FailedToResolve { label, suggestion }); + self.report_error( + span, + ResolutionError::FailedToResolve { + last_segment: None, + label, + suggestion, + module, + }, + ); } return None; } - PathResult::Failed { is_error_from_last_segment: true, span, label, suggestion } => { + PathResult::Failed { + is_error_from_last_segment: true, + span, + label, + suggestion, + .. + } => { if no_ambiguity { assert!(import.imported_module.get().is_none()); let err = match self.make_path_suggestion( diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index e0611907613c4..ddd75ea3b33e0 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -3524,7 +3524,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { None }; - this.r.use_injections.push(UseError { + let ue = UseError { err, candidates, def_id, @@ -3532,7 +3532,9 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { suggestion, path: path.into(), is_call: source.is_call(), - }); + }; + + this.r.use_injections.push(ue); } PartialRes::new(Res::Err) @@ -3866,8 +3868,22 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { PathResult::Module(ModuleOrUniformRoot::Module(module)) => { PartialRes::new(module.res().unwrap()) } - PathResult::Failed { is_error_from_last_segment: false, span, label, suggestion } => { - return Err(respan(span, ResolutionError::FailedToResolve { label, suggestion })); + PathResult::Failed { + is_error_from_last_segment: false, + span, + label, + suggestion, + module, + } => { + return Err(respan( + span, + ResolutionError::FailedToResolve { + last_segment: None, + label, + suggestion, + module, + }, + )); } PathResult::Module(..) | PathResult::Failed { .. } => return Ok(None), PathResult::Indeterminate => bug!("indeterminate path result in resolve_qpath"), diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index f79f8d0c6ca4e..2f9759a668bbe 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -149,6 +149,7 @@ struct BaseError { span_label: Option<(Span, &'static str)>, could_be_expr: bool, suggestion: Option<(Span, &'static str, String)>, + module: Option, } #[derive(Debug)] @@ -210,10 +211,11 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { _ => false, }, suggestion: None, + module: None, } } else { let item_span = path.last().unwrap().ident.span; - let (mod_prefix, mod_str, suggestion) = if path.len() == 1 { + let (mod_prefix, mod_str, module, suggestion) = if path.len() == 1 { debug!(?self.diagnostic_metadata.current_impl_items); debug!(?self.diagnostic_metadata.current_function); let suggestion = if self.current_trait_ref.is_none() @@ -247,26 +249,37 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { } else { None }; - (String::new(), "this scope".to_string(), suggestion) + (String::new(), "this scope".to_string(), None, suggestion) } else if path.len() == 2 && path[0].ident.name == kw::PathRoot { if self.r.tcx.sess.edition() > Edition::Edition2015 { // In edition 2018 onwards, the `::foo` syntax may only pull from the extern prelude // which overrides all other expectations of item type expected = "crate"; - (String::new(), "the list of imported crates".to_string(), None) + (String::new(), "the list of imported crates".to_string(), None, None) } else { - (String::new(), "the crate root".to_string(), None) + ( + String::new(), + "the crate root".to_string(), + Some(CRATE_DEF_ID.to_def_id()), + None, + ) } } else if path.len() == 2 && path[0].ident.name == kw::Crate { - (String::new(), "the crate root".to_string(), None) + (String::new(), "the crate root".to_string(), Some(CRATE_DEF_ID.to_def_id()), None) } else { let mod_path = &path[..path.len() - 1]; - let mod_prefix = match self.resolve_path(mod_path, Some(TypeNS), None) { + let mod_res = self.resolve_path(mod_path, Some(TypeNS), None); + let mod_prefix = match mod_res { PathResult::Module(ModuleOrUniformRoot::Module(module)) => module.res(), _ => None, - } - .map_or_else(String::new, |res| format!("{} ", res.descr())); - (mod_prefix, format!("`{}`", Segment::names_to_string(mod_path)), None) + }; + + let module_did = mod_prefix.as_ref().and_then(Res::mod_def_id); + + let mod_prefix = + mod_prefix.map_or_else(String::new, |res| (format!("{} ", res.descr()))); + + (mod_prefix, format!("`{}`", Segment::names_to_string(mod_path)), module_did, None) }; let (fallback_label, suggestion) = if path_str == "async" @@ -300,6 +313,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { span_label: None, could_be_expr: false, suggestion, + module, } } } @@ -315,6 +329,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { ) -> (DiagnosticBuilder<'tcx, ErrorGuaranteed>, Vec) { debug!(?res, ?source); let base_error = self.make_base_error(path, span, source, res); + let code = source.error_code(res.is_some()); let mut err = self.r.tcx.sess.struct_span_err_with_code( base_error.span, @@ -366,6 +381,10 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { } self.err_code_special_cases(&mut err, source, path, span); + if let Some(module) = base_error.module { + self.r.find_cfg_stripped(&mut err, &path.last().unwrap().ident.name, module); + } + (err, candidates) } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index fd977e8e254a2..db97039b907c4 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -25,6 +25,7 @@ use errors::{ ParamKindInEnumDiscriminant, ParamKindInNonTrivialAnonConst, ParamKindInTyOfConstParam, }; use rustc_arena::{DroplessArena, TypedArena}; +use rustc_ast::expand::StrippedCfgItem; use rustc_ast::node_id::NodeMap; use rustc_ast::{self as ast, attr, NodeId, CRATE_NODE_ID}; use rustc_ast::{AngleBracketedArg, Crate, Expr, ExprKind, GenericArg, GenericArgs, LitKind, Path}; @@ -171,6 +172,7 @@ enum ImplTraitContext { Universal(LocalDefId), } +#[derive(Debug)] struct BindingError { name: Symbol, origin: BTreeSet, @@ -178,6 +180,7 @@ struct BindingError { could_be_path: bool, } +#[derive(Debug)] enum ResolutionError<'a> { /// Error E0401: can't use type or const parameters from outer function. GenericParamsFromOuterFunction(Res, HasGenericParams), @@ -207,7 +210,12 @@ enum ResolutionError<'a> { /// Error E0431: `self` import can only appear in an import list with a non-empty prefix. SelfImportOnlyInImportListWithNonEmptyPrefix, /// Error E0433: failed to resolve. - FailedToResolve { label: String, suggestion: Option }, + FailedToResolve { + last_segment: Option, + label: String, + suggestion: Option, + module: Option>, + }, /// Error E0434: can't capture dynamic environment in a fn item. CannotCaptureDynamicEnvironmentInFnItem, /// Error E0435: attempt to use a non-constant value in a constant. @@ -402,6 +410,7 @@ enum PathResult<'a> { label: String, suggestion: Option, is_error_from_last_segment: bool, + module: Option>, }, } @@ -410,11 +419,12 @@ impl<'a> PathResult<'a> { span: Span, is_error_from_last_segment: bool, finalize: bool, + module: Option>, label_and_suggestion: impl FnOnce() -> (String, Option), ) -> PathResult<'a> { let (label, suggestion) = if finalize { label_and_suggestion() } else { (String::new(), None) }; - PathResult::Failed { span, label, suggestion, is_error_from_last_segment } + PathResult::Failed { span, label, suggestion, is_error_from_last_segment, module } } } @@ -685,6 +695,7 @@ struct PrivacyError<'a> { dedup_span: Span, } +#[derive(Debug)] struct UseError<'a> { err: DiagnosticBuilder<'a, ErrorGuaranteed>, /// Candidates which user could `use` to access the missing type. @@ -1059,6 +1070,9 @@ pub struct Resolver<'a, 'tcx> { /// Whether lifetime elision was successful. lifetime_elision_allowed: FxHashSet, + /// Names of items that were stripped out via cfg with their corresponding cfg meta item. + stripped_cfg_items: Vec>, + effective_visibilities: EffectiveVisibilities, doc_link_resolutions: FxHashMap, doc_link_traits_in_scope: FxHashMap>, @@ -1353,6 +1367,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { proc_macros: Default::default(), confused_type_with_std_module: Default::default(), lifetime_elision_allowed: Default::default(), + stripped_cfg_items: Default::default(), effective_visibilities: Default::default(), doc_link_resolutions: Default::default(), doc_link_traits_in_scope: Default::default(), @@ -1410,6 +1425,14 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let main_def = self.main_def; let confused_type_with_std_module = self.confused_type_with_std_module; let effective_visibilities = self.effective_visibilities; + + self.tcx.feed_local_crate().stripped_cfg_items(self.tcx.arena.alloc_from_iter( + self.stripped_cfg_items.into_iter().filter_map(|item| { + let parent_module = self.node_id_to_def_id.get(&item.parent_module)?.to_def_id(); + Some(StrippedCfgItem { parent_module, name: item.name, cfg: item.cfg }) + }), + )); + let global_ctxt = ResolverGlobalCtxt { expn_that_defined, visibilities, @@ -1499,7 +1522,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { self.tcx.sess.time("check_hidden_glob_reexports", || { self.check_hidden_glob_reexports(exported_ambiguities) }); - self.tcx.sess.time("finalize_macro_resolutions", || self.finalize_macro_resolutions()); + self.tcx + .sess + .time("finalize_macro_resolutions", || self.finalize_macro_resolutions(krate)); self.tcx.sess.time("late_resolve_crate", || self.late_resolve_crate(krate)); self.tcx.sess.time("resolve_main", || self.resolve_main()); self.tcx.sess.time("resolve_check_unused", || self.check_unused(krate)); diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index d8a7bcbfff955..ca4f3331b9a2d 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -6,7 +6,8 @@ use crate::Namespace::*; use crate::{BuiltinMacroState, Determinacy}; use crate::{DeriveData, Finalize, ParentScope, ResolutionError, Resolver, ScopeSet}; use crate::{ModuleKind, ModuleOrUniformRoot, NameBinding, PathResult, Segment}; -use rustc_ast::{self as ast, attr, Inline, ItemKind, ModKind, NodeId}; +use rustc_ast::expand::StrippedCfgItem; +use rustc_ast::{self as ast, attr, Crate, Inline, ItemKind, ModKind, NodeId}; use rustc_ast_pretty::pprust; use rustc_attr::StabilityLevel; use rustc_data_structures::intern::Interned; @@ -465,6 +466,10 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> { self.proc_macros.push(id) } + fn append_stripped_cfg_item(&mut self, parent_node: NodeId, name: Ident, cfg: ast::MetaItem) { + self.stripped_cfg_items.push(StrippedCfgItem { parent_module: parent_node, name, cfg }); + } + fn registered_tools(&self) -> &RegisteredTools { &self.registered_tools } @@ -669,7 +674,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { res.map(|res| (self.get_macro(res).map(|macro_data| macro_data.ext), res)) } - pub(crate) fn finalize_macro_resolutions(&mut self) { + pub(crate) fn finalize_macro_resolutions(&mut self, krate: &Crate) { let check_consistency = |this: &mut Self, path: &[Segment], span, @@ -721,7 +726,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } path_res @ (PathResult::NonModule(..) | PathResult::Failed { .. }) => { let mut suggestion = None; - let (span, label) = if let PathResult::Failed { span, label, .. } = path_res { + let (span, label, module) = if let PathResult::Failed { span, label, module, .. } = path_res { // try to suggest if it's not a macro, maybe a function if let PathResult::NonModule(partial_res) = self.maybe_resolve_path(&path, Some(ValueNS), &parent_scope) && partial_res.unresolved_segments() == 0 { @@ -733,7 +738,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { Applicability::MaybeIncorrect )); } - (span, label) + (span, label, module) } else { ( path_span, @@ -742,11 +747,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { kind.article(), kind.descr() ), + None, ) }; self.report_error( span, - ResolutionError::FailedToResolve { label, suggestion }, + ResolutionError::FailedToResolve { last_segment: path.last().map(|segment| segment.ident.name), label, suggestion, module }, ); } PathResult::Module(..) | PathResult::Indeterminate => unreachable!(), @@ -789,7 +795,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let expected = kind.descr_expected(); let msg = format!("cannot find {} `{}` in this scope", expected, ident); let mut err = self.tcx.sess.struct_span_err(ident.span, msg); - self.unresolved_macro_suggestions(&mut err, kind, &parent_scope, ident); + self.unresolved_macro_suggestions(&mut err, kind, &parent_scope, ident, krate); err.emit(); } } diff --git a/compiler/rustc_session/Cargo.toml b/compiler/rustc_session/Cargo.toml index 3af83aaaaa8a2..90ad3f90f2c31 100644 --- a/compiler/rustc_session/Cargo.toml +++ b/compiler/rustc_session/Cargo.toml @@ -4,6 +4,7 @@ version = "0.0.0" edition = "2021" [dependencies] +atty = "0.2.13" getopts = "0.2" rustc_macros = { path = "../rustc_macros" } tracing = "0.1" diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 0ce83e7909771..a9668ae482894 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -30,6 +30,7 @@ use std::collections::btree_map::{ Iter as BTreeMapIter, Keys as BTreeMapKeysIter, Values as BTreeMapValuesIter, }; use std::collections::{BTreeMap, BTreeSet}; +use std::ffi::OsStr; use std::fmt; use std::hash::Hash; use std::iter; @@ -311,7 +312,9 @@ pub enum OutputType { } // Safety: Trivial C-Style enums have a stable sort order across compilation sessions. -unsafe impl StableOrd for OutputType {} +unsafe impl StableOrd for OutputType { + const CAN_USE_UNSTABLE_SORT: bool = true; +} impl ToStableHashKey for OutputType { type KeyType = Self; @@ -333,7 +336,7 @@ impl OutputType { } } - fn shorthand(&self) -> &'static str { + pub fn shorthand(&self) -> &'static str { match *self { OutputType::Bitcode => "llvm-bc", OutputType::Assembly => "asm", @@ -386,6 +389,18 @@ impl OutputType { OutputType::Exe => "", } } + + pub fn is_text_output(&self) -> bool { + match *self { + OutputType::Assembly + | OutputType::LlvmAssembly + | OutputType::Mir + | OutputType::DepInfo => true, + OutputType::Bitcode | OutputType::Object | OutputType::Metadata | OutputType::Exe => { + false + } + } + } } /// The type of diagnostics output to generate. @@ -438,14 +453,14 @@ pub enum ResolveDocLinks { /// dependency tracking for command-line arguments. Also only hash keys, since tracking /// should only depend on the output types, not the paths they're written to. #[derive(Clone, Debug, Hash, HashStable_Generic)] -pub struct OutputTypes(BTreeMap>); +pub struct OutputTypes(BTreeMap>); impl OutputTypes { - pub fn new(entries: &[(OutputType, Option)]) -> OutputTypes { + pub fn new(entries: &[(OutputType, Option)]) -> OutputTypes { OutputTypes(BTreeMap::from_iter(entries.iter().map(|&(k, ref v)| (k, v.clone())))) } - pub fn get(&self, key: &OutputType) -> Option<&Option> { + pub fn get(&self, key: &OutputType) -> Option<&Option> { self.0.get(key) } @@ -453,11 +468,15 @@ impl OutputTypes { self.0.contains_key(key) } - pub fn keys(&self) -> BTreeMapKeysIter<'_, OutputType, Option> { + pub fn iter(&self) -> BTreeMapIter<'_, OutputType, Option> { + self.0.iter() + } + + pub fn keys(&self) -> BTreeMapKeysIter<'_, OutputType, Option> { self.0.keys() } - pub fn values(&self) -> BTreeMapValuesIter<'_, OutputType, Option> { + pub fn values(&self) -> BTreeMapValuesIter<'_, OutputType, Option> { self.0.values() } @@ -610,6 +629,8 @@ pub enum TraitSolver { Chalk, /// Experimental trait solver in `rustc_trait_selection::solve` Next, + /// Use the new trait solver during coherence + NextCoherence, } pub enum Input { @@ -658,11 +679,71 @@ impl Input { } } +#[derive(Clone, Hash, Debug, HashStable_Generic, PartialEq)] +pub enum OutFileName { + Real(PathBuf), + Stdout, +} + +impl OutFileName { + pub fn parent(&self) -> Option<&Path> { + match *self { + OutFileName::Real(ref path) => path.parent(), + OutFileName::Stdout => None, + } + } + + pub fn filestem(&self) -> Option<&OsStr> { + match *self { + OutFileName::Real(ref path) => path.file_stem(), + OutFileName::Stdout => Some(OsStr::new("stdout")), + } + } + + pub fn is_stdout(&self) -> bool { + match *self { + OutFileName::Real(_) => false, + OutFileName::Stdout => true, + } + } + + pub fn is_tty(&self) -> bool { + match *self { + OutFileName::Real(_) => false, + OutFileName::Stdout => atty::is(atty::Stream::Stdout), + } + } + + pub fn as_path(&self) -> &Path { + match *self { + OutFileName::Real(ref path) => path.as_ref(), + OutFileName::Stdout => &Path::new("stdout"), + } + } + + /// For a given output filename, return the actual name of the file that + /// can be used to write codegen data of type `flavor`. For real-path + /// output filenames, this would be trivial as we can just use the path. + /// Otherwise for stdout, return a temporary path so that the codegen data + /// may be later copied to stdout. + pub fn file_for_writing( + &self, + outputs: &OutputFilenames, + flavor: OutputType, + codegen_unit_name: Option<&str>, + ) -> PathBuf { + match *self { + OutFileName::Real(ref path) => path.clone(), + OutFileName::Stdout => outputs.temp_path(flavor, codegen_unit_name), + } + } +} + #[derive(Clone, Hash, Debug, HashStable_Generic)] pub struct OutputFilenames { pub out_directory: PathBuf, filestem: String, - pub single_output_file: Option, + pub single_output_file: Option, pub temps_directory: Option, pub outputs: OutputTypes, } @@ -675,7 +756,7 @@ impl OutputFilenames { pub fn new( out_directory: PathBuf, out_filestem: String, - single_output_file: Option, + single_output_file: Option, temps_directory: Option, extra: String, outputs: OutputTypes, @@ -689,12 +770,12 @@ impl OutputFilenames { } } - pub fn path(&self, flavor: OutputType) -> PathBuf { + pub fn path(&self, flavor: OutputType) -> OutFileName { self.outputs .get(&flavor) .and_then(|p| p.to_owned()) .or_else(|| self.single_output_file.clone()) - .unwrap_or_else(|| self.output_path(flavor)) + .unwrap_or_else(|| OutFileName::Real(self.output_path(flavor))) } /// Gets the output path where a compilation artifact of the given type @@ -1821,7 +1902,10 @@ fn parse_output_types( for output_type in list.split(',') { let (shorthand, path) = match output_type.split_once('=') { None => (output_type, None), - Some((shorthand, path)) => (shorthand, Some(PathBuf::from(path))), + Some((shorthand, "-")) => (shorthand, Some(OutFileName::Stdout)), + Some((shorthand, path)) => { + (shorthand, Some(OutFileName::Real(PathBuf::from(path)))) + } }; let output_type = OutputType::from_shorthand(shorthand).unwrap_or_else(|| { early_error( @@ -2905,7 +2989,7 @@ pub(crate) mod dep_tracking { use super::{ BranchProtection, CFGuard, CFProtection, CrateType, DebugInfo, ErrorOutputType, InstrumentCoverage, InstrumentXRay, LdImpl, LinkerPluginLto, LocationDetail, LtoCli, - OomStrategy, OptLevel, OutputType, OutputTypes, Passes, ResolveDocLinks, + OomStrategy, OptLevel, OutFileName, OutputType, OutputTypes, Passes, ResolveDocLinks, SourceFileHashAlgorithm, SplitDwarfKind, SwitchWithOptPath, SymbolManglingVersion, TraitSolver, TrimmedDefPaths, }; @@ -3003,6 +3087,7 @@ pub(crate) mod dep_tracking { SourceFileHashAlgorithm, TrimmedDefPaths, Option, + OutFileName, OutputType, RealFileName, LocationDetail, diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 7cc2b2c880c60..201066e395017 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -986,6 +986,7 @@ mod parse { Some("classic") => *slot = TraitSolver::Classic, Some("chalk") => *slot = TraitSolver::Chalk, Some("next") => *slot = TraitSolver::Next, + Some("next-coherence") => *slot = TraitSolver::NextCoherence, // default trait solver is subject to change.. Some("default") => *slot = TraitSolver::Classic, _ => return false, diff --git a/compiler/rustc_session/src/output.rs b/compiler/rustc_session/src/output.rs index fdb9fae44e153..2088744bc5bff 100644 --- a/compiler/rustc_session/src/output.rs +++ b/compiler/rustc_session/src/output.rs @@ -1,5 +1,5 @@ //! Related to out filenames of compilation (e.g. save analysis, binaries). -use crate::config::{CrateType, Input, OutputFilenames, OutputType}; +use crate::config::{CrateType, Input, OutFileName, OutputFilenames, OutputType}; use crate::errors::{ CrateNameDoesNotMatch, CrateNameEmpty, CrateNameInvalid, FileIsNotWriteable, InvalidCharacterInCrateName, @@ -8,14 +8,14 @@ use crate::Session; use rustc_ast::{self as ast, attr}; use rustc_span::symbol::sym; use rustc_span::{Span, Symbol}; -use std::path::{Path, PathBuf}; +use std::path::Path; pub fn out_filename( sess: &Session, crate_type: CrateType, outputs: &OutputFilenames, crate_name: Symbol, -) -> PathBuf { +) -> OutFileName { let default_filename = filename_for_input(sess, crate_type, crate_name, outputs); let out_filename = outputs .outputs @@ -24,7 +24,9 @@ pub fn out_filename( .or_else(|| outputs.single_output_file.clone()) .unwrap_or(default_filename); - check_file_is_writeable(&out_filename, sess); + if let OutFileName::Real(ref path) = out_filename { + check_file_is_writeable(path, sess); + } out_filename } @@ -112,7 +114,7 @@ pub fn filename_for_metadata( sess: &Session, crate_name: Symbol, outputs: &OutputFilenames, -) -> PathBuf { +) -> OutFileName { // If the command-line specified the path, use that directly. if let Some(Some(out_filename)) = sess.opts.output_types.get(&OutputType::Metadata) { return out_filename.clone(); @@ -120,12 +122,13 @@ pub fn filename_for_metadata( let libname = format!("{}{}", crate_name, sess.opts.cg.extra_filename); - let out_filename = outputs - .single_output_file - .clone() - .unwrap_or_else(|| outputs.out_directory.join(&format!("lib{libname}.rmeta"))); + let out_filename = outputs.single_output_file.clone().unwrap_or_else(|| { + OutFileName::Real(outputs.out_directory.join(&format!("lib{libname}.rmeta"))) + }); - check_file_is_writeable(&out_filename, sess); + if let OutFileName::Real(ref path) = out_filename { + check_file_is_writeable(path, sess); + } out_filename } @@ -135,23 +138,33 @@ pub fn filename_for_input( crate_type: CrateType, crate_name: Symbol, outputs: &OutputFilenames, -) -> PathBuf { +) -> OutFileName { let libname = format!("{}{}", crate_name, sess.opts.cg.extra_filename); match crate_type { - CrateType::Rlib => outputs.out_directory.join(&format!("lib{libname}.rlib")), + CrateType::Rlib => { + OutFileName::Real(outputs.out_directory.join(&format!("lib{libname}.rlib"))) + } CrateType::Cdylib | CrateType::ProcMacro | CrateType::Dylib => { let (prefix, suffix) = (&sess.target.dll_prefix, &sess.target.dll_suffix); - outputs.out_directory.join(&format!("{prefix}{libname}{suffix}")) + OutFileName::Real(outputs.out_directory.join(&format!("{prefix}{libname}{suffix}"))) } CrateType::Staticlib => { let (prefix, suffix) = (&sess.target.staticlib_prefix, &sess.target.staticlib_suffix); - outputs.out_directory.join(&format!("{prefix}{libname}{suffix}")) + OutFileName::Real(outputs.out_directory.join(&format!("{prefix}{libname}{suffix}"))) } CrateType::Executable => { let suffix = &sess.target.exe_suffix; let out_filename = outputs.path(OutputType::Exe); - if suffix.is_empty() { out_filename } else { out_filename.with_extension(&suffix[1..]) } + if let OutFileName::Real(ref path) = out_filename { + if suffix.is_empty() { + out_filename + } else { + OutFileName::Real(path.with_extension(&suffix[1..])) + } + } else { + out_filename + } } } } diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 2ebd83540ee15..2cc02003218ee 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -2,7 +2,9 @@ use crate::cgu_reuse_tracker::CguReuseTracker; use crate::code_stats::CodeStats; pub use crate::code_stats::{DataTypeKind, FieldInfo, FieldKind, SizeKind, VariantInfo}; use crate::config::Input; -use crate::config::{self, CrateType, InstrumentCoverage, OptLevel, OutputType, SwitchWithOptPath}; +use crate::config::{ + self, CrateType, InstrumentCoverage, OptLevel, OutFileName, OutputType, SwitchWithOptPath, +}; use crate::errors; use crate::parse::{add_feature_diagnostics, ParseSess}; use crate::search_paths::{PathKind, SearchPath}; @@ -133,7 +135,7 @@ pub struct Limits { pub struct CompilerIO { pub input: Input, pub output_dir: Option, - pub output_file: Option, + pub output_file: Option, pub temps_dir: Option, } diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index 5572108f49567..6d8d99cfb5f1f 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -287,6 +287,27 @@ fn rustc_generator_to_generator( } } +fn rustc_inline_asm_operand_to_inline_asm_operand( + operand: &rustc_middle::mir::InlineAsmOperand<'_>, +) -> stable_mir::mir::InlineAsmOperand { + use rustc_middle::mir::InlineAsmOperand; + + let (in_value, out_place) = match operand { + InlineAsmOperand::In { value, .. } => (Some(rustc_op_to_op(value)), None), + InlineAsmOperand::Out { place, .. } => { + (None, place.map(|place| rustc_place_to_place(&place))) + } + InlineAsmOperand::InOut { in_value, out_place, .. } => { + (Some(rustc_op_to_op(in_value)), out_place.map(|place| rustc_place_to_place(&place))) + } + InlineAsmOperand::Const { .. } + | InlineAsmOperand::SymFn { .. } + | InlineAsmOperand::SymStatic { .. } => (None, None), + }; + + stable_mir::mir::InlineAsmOperand { in_value, out_place, raw_rpr: format!("{:?}", operand) } +} + fn rustc_terminator_to_terminator( terminator: &rustc_middle::mir::Terminator<'_>, ) -> stable_mir::mir::Terminator { @@ -330,10 +351,19 @@ fn rustc_terminator_to_terminator( target: target.as_usize(), unwind: rustc_unwind_to_unwind(unwind), }, - Yield { .. } => todo!(), - GeneratorDrop => Terminator::GeneratorDrop, - FalseEdge { .. } => todo!(), - FalseUnwind { .. } => todo!(), - InlineAsm { .. } => todo!(), + InlineAsm { template, operands, options, line_spans, destination, unwind } => { + Terminator::InlineAsm { + template: format!("{:?}", template), + operands: operands + .iter() + .map(|operand| rustc_inline_asm_operand_to_inline_asm_operand(operand)) + .collect(), + options: format!("{:?}", options), + line_spans: format!("{:?}", line_spans), + destination: destination.map(|d| d.as_usize()), + unwind: rustc_unwind_to_unwind(unwind), + } + } + Yield { .. } | GeneratorDrop | FalseEdge { .. } | FalseUnwind { .. } => unreachable!(), } } diff --git a/compiler/rustc_smir/src/stable_mir/mir/body.rs b/compiler/rustc_smir/src/stable_mir/mir/body.rs index 6328c35aa5982..9df7b4945b70a 100644 --- a/compiler/rustc_smir/src/stable_mir/mir/body.rs +++ b/compiler/rustc_smir/src/stable_mir/mir/body.rs @@ -46,6 +46,23 @@ pub enum Terminator { unwind: UnwindAction, }, GeneratorDrop, + InlineAsm { + template: String, + operands: Vec, + options: String, + line_spans: String, + destination: Option, + unwind: UnwindAction, + }, +} + +#[derive(Clone, Debug)] +pub struct InlineAsmOperand { + pub in_value: Option, + pub out_place: Option, + // This field has a raw debug representation of MIR's InlineAsmOperand. + // For now we care about place/operand + the rest in a debug format. + pub raw_rpr: String, } #[derive(Clone, Debug)] diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 2f002e424277e..c5ce2575fff06 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -701,7 +701,9 @@ symbols! { f, f16c_target_feature, f32, + f32_nan, f64, + f64_nan, fabsf32, fabsf64, fadd_fast, diff --git a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs index a1cb317c50ea6..85825513ce96b 100644 --- a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs +++ b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs @@ -609,9 +609,7 @@ fn encode_ty<'tcx>( } // Function types - ty::FnDef(def_id, substs) - | ty::Closure(def_id, substs) - | ty::Generator(def_id, substs, ..) => { + ty::FnDef(def_id, substs) | ty::Closure(def_id, substs) => { // u[IE], where is , // as vendor extended type. let mut s = String::new(); @@ -622,6 +620,23 @@ fn encode_ty<'tcx>( typeid.push_str(&s); } + ty::Generator(def_id, substs, ..) => { + // u[IE], where is , + // as vendor extended type. + let mut s = String::new(); + let name = encode_ty_name(tcx, *def_id); + let _ = write!(s, "u{}{}", name.len(), &name); + // Encode parent substs only + s.push_str(&encode_substs( + tcx, + tcx.mk_substs(substs.as_generator().parent_substs()), + dict, + options, + )); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + typeid.push_str(&s); + } + // Pointer types ty::Ref(region, ty0, ..) => { // [U3mut]u3refIE as vendor extended type qualifier and type @@ -682,12 +697,12 @@ fn encode_ty<'tcx>( } // Unexpected types - ty::Bound(..) + ty::Alias(..) + | ty::Bound(..) | ty::Error(..) | ty::GeneratorWitness(..) | ty::GeneratorWitnessMIR(..) | ty::Infer(..) - | ty::Alias(..) | ty::Placeholder(..) => { bug!("encode_ty: unexpected `{:?}`", ty.kind()); } @@ -740,7 +755,12 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio let mut ty = ty; match ty.kind() { - ty::Float(..) | ty::Char | ty::Str | ty::Never | ty::Foreign(..) => {} + ty::Float(..) + | ty::Char + | ty::Str + | ty::Never + | ty::Foreign(..) + | ty::GeneratorWitness(..) => {} ty::Bool => { if options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) { @@ -926,12 +946,18 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio ); } + ty::Alias(..) => { + ty = transform_ty( + tcx, + tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), ty), + options, + ); + } + ty::Bound(..) | ty::Error(..) - | ty::GeneratorWitness(..) | ty::GeneratorWitnessMIR(..) | ty::Infer(..) - | ty::Alias(..) | ty::Param(..) | ty::Placeholder(..) => { bug!("transform_ty: unexpected `{:?}`", ty.kind()); diff --git a/compiler/rustc_target/src/abi/call/x86_64.rs b/compiler/rustc_target/src/abi/call/x86_64.rs index 9427f27d1b7bb..74ef53915c929 100644 --- a/compiler/rustc_target/src/abi/call/x86_64.rs +++ b/compiler/rustc_target/src/abi/call/x86_64.rs @@ -1,5 +1,5 @@ // The classification code for the x86_64 ABI is taken from the clay language -// https://github.com/jckarter/clay/blob/master/compiler/src/externals.cpp +// https://github.com/jckarter/clay/blob/db0bd2702ab0b6e48965cd85f8859bbd5f60e48e/compiler/externals.cpp use crate::abi::call::{ArgAbi, CastTarget, FnAbi, Reg, RegKind}; use crate::abi::{self, Abi, HasDataLayout, Size, TyAbiInterface, TyAndLayout}; diff --git a/compiler/rustc_target/src/spec/armv7_sony_vita_newlibeabihf.rs b/compiler/rustc_target/src/spec/armv7_sony_vita_newlibeabihf.rs index e2c0808f1fdbf..62c93603621ab 100644 --- a/compiler/rustc_target/src/spec/armv7_sony_vita_newlibeabihf.rs +++ b/compiler/rustc_target/src/spec/armv7_sony_vita_newlibeabihf.rs @@ -1,15 +1,18 @@ use crate::abi::Endian; -use crate::spec::{cvs, Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetOptions}; +use crate::spec::{cvs, Cc, LinkerFlavor, Lld, RelocModel, Target, TargetOptions}; /// A base target for PlayStation Vita devices using the VITASDK toolchain (using newlib). /// /// Requires the VITASDK toolchain on the host system. pub fn target() -> Target { - let pre_link_args = TargetOptions::link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-Wl,-q"]); + let pre_link_args = TargetOptions::link_args( + LinkerFlavor::Gnu(Cc::Yes, Lld::No), + &["-Wl,-q", "-Wl,--pic-veneer"], + ); Target { - llvm_target: "armv7a-vita-eabihf".into(), + llvm_target: "thumbv7a-vita-eabihf".into(), pointer_width: 32, data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), arch: "arm".into(), @@ -18,21 +21,19 @@ pub fn target() -> Target { os: "vita".into(), endian: Endian::Little, c_int_width: "32".into(), - dynamic_linking: false, env: "newlib".into(), vendor: "sony".into(), abi: "eabihf".into(), linker_flavor: LinkerFlavor::Gnu(Cc::Yes, Lld::No), no_default_libraries: false, cpu: "cortex-a9".into(), - executables: true, families: cvs!["unix"], linker: Some("arm-vita-eabi-gcc".into()), relocation_model: RelocModel::Static, - features: "+v7,+neon".into(), + features: "+v7,+neon,+vfp3,+thumb2,+thumb-mode".into(), pre_link_args, exe_suffix: ".elf".into(), - panic_strategy: PanicStrategy::Abort, + has_thumb_interworking: true, max_atomic_width: Some(64), ..Default::default() }, diff --git a/compiler/rustc_target/src/spec/loongarch64_unknown_none.rs b/compiler/rustc_target/src/spec/loongarch64_unknown_none.rs new file mode 100644 index 0000000000000..618250591ad88 --- /dev/null +++ b/compiler/rustc_target/src/spec/loongarch64_unknown_none.rs @@ -0,0 +1,23 @@ +use super::{Cc, CodeModel, LinkerFlavor, Lld, PanicStrategy}; +use super::{Target, TargetOptions}; + +pub fn target() -> Target { + Target { + llvm_target: "loongarch64-unknown-none".into(), + pointer_width: 64, + data_layout: "e-m:e-p:64:64-i64:64-i128:128-n64-S128".into(), + arch: "loongarch64".into(), + options: TargetOptions { + cpu: "generic".into(), + features: "+f,+d".into(), + linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::No), + llvm_abiname: "lp64d".into(), + max_atomic_width: Some(64), + position_independent_executables: true, + static_position_independent_executables: true, + panic_strategy: PanicStrategy::Abort, + code_model: Some(CodeModel::Small), + ..Default::default() + }, + } +} diff --git a/compiler/rustc_target/src/spec/loongarch64_unknown_none_softfloat.rs b/compiler/rustc_target/src/spec/loongarch64_unknown_none_softfloat.rs new file mode 100644 index 0000000000000..23123d7630c8b --- /dev/null +++ b/compiler/rustc_target/src/spec/loongarch64_unknown_none_softfloat.rs @@ -0,0 +1,24 @@ +use super::{Cc, CodeModel, LinkerFlavor, Lld, PanicStrategy}; +use super::{Target, TargetOptions}; + +pub fn target() -> Target { + Target { + llvm_target: "loongarch64-unknown-none-softfloat".into(), + pointer_width: 64, + data_layout: "e-m:e-p:64:64-i64:64-i128:128-n64-S128".into(), + arch: "loongarch64".into(), + options: TargetOptions { + cpu: "generic".into(), + features: "-f,-d".into(), + abi: "softfloat".into(), + linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::No), + llvm_abiname: "lp64s".into(), + max_atomic_width: Some(64), + position_independent_executables: true, + static_position_independent_executables: true, + panic_strategy: PanicStrategy::Abort, + code_model: Some(CodeModel::Small), + ..Default::default() + }, + } +} diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 05cb7e87a9365..0f26b703536a8 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -675,6 +675,17 @@ impl LinkOutputKind { _ => return None, }) } + + pub fn can_link_dylib(self) -> bool { + match self { + LinkOutputKind::StaticNoPicExe | LinkOutputKind::StaticPicExe => false, + LinkOutputKind::DynamicNoPicExe + | LinkOutputKind::DynamicPicExe + | LinkOutputKind::DynamicDylib + | LinkOutputKind::StaticDylib + | LinkOutputKind::WasiReactorExe => true, + } + } } impl fmt::Display for LinkOutputKind { @@ -1284,6 +1295,7 @@ supported_targets! { ("riscv32im-unknown-none-elf", riscv32im_unknown_none_elf), ("riscv32imc-unknown-none-elf", riscv32imc_unknown_none_elf), ("riscv32imc-esp-espidf", riscv32imc_esp_espidf), + ("riscv32imac-esp-espidf", riscv32imac_esp_espidf), ("riscv32imac-unknown-none-elf", riscv32imac_unknown_none_elf), ("riscv32imac-unknown-xous-elf", riscv32imac_unknown_xous_elf), ("riscv32gc-unknown-linux-gnu", riscv32gc_unknown_linux_gnu), @@ -1293,6 +1305,9 @@ supported_targets! { ("riscv64gc-unknown-linux-gnu", riscv64gc_unknown_linux_gnu), ("riscv64gc-unknown-linux-musl", riscv64gc_unknown_linux_musl), + ("loongarch64-unknown-none", loongarch64_unknown_none), + ("loongarch64-unknown-none-softfloat", loongarch64_unknown_none_softfloat), + ("aarch64-unknown-none", aarch64_unknown_none), ("aarch64-unknown-none-softfloat", aarch64_unknown_none_softfloat), diff --git a/compiler/rustc_target/src/spec/riscv32imac_esp_espidf.rs b/compiler/rustc_target/src/spec/riscv32imac_esp_espidf.rs new file mode 100644 index 0000000000000..0795065409ad9 --- /dev/null +++ b/compiler/rustc_target/src/spec/riscv32imac_esp_espidf.rs @@ -0,0 +1,31 @@ +use crate::spec::{cvs, PanicStrategy, RelocModel, Target, TargetOptions}; + +pub fn target() -> Target { + Target { + data_layout: "e-m:e-p:32:32-i64:64-n32-S128".into(), + llvm_target: "riscv32".into(), + pointer_width: 32, + arch: "riscv32".into(), + + options: TargetOptions { + families: cvs!["unix"], + os: "espidf".into(), + env: "newlib".into(), + vendor: "espressif".into(), + linker: Some("riscv32-esp-elf-gcc".into()), + cpu: "generic-rv32".into(), + + // As RiscV32IMAC architecture does natively support atomics, + // automatically enable the support for the Rust STD library. + max_atomic_width: Some(64), + atomic_cas: true, + + features: "+m,+a,+c".into(), + panic_strategy: PanicStrategy::Abort, + relocation_model: RelocModel::Static, + emit_debug_gdb_scripts: false, + eh_frame_header: false, + ..Default::default() + }, + } +} diff --git a/compiler/rustc_target/src/spec/x86_64_apple_ios.rs b/compiler/rustc_target/src/spec/x86_64_apple_ios.rs index 1dcb47056a463..061b6a96fc888 100644 --- a/compiler/rustc_target/src/spec/x86_64_apple_ios.rs +++ b/compiler/rustc_target/src/spec/x86_64_apple_ios.rs @@ -13,7 +13,7 @@ pub fn target() -> Target { .into(), arch: arch.target_arch(), options: TargetOptions { - max_atomic_width: Some(64), + max_atomic_width: Some(128), stack_probes: StackProbeType::X86, ..base }, diff --git a/compiler/rustc_target/src/spec/x86_64_apple_ios_macabi.rs b/compiler/rustc_target/src/spec/x86_64_apple_ios_macabi.rs index 9f3b0fab697e6..50f359c357bdf 100644 --- a/compiler/rustc_target/src/spec/x86_64_apple_ios_macabi.rs +++ b/compiler/rustc_target/src/spec/x86_64_apple_ios_macabi.rs @@ -15,7 +15,7 @@ pub fn target() -> Target { .into(), arch: arch.target_arch(), options: TargetOptions { - max_atomic_width: Some(64), + max_atomic_width: Some(128), stack_probes: StackProbeType::X86, ..base }, diff --git a/compiler/rustc_target/src/spec/x86_64_apple_tvos.rs b/compiler/rustc_target/src/spec/x86_64_apple_tvos.rs index 550ce0b9ce577..76de7d20c4c6f 100644 --- a/compiler/rustc_target/src/spec/x86_64_apple_tvos.rs +++ b/compiler/rustc_target/src/spec/x86_64_apple_tvos.rs @@ -9,7 +9,7 @@ pub fn target() -> Target { data_layout: "e-m:o-i64:64-f80:128-n8:16:32:64-S128".into(), arch: arch.target_arch(), options: TargetOptions { - max_atomic_width: Some(64), + max_atomic_width: Some(128), stack_probes: StackProbeType::X86, ..opts("tvos", arch) }, diff --git a/compiler/rustc_target/src/spec/x86_64_apple_watchos_sim.rs b/compiler/rustc_target/src/spec/x86_64_apple_watchos_sim.rs index 75ce02cba1de0..5fcc00a86ff95 100644 --- a/compiler/rustc_target/src/spec/x86_64_apple_watchos_sim.rs +++ b/compiler/rustc_target/src/spec/x86_64_apple_watchos_sim.rs @@ -10,7 +10,7 @@ pub fn target() -> Target { .into(), arch: arch.target_arch(), options: TargetOptions { - max_atomic_width: Some(64), + max_atomic_width: Some(128), stack_probes: StackProbeType::X86, forces_embed_bitcode: true, // Taken from a clang build on Xcode 11.4.1. diff --git a/compiler/rustc_trait_selection/src/solve/alias_relate.rs b/compiler/rustc_trait_selection/src/solve/alias_relate.rs new file mode 100644 index 0000000000000..66a4d36a1e5a7 --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/alias_relate.rs @@ -0,0 +1,195 @@ +use super::{EvalCtxt, SolverMode}; +use rustc_infer::traits::query::NoSolution; +use rustc_middle::traits::solve::{Certainty, Goal, QueryResult}; +use rustc_middle::ty; + +/// We may need to invert the alias relation direction if dealing an alias on the RHS. +#[derive(Debug)] +enum Invert { + No, + Yes, +} + +impl<'tcx> EvalCtxt<'_, 'tcx> { + #[instrument(level = "debug", skip(self), ret)] + pub(super) fn compute_alias_relate_goal( + &mut self, + goal: Goal<'tcx, (ty::Term<'tcx>, ty::Term<'tcx>, ty::AliasRelationDirection)>, + ) -> QueryResult<'tcx> { + let tcx = self.tcx(); + let Goal { param_env, predicate: (lhs, rhs, direction) } = goal; + if lhs.is_infer() || rhs.is_infer() { + bug!( + "`AliasRelate` goal with an infer var on lhs or rhs which should have been instantiated" + ); + } + + match (lhs.to_alias_ty(tcx), rhs.to_alias_ty(tcx)) { + (None, None) => bug!("`AliasRelate` goal without an alias on either lhs or rhs"), + + // RHS is not a projection, only way this is true is if LHS normalizes-to RHS + (Some(alias_lhs), None) => self.assemble_normalizes_to_candidate( + param_env, + alias_lhs, + rhs, + direction, + Invert::No, + ), + + // LHS is not a projection, only way this is true is if RHS normalizes-to LHS + (None, Some(alias_rhs)) => self.assemble_normalizes_to_candidate( + param_env, + alias_rhs, + lhs, + direction, + Invert::Yes, + ), + + (Some(alias_lhs), Some(alias_rhs)) => { + debug!("both sides are aliases"); + + let mut candidates = Vec::new(); + // LHS normalizes-to RHS + candidates.extend(self.assemble_normalizes_to_candidate( + param_env, + alias_lhs, + rhs, + direction, + Invert::No, + )); + // RHS normalizes-to RHS + candidates.extend(self.assemble_normalizes_to_candidate( + param_env, + alias_rhs, + lhs, + direction, + Invert::Yes, + )); + // Relate via substs + let subst_relate_response = self + .assemble_subst_relate_candidate(param_env, alias_lhs, alias_rhs, direction); + candidates.extend(subst_relate_response); + debug!(?candidates); + + if let Some(merged) = self.try_merge_responses(&candidates) { + Ok(merged) + } else { + // When relating two aliases and we have ambiguity, we prefer + // relating the generic arguments of the aliases over normalizing + // them. This is necessary for inference during typeck. + // + // As this is incomplete, we must not do so during coherence. + match self.solver_mode() { + SolverMode::Normal => { + if let Ok(subst_relate_response) = subst_relate_response { + Ok(subst_relate_response) + } else if let Ok(bidirectional_normalizes_to_response) = self + .assemble_bidirectional_normalizes_to_candidate( + param_env, lhs, rhs, direction, + ) + { + Ok(bidirectional_normalizes_to_response) + } else { + self.flounder(&candidates) + } + } + SolverMode::Coherence => self.flounder(&candidates), + } + } + } + } + } + + #[instrument(level = "debug", skip(self), ret)] + fn assemble_normalizes_to_candidate( + &mut self, + param_env: ty::ParamEnv<'tcx>, + alias: ty::AliasTy<'tcx>, + other: ty::Term<'tcx>, + direction: ty::AliasRelationDirection, + invert: Invert, + ) -> QueryResult<'tcx> { + self.probe(|ecx| { + ecx.normalizes_to_inner(param_env, alias, other, direction, invert)?; + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + } + + fn normalizes_to_inner( + &mut self, + param_env: ty::ParamEnv<'tcx>, + alias: ty::AliasTy<'tcx>, + other: ty::Term<'tcx>, + direction: ty::AliasRelationDirection, + invert: Invert, + ) -> Result<(), NoSolution> { + let other = match direction { + // This is purely an optimization. + ty::AliasRelationDirection::Equate => other, + + ty::AliasRelationDirection::Subtype => { + let fresh = self.next_term_infer_of_kind(other); + let (sub, sup) = match invert { + Invert::No => (fresh, other), + Invert::Yes => (other, fresh), + }; + self.sub(param_env, sub, sup)?; + fresh + } + }; + self.add_goal(Goal::new( + self.tcx(), + param_env, + ty::Binder::dummy(ty::ProjectionPredicate { projection_ty: alias, term: other }), + )); + + Ok(()) + } + + fn assemble_subst_relate_candidate( + &mut self, + param_env: ty::ParamEnv<'tcx>, + alias_lhs: ty::AliasTy<'tcx>, + alias_rhs: ty::AliasTy<'tcx>, + direction: ty::AliasRelationDirection, + ) -> QueryResult<'tcx> { + self.probe(|ecx| { + match direction { + ty::AliasRelationDirection::Equate => { + ecx.eq(param_env, alias_lhs, alias_rhs)?; + } + ty::AliasRelationDirection::Subtype => { + ecx.sub(param_env, alias_lhs, alias_rhs)?; + } + } + + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + } + + fn assemble_bidirectional_normalizes_to_candidate( + &mut self, + param_env: ty::ParamEnv<'tcx>, + lhs: ty::Term<'tcx>, + rhs: ty::Term<'tcx>, + direction: ty::AliasRelationDirection, + ) -> QueryResult<'tcx> { + self.probe(|ecx| { + ecx.normalizes_to_inner( + param_env, + lhs.to_alias_ty(ecx.tcx()).unwrap(), + rhs, + direction, + Invert::No, + )?; + ecx.normalizes_to_inner( + param_env, + rhs.to_alias_ty(ecx.tcx()).unwrap(), + lhs, + direction, + Invert::Yes, + )?; + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + } +} diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs index f91c672775301..3001d9f1b1f3f 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs @@ -187,6 +187,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { let (ref infcx, input, var_values) = tcx .infer_ctxt() .intercrate(intercrate) + .with_next_trait_solver(true) .with_opaque_type_inference(canonical_input.value.anchor) .build_with_canonical(DUMMY_SP, &canonical_input); @@ -322,10 +323,13 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { ty::PredicateKind::Ambiguous => { self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) } - // FIXME: implement these predicates :) - ty::PredicateKind::ConstEvaluatable(_) | ty::PredicateKind::ConstEquate(_, _) => { + // FIXME: implement this predicate :) + ty::PredicateKind::ConstEvaluatable(_) => { self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } + ty::PredicateKind::ConstEquate(_, _) => { + bug!("ConstEquate should not be emitted when `-Ztrait-solver=next` is active") + } ty::PredicateKind::TypeWellFormedFromEnv(..) => { bug!("TypeWellFormedFromEnv is only used for Chalk") } @@ -772,4 +776,21 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } values } + + // Try to evaluate a const, or return `None` if the const is too generic. + // This doesn't mean the const isn't evaluatable, though, and should be treated + // as an ambiguity rather than no-solution. + pub(super) fn try_const_eval_resolve( + &self, + param_env: ty::ParamEnv<'tcx>, + unevaluated: ty::UnevaluatedConst<'tcx>, + ty: Ty<'tcx>, + ) -> Option> { + use rustc_middle::mir::interpret::ErrorHandled; + match self.infcx.try_const_eval_resolve(param_env, unevaluated, ty, None) { + Ok(ct) => Some(ct), + Err(ErrorHandled::Reported(e)) => Some(self.tcx().const_error(ty, e.into())), + Err(ErrorHandled::TooGeneric) => None, + } + } } diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs index bca2343e42422..72b3c3d01804a 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs @@ -11,6 +11,7 @@ use super::{CanonicalInput, Certainty, EvalCtxt, Goal}; use crate::solve::canonicalize::{CanonicalizeMode, Canonicalizer}; use crate::solve::{CanonicalResponse, QueryResult, Response}; +use rustc_data_structures::fx::FxHashSet; use rustc_index::IndexVec; use rustc_infer::infer::canonical::query_response::make_query_region_constraints; use rustc_infer::infer::canonical::CanonicalVarValues; @@ -147,7 +148,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // Cannot use `take_registered_region_obligations` as we may compute the response // inside of a `probe` whenever we have multiple choices inside of the solver. let region_obligations = self.infcx.inner.borrow().region_obligations().to_owned(); - let region_constraints = self.infcx.with_region_constraints(|region_constraints| { + let mut region_constraints = self.infcx.with_region_constraints(|region_constraints| { make_query_region_constraints( self.tcx(), region_obligations @@ -157,6 +158,9 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ) }); + let mut seen = FxHashSet::default(); + region_constraints.outlives.retain(|outlives| seen.insert(*outlives)); + let mut opaque_types = self.infcx.clone_opaque_types_for_query_response(); // Only return opaque type keys for newly-defined opaques opaque_types.retain(|(a, _)| { diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 4a403196c7e05..212327448c874 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -118,16 +118,6 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> { TypeError::Sorts(expected_found), ) } - ty::PredicateKind::ConstEquate(a, b) => { - let (a, b) = infcx.instantiate_binder_with_placeholders( - goal.predicate.kind().rebind((a, b)), - ); - let expected_found = ExpectedFound::new(true, a, b); - FulfillmentErrorCode::CodeConstEquateError( - expected_found, - TypeError::ConstMismatch(expected_found), - ) - } ty::PredicateKind::Clause(_) | ty::PredicateKind::WellFormed(_) | ty::PredicateKind::ObjectSafe(_) @@ -138,7 +128,8 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> { SelectionError::Unimplemented, ) } - ty::PredicateKind::TypeWellFormedFromEnv(_) => { + ty::PredicateKind::ConstEquate(..) + | ty::PredicateKind::TypeWellFormedFromEnv(_) => { bug!("unexpected goal: {goal:?}") } }, diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index 56a254d9c07e1..f4c29c837b884 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -20,6 +20,7 @@ use rustc_middle::ty::{ CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, TypeOutlivesPredicate, }; +mod alias_relate; mod assembly; mod canonicalize; mod eval_ctxt; @@ -154,142 +155,6 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { } } - #[instrument(level = "debug", skip(self), ret)] - fn compute_alias_relate_goal( - &mut self, - goal: Goal<'tcx, (ty::Term<'tcx>, ty::Term<'tcx>, ty::AliasRelationDirection)>, - ) -> QueryResult<'tcx> { - let tcx = self.tcx(); - // We may need to invert the alias relation direction if dealing an alias on the RHS. - #[derive(Debug)] - enum Invert { - No, - Yes, - } - let evaluate_normalizes_to = - |ecx: &mut EvalCtxt<'_, 'tcx>, alias, other, direction, invert| { - let span = tracing::span!( - tracing::Level::DEBUG, - "compute_alias_relate_goal(evaluate_normalizes_to)", - ?alias, - ?other, - ?direction, - ?invert - ); - let _enter = span.enter(); - let result = ecx.probe(|ecx| { - let other = match direction { - // This is purely an optimization. - ty::AliasRelationDirection::Equate => other, - - ty::AliasRelationDirection::Subtype => { - let fresh = ecx.next_term_infer_of_kind(other); - let (sub, sup) = match invert { - Invert::No => (fresh, other), - Invert::Yes => (other, fresh), - }; - ecx.sub(goal.param_env, sub, sup)?; - fresh - } - }; - ecx.add_goal(goal.with( - tcx, - ty::Binder::dummy(ty::ProjectionPredicate { - projection_ty: alias, - term: other, - }), - )); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }); - debug!(?result); - result - }; - - let (lhs, rhs, direction) = goal.predicate; - - if lhs.is_infer() || rhs.is_infer() { - bug!( - "`AliasRelate` goal with an infer var on lhs or rhs which should have been instantiated" - ); - } - - match (lhs.to_alias_ty(tcx), rhs.to_alias_ty(tcx)) { - (None, None) => bug!("`AliasRelate` goal without an alias on either lhs or rhs"), - - // RHS is not a projection, only way this is true is if LHS normalizes-to RHS - (Some(alias_lhs), None) => { - evaluate_normalizes_to(self, alias_lhs, rhs, direction, Invert::No) - } - - // LHS is not a projection, only way this is true is if RHS normalizes-to LHS - (None, Some(alias_rhs)) => { - evaluate_normalizes_to(self, alias_rhs, lhs, direction, Invert::Yes) - } - - (Some(alias_lhs), Some(alias_rhs)) => { - debug!("both sides are aliases"); - - let mut candidates = Vec::new(); - // LHS normalizes-to RHS - candidates.extend(evaluate_normalizes_to( - self, - alias_lhs, - rhs, - direction, - Invert::No, - )); - // RHS normalizes-to RHS - candidates.extend(evaluate_normalizes_to( - self, - alias_rhs, - lhs, - direction, - Invert::Yes, - )); - // Relate via substs - let subst_relate_response = self.probe(|ecx| { - let span = tracing::span!( - tracing::Level::DEBUG, - "compute_alias_relate_goal(relate_via_substs)", - ?alias_lhs, - ?alias_rhs, - ?direction - ); - let _enter = span.enter(); - - match direction { - ty::AliasRelationDirection::Equate => { - ecx.eq(goal.param_env, alias_lhs, alias_rhs)?; - } - ty::AliasRelationDirection::Subtype => { - ecx.sub(goal.param_env, alias_lhs, alias_rhs)?; - } - } - - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }); - candidates.extend(subst_relate_response); - debug!(?candidates); - - if let Some(merged) = self.try_merge_responses(&candidates) { - Ok(merged) - } else { - // When relating two aliases and we have ambiguity, we prefer - // relating the generic arguments of the aliases over normalizing - // them. This is necessary for inference during typeck. - // - // As this is incomplete, we must not do so during coherence. - match (self.solver_mode(), subst_relate_response) { - (SolverMode::Normal, Ok(response)) => Ok(response), - (SolverMode::Normal, Err(NoSolution)) | (SolverMode::Coherence, _) => { - self.flounder(&candidates) - } - } - } - } - } - } - #[instrument(level = "debug", skip(self), ret)] fn compute_const_arg_has_type_goal( &mut self, diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index 7d7dfa2c83776..242f9ba87473f 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -22,25 +22,65 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { &mut self, goal: Goal<'tcx, ProjectionPredicate<'tcx>>, ) -> QueryResult<'tcx> { - match goal.predicate.projection_ty.kind(self.tcx()) { - ty::AliasKind::Projection => { + let def_id = goal.predicate.def_id(); + match self.tcx().def_kind(def_id) { + DefKind::AssocTy | DefKind::AssocConst => { // To only compute normalization once for each projection we only - // normalize if the expected term is an unconstrained inference variable. + // assemble normalization candidates if the expected term is an + // unconstrained inference variable. + // + // Why: For better cache hits, since if we have an unconstrained RHS then + // there are only as many cache keys as there are (canonicalized) alias + // types in each normalizes-to goal. This also weakens inference in a + // forwards-compatible way so we don't use the value of the RHS term to + // affect candidate assembly for projections. // // E.g. for `::Assoc == u32` we recursively compute the goal // `exists ::Assoc == U` and then take the resulting type for // `U` and equate it with `u32`. This means that we don't need a separate - // projection cache in the solver. + // projection cache in the solver, since we're piggybacking off of regular + // goal caching. if self.term_is_fully_unconstrained(goal) { - let candidates = self.assemble_and_evaluate_candidates(goal); - self.merge_candidates(candidates) + match self.tcx().associated_item(def_id).container { + ty::AssocItemContainer::TraitContainer => { + let candidates = self.assemble_and_evaluate_candidates(goal); + self.merge_candidates(candidates) + } + ty::AssocItemContainer::ImplContainer => { + bug!("IATs not supported here yet") + } + } } else { self.set_normalizes_to_hack_goal(goal); self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } } - ty::AliasKind::Opaque => self.normalize_opaque_type(goal), - ty::AliasKind::Inherent => bug!("IATs not supported here yet"), + DefKind::AnonConst => self.normalize_anon_const(goal), + DefKind::OpaqueTy => self.normalize_opaque_type(goal), + kind => bug!("unknown DefKind {} in projection goal: {goal:#?}", kind.descr(def_id)), + } + } + + #[instrument(level = "debug", skip(self), ret)] + fn normalize_anon_const( + &mut self, + goal: Goal<'tcx, ty::ProjectionPredicate<'tcx>>, + ) -> QueryResult<'tcx> { + if let Some(normalized_const) = self.try_const_eval_resolve( + goal.param_env, + ty::UnevaluatedConst::new( + goal.predicate.projection_ty.def_id, + goal.predicate.projection_ty.substs, + ), + self.tcx() + .type_of(goal.predicate.projection_ty.def_id) + .no_bound_vars() + .expect("const ty should not rely on other generics"), + ) { + self.eq(goal.param_env, normalized_const, goal.predicate.term.ct().unwrap())?; + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } else { + self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) } } } @@ -173,17 +213,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { ); // Finally we construct the actual value of the associated type. - let is_const = matches!(tcx.def_kind(assoc_def.item.def_id), DefKind::AssocConst); - let ty = tcx.type_of(assoc_def.item.def_id); - let term: ty::EarlyBinder> = if is_const { - let identity_substs = - ty::InternalSubsts::identity_for_item(tcx, assoc_def.item.def_id); - let did = assoc_def.item.def_id; - let kind = - ty::ConstKind::Unevaluated(ty::UnevaluatedConst::new(did, identity_substs)); - ty.map_bound(|ty| tcx.mk_const(kind, ty).into()) - } else { - ty.map_bound(|ty| ty.into()) + let term = match assoc_def.item.kind { + ty::AssocKind::Type => tcx.type_of(assoc_def.item.def_id).map_bound(|ty| ty.into()), + ty::AssocKind::Const => bug!("associated const projection is not supported yet"), + ty::AssocKind::Fn => unreachable!("we should never project to a fn"), }; ecx.eq(goal.param_env, goal.predicate.term, term.subst(tcx, substs)) @@ -193,10 +226,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { } fn consider_auto_trait_candidate( - _ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { - bug!("auto traits do not have associated types: {:?}", goal); + ecx.tcx().sess.delay_span_bug( + ecx.tcx().def_span(goal.predicate.def_id()), + "associated types not allowed on auto traits", + ); + Err(NoSolution) } fn consider_trait_alias_candidate( diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index d6fd457de06da..2c793ea33dc88 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -182,6 +182,7 @@ fn overlap<'tcx>( .with_opaque_type_inference(DefiningAnchor::Bubble) .skip_leak_check(skip_leak_check.is_yes()) .intercrate(true) + .with_next_trait_solver(tcx.next_trait_solver_in_coherence()) .build(); let selcx = &mut SelectionContext::new(&infcx); if track_ambiguity_causes.is_yes() { diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs index 2c5ffd664fe1d..90699c3cadcf5 100644 --- a/compiler/rustc_trait_selection/src/traits/engine.rs +++ b/compiler/rustc_trait_selection/src/traits/engine.rs @@ -27,24 +27,42 @@ use rustc_session::config::TraitSolver; use rustc_span::Span; pub trait TraitEngineExt<'tcx> { - fn new(tcx: TyCtxt<'tcx>) -> Box; - fn new_in_snapshot(tcx: TyCtxt<'tcx>) -> Box; + fn new(infcx: &InferCtxt<'tcx>) -> Box; + fn new_in_snapshot(infcx: &InferCtxt<'tcx>) -> Box; } impl<'tcx> TraitEngineExt<'tcx> for dyn TraitEngine<'tcx> { - fn new(tcx: TyCtxt<'tcx>) -> Box { - match tcx.sess.opts.unstable_opts.trait_solver { - TraitSolver::Classic => Box::new(FulfillmentContext::new()), - TraitSolver::Chalk => Box::new(ChalkFulfillmentContext::new()), - TraitSolver::Next => Box::new(NextFulfillmentCtxt::new()), + fn new(infcx: &InferCtxt<'tcx>) -> Box { + match (infcx.tcx.sess.opts.unstable_opts.trait_solver, infcx.next_trait_solver()) { + (TraitSolver::Classic, false) | (TraitSolver::NextCoherence, false) => { + Box::new(FulfillmentContext::new()) + } + (TraitSolver::Next | TraitSolver::NextCoherence, true) => { + Box::new(NextFulfillmentCtxt::new()) + } + (TraitSolver::Chalk, false) => Box::new(ChalkFulfillmentContext::new()), + _ => bug!( + "incompatible combination of -Ztrait-solver flag ({:?}) and InferCtxt::next_trait_solver ({:?})", + infcx.tcx.sess.opts.unstable_opts.trait_solver, + infcx.next_trait_solver() + ), } } - fn new_in_snapshot(tcx: TyCtxt<'tcx>) -> Box { - match tcx.sess.opts.unstable_opts.trait_solver { - TraitSolver::Classic => Box::new(FulfillmentContext::new_in_snapshot()), - TraitSolver::Chalk => Box::new(ChalkFulfillmentContext::new_in_snapshot()), - TraitSolver::Next => Box::new(NextFulfillmentCtxt::new()), + fn new_in_snapshot(infcx: &InferCtxt<'tcx>) -> Box { + match (infcx.tcx.sess.opts.unstable_opts.trait_solver, infcx.next_trait_solver()) { + (TraitSolver::Classic, false) | (TraitSolver::NextCoherence, false) => { + Box::new(FulfillmentContext::new_in_snapshot()) + } + (TraitSolver::Next | TraitSolver::NextCoherence, true) => { + Box::new(NextFulfillmentCtxt::new()) + } + (TraitSolver::Chalk, false) => Box::new(ChalkFulfillmentContext::new_in_snapshot()), + _ => bug!( + "incompatible combination of -Ztrait-solver flag ({:?}) and InferCtxt::next_trait_solver ({:?})", + infcx.tcx.sess.opts.unstable_opts.trait_solver, + infcx.next_trait_solver() + ), } } } @@ -58,11 +76,11 @@ pub struct ObligationCtxt<'a, 'tcx> { impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> { pub fn new(infcx: &'a InferCtxt<'tcx>) -> Self { - Self { infcx, engine: RefCell::new(>::new(infcx.tcx)) } + Self { infcx, engine: RefCell::new(>::new(infcx)) } } pub fn new_in_snapshot(infcx: &'a InferCtxt<'tcx>) -> Self { - Self { infcx, engine: RefCell::new(>::new_in_snapshot(infcx.tcx)) } + Self { infcx, engine: RefCell::new(>::new_in_snapshot(infcx)) } } pub fn register_obligation(&self, obligation: PredicateObligation<'tcx>) { diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index 1470dc452a18d..c4481b39e14be 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -149,6 +149,12 @@ pub trait TypeErrCtxtExt<'tcx> { root_obligation: &PredicateObligation<'tcx>, error: &SelectionError<'tcx>, ); + + fn report_const_param_not_wf( + &self, + ty: Ty<'tcx>, + obligation: &PredicateObligation<'tcx>, + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>; } impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { @@ -641,6 +647,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { span = obligation.cause.span; } } + if let ObligationCauseCode::CompareImplItemObligation { impl_item_def_id, trait_item_def_id, @@ -657,6 +664,13 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { return; } + // Report a const-param specific error + if let ObligationCauseCode::ConstParam(ty) = *obligation.cause.code().peel_derives() + { + self.report_const_param_not_wf(ty, &obligation).emit(); + return; + } + let bound_predicate = obligation.predicate.kind(); match bound_predicate.skip_binder() { ty::PredicateKind::Clause(ty::Clause::Trait(trait_predicate)) => { @@ -1033,7 +1047,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // (which may fail). span_bug!(span, "WF predicate not satisfied for {:?}", ty); } - TraitSolver::Chalk | TraitSolver::Next => { + TraitSolver::Chalk | TraitSolver::Next | TraitSolver::NextCoherence => { // FIXME: we'll need a better message which takes into account // which bounds actually failed to hold. self.tcx.sess.struct_span_err( @@ -1163,6 +1177,102 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { self.point_at_returns_when_relevant(&mut err, &obligation); err.emit(); } + + fn report_const_param_not_wf( + &self, + ty: Ty<'tcx>, + obligation: &PredicateObligation<'tcx>, + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { + let span = obligation.cause.span; + + let mut diag = match ty.kind() { + _ if ty.has_param() => { + span_bug!(span, "const param tys cannot mention other generic parameters"); + } + ty::Float(_) => { + struct_span_err!( + self.tcx.sess, + span, + E0741, + "`{ty}` is forbidden as the type of a const generic parameter", + ) + } + ty::FnPtr(_) => { + struct_span_err!( + self.tcx.sess, + span, + E0741, + "using function pointers as const generic parameters is forbidden", + ) + } + ty::RawPtr(_) => { + struct_span_err!( + self.tcx.sess, + span, + E0741, + "using raw pointers as const generic parameters is forbidden", + ) + } + ty::Adt(def, _) => { + // We should probably see if we're *allowed* to derive `ConstParamTy` on the type... + let mut diag = struct_span_err!( + self.tcx.sess, + span, + E0741, + "`{ty}` must implement `ConstParamTy` to be used as the type of a const generic parameter", + ); + // Only suggest derive if this isn't a derived obligation, + // and the struct is local. + if let Some(span) = self.tcx.hir().span_if_local(def.did()) + && obligation.cause.code().parent().is_none() + { + if ty.is_structural_eq_shallow(self.tcx) { + diag.span_suggestion( + span, + "add `#[derive(ConstParamTy)]` to the struct", + "#[derive(ConstParamTy)]\n", + Applicability::MachineApplicable, + ); + } else { + // FIXME(adt_const_params): We should check there's not already an + // overlapping `Eq`/`PartialEq` impl. + diag.span_suggestion( + span, + "add `#[derive(ConstParamTy, PartialEq, Eq)]` to the struct", + "#[derive(ConstParamTy, PartialEq, Eq)]\n", + Applicability::MachineApplicable, + ); + } + } + diag + } + _ => { + struct_span_err!( + self.tcx.sess, + span, + E0741, + "`{ty}` can't be used as a const parameter type", + ) + } + }; + + let mut code = obligation.cause.code(); + let mut pred = obligation.predicate.to_opt_poly_trait_pred(); + while let Some((next_code, next_pred)) = code.parent() { + if let Some(pred) = pred { + let pred = self.instantiate_binder_with_placeholders(pred); + diag.note(format!( + "`{}` must implement `{}`, but it does not", + pred.self_ty(), + pred.print_modifiers_and_trait_path() + )); + } + code = next_code; + pred = next_pred; + } + + diag + } } trait InferCtxtPrivExt<'tcx> { diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index b5b8c7fe3ac56..e73d917a8aed1 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -13,7 +13,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::{ error_code, pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, - ErrorGuaranteed, MultiSpan, Style, + ErrorGuaranteed, MultiSpan, Style, SuggestionStyle, }; use rustc_hir as hir; use rustc_hir::def::DefKind; @@ -362,6 +362,15 @@ pub trait TypeErrCtxtExt<'tcx> { err: &mut Diagnostic, trait_pred: ty::PolyTraitPredicate<'tcx>, ); + + fn suggest_option_method_if_applicable( + &self, + failed_pred: ty::Predicate<'tcx>, + param_env: ty::ParamEnv<'tcx>, + err: &mut Diagnostic, + expr: &hir::Expr<'_>, + ); + fn note_function_argument_obligation( &self, body_id: LocalDefId, @@ -2655,7 +2664,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { | ObligationCauseCode::BinOp { .. } | ObligationCauseCode::AscribeUserTypeProvePredicate(..) | ObligationCauseCode::RustCall - | ObligationCauseCode::DropImpl => {} + | ObligationCauseCode::DropImpl + | ObligationCauseCode::ConstParam(_) => {} ObligationCauseCode::SliceOrArrayElem => { err.note("slice and array elements must have `Sized` type"); } @@ -2797,8 +2807,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err.help("unsized locals are gated as an unstable feature"); } } - ObligationCauseCode::SizedArgumentType(sp) => { - if let Some(span) = sp { + ObligationCauseCode::SizedArgumentType(ty_span) => { + if let Some(span) = ty_span { if let ty::PredicateKind::Clause(clause) = predicate.kind().skip_binder() && let ty::Clause::Trait(trait_pred) = clause && let ty::Dynamic(..) = trait_pred.self_ty().kind() @@ -3520,15 +3530,93 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err.replace_span_with(path.ident.span, true); } } - if let Some(Node::Expr(hir::Expr { - kind: - hir::ExprKind::Call(hir::Expr { span, .. }, _) - | hir::ExprKind::MethodCall(hir::PathSegment { ident: Ident { span, .. }, .. }, ..), - .. - })) = hir.find(call_hir_id) + + if let Some(Node::Expr(expr)) = hir.find(call_hir_id) { + if let hir::ExprKind::Call(hir::Expr { span, .. }, _) + | hir::ExprKind::MethodCall( + hir::PathSegment { ident: Ident { span, .. }, .. }, + .., + ) = expr.kind + { + if Some(*span) != err.span.primary_span() { + err.span_label(*span, "required by a bound introduced by this call"); + } + } + + if let hir::ExprKind::MethodCall(_, expr, ..) = expr.kind { + self.suggest_option_method_if_applicable(failed_pred, param_env, err, expr); + } + } + } + + fn suggest_option_method_if_applicable( + &self, + failed_pred: ty::Predicate<'tcx>, + param_env: ty::ParamEnv<'tcx>, + err: &mut Diagnostic, + expr: &hir::Expr<'_>, + ) { + let tcx = self.tcx; + let infcx = self.infcx; + let Some(typeck_results) = self.typeck_results.as_ref() else { return }; + + // Make sure we're dealing with the `Option` type. + let Some(option_ty_adt) = typeck_results.expr_ty_adjusted(expr).ty_adt_def() else { return }; + if !tcx.is_diagnostic_item(sym::Option, option_ty_adt.did()) { + return; + } + + // Given the predicate `fn(&T): FnOnce<(U,)>`, extract `fn(&T)` and `(U,)`, + // then suggest `Option::as_deref(_mut)` if `U` can deref to `T` + if let ty::PredicateKind::Clause(ty::Clause::Trait(ty::TraitPredicate { trait_ref, .. })) + = failed_pred.kind().skip_binder() + && tcx.is_fn_trait(trait_ref.def_id) + && let [self_ty, found_ty] = trait_ref.substs.as_slice() + && let Some(fn_ty) = self_ty.as_type().filter(|ty| ty.is_fn()) + && let fn_sig @ ty::FnSig { + abi: abi::Abi::Rust, + c_variadic: false, + unsafety: hir::Unsafety::Normal, + .. + } = fn_ty.fn_sig(tcx).skip_binder() + + // Extract first param of fn sig with peeled refs, e.g. `fn(&T)` -> `T` + && let Some(&ty::Ref(_, target_ty, needs_mut)) = fn_sig.inputs().first().map(|t| t.kind()) + && !target_ty.has_escaping_bound_vars() + + // Extract first tuple element out of fn trait, e.g. `FnOnce<(U,)>` -> `U` + && let Some(ty::Tuple(tys)) = found_ty.as_type().map(Ty::kind) + && let &[found_ty] = tys.as_slice() + && !found_ty.has_escaping_bound_vars() + + // Extract `::Target` assoc type and check that it is `T` + && let Some(deref_target_did) = tcx.lang_items().deref_target() + && let projection = tcx.mk_projection(deref_target_did, tcx.mk_substs(&[ty::GenericArg::from(found_ty)])) + && let InferOk { value: deref_target, obligations } = infcx.at(&ObligationCause::dummy(), param_env).normalize(projection) + && obligations.iter().all(|obligation| infcx.predicate_must_hold_modulo_regions(obligation)) + && infcx.can_eq(param_env, deref_target, target_ty) { - if Some(*span) != err.span.primary_span() { - err.span_label(*span, "required by a bound introduced by this call"); + let help = if let hir::Mutability::Mut = needs_mut + && let Some(deref_mut_did) = tcx.lang_items().deref_mut_trait() + && infcx + .type_implements_trait(deref_mut_did, iter::once(found_ty), param_env) + .must_apply_modulo_regions() + { + Some(("call `Option::as_deref_mut()` first", ".as_deref_mut()")) + } else if let hir::Mutability::Not = needs_mut { + Some(("call `Option::as_deref()` first", ".as_deref()")) + } else { + None + }; + + if let Some((msg, sugg)) = help { + err.span_suggestion_with_style( + expr.span.shrink_to_hi(), + msg, + sugg, + Applicability::MaybeIncorrect, + SuggestionStyle::ShowAlways + ); } } } diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs index 2210ef975e6c8..e9cfd63e2eddc 100644 --- a/compiler/rustc_trait_selection/src/traits/misc.rs +++ b/compiler/rustc_trait_selection/src/traits/misc.rs @@ -100,7 +100,8 @@ pub fn type_allowed_to_implement_const_param_ty<'tcx>( | ty::Str | ty::Array(..) | ty::Slice(_) - | ty::Ref(.., hir::Mutability::Not) => return Ok(()), + | ty::Ref(.., hir::Mutability::Not) + | ty::Tuple(_) => return Ok(()), &ty::Adt(adt, substs) => (adt, substs), diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index f7389bda159e5..c2f94cb638566 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -62,9 +62,7 @@ pub use self::specialize::specialization_graph::FutureCompatOverlapErrorKind; pub use self::specialize::{ specialization_graph, translate_substs, translate_substs_with_cause, OverlapError, }; -pub use self::structural_match::{ - search_for_adt_const_param_violation, search_for_structural_match_violation, -}; +pub use self::structural_match::search_for_structural_match_violation; pub use self::structural_normalize::StructurallyNormalizeExt; pub use self::util::elaborate; pub use self::util::{expand_trait_aliases, TraitAliasExpander}; diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index 048302187cf9f..b2771915eef8e 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -115,15 +115,11 @@ fn object_safety_violations_for_trait( tcx: TyCtxt<'_>, trait_def_id: DefId, ) -> Vec { - // Check methods for violations. + // Check assoc items for violations. let mut violations: Vec<_> = tcx .associated_items(trait_def_id) .in_definition_order() - .filter(|item| item.kind == ty::AssocKind::Fn) - .filter_map(|&item| { - object_safety_violation_for_method(tcx, trait_def_id, item) - .map(|(code, span)| ObjectSafetyViolation::Method(item.name, code, span)) - }) + .filter_map(|&item| object_safety_violation_for_assoc_item(tcx, trait_def_id, item)) .collect(); // Check the trait itself. @@ -145,30 +141,6 @@ fn object_safety_violations_for_trait( violations.push(ObjectSafetyViolation::SupertraitNonLifetimeBinder(spans)); } - violations.extend( - tcx.associated_items(trait_def_id) - .in_definition_order() - .filter(|item| item.kind == ty::AssocKind::Const) - .map(|item| { - let ident = item.ident(tcx); - ObjectSafetyViolation::AssocConst(ident.name, ident.span) - }), - ); - - if !tcx.features().generic_associated_types_extended { - violations.extend( - tcx.associated_items(trait_def_id) - .in_definition_order() - .filter(|item| item.kind == ty::AssocKind::Type) - .filter(|item| !tcx.generics_of(item.def_id).params.is_empty()) - .filter(|item| tcx.opt_rpitit_info(item.def_id).is_none()) - .map(|item| { - let ident = item.ident(tcx); - ObjectSafetyViolation::GAT(ident.name, ident.span) - }), - ); - } - debug!( "object_safety_violations_for_trait(trait_def_id={:?}) = {:?}", trait_def_id, violations @@ -401,34 +373,54 @@ fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool { }) } -/// Returns `Some(_)` if this method makes the containing trait not object safe. -fn object_safety_violation_for_method( +/// Returns `Some(_)` if this item makes the containing trait not object safe. +#[instrument(level = "debug", skip(tcx), ret)] +fn object_safety_violation_for_assoc_item( tcx: TyCtxt<'_>, trait_def_id: DefId, - method: ty::AssocItem, -) -> Option<(MethodViolationCode, Span)> { - debug!("object_safety_violation_for_method({:?}, {:?})", trait_def_id, method); - // Any method that has a `Self : Sized` requisite is otherwise + item: ty::AssocItem, +) -> Option { + // Any item that has a `Self : Sized` requisite is otherwise // exempt from the regulations. - if generics_require_sized_self(tcx, method.def_id) { + if generics_require_sized_self(tcx, item.def_id) { return None; } - let violation = virtual_call_violation_for_method(tcx, trait_def_id, method); - // Get an accurate span depending on the violation. - violation.map(|v| { - let node = tcx.hir().get_if_local(method.def_id); - let span = match (&v, node) { - (MethodViolationCode::ReferencesSelfInput(Some(span)), _) => *span, - (MethodViolationCode::UndispatchableReceiver(Some(span)), _) => *span, - (MethodViolationCode::ReferencesImplTraitInTrait(span), _) => *span, - (MethodViolationCode::ReferencesSelfOutput, Some(node)) => { - node.fn_decl().map_or(method.ident(tcx).span, |decl| decl.output.span()) + match item.kind { + // Associated consts are never object safe, as they can't have `where` bounds yet at all, + // and associated const bounds in trait objects aren't a thing yet either. + ty::AssocKind::Const => { + Some(ObjectSafetyViolation::AssocConst(item.name, item.ident(tcx).span)) + } + ty::AssocKind::Fn => virtual_call_violation_for_method(tcx, trait_def_id, item).map(|v| { + let node = tcx.hir().get_if_local(item.def_id); + // Get an accurate span depending on the violation. + let span = match (&v, node) { + (MethodViolationCode::ReferencesSelfInput(Some(span)), _) => *span, + (MethodViolationCode::UndispatchableReceiver(Some(span)), _) => *span, + (MethodViolationCode::ReferencesImplTraitInTrait(span), _) => *span, + (MethodViolationCode::ReferencesSelfOutput, Some(node)) => { + node.fn_decl().map_or(item.ident(tcx).span, |decl| decl.output.span()) + } + _ => item.ident(tcx).span, + }; + + ObjectSafetyViolation::Method(item.name, v, span) + }), + // Associated types can only be object safe if they have `Self: Sized` bounds. + ty::AssocKind::Type => { + if !tcx.features().generic_associated_types_extended + && !tcx.generics_of(item.def_id).params.is_empty() + && item.opt_rpitit_info.is_none() + { + Some(ObjectSafetyViolation::GAT(item.name, item.ident(tcx).span)) + } else { + // We will permit associated types if they are explicitly mentioned in the trait object. + // We can't check this here, as here we only check if it is guaranteed to not be possible. + None } - _ => method.ident(tcx).span, - }; - (v, span) - }) + } + } } /// Returns `Some(_)` if this method cannot be called on a trait diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 65af0bb1c4e1f..563cc257e0349 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -672,7 +672,9 @@ impl<'a, 'b, 'tcx> TypeFolder> for AssocTypeNormalizer<'a, 'b, 'tcx #[instrument(skip(self), level = "debug")] fn fold_const(&mut self, constant: ty::Const<'tcx>) -> ty::Const<'tcx> { let tcx = self.selcx.tcx(); - if tcx.lazy_normalization() || !needs_normalization(&constant, self.param_env.reveal()) { + if tcx.features().generic_const_exprs + || !needs_normalization(&constant, self.param_env.reveal()) + { constant } else { let constant = constant.super_fold_with(self); diff --git a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs index a8a74d7501abf..f8ceee5005443 100644 --- a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs +++ b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs @@ -78,7 +78,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { _ => obligation.param_env.without_const(), }; - if self.tcx.trait_solver_next() { + if self.next_trait_solver() { self.probe(|snapshot| { let mut fulfill_cx = crate::solve::FulfillmentCtxt::new(); fulfill_cx.register_predicate_obligation(self, obligation.clone()); diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs index 6d8d2103f3904..8b0973021bcc8 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs @@ -5,6 +5,7 @@ use crate::traits::ObligationCtxt; use rustc_errors::ErrorGuaranteed; use rustc_infer::infer::region_constraints::RegionConstraintData; use rustc_middle::traits::query::NoSolution; +use rustc_middle::ty::{TyCtxt, TypeFoldable}; use rustc_span::source_map::DUMMY_SP; use rustc_span::Span; @@ -24,9 +25,10 @@ impl CustomTypeOp { } } -impl<'tcx, F, R: fmt::Debug> super::TypeOp<'tcx> for CustomTypeOp +impl<'tcx, F, R> super::TypeOp<'tcx> for CustomTypeOp where F: FnOnce(&ObligationCtxt<'_, 'tcx>) -> Result, + R: fmt::Debug + TypeFoldable>, { type Output = R; /// We can't do any custom error reporting for `CustomTypeOp`, so @@ -57,12 +59,16 @@ impl fmt::Debug for CustomTypeOp { /// Executes `op` and then scrapes out all the "old style" region /// constraints that result, creating query-region-constraints. -pub fn scrape_region_constraints<'tcx, Op: super::TypeOp<'tcx, Output = R>, R>( +pub fn scrape_region_constraints<'tcx, Op, R>( infcx: &InferCtxt<'tcx>, op: impl FnOnce(&ObligationCtxt<'_, 'tcx>) -> Result, name: &'static str, span: Span, -) -> Result<(TypeOpOutput<'tcx, Op>, RegionConstraintData<'tcx>), ErrorGuaranteed> { +) -> Result<(TypeOpOutput<'tcx, Op>, RegionConstraintData<'tcx>), ErrorGuaranteed> +where + R: TypeFoldable>, + Op: super::TypeOp<'tcx, Output = R>, +{ // During NLL, we expect that nobody will register region // obligations **except** as part of a custom type op (and, at the // end of each custom type op, we scrape out the region @@ -91,6 +97,9 @@ pub fn scrape_region_constraints<'tcx, Op: super::TypeOp<'tcx, Output = R>, R>( } })?; + // Next trait solver performs operations locally, and normalize goals should resolve vars. + let value = infcx.resolve_vars_if_possible(value); + let region_obligations = infcx.take_registered_region_obligations(); let region_constraint_data = infcx.take_and_reset_region_constraints(); let region_constraints = query_response::make_query_region_constraints( diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs index 642fdec2d9ae3..9d7933e23a8b2 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs @@ -146,7 +146,7 @@ where infcx: &InferCtxt<'tcx>, span: Span, ) -> Result, ErrorGuaranteed> { - if infcx.tcx.trait_solver_next() { + if infcx.next_trait_solver() { return Ok(scrape_region_constraints( infcx, |ocx| QueryTypeOp::perform_locally_in_new_solver(ocx, self), diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 42c1b629ac242..25e5b5e17deff 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -539,7 +539,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.evaluation_probe(|this| { let goal = this.infcx.resolve_vars_if_possible((obligation.predicate, obligation.param_env)); - let mut result = if this.tcx().trait_solver_next() { + let mut result = if this.infcx.next_trait_solver() { this.evaluate_predicates_recursively_in_new_solver([obligation.clone()])? } else { this.evaluate_predicate_recursively( @@ -593,7 +593,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { where I: IntoIterator> + std::fmt::Debug, { - if self.tcx().trait_solver_next() { + if self.infcx.next_trait_solver() { self.evaluate_predicates_recursively_in_new_solver(predicates) } else { let mut result = EvaluatedToOk; diff --git a/compiler/rustc_trait_selection/src/traits/structural_match.rs b/compiler/rustc_trait_selection/src/traits/structural_match.rs index e38ae9381c1d7..420f8c5dceb50 100644 --- a/compiler/rustc_trait_selection/src/traits/structural_match.rs +++ b/compiler/rustc_trait_selection/src/traits/structural_match.rs @@ -34,24 +34,7 @@ pub fn search_for_structural_match_violation<'tcx>( tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, ) -> Option> { - ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default(), adt_const_param: false }) - .break_value() -} - -/// This method traverses the structure of `ty`, trying to find any -/// types that are not allowed to be used in a const generic. -/// -/// This is either because the type does not implement `StructuralEq` -/// and `StructuralPartialEq`, or because the type is intentionally -/// not supported in const generics (such as floats and raw pointers, -/// which are allowed in match blocks). -pub fn search_for_adt_const_param_violation<'tcx>( - span: Span, - tcx: TyCtxt<'tcx>, - ty: Ty<'tcx>, -) -> Option> { - ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default(), adt_const_param: true }) - .break_value() + ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default() }).break_value() } /// This implements the traversal over the structure of a given type to try to @@ -65,11 +48,6 @@ struct Search<'tcx> { /// Tracks ADTs previously encountered during search, so that /// we will not recur on them again. seen: FxHashSet, - - // Additionally deny things that have been allowed in patterns, - // but are not allowed in adt const params, such as floats and - // fn ptrs. - adt_const_param: bool, } impl<'tcx> Search<'tcx> { @@ -124,41 +102,29 @@ impl<'tcx> TypeVisitor> for Search<'tcx> { } ty::FnPtr(..) => { - if !self.adt_const_param { - return ControlFlow::Continue(()); - } else { - return ControlFlow::Break(ty); - } + return ControlFlow::Continue(()); } ty::RawPtr(..) => { - if !self.adt_const_param { - // structural-match ignores substructure of - // `*const _`/`*mut _`, so skip `super_visit_with`. - // - // For example, if you have: - // ``` - // struct NonStructural; - // #[derive(PartialEq, Eq)] - // struct T(*const NonStructural); - // const C: T = T(std::ptr::null()); - // ``` - // - // Even though `NonStructural` does not implement `PartialEq`, - // structural equality on `T` does not recur into the raw - // pointer. Therefore, one can still use `C` in a pattern. - return ControlFlow::Continue(()); - } else { - return ControlFlow::Break(ty); - } + // structural-match ignores substructure of + // `*const _`/`*mut _`, so skip `super_visit_with`. + // + // For example, if you have: + // ``` + // struct NonStructural; + // #[derive(PartialEq, Eq)] + // struct T(*const NonStructural); + // const C: T = T(std::ptr::null()); + // ``` + // + // Even though `NonStructural` does not implement `PartialEq`, + // structural equality on `T` does not recur into the raw + // pointer. Therefore, one can still use `C` in a pattern. + return ControlFlow::Continue(()); } ty::Float(_) => { - if !self.adt_const_param { - return ControlFlow::Continue(()); - } else { - return ControlFlow::Break(ty); - } + return ControlFlow::Continue(()); } ty::Array(..) | ty::Slice(_) | ty::Ref(..) | ty::Tuple(..) => { diff --git a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs index af8dd0da5792a..84746eba3ecf8 100644 --- a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs @@ -21,7 +21,7 @@ impl<'tcx> StructurallyNormalizeExt<'tcx> for At<'_, 'tcx> { ) -> Result, Vec>> { assert!(!ty.is_ty_var(), "should have resolved vars before calling"); - if self.infcx.tcx.trait_solver_next() { + if self.infcx.next_trait_solver() { while let ty::Alias(ty::Projection, projection_ty) = *ty.kind() { let new_infer_ty = self.infcx.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::NormalizeProjectionType, diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index e2c9c62512e7e..537405b7eb91a 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -295,7 +295,7 @@ pub fn future_trait_ref_and_outputs<'tcx>( pub fn impl_item_is_final(tcx: TyCtxt<'_>, assoc_item: &ty::AssocItem) -> bool { assoc_item.defaultness(tcx).is_final() - && tcx.impl_defaultness(assoc_item.container_id(tcx)).is_final() + && tcx.defaultness(assoc_item.container_id(tcx)).is_final() } pub enum TupleArgumentsFlag { diff --git a/compiler/rustc_traits/src/codegen.rs b/compiler/rustc_traits/src/codegen.rs index ddba03b0b12b3..5f84acc8a0473 100644 --- a/compiler/rustc_traits/src/codegen.rs +++ b/compiler/rustc_traits/src/codegen.rs @@ -55,7 +55,7 @@ pub fn codegen_select_candidate<'tcx>( // Currently, we use a fulfillment context to completely resolve // all nested obligations. This is because they can inform the // inference of the impl's type parameters. - let mut fulfill_cx = >::new(tcx); + let mut fulfill_cx = >::new(&infcx); let impl_source = selection.map(|predicate| { fulfill_cx.register_predicate_obligation(&infcx, predicate); }); diff --git a/compiler/rustc_traits/src/evaluate_obligation.rs b/compiler/rustc_traits/src/evaluate_obligation.rs index f5b2753b7973d..73756caf37272 100644 --- a/compiler/rustc_traits/src/evaluate_obligation.rs +++ b/compiler/rustc_traits/src/evaluate_obligation.rs @@ -16,7 +16,7 @@ fn evaluate_obligation<'tcx>( tcx: TyCtxt<'tcx>, canonical_goal: CanonicalPredicateGoal<'tcx>, ) -> Result { - assert!(!tcx.trait_solver_next()); + assert!(!tcx.next_trait_solver_globally()); debug!("evaluate_obligation(canonical_goal={:#?})", canonical_goal); // HACK This bubble is required for this tests to pass: // impl-trait/issue99642.rs diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs index 57b4183d33671..658ab03c0f48c 100644 --- a/compiler/rustc_ty_utils/src/assoc.rs +++ b/compiler/rustc_ty_utils/src/assoc.rs @@ -297,8 +297,8 @@ fn associated_type_for_impl_trait_in_trait( // Copy visility of the containing function. trait_assoc_ty.visibility(tcx.visibility(fn_def_id)); - // Copy impl_defaultness of the containing function. - trait_assoc_ty.impl_defaultness(tcx.impl_defaultness(fn_def_id)); + // Copy defaultness of the containing function. + trait_assoc_ty.defaultness(tcx.defaultness(fn_def_id)); // Copy type_of of the opaque. trait_assoc_ty.type_of(ty::EarlyBinder::bind(tcx.mk_opaque( @@ -393,8 +393,8 @@ fn associated_type_for_impl_trait_in_impl( // Copy visility of the containing function. impl_assoc_ty.visibility(tcx.visibility(impl_fn_def_id)); - // Copy impl_defaultness of the containing function. - impl_assoc_ty.impl_defaultness(tcx.impl_defaultness(impl_fn_def_id)); + // Copy defaultness of the containing function. + impl_assoc_ty.defaultness(tcx.defaultness(impl_fn_def_id)); // Copy generics_of the trait's associated item but the impl as the parent. // FIXME(-Zlower-impl-trait-in-trait-to-assoc-ty) resolves to the trait instead of the impl diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index 36a20c78fcc7b..dd911239ac451 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -204,7 +204,7 @@ fn resolve_associated_item<'tcx>( } else { // All other methods are default methods of the `Future` trait. // (this assumes that `ImplSource::Future` is only used for methods on `Future`) - debug_assert!(tcx.impl_defaultness(trait_item_id).has_value()); + debug_assert!(tcx.defaultness(trait_item_id).has_value()); Some(Instance::new(trait_item_id, rcvr_substs)) } } diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index 12ad8b0842f5e..fe2d1fba7fe6e 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -75,13 +75,13 @@ fn sized_constraint_for_ty<'tcx>( result } -fn impl_defaultness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Defaultness { +fn defaultness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Defaultness { match tcx.hir().get_by_def_id(def_id) { hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(impl_), .. }) => impl_.defaultness, hir::Node::ImplItem(hir::ImplItem { defaultness, .. }) | hir::Node::TraitItem(hir::TraitItem { defaultness, .. }) => *defaultness, node => { - bug!("`impl_defaultness` called on {:?}", node); + bug!("`defaultness` called on {:?}", node); } } } @@ -186,6 +186,7 @@ fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> { kind: hir::TraitItemKind::Const(..), .. }) | hir::Node::AnonConst(_) + | hir::Node::ConstBlock(_) | hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Const(..), .. }) | hir::Node::ImplItem(hir::ImplItem { kind: @@ -574,7 +575,7 @@ pub fn provide(providers: &mut Providers) { param_env_reveal_all_normalized, instance_def_size_estimate, issue33140_self_ty, - impl_defaultness, + defaultness, unsizing_params_for_adt, ..*providers }; diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 47661a3d38429..d89cdff8e366c 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -2355,7 +2355,7 @@ impl Vec { let len = self.len(); if new_len > len { - self.extend_with(new_len - len, ExtendElement(value)) + self.extend_with(new_len - len, value) } else { self.truncate(new_len); } @@ -2469,26 +2469,10 @@ impl Vec<[T; N], A> { } } -// This code generalizes `extend_with_{element,default}`. -trait ExtendWith { - fn next(&mut self) -> T; - fn last(self) -> T; -} - -struct ExtendElement(T); -impl ExtendWith for ExtendElement { - fn next(&mut self) -> T { - self.0.clone() - } - fn last(self) -> T { - self.0 - } -} - -impl Vec { +impl Vec { #[cfg(not(no_global_oom_handling))] - /// Extend the vector by `n` values, using the given generator. - fn extend_with>(&mut self, n: usize, mut value: E) { + /// Extend the vector by `n` clones of value. + fn extend_with(&mut self, n: usize, value: T) { self.reserve(n); unsafe { @@ -2500,15 +2484,15 @@ impl Vec { // Write all elements except the last one for _ in 1..n { - ptr::write(ptr, value.next()); + ptr::write(ptr, value.clone()); ptr = ptr.add(1); - // Increment the length in every step in case next() panics + // Increment the length in every step in case clone() panics local_len.increment_len(1); } if n > 0 { // We can write the last element directly without cloning needlessly - ptr::write(ptr, value.last()); + ptr::write(ptr, value); local_len.increment_len(1); } diff --git a/library/alloc/src/vec/spec_from_elem.rs b/library/alloc/src/vec/spec_from_elem.rs index ff364c033ee98..da43d17bf3624 100644 --- a/library/alloc/src/vec/spec_from_elem.rs +++ b/library/alloc/src/vec/spec_from_elem.rs @@ -3,7 +3,7 @@ use core::ptr; use crate::alloc::Allocator; use crate::raw_vec::RawVec; -use super::{ExtendElement, IsZero, Vec}; +use super::{IsZero, Vec}; // Specialization trait used for Vec::from_elem pub(super) trait SpecFromElem: Sized { @@ -13,7 +13,7 @@ pub(super) trait SpecFromElem: Sized { impl SpecFromElem for T { default fn from_elem(elem: Self, n: usize, alloc: A) -> Vec { let mut v = Vec::with_capacity_in(n, alloc); - v.extend_with(n, ExtendElement(elem)); + v.extend_with(n, elem); v } } @@ -25,7 +25,7 @@ impl SpecFromElem for T { return Vec { buf: RawVec::with_capacity_zeroed_in(n, alloc), len: n }; } let mut v = Vec::with_capacity_in(n, alloc); - v.extend_with(n, ExtendElement(elem)); + v.extend_with(n, elem); v } } diff --git a/library/core/src/any.rs b/library/core/src/any.rs index 7969f4055dd2b..09f52d692d0aa 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -153,6 +153,7 @@ #![stable(feature = "rust1", since = "1.0.0")] use crate::fmt; +use crate::hash; use crate::intrinsics; /////////////////////////////////////////////////////////////////////////////// @@ -662,10 +663,10 @@ impl dyn Any + Send + Sync { /// While `TypeId` implements `Hash`, `PartialOrd`, and `Ord`, it is worth /// noting that the hashes and ordering will vary between Rust releases. Beware /// of relying on them inside of your code! -#[derive(Clone, Copy, Debug, Hash, Eq, PartialOrd, Ord)] +#[derive(Clone, Copy, Debug, Eq, PartialOrd, Ord)] #[stable(feature = "rust1", since = "1.0.0")] pub struct TypeId { - t: u64, + t: u128, } #[stable(feature = "rust1", since = "1.0.0")] @@ -696,7 +697,31 @@ impl TypeId { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_type_id", issue = "77125")] pub const fn of() -> TypeId { - TypeId { t: intrinsics::type_id::() } + #[cfg(bootstrap)] + let t = intrinsics::type_id::() as u128; + #[cfg(not(bootstrap))] + let t: u128 = intrinsics::type_id::(); + TypeId { t } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl hash::Hash for TypeId { + #[inline] + fn hash(&self, state: &mut H) { + // We only hash the lower 64 bits of our (128 bit) internal numeric ID, + // because: + // - The hashing algorithm which backs `TypeId` is expected to be + // unbiased and high quality, meaning further mixing would be somewhat + // redundant compared to choosing (the lower) 64 bits arbitrarily. + // - `Hasher::finish` returns a u64 anyway, so the extra entropy we'd + // get from hashing the full value would probably not be useful + // (especially given the previous point about the lower 64 bits being + // high quality on their own). + // - It is correct to do so -- only hashing a subset of `self` is still + // with an `Eq` implementation that considers the entire value, as + // ours does. + (self.t as u64).hash(state); } } diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index 744767aae44ca..c1cc892eb8607 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -59,7 +59,7 @@ //! [`borrow`](`RefCell::borrow`), and a mutable borrow (`&mut T`) can be obtained with //! [`borrow_mut`](`RefCell::borrow_mut`). When these functions are called, they first verify that //! Rust's borrow rules will be satisfied: any number of immutable borrows are allowed or a -//! single immutable borrow is allowed, but never both. If a borrow is attempted that would violate +//! single mutable borrow is allowed, but never both. If a borrow is attempted that would violate //! these rules, the thread will panic. //! //! The corresponding [`Sync`] version of `RefCell` is [`RwLock`]. diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 6dca1fe1e6961..9b8612485ac1e 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -1057,8 +1057,25 @@ extern "rust-intrinsic" { #[rustc_const_unstable(feature = "const_type_id", issue = "77125")] #[rustc_safe_intrinsic] #[rustc_nounwind] + #[cfg(bootstrap)] pub fn type_id() -> u64; + /// Gets an identifier which is globally unique to the specified type. This + /// function will return the same value for a type regardless of whichever + /// crate it is invoked in. + /// + /// Note that, unlike most intrinsics, this is safe to call; + /// it does not require an `unsafe` block. + /// Therefore, implementations must not require the user to uphold + /// any safety invariants. + /// + /// The stabilized version of this intrinsic is [`core::any::TypeId::of`]. + #[rustc_const_unstable(feature = "const_type_id", issue = "77125")] + #[rustc_safe_intrinsic] + #[rustc_nounwind] + #[cfg(not(bootstrap))] + pub fn type_id() -> u128; + /// A guard for unsafe functions that cannot ever be executed if `T` is uninhabited: /// This will statically either panic, or do nothing. /// diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 2d2d5d4917588..9a541ccaeacbc 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -205,6 +205,20 @@ pub trait StructuralPartialEq { // Empty. } +marker_impls! { + #[unstable(feature = "structural_match", issue = "31434")] + StructuralPartialEq for + usize, u8, u16, u32, u64, u128, + isize, i8, i16, i32, i64, i128, + bool, + char, + str /* Technically requires `[u8]: StructuralEq` */, + (), + {T, const N: usize} [T; N], + {T} [T], + {T: ?Sized} &T, +} + /// Required trait for constants used in pattern matches. /// /// Any type that derives `Eq` automatically implements this trait, *regardless* @@ -267,6 +281,7 @@ marker_impls! { bool, char, str /* Technically requires `[u8]: StructuralEq` */, + (), {T, const N: usize} [T; N], {T} [T], {T: ?Sized} &T, @@ -974,7 +989,8 @@ pub trait PointerLike {} #[lang = "const_param_ty"] #[unstable(feature = "adt_const_params", issue = "95174")] #[rustc_on_unimplemented(message = "`{Self}` can't be used as a const parameter type")] -pub trait ConstParamTy: StructuralEq {} +#[allow(multiple_supertrait_upcastable)] +pub trait ConstParamTy: StructuralEq + StructuralPartialEq {} /// Derive macro generating an impl of the trait `ConstParamTy`. #[rustc_builtin_macro] @@ -983,8 +999,7 @@ pub macro ConstParamTy($item:item) { /* compiler built-in */ } -// FIXME(generic_const_parameter_types): handle `ty::FnDef`/`ty::Closure` -// FIXME(generic_const_parameter_types): handle `ty::Tuple` +// FIXME(adt_const_params): handle `ty::FnDef`/`ty::Closure` marker_impls! { #[unstable(feature = "adt_const_params", issue = "95174")] ConstParamTy for @@ -998,6 +1013,11 @@ marker_impls! { {T: ?Sized + ConstParamTy} &T, } +// FIXME(adt_const_params): Add to marker_impls call above once not in bootstrap +#[unstable(feature = "adt_const_params", issue = "95174")] +#[cfg(not(bootstrap))] +impl ConstParamTy for () {} + /// A common trait implemented by all function pointers. #[unstable( feature = "fn_ptr_trait", diff --git a/library/core/src/mem/transmutability.rs b/library/core/src/mem/transmutability.rs index 87ae30619c63b..a6f792ed0e3e9 100644 --- a/library/core/src/mem/transmutability.rs +++ b/library/core/src/mem/transmutability.rs @@ -1,3 +1,5 @@ +use crate::marker::ConstParamTy; + /// Are values of a type transmutable into values of another type? /// /// This trait is implemented on-the-fly by the compiler for types `Src` and `Self` when the bits of @@ -33,6 +35,9 @@ pub struct Assume { pub validity: bool, } +#[unstable(feature = "transmutability", issue = "99571")] +impl ConstParamTy for Assume {} + impl Assume { /// Do not assume that *you* have ensured any safety properties are met. #[unstable(feature = "transmutability", issue = "99571")] diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 4a035ad61e107..d050d21c8c575 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -403,6 +403,7 @@ impl f32 { /// and the stability of its representation over Rust versions /// and target platforms isn't guaranteed. #[stable(feature = "assoc_int_consts", since = "1.43.0")] + #[rustc_diagnostic_item = "f32_nan"] pub const NAN: f32 = 0.0_f32 / 0.0_f32; /// Infinity (∞). #[stable(feature = "assoc_int_consts", since = "1.43.0")] diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 3aafc435f1e17..d9a738191f7f1 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -401,6 +401,7 @@ impl f64 { /// This constant isn't guaranteed to equal to any specific NaN bitpattern, /// and the stability of its representation over Rust versions /// and target platforms isn't guaranteed. + #[rustc_diagnostic_item = "f64_nan"] #[stable(feature = "assoc_int_consts", since = "1.43.0")] pub const NAN: f64 = 0.0_f64 / 0.0_f64; /// Infinity (∞). diff --git a/library/core/src/option.rs b/library/core/src/option.rs index ec1ef3cf43d12..9b6ff76b24047 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -1138,7 +1138,7 @@ impl Option { /// Computes a default function result (if none), or /// applies a different function to the contained value (if any). /// - /// # Examples + /// # Basic examples /// /// ``` /// let k = 21; @@ -1149,6 +1149,25 @@ impl Option { /// let x: Option<&str> = None; /// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 42); /// ``` + /// + /// # Handling a Result-based fallback + /// + /// A somewhat common occurrence when dealing with optional values + /// in combination with [`Result`] is the case where one wants to invoke + /// a fallible fallback if the option is not present. This example + /// parses a command line argument (if present), or the contents of a file to + /// an integer. However, unlike accessing the command line argument, reading + /// the file is fallible, so it must be wrapped with `Ok`. + /// + /// ```no_run + /// # fn main() -> Result<(), Box> { + /// let v: u64 = std::env::args() + /// .nth(1) + /// .map_or_else(|| std::fs::read_to_string("/etc/someconfig.conf"), Ok)? + /// .parse()?; + /// # Ok(()) + /// # } + /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn map_or_else(self, default: D, f: F) -> U @@ -1383,6 +1402,7 @@ impl Option { /// let item_2_0 = arr_2d.get(2).and_then(|row| row.get(0)); /// assert_eq!(item_2_0, None); /// ``` + #[doc(alias = "flatmap")] #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn and_then(self, f: F) -> Option diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index c6f43857887c9..e753647ff7868 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -106,7 +106,7 @@ impl *mut T { /// with [`cast_mut`] on `*const T` and may have documentation value if used instead of implicit /// coercion. /// - /// [`cast_mut`]: #method.cast_mut + /// [`cast_mut`]: pointer::cast_mut #[stable(feature = "ptr_const_cast", since = "1.65.0")] #[rustc_const_stable(feature = "ptr_const_cast", since = "1.65.0")] #[inline(always)] @@ -117,7 +117,7 @@ impl *mut T { /// Casts a pointer to its raw bits. /// /// This is equivalent to `as usize`, but is more specific to enhance readability. - /// The inverse method is [`from_bits`](#method.from_bits-1). + /// The inverse method is [`from_bits`](pointer#method.from_bits-1). /// /// In particular, `*p as usize` and `p as usize` will both compile for /// pointers to numeric types but do very different things, so using this @@ -153,7 +153,7 @@ impl *mut T { /// Creates a pointer from its raw bits. /// /// This is equivalent to `as *mut T`, but is more specific to enhance readability. - /// The inverse method is [`to_bits`](#method.to_bits-1). + /// The inverse method is [`to_bits`](pointer#method.to_bits-1). /// /// # Examples /// @@ -303,7 +303,7 @@ impl *mut T { /// /// For the mutable counterpart see [`as_mut`]. /// - /// [`as_uninit_ref`]: #method.as_uninit_ref-1 + /// [`as_uninit_ref`]: pointer#method.as_uninit_ref-1 /// [`as_mut`]: #method.as_mut /// /// # Safety @@ -369,7 +369,7 @@ impl *mut T { /// /// For the mutable counterpart see [`as_uninit_mut`]. /// - /// [`as_ref`]: #method.as_ref-1 + /// [`as_ref`]: pointer#method.as_ref-1 /// [`as_uninit_mut`]: #method.as_uninit_mut /// /// # Safety @@ -624,7 +624,7 @@ impl *mut T { /// For the shared counterpart see [`as_ref`]. /// /// [`as_uninit_mut`]: #method.as_uninit_mut - /// [`as_ref`]: #method.as_ref-1 + /// [`as_ref`]: pointer#method.as_ref-1 /// /// # Safety /// @@ -689,7 +689,7 @@ impl *mut T { /// For the shared counterpart see [`as_uninit_ref`]. /// /// [`as_mut`]: #method.as_mut - /// [`as_uninit_ref`]: #method.as_uninit_ref-1 + /// [`as_uninit_ref`]: pointer#method.as_uninit_ref-1 /// /// # Safety /// @@ -779,7 +779,7 @@ impl *mut T { /// /// This function is the inverse of [`offset`]. /// - /// [`offset`]: #method.offset-1 + /// [`offset`]: pointer#method.offset-1 /// /// # Safety /// @@ -2051,7 +2051,7 @@ impl *mut [T] { /// /// For the mutable counterpart see [`as_uninit_slice_mut`]. /// - /// [`as_ref`]: #method.as_ref-1 + /// [`as_ref`]: pointer#method.as_ref-1 /// [`as_uninit_slice_mut`]: #method.as_uninit_slice_mut /// /// # Safety diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index 7043ab5ff2b41..b63fd5c9095fc 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -2,6 +2,7 @@ use crate::fmt; use crate::marker::{PhantomData, Unpin}; +use crate::ptr; /// A `RawWaker` allows the implementor of a task executor to create a [`Waker`] /// which provides customized wakeup behavior. @@ -322,6 +323,45 @@ impl Waker { Waker { waker } } + /// Creates a new `Waker` that does nothing when `wake` is called. + /// + /// This is mostly useful for writing tests that need a [`Context`] to poll + /// some futures, but are not expecting those futures to wake the waker or + /// do not need to do anything specific if it happens. + /// + /// # Examples + /// + /// ``` + /// #![feature(noop_waker)] + /// + /// use std::future::Future; + /// use std::task; + /// + /// let waker = task::Waker::noop(); + /// let mut cx = task::Context::from_waker(&waker); + /// + /// let mut future = Box::pin(async { 10 }); + /// assert_eq!(future.as_mut().poll(&mut cx), task::Poll::Ready(10)); + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "noop_waker", issue = "98286")] + pub const fn noop() -> Waker { + const VTABLE: RawWakerVTable = RawWakerVTable::new( + // Cloning just returns a new no-op raw waker + |_| RAW, + // `wake` does nothing + |_| {}, + // `wake_by_ref` does nothing + |_| {}, + // Dropping does nothing as we don't allocate anything + |_| {}, + ); + const RAW: RawWaker = RawWaker::new(ptr::null(), &VTABLE); + + Waker { waker: RAW } + } + /// Get a reference to the underlying [`RawWaker`]. #[inline] #[must_use] diff --git a/library/core/src/tuple.rs b/library/core/src/tuple.rs index a1388dfeee643..ac8d04a82860e 100644 --- a/library/core/src/tuple.rs +++ b/library/core/src/tuple.rs @@ -1,6 +1,9 @@ // See src/libstd/primitive_docs.rs for documentation. use crate::cmp::Ordering::{self, *}; +#[cfg(not(bootstrap))] +use crate::marker::ConstParamTy; +use crate::marker::{StructuralEq, StructuralPartialEq}; // Recursive macro for implementing n-ary tuple functions and operations // @@ -45,6 +48,28 @@ macro_rules! tuple_impls { {} } + maybe_tuple_doc! { + $($T)+ @ + #[unstable(feature = "structural_match", issue = "31434")] + #[cfg(not(bootstrap))] + impl<$($T: ConstParamTy),+> ConstParamTy for ($($T,)+) + {} + } + + maybe_tuple_doc! { + $($T)+ @ + #[unstable(feature = "structural_match", issue = "31434")] + impl<$($T),+> StructuralPartialEq for ($($T,)+) + {} + } + + maybe_tuple_doc! { + $($T)+ @ + #[unstable(feature = "structural_match", issue = "31434")] + impl<$($T),+> StructuralEq for ($($T,)+) + {} + } + maybe_tuple_doc! { $($T)+ @ #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/core/tests/manually_drop.rs b/library/core/tests/manually_drop.rs index 9eac279733af9..22d72d219a758 100644 --- a/library/core/tests/manually_drop.rs +++ b/library/core/tests/manually_drop.rs @@ -1,3 +1,5 @@ +#![cfg_attr(not(bootstrap), allow(undropped_manually_drops))] + use core::mem::ManuallyDrop; #[test] diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 72d910bf95746..4b52894c509da 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -17,7 +17,7 @@ cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] } panic_unwind = { path = "../panic_unwind", optional = true } panic_abort = { path = "../panic_abort" } core = { path = "../core", public = true } -libc = { version = "0.2.143", default-features = false, features = ['rustc-dep-of-std'], public = true } +libc = { version = "0.2.146", default-features = false, features = ['rustc-dep-of-std'], public = true } compiler_builtins = { version = "0.1.92" } profiler_builtins = { path = "../profiler_builtins", optional = true } unwind = { path = "../unwind" } diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 318a46d1b637e..d53f1a2b2fff1 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -240,6 +240,7 @@ #![feature(allocator_internals)] #![feature(allow_internal_unsafe)] #![feature(allow_internal_unstable)] +#![feature(c_str_literals)] #![feature(c_unwind)] #![feature(cfg_target_thread_local)] #![feature(concat_idents)] diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs index 729c63d184f2c..2b40b672d9f0a 100644 --- a/library/std/src/os/unix/process.rs +++ b/library/std/src/os/unix/process.rs @@ -15,7 +15,7 @@ use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; use cfg_if::cfg_if; cfg_if! { - if #[cfg(any(target_os = "vxworks", target_os = "espidf", target_os = "horizon"))] { + if #[cfg(any(target_os = "vxworks", target_os = "espidf", target_os = "horizon", target_os = "vita"))] { type UserId = u16; type GroupId = u16; } else if #[cfg(target_os = "nto")] { diff --git a/library/std/src/sync/once.rs b/library/std/src/sync/once.rs index 1b17c31089ff2..8c46080e43b38 100644 --- a/library/std/src/sync/once.rs +++ b/library/std/src/sync/once.rs @@ -91,7 +91,7 @@ impl Once { /// return). /// /// If the given closure recursively invokes `call_once` on the same [`Once`] - /// instance the exact behavior is not specified, allowed outcomes are + /// instance, the exact behavior is not specified: allowed outcomes are /// a panic or a deadlock. /// /// # Examples diff --git a/library/std/src/sys/common/thread_local/fast_local.rs b/library/std/src/sys/common/thread_local/fast_local.rs index 447044a798ba6..bc5da1a189677 100644 --- a/library/std/src/sys/common/thread_local/fast_local.rs +++ b/library/std/src/sys/common/thread_local/fast_local.rs @@ -33,20 +33,21 @@ pub macro thread_local_inner { // 1 == dtor registered, dtor not run // 2 == dtor registered and is running or has run #[thread_local] - static mut STATE: $crate::primitive::u8 = 0; + static STATE: $crate::cell::Cell<$crate::primitive::u8> = $crate::cell::Cell::new(0); + // Safety: Performs `drop_in_place(ptr as *mut $t)`, and requires + // all that comes with it. unsafe extern "C" fn destroy(ptr: *mut $crate::primitive::u8) { - let ptr = ptr as *mut $t; - - unsafe { - $crate::debug_assert_eq!(STATE, 1); - STATE = 2; - $crate::ptr::drop_in_place(ptr); - } + $crate::thread::local_impl::abort_on_dtor_unwind(|| { + let old_state = STATE.replace(2); + $crate::debug_assert_eq!(old_state, 1); + // Safety: safety requirement is passed on to caller. + unsafe { $crate::ptr::drop_in_place(ptr.cast::<$t>()); } + }); } unsafe { - match STATE { + match STATE.get() { // 0 == we haven't registered a destructor, so do // so now. 0 => { @@ -54,7 +55,7 @@ pub macro thread_local_inner { $crate::ptr::addr_of_mut!(VAL) as *mut $crate::primitive::u8, destroy, ); - STATE = 1; + STATE.set(1); $crate::option::Option::Some(&VAL) } // 1 == the destructor is registered and the value @@ -148,7 +149,6 @@ impl fmt::Debug for Key { f.debug_struct("Key").finish_non_exhaustive() } } - impl Key { pub const fn new() -> Key { Key { inner: LazyKeyInner::new(), dtor_state: Cell::new(DtorState::Unregistered) } diff --git a/library/std/src/sys/common/thread_local/mod.rs b/library/std/src/sys/common/thread_local/mod.rs index 77f645883102c..975509bd412b0 100644 --- a/library/std/src/sys/common/thread_local/mod.rs +++ b/library/std/src/sys/common/thread_local/mod.rs @@ -101,3 +101,24 @@ mod lazy { } } } + +/// Run a callback in a scenario which must not unwind (such as a `extern "C" +/// fn` declared in a user crate). If the callback unwinds anyway, then +/// `rtabort` with a message about thread local panicking on drop. +#[inline] +pub fn abort_on_dtor_unwind(f: impl FnOnce()) { + // Using a guard like this is lower cost. + let guard = DtorUnwindGuard; + f(); + core::mem::forget(guard); + + struct DtorUnwindGuard; + impl Drop for DtorUnwindGuard { + #[inline] + fn drop(&mut self) { + // This is not terribly descriptive, but it doesn't need to be as we'll + // already have printed a panic message at this point. + rtabort!("thread local panicked on drop"); + } + } +} diff --git a/library/std/src/sys/unix/args.rs b/library/std/src/sys/unix/args.rs index 9ed4d9c1e0dd3..0efe2570d6775 100644 --- a/library/std/src/sys/unix/args.rs +++ b/library/std/src/sys/unix/args.rs @@ -242,13 +242,15 @@ mod imp { let mut res = Vec::new(); unsafe { - let process_info_sel = sel_registerName("processInfo\0".as_ptr()); - let arguments_sel = sel_registerName("arguments\0".as_ptr()); - let utf8_sel = sel_registerName("UTF8String\0".as_ptr()); - let count_sel = sel_registerName("count\0".as_ptr()); - let object_at_sel = sel_registerName("objectAtIndex:\0".as_ptr()); - - let klass = objc_getClass("NSProcessInfo\0".as_ptr()); + let process_info_sel = + sel_registerName(c"processInfo".as_ptr() as *const libc::c_uchar); + let arguments_sel = sel_registerName(c"arguments".as_ptr() as *const libc::c_uchar); + let utf8_sel = sel_registerName(c"UTF8String".as_ptr() as *const libc::c_uchar); + let count_sel = sel_registerName(c"count".as_ptr() as *const libc::c_uchar); + let object_at_sel = + sel_registerName(c"objectAtIndex:".as_ptr() as *const libc::c_uchar); + + let klass = objc_getClass(c"NSProcessInfo".as_ptr() as *const libc::c_uchar); let info = objc_msgSend(klass, process_info_sel); let args = objc_msgSend(info, arguments_sel); diff --git a/library/std/src/sys/unix/fd.rs b/library/std/src/sys/unix/fd.rs index cb630eede6da0..69c93c9200367 100644 --- a/library/std/src/sys/unix/fd.rs +++ b/library/std/src/sys/unix/fd.rs @@ -402,7 +402,10 @@ impl FileDesc { } } #[cfg(any( - all(target_env = "newlib", not(any(target_os = "espidf", target_os = "horizon"))), + all( + target_env = "newlib", + not(any(target_os = "espidf", target_os = "horizon", target_os = "vita")) + ), target_os = "solaris", target_os = "illumos", target_os = "emscripten", @@ -424,10 +427,10 @@ impl FileDesc { Ok(()) } } - #[cfg(any(target_os = "espidf", target_os = "horizon"))] + #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))] pub fn set_cloexec(&self) -> io::Result<()> { - // FD_CLOEXEC is not supported in ESP-IDF and Horizon OS but there's no need to, - // because neither supports spawning processes. + // FD_CLOEXEC is not supported in ESP-IDF, Horizon OS and Vita but there's no need to, + // because none of them supports spawning processes. Ok(()) } diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs index 09e9ae2720f5b..d2fb6238387a1 100644 --- a/library/std/src/sys/unix/fs.rs +++ b/library/std/src/sys/unix/fs.rs @@ -15,6 +15,7 @@ use crate::mem; target_os = "redox", target_os = "illumos", target_os = "nto", + target_os = "vita", ))] use crate::mem::MaybeUninit; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd}; @@ -58,6 +59,7 @@ use libc::fstatat64; target_os = "redox", target_os = "illumos", target_os = "nto", + target_os = "vita", ))] use libc::readdir as readdir64; #[cfg(target_os = "linux")] @@ -74,6 +76,7 @@ use libc::readdir64_r; target_os = "fuchsia", target_os = "redox", target_os = "nto", + target_os = "vita", )))] use libc::readdir_r as readdir64_r; #[cfg(target_os = "android")] @@ -283,6 +286,7 @@ unsafe impl Sync for Dir {} target_os = "fuchsia", target_os = "redox", target_os = "nto", + target_os = "vita" ))] pub struct DirEntry { dir: Arc, @@ -304,10 +308,16 @@ pub struct DirEntry { target_os = "fuchsia", target_os = "redox", target_os = "nto", + target_os = "vita", ))] struct dirent64_min { d_ino: u64, - #[cfg(not(any(target_os = "solaris", target_os = "illumos", target_os = "nto")))] + #[cfg(not(any( + target_os = "solaris", + target_os = "illumos", + target_os = "nto", + target_os = "vita" + )))] d_type: u8, } @@ -319,6 +329,7 @@ struct dirent64_min { target_os = "fuchsia", target_os = "redox", target_os = "nto", + target_os = "vita", )))] pub struct DirEntry { dir: Arc, @@ -520,6 +531,7 @@ impl FileAttr { target_os = "macos", target_os = "ios", target_os = "watchos", + target_os = "vita", )))] pub fn created(&self) -> io::Result { cfg_has_statx! { @@ -541,6 +553,11 @@ impl FileAttr { currently", )) } + + #[cfg(target_os = "vita")] + pub fn created(&self) -> io::Result { + Ok(SystemTime::new(self.stat.st_ctime as i64, 0)) + } } #[cfg(target_os = "nto")] @@ -645,6 +662,7 @@ impl Iterator for ReadDir { target_os = "redox", target_os = "illumos", target_os = "nto", + target_os = "vita", ))] fn next(&mut self) -> Option> { if self.end_of_stream { @@ -725,6 +743,7 @@ impl Iterator for ReadDir { continue; } + #[cfg(not(target_os = "vita"))] let entry = dirent64_min { d_ino: *offset_ptr!(entry_ptr, d_ino) as u64, #[cfg(not(any( @@ -735,6 +754,9 @@ impl Iterator for ReadDir { d_type: *offset_ptr!(entry_ptr, d_type) as u8, }; + #[cfg(target_os = "vita")] + let entry = dirent64_min { d_ino: 0u64 }; + return Some(Ok(DirEntry { entry, name: name.to_owned(), @@ -752,6 +774,7 @@ impl Iterator for ReadDir { target_os = "redox", target_os = "illumos", target_os = "nto", + target_os = "vita", )))] fn next(&mut self) -> Option> { if self.end_of_stream { @@ -842,6 +865,7 @@ impl DirEntry { target_os = "haiku", target_os = "vxworks", target_os = "nto", + target_os = "vita", ))] pub fn file_type(&self) -> io::Result { self.metadata().map(|m| m.file_type()) @@ -853,6 +877,7 @@ impl DirEntry { target_os = "haiku", target_os = "vxworks", target_os = "nto", + target_os = "vita", )))] pub fn file_type(&self) -> io::Result { match self.entry.d_type { @@ -939,6 +964,7 @@ impl DirEntry { target_os = "fuchsia", target_os = "redox", target_os = "nto", + target_os = "vita", )))] fn name_cstr(&self) -> &CStr { unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()) } @@ -951,6 +977,7 @@ impl DirEntry { target_os = "fuchsia", target_os = "redox", target_os = "nto", + target_os = "vita", ))] fn name_cstr(&self) -> &CStr { &self.name @@ -1063,7 +1090,7 @@ impl File { cfg_has_statx! { if let Some(ret) = unsafe { try_statx( fd, - b"\0" as *const _ as *const c_char, + c"".as_ptr() as *const c_char, libc::AT_EMPTY_PATH | libc::AT_STATX_SYNC_AS_STAT, libc::STATX_ALL, ) } { @@ -1543,7 +1570,7 @@ pub fn link(original: &Path, link: &Path) -> io::Result<()> { run_path_with_cstr(original, |original| { run_path_with_cstr(link, |link| { cfg_if::cfg_if! { - if #[cfg(any(target_os = "vxworks", target_os = "redox", target_os = "android", target_os = "espidf", target_os = "horizon"))] { + if #[cfg(any(target_os = "vxworks", target_os = "redox", target_os = "android", target_os = "espidf", target_os = "horizon", target_os = "vita"))] { // VxWorks, Redox and ESP-IDF lack `linkat`, so use `link` instead. POSIX leaves // it implementation-defined whether `link` follows symlinks, so rely on the // `symlink_hard_link` test in library/std/src/fs/tests.rs to check the behavior. @@ -1666,6 +1693,8 @@ fn open_to_and_set_permissions( .truncate(true) .open(to)?; let writer_metadata = writer.metadata()?; + // fchmod is broken on vita + #[cfg(not(target_os = "vita"))] if writer_metadata.is_file() { // Set the correct file permissions, in case the file already existed. // Don't set the permissions on already existing non-files like @@ -1844,11 +1873,12 @@ pub fn chroot(dir: &Path) -> io::Result<()> { pub use remove_dir_impl::remove_dir_all; -// Fallback for REDOX, ESP-ID, Horizon, and Miri +// Fallback for REDOX, ESP-ID, Horizon, Vita and Miri #[cfg(any( target_os = "redox", target_os = "espidf", target_os = "horizon", + target_os = "vita", target_os = "nto", miri ))] @@ -1861,6 +1891,7 @@ mod remove_dir_impl { target_os = "redox", target_os = "espidf", target_os = "horizon", + target_os = "vita", target_os = "nto", miri )))] diff --git a/library/std/src/sys/unix/mod.rs b/library/std/src/sys/unix/mod.rs index bb9e65e68e5e8..24566d96bcd61 100644 --- a/library/std/src/sys/unix/mod.rs +++ b/library/std/src/sys/unix/mod.rs @@ -1,6 +1,5 @@ #![allow(missing_docs, nonstandard_style)] -use crate::ffi::CStr; use crate::io::ErrorKind; pub use self::rand::hashmap_random_keys; @@ -75,7 +74,7 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { // thread-id for the main thread and so renaming the main thread will rename the // process and we only want to enable this on platforms we've tested. if cfg!(target_os = "macos") { - thread::Thread::set_name(&CStr::from_bytes_with_nul_unchecked(b"main\0")); + thread::Thread::set_name(&c"main"); } unsafe fn sanitize_standard_fds() { @@ -121,7 +120,7 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { if pfd.revents & libc::POLLNVAL == 0 { continue; } - if open64("/dev/null\0".as_ptr().cast(), libc::O_RDWR, 0) == -1 { + if open64(c"/dev/null".as_ptr().cast(), libc::O_RDWR, 0) == -1 { // If the stream is closed but we failed to reopen it, abort the // process. Otherwise we wouldn't preserve the safety of // operations on the corresponding Rust object Stdin, Stdout, or @@ -151,7 +150,7 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { use libc::open64; for fd in 0..3 { if libc::fcntl(fd, libc::F_GETFD) == -1 && errno() == libc::EBADF { - if open64("/dev/null\0".as_ptr().cast(), libc::O_RDWR, 0) == -1 { + if open64(c"/dev/null".as_ptr().cast(), libc::O_RDWR, 0) == -1 { // If the stream is closed but we failed to reopen it, abort the // process. Otherwise we wouldn't preserve the safety of // operations on the corresponding Rust object Stdin, Stdout, or @@ -164,12 +163,7 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { } unsafe fn reset_sigpipe(#[allow(unused_variables)] sigpipe: u8) { - #[cfg(not(any( - target_os = "emscripten", - target_os = "fuchsia", - target_os = "horizon", - target_os = "vita" - )))] + #[cfg(not(any(target_os = "emscripten", target_os = "fuchsia", target_os = "horizon")))] { // We don't want to add this as a public type to std, nor do we // want to `include!` a file from the compiler (which would break @@ -207,7 +201,6 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { target_os = "emscripten", target_os = "fuchsia", target_os = "horizon", - target_os = "vita" )))] static UNIX_SIGPIPE_ATTR_SPECIFIED: crate::sync::atomic::AtomicBool = crate::sync::atomic::AtomicBool::new(false); @@ -217,7 +210,6 @@ static UNIX_SIGPIPE_ATTR_SPECIFIED: crate::sync::atomic::AtomicBool = target_os = "emscripten", target_os = "fuchsia", target_os = "horizon", - target_os = "vita", )))] pub(crate) fn unix_sigpipe_attr_specified() -> bool { UNIX_SIGPIPE_ATTR_SPECIFIED.load(crate::sync::atomic::Ordering::Relaxed) @@ -408,6 +400,9 @@ cfg_if::cfg_if! { } else if #[cfg(all(target_os = "linux", target_env = "uclibc"))] { #[link(name = "dl")] extern "C" {} + } else if #[cfg(target_os = "vita")] { + #[link(name = "pthread", kind = "static", modifiers = "-bundle")] + extern "C" {} } } diff --git a/library/std/src/sys/unix/net.rs b/library/std/src/sys/unix/net.rs index 39edb136c24fd..7258c222a6c19 100644 --- a/library/std/src/sys/unix/net.rs +++ b/library/std/src/sys/unix/net.rs @@ -454,12 +454,18 @@ impl Socket { Ok(passcred != 0) } - #[cfg(not(any(target_os = "solaris", target_os = "illumos")))] + #[cfg(not(any(target_os = "solaris", target_os = "illumos", target_os = "vita")))] pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { let mut nonblocking = nonblocking as libc::c_int; cvt(unsafe { libc::ioctl(self.as_raw_fd(), libc::FIONBIO, &mut nonblocking) }).map(drop) } + #[cfg(target_os = "vita")] + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + let option = nonblocking as libc::c_int; + setsockopt(self, libc::SOL_SOCKET, libc::SO_NONBLOCK, option) + } + #[cfg(any(target_os = "solaris", target_os = "illumos"))] pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { // FIONBIO is inadequate for sockets on illumos/Solaris, so use the diff --git a/library/std/src/sys/unix/process/process_common.rs b/library/std/src/sys/unix/process/process_common.rs index 640648e870748..5f316b12b625d 100644 --- a/library/std/src/sys/unix/process/process_common.rs +++ b/library/std/src/sys/unix/process/process_common.rs @@ -24,11 +24,11 @@ cfg_if::cfg_if! { if #[cfg(target_os = "fuchsia")] { // fuchsia doesn't have /dev/null } else if #[cfg(target_os = "redox")] { - const DEV_NULL: &str = "null:\0"; + const DEV_NULL: &CStr = c"null:"; } else if #[cfg(target_os = "vxworks")] { - const DEV_NULL: &str = "/null\0"; + const DEV_NULL: &CStr = c"/null"; } else { - const DEV_NULL: &str = "/dev/null\0"; + const DEV_NULL: &CStr = c"/dev/null"; } } @@ -474,8 +474,7 @@ impl Stdio { let mut opts = OpenOptions::new(); opts.read(readable); opts.write(!readable); - let path = unsafe { CStr::from_ptr(DEV_NULL.as_ptr() as *const _) }; - let fd = File::open_c(&path, &opts)?; + let fd = File::open_c(DEV_NULL, &opts)?; Ok((ChildStdio::Owned(fd.into_inner()), None)) } diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs index 612d43fe20414..3721988b405b4 100644 --- a/library/std/src/sys/unix/process/process_unix.rs +++ b/library/std/src/sys/unix/process/process_unix.rs @@ -35,8 +35,26 @@ cfg_if::cfg_if! { if #[cfg(all(target_os = "nto", target_env = "nto71"))] { use crate::thread; use libc::{c_char, posix_spawn_file_actions_t, posix_spawnattr_t}; - // arbitrary number of tries: - const MAX_FORKSPAWN_TRIES: u32 = 4; + use crate::time::Duration; + use crate::sync::LazyLock; + // Get smallest amount of time we can sleep. + // Return a common value if it cannot be determined. + fn get_clock_resolution() -> Duration { + static MIN_DELAY: LazyLock Duration> = LazyLock::new(|| { + let mut mindelay = libc::timespec { tv_sec: 0, tv_nsec: 0 }; + if unsafe { libc::clock_getres(libc::CLOCK_MONOTONIC, &mut mindelay) } == 0 + { + Duration::from_nanos(mindelay.tv_nsec as u64) + } else { + Duration::from_millis(1) + } + }); + *MIN_DELAY + } + // Arbitrary minimum sleep duration for retrying fork/spawn + const MIN_FORKSPAWN_SLEEP: Duration = Duration::from_nanos(1); + // Maximum duration of sleeping before giving up and returning an error + const MAX_FORKSPAWN_SLEEP: Duration = Duration::from_millis(1000); } } @@ -163,12 +181,25 @@ impl Command { unsafe fn do_fork(&mut self) -> Result<(pid_t, pid_t), io::Error> { use crate::sys::os::errno; - let mut tries_left = MAX_FORKSPAWN_TRIES; + let mut delay = MIN_FORKSPAWN_SLEEP; + loop { let r = libc::fork(); - if r == -1 as libc::pid_t && tries_left > 0 && errno() as libc::c_int == libc::EBADF { - thread::yield_now(); - tries_left -= 1; + if r == -1 as libc::pid_t && errno() as libc::c_int == libc::EBADF { + if delay < get_clock_resolution() { + // We cannot sleep this short (it would be longer). + // Yield instead. + thread::yield_now(); + } else if delay < MAX_FORKSPAWN_SLEEP { + thread::sleep(delay); + } else { + return Err(io::const_io_error!( + ErrorKind::WouldBlock, + "forking returned EBADF too often", + )); + } + delay *= 2; + continue; } else { return cvt(r).map(|res| (res, -1)); } @@ -480,17 +511,28 @@ impl Command { attrp: *const posix_spawnattr_t, argv: *const *mut c_char, envp: *const *mut c_char, - ) -> i32 { - let mut tries_left = MAX_FORKSPAWN_TRIES; + ) -> io::Result { + let mut delay = MIN_FORKSPAWN_SLEEP; loop { match libc::posix_spawnp(pid, file, file_actions, attrp, argv, envp) { - libc::EBADF if tries_left > 0 => { - thread::yield_now(); - tries_left -= 1; + libc::EBADF => { + if delay < get_clock_resolution() { + // We cannot sleep this short (it would be longer). + // Yield instead. + thread::yield_now(); + } else if delay < MAX_FORKSPAWN_SLEEP { + thread::sleep(delay); + } else { + return Err(io::const_io_error!( + ErrorKind::WouldBlock, + "posix_spawnp returned EBADF too often", + )); + } + delay *= 2; continue; } r => { - return r; + return Ok(r); } } } @@ -620,14 +662,20 @@ impl Command { let spawn_fn = libc::posix_spawnp; #[cfg(target_os = "nto")] let spawn_fn = retrying_libc_posix_spawnp; - cvt_nz(spawn_fn( + + let spawn_res = spawn_fn( &mut p.pid, self.get_program_cstr().as_ptr(), file_actions.0.as_ptr(), attrs.0.as_ptr(), self.get_argv().as_ptr() as *const _, envp as *const _, - ))?; + ); + + #[cfg(target_os = "nto")] + let spawn_res = spawn_res?; + + cvt_nz(spawn_res)?; Ok(Some(p)) } } diff --git a/library/std/src/sys/unix/thread.rs b/library/std/src/sys/unix/thread.rs index 7307d9b2c8616..878af5088d9f4 100644 --- a/library/std/src/sys/unix/thread.rs +++ b/library/std/src/sys/unix/thread.rs @@ -163,10 +163,9 @@ impl Thread { #[cfg(target_os = "netbsd")] pub fn set_name(name: &CStr) { unsafe { - let cname = CStr::from_bytes_with_nul_unchecked(b"%s\0".as_slice()); let res = libc::pthread_setname_np( libc::pthread_self(), - cname.as_ptr(), + c"%s".as_ptr(), name.as_ptr() as *mut libc::c_void, ); debug_assert_eq!(res, 0); diff --git a/library/std/src/sys/unix/thread_parking/pthread.rs b/library/std/src/sys/unix/thread_parking/pthread.rs index 43046ed07b82c..8bf4bae7a3f77 100644 --- a/library/std/src/sys/unix/thread_parking/pthread.rs +++ b/library/std/src/sys/unix/thread_parking/pthread.rs @@ -123,7 +123,8 @@ impl Parker { target_os = "watchos", target_os = "l4re", target_os = "android", - target_os = "redox" + target_os = "redox", + target_os = "vita", ))] { addr_of_mut!((*parker).cvar).write(UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER)); } else if #[cfg(any(target_os = "espidf", target_os = "horizon"))] { diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs index 2bc40c4748a35..07b0610d463fa 100644 --- a/library/std/src/sys/windows/c.rs +++ b/library/std/src/sys/windows/c.rs @@ -317,7 +317,7 @@ pub unsafe fn NtWriteFile( // Functions that aren't available on every version of Windows that we support, // but we still use them and just provide some form of a fallback implementation. compat_fn_with_fallback! { - pub static KERNEL32: &CStr = ansi_str!("kernel32"); + pub static KERNEL32: &CStr = c"kernel32"; // >= Win10 1607 // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setthreaddescription @@ -350,7 +350,7 @@ compat_fn_optional! { } compat_fn_with_fallback! { - pub static NTDLL: &CStr = ansi_str!("ntdll"); + pub static NTDLL: &CStr = c"ntdll"; pub fn NtCreateKeyedEvent( KeyedEventHandle: LPHANDLE, diff --git a/library/std/src/sys/windows/compat.rs b/library/std/src/sys/windows/compat.rs index 4fe95d41116b5..649cc4bfdbf1a 100644 --- a/library/std/src/sys/windows/compat.rs +++ b/library/std/src/sys/windows/compat.rs @@ -228,9 +228,9 @@ macro_rules! compat_fn_optional { /// Load all needed functions from "api-ms-win-core-synch-l1-2-0". pub(super) fn load_synch_functions() { fn try_load() -> Option<()> { - const MODULE_NAME: &CStr = ansi_str!("api-ms-win-core-synch-l1-2-0"); - const WAIT_ON_ADDRESS: &CStr = ansi_str!("WaitOnAddress"); - const WAKE_BY_ADDRESS_SINGLE: &CStr = ansi_str!("WakeByAddressSingle"); + const MODULE_NAME: &CStr = c"api-ms-win-core-synch-l1-2-0"; + const WAIT_ON_ADDRESS: &CStr = c"WaitOnAddress"; + const WAKE_BY_ADDRESS_SINGLE: &CStr = c"WakeByAddressSingle"; // Try loading the library and all the required functions. // If any step fails, then they all fail. diff --git a/library/std/src/sys/windows/mod.rs b/library/std/src/sys/windows/mod.rs index bcc172b0fae36..b11c89622032e 100644 --- a/library/std/src/sys/windows/mod.rs +++ b/library/std/src/sys/windows/mod.rs @@ -1,6 +1,6 @@ #![allow(missing_docs, nonstandard_style)] -use crate::ffi::{CStr, OsStr, OsString}; +use crate::ffi::{OsStr, OsString}; use crate::io::ErrorKind; use crate::mem::MaybeUninit; use crate::os::windows::ffi::{OsStrExt, OsStringExt}; @@ -51,7 +51,7 @@ pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) { // Normally, `thread::spawn` will call `Thread::set_name` but since this thread already // exists, we have to call it ourselves. - thread::Thread::set_name(&CStr::from_bytes_with_nul_unchecked(b"main\0")); + thread::Thread::set_name(&c"main"); } // SAFETY: must be called only once during runtime cleanup. diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index f712c872708ac..d9973185bc45e 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -206,7 +206,7 @@ cfg_if::cfg_if! { #[doc(hidden)] #[unstable(feature = "thread_local_internals", issue = "none")] pub mod local_impl { - pub use crate::sys::common::thread_local::{thread_local_inner, Key}; + pub use crate::sys::common::thread_local::{thread_local_inner, Key, abort_on_dtor_unwind}; } } } diff --git a/src/bootstrap/CHANGELOG.md b/src/bootstrap/CHANGELOG.md index d6924cf2cfb23..1aba0713850ae 100644 --- a/src/bootstrap/CHANGELOG.md +++ b/src/bootstrap/CHANGELOG.md @@ -18,6 +18,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - `x.py fmt` now formats only files modified between the merge-base of HEAD and the last commit in the master branch of the rust-lang repository and the current working directory. To restore old behaviour, use `x.py fmt .`. The check mode is not affected by this change. [#105702](https://github.com/rust-lang/rust/pull/105702) - The `llvm.version-check` config option has been removed. Older versions were never supported. If you still need to support older versions (e.g. you are applying custom patches), patch `check_llvm_version` in bootstrap to change the minimum version. [#108619](https://github.com/rust-lang/rust/pull/108619) - The `rust.ignore-git` option has been renamed to `rust.omit-git-hash`. [#110059](https://github.com/rust-lang/rust/pull/110059) +- `--exclude` no longer accepts a `Kind` as part of a Step; instead it uses the top-level Kind of the subcommand. If this matches how you were already using --exclude (e.g. `x test --exclude test::std`), simply remove the kind: `--exclude std`. If you were using a kind that did not match the top-level subcommand, please open an issue explaining why you wanted this feature. ### Non-breaking changes diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock index 311ac1751606e..d48866d58ce1b 100644 --- a/src/bootstrap/Cargo.lock +++ b/src/bootstrap/Cargo.lock @@ -51,13 +51,13 @@ dependencies = [ "filetime", "hex", "ignore", - "is-terminal", "junction", "libc", "object", "once_cell", "opener", "pretty_assertions", + "semver", "serde", "serde_derive", "serde_json", @@ -385,18 +385,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "is-terminal" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "256017f749ab3117e93acb91063009e1f1bb56d03965b14c2c8df4eb02c524d8" -dependencies = [ - "hermit-abi 0.3.1", - "io-lifetimes", - "rustix", - "windows-sys", -] - [[package]] name = "itoa" version = "1.0.2" @@ -645,6 +633,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "semver" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" + [[package]] name = "serde" version = "1.0.160" diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index 70ade776d7da7..85eb543e48ead 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -30,7 +30,6 @@ path = "bin/sccache-plus-cl.rs" test = false [dependencies] -is-terminal = "0.4" build_helper = { path = "../tools/build_helper" } cmake = "0.1.38" filetime = "0.2" @@ -57,6 +56,7 @@ walkdir = "2" sysinfo = { version = "0.26.0", optional = true } clap = { version = "4.2.4", default-features = false, features = ["std", "usage", "help", "derive", "error-context"] } clap_complete = "4.2.2" +semver = "1.0.17" # Solaris doesn't support flock() and thus fd-lock is not option now [target.'cfg(not(target_os = "solaris"))'.dependencies] diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 43c859b00631e..ea1b34812e5ca 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -8,7 +8,7 @@ use std::fs::{self, File}; use std::hash::Hash; use std::io::{BufRead, BufReader}; use std::ops::Deref; -use std::path::{Component, Path, PathBuf}; +use std::path::{Path, PathBuf}; use std::process::Command; use std::time::{Duration, Instant}; @@ -150,29 +150,6 @@ pub struct TaskPath { pub kind: Option, } -impl TaskPath { - pub fn parse(path: impl Into) -> TaskPath { - let mut kind = None; - let mut path = path.into(); - - let mut components = path.components(); - if let Some(Component::Normal(os_str)) = components.next() { - if let Some(str) = os_str.to_str() { - if let Some((found_kind, found_prefix)) = str.split_once("::") { - if found_kind.is_empty() { - panic!("empty kind in task path {}", path.display()); - } - kind = Kind::parse(found_kind); - assert!(kind.is_some()); - path = Path::new(found_prefix).join(components.as_path()); - } - } - } - - TaskPath { path, kind } - } -} - impl Debug for TaskPath { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { if let Some(kind) = &self.kind { @@ -216,7 +193,7 @@ impl PathSet { PathSet::Set(set) } - fn has(&self, needle: &Path, module: Option) -> bool { + fn has(&self, needle: &Path, module: Kind) -> bool { match self { PathSet::Set(set) => set.iter().any(|p| Self::check(p, needle, module)), PathSet::Suite(suite) => Self::check(suite, needle, module), @@ -224,9 +201,9 @@ impl PathSet { } // internal use only - fn check(p: &TaskPath, needle: &Path, module: Option) -> bool { - if let (Some(p_kind), Some(kind)) = (&p.kind, module) { - p.path.ends_with(needle) && *p_kind == kind + fn check(p: &TaskPath, needle: &Path, module: Kind) -> bool { + if let Some(p_kind) = &p.kind { + p.path.ends_with(needle) && *p_kind == module } else { p.path.ends_with(needle) } @@ -238,11 +215,7 @@ impl PathSet { /// This is used for `StepDescription::krate`, which passes all matching crates at once to /// `Step::make_run`, rather than calling it many times with a single crate. /// See `tests.rs` for examples. - fn intersection_removing_matches( - &self, - needles: &mut Vec<&Path>, - module: Option, - ) -> PathSet { + fn intersection_removing_matches(&self, needles: &mut Vec<&Path>, module: Kind) -> PathSet { let mut check = |p| { for (i, n) in needles.iter().enumerate() { let matched = Self::check(p, n, module); @@ -307,7 +280,7 @@ impl StepDescription { } fn is_excluded(&self, builder: &Builder<'_>, pathset: &PathSet) -> bool { - if builder.config.exclude.iter().any(|e| pathset.has(&e.path, e.kind)) { + if builder.config.exclude.iter().any(|e| pathset.has(&e, builder.kind)) { println!("Skipping {:?} because it is excluded", pathset); return true; } @@ -562,7 +535,7 @@ impl<'a> ShouldRun<'a> { ) -> Vec { let mut sets = vec![]; for pathset in &self.paths { - let subset = pathset.intersection_removing_matches(paths, Some(kind)); + let subset = pathset.intersection_removing_matches(paths, kind); if subset != PathSet::empty() { sets.push(subset); } @@ -571,7 +544,7 @@ impl<'a> ShouldRun<'a> { } } -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, ValueEnum)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)] pub enum Kind { #[clap(alias = "b")] Build, @@ -642,7 +615,10 @@ impl Kind { Kind::Doc => "Documenting", Kind::Run => "Running", Kind::Suggest => "Suggesting", - _ => return format!("{self:?}"), + _ => { + let title_letter = self.as_str()[0..1].to_ascii_uppercase(); + return format!("{title_letter}{}ing", &self.as_str()[1..]); + } } .to_owned() } @@ -700,8 +676,8 @@ impl<'a> Builder<'a> { check::CargoMiri, check::MiroptTestTools, check::Rls, - check::RustAnalyzer, check::Rustfmt, + check::RustAnalyzer, check::Bootstrap ), Kind::Test => describe!( @@ -992,7 +968,7 @@ impl<'a> Builder<'a> { } pub fn sysroot(&self, compiler: Compiler) -> Interned { - self.ensure(compile::Sysroot { compiler }) + self.ensure(compile::Sysroot::new(compiler)) } /// Returns the libdir where the standard library and other artifacts are @@ -1632,7 +1608,7 @@ impl<'a> Builder<'a> { // flesh out rpath support more fully in the future. rustflags.arg("-Zosx-rpath-install-name"); Some("-Wl,-rpath,@loader_path/../lib") - } else if !target.contains("windows") { + } else if !target.contains("windows") && !target.contains("aix") { rustflags.arg("-Clink-args=-Wl,-z,origin"); Some("-Wl,-rpath,$ORIGIN/../lib") } else { @@ -1673,7 +1649,15 @@ impl<'a> Builder<'a> { self.config.rust_debuginfo_level_tools } }; - cargo.env(profile_var("DEBUG"), debuginfo_level.to_string()); + if debuginfo_level == 1 { + // Use less debuginfo than the default to save on disk space. + cargo.env(profile_var("DEBUG"), "line-tables-only"); + } else { + cargo.env(profile_var("DEBUG"), debuginfo_level.to_string()); + }; + if self.cc[&target].args().iter().any(|arg| arg == "-gz") { + rustflags.arg("-Clink-arg=-gz"); + } cargo.env( profile_var("DEBUG_ASSERTIONS"), if mode == Mode::Std { @@ -1783,7 +1767,10 @@ impl<'a> Builder<'a> { cargo.env("RUSTC_TLS_MODEL_INITIAL_EXEC", "1"); } - if self.config.incremental { + // Ignore incremental modes except for stage0, since we're + // not guaranteeing correctness across builds if the compiler + // is changing under your feet. + if self.config.incremental && compiler.stage == 0 { cargo.env("CARGO_INCREMENTAL", "1"); } else { // Don't rely on any default setting for incr. comp. in Cargo @@ -2124,7 +2111,7 @@ impl<'a> Builder<'a> { let should_run = (desc.should_run)(ShouldRun::new(self, desc.kind)); for path in &self.paths { - if should_run.paths.iter().any(|s| s.has(path, Some(desc.kind))) + if should_run.paths.iter().any(|s| s.has(path, desc.kind)) && !desc.is_excluded( self, &PathSet::Suite(TaskPath { path: path.clone(), kind: Some(desc.kind) }), diff --git a/src/bootstrap/builder/tests.rs b/src/bootstrap/builder/tests.rs index d76b830b0e530..31dcee58216ae 100644 --- a/src/bootstrap/builder/tests.rs +++ b/src/bootstrap/builder/tests.rs @@ -101,23 +101,21 @@ fn test_invalid() { #[test] fn test_intersection() { - let set = PathSet::Set( - ["library/core", "library/alloc", "library/std"].into_iter().map(TaskPath::parse).collect(), - ); + let set = |paths: &[&str]| { + PathSet::Set(paths.into_iter().map(|p| TaskPath { path: p.into(), kind: None }).collect()) + }; + let library_set = set(&["library/core", "library/alloc", "library/std"]); let mut command_paths = vec![Path::new("library/core"), Path::new("library/alloc"), Path::new("library/stdarch")]; - let subset = set.intersection_removing_matches(&mut command_paths, None); - assert_eq!( - subset, - PathSet::Set(["library/core", "library/alloc"].into_iter().map(TaskPath::parse).collect()) - ); + let subset = library_set.intersection_removing_matches(&mut command_paths, Kind::Build); + assert_eq!(subset, set(&["library/core", "library/alloc"]),); assert_eq!(command_paths, vec![Path::new("library/stdarch")]); } #[test] fn test_exclude() { let mut config = configure("test", &["A"], &["A"]); - config.exclude = vec![TaskPath::parse("src/tools/tidy")]; + config.exclude = vec!["src/tools/tidy".into()]; let cache = run_build(&[], config); // Ensure we have really excluded tidy @@ -129,21 +127,16 @@ fn test_exclude() { #[test] fn test_exclude_kind() { - let path = PathBuf::from("src/tools/cargotest"); - let exclude = TaskPath::parse("test::src/tools/cargotest"); - assert_eq!(exclude, TaskPath { kind: Some(Kind::Test), path: path.clone() }); + let path = PathBuf::from("compiler/rustc_data_structures"); let mut config = configure("test", &["A"], &["A"]); - // Ensure our test is valid, and `test::Cargotest` would be run without the exclude. - assert!(run_build(&[path.clone()], config.clone()).contains::()); - // Ensure tests for cargotest are skipped. - config.exclude = vec![exclude.clone()]; - assert!(!run_build(&[path.clone()], config).contains::()); - - // Ensure builds for cargotest are not skipped. - let mut config = configure("build", &["A"], &["A"]); - config.exclude = vec![exclude]; - assert!(run_build(&[path], config).contains::()); + // Ensure our test is valid, and `test::Rustc` would be run without the exclude. + assert!(run_build(&[], config.clone()).contains::()); + // Ensure tests for rustc are skipped. + config.exclude = vec![path.clone()]; + assert!(!run_build(&[], config.clone()).contains::()); + // Ensure builds for rustc are not skipped. + assert!(run_build(&[], config).contains::()); } /// Ensure that if someone passes both a single crate and `library`, all library crates get built. diff --git a/src/bootstrap/cc_detect.rs b/src/bootstrap/cc_detect.rs index 65c882fb801e5..db3b69d18c8fc 100644 --- a/src/bootstrap/cc_detect.rs +++ b/src/bootstrap/cc_detect.rs @@ -69,6 +69,8 @@ fn new_cc_build(build: &Build, target: TargetSelection) -> cc::Build { .opt_level(2) .warnings(false) .debug(false) + // Compress debuginfo + .flag_if_supported("-gz") .target(&target.triple) .host(&build.build.triple); match build.crt_static(target) { diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs index f5a93854bf2c4..1a0f0047812ed 100644 --- a/src/bootstrap/check.rs +++ b/src/bootstrap/check.rs @@ -347,7 +347,7 @@ pub struct RustAnalyzer { impl Step for RustAnalyzer { type Output = (); const ONLY_HOSTS: bool = true; - const DEFAULT: bool = true; + const DEFAULT: bool = false; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.path("src/tools/rust-analyzer") diff --git a/src/bootstrap/clean.rs b/src/bootstrap/clean.rs index 0d9fd56b03814..c1d867a0bd14b 100644 --- a/src/bootstrap/clean.rs +++ b/src/bootstrap/clean.rs @@ -85,6 +85,10 @@ clean_crate_tree! { } fn clean_default(build: &Build, all: bool) { + if build.config.dry_run() { + return; + } + rm_rf("tmp".as_ref()); if all { diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index 1c66c00eda726..72d791b543428 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -40,11 +40,18 @@ pub struct Std { /// /// This shouldn't be used from other steps; see the comment on [`Rustc`]. crates: Interned>, + /// When using download-rustc, we need to use a new build of `std` for running unit tests of Std itself, + /// but we need to use the downloaded copy of std for linking to rustdoc. Allow this to be overriden by `builder.ensure` from other steps. + force_recompile: bool, } impl Std { pub fn new(compiler: Compiler, target: TargetSelection) -> Self { - Self { target, compiler, crates: Default::default() } + Self { target, compiler, crates: Default::default(), force_recompile: false } + } + + pub fn force_recompile(compiler: Compiler, target: TargetSelection) -> Self { + Self { target, compiler, crates: Default::default(), force_recompile: true } } } @@ -77,6 +84,7 @@ impl Step for Std { compiler: run.builder.compiler(run.builder.top_stage, run.build_triple()), target: run.target, crates: make_run_crates(&run, "library"), + force_recompile: false, }); } @@ -89,11 +97,20 @@ impl Step for Std { let target = self.target; let compiler = self.compiler; - // When using `download-rustc`, we already have artifacts for the host available - // (they were copied in `impl Step for Sysroot`). Don't recompile them. - // NOTE: the ABI of the beta compiler is different from the ABI of the downloaded compiler, - // so its artifacts can't be reused. - if builder.download_rustc() && compiler.stage != 0 && target == builder.build.build { + // When using `download-rustc`, we already have artifacts for the host available. Don't + // recompile them. + if builder.download_rustc() && target == builder.build.build + // NOTE: the beta compiler may generate different artifacts than the downloaded compiler, so + // its artifacts can't be reused. + && compiler.stage != 0 + // This check is specific to testing std itself; see `test::Std` for more details. + && !self.force_recompile + { + cp_rustc_component_to_ci_sysroot( + builder, + compiler, + builder.config.ci_rust_std_contents(), + ); return; } @@ -101,6 +118,10 @@ impl Step for Std { || builder.config.keep_stage_std.contains(&compiler.stage) { builder.info("Warning: Using a potentially old libstd. This may not behave well."); + + copy_third_party_objects(builder, &compiler, target); + copy_self_contained_objects(builder, &compiler, target); + builder.ensure(StdLink::from_std(self, compiler)); return; } @@ -428,6 +449,8 @@ struct StdLink { pub target: TargetSelection, /// Not actually used; only present to make sure the cache invalidation is correct. crates: Interned>, + /// See [`Std::force_recompile`]. + force_recompile: bool, } impl StdLink { @@ -437,6 +460,7 @@ impl StdLink { target_compiler: std.compiler, target: std.target, crates: std.crates, + force_recompile: std.force_recompile, } } } @@ -460,8 +484,24 @@ impl Step for StdLink { let compiler = self.compiler; let target_compiler = self.target_compiler; let target = self.target; - let libdir = builder.sysroot_libdir(target_compiler, target); - let hostdir = builder.sysroot_libdir(target_compiler, compiler.host); + + // NOTE: intentionally does *not* check `target == builder.build` to avoid having to add the same check in `test::Crate`. + let (libdir, hostdir) = if self.force_recompile && builder.download_rustc() { + // NOTE: copies part of `sysroot_libdir` to avoid having to add a new `force_recompile` argument there too + let lib = builder.sysroot_libdir_relative(self.compiler); + let sysroot = builder.ensure(crate::compile::Sysroot { + compiler: self.compiler, + force_recompile: self.force_recompile, + }); + let libdir = sysroot.join(lib).join("rustlib").join(target.triple).join("lib"); + let hostdir = sysroot.join(lib).join("rustlib").join(compiler.host.triple).join("lib"); + (INTERNER.intern_path(libdir), INTERNER.intern_path(hostdir)) + } else { + let libdir = builder.sysroot_libdir(target_compiler, target); + let hostdir = builder.sysroot_libdir(target_compiler, compiler.host); + (libdir, hostdir) + }; + add_to_sysroot(builder, &libdir, &hostdir, &libstd_stamp(builder, compiler, target)); } } @@ -594,6 +634,25 @@ impl Step for StartupObjects { } } +fn cp_rustc_component_to_ci_sysroot( + builder: &Builder<'_>, + compiler: Compiler, + contents: Vec, +) { + let sysroot = builder.ensure(Sysroot { compiler, force_recompile: false }); + + let ci_rustc_dir = builder.out.join(&*builder.build.build.triple).join("ci-rustc"); + for file in contents { + let src = ci_rustc_dir.join(&file); + let dst = sysroot.join(file); + if src.is_dir() { + t!(fs::create_dir_all(dst)); + } else { + builder.copy(&src, &dst); + } + } +} + #[derive(Debug, PartialOrd, Ord, Copy, Clone, PartialEq, Eq, Hash)] pub struct Rustc { pub target: TargetSelection, @@ -653,18 +712,11 @@ impl Step for Rustc { if builder.download_rustc() && compiler.stage != 0 { // Copy the existing artifacts instead of rebuilding them. // NOTE: this path is only taken for tools linking to rustc-dev (including ui-fulldeps tests). - let sysroot = builder.ensure(Sysroot { compiler }); - - let ci_rustc_dir = builder.out.join(&*builder.build.build.triple).join("ci-rustc"); - for file in builder.config.rustc_dev_contents() { - let src = ci_rustc_dir.join(&file); - let dst = sysroot.join(file); - if src.is_dir() { - t!(fs::create_dir_all(dst)); - } else { - builder.copy(&src, &dst); - } - } + cp_rustc_component_to_ci_sysroot( + builder, + compiler, + builder.config.ci_rustc_dev_contents(), + ); return; } @@ -1225,6 +1277,14 @@ pub fn compiler_file( #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct Sysroot { pub compiler: Compiler, + /// See [`Std::force_recompile`]. + force_recompile: bool, +} + +impl Sysroot { + pub(crate) fn new(compiler: Compiler) -> Self { + Sysroot { compiler, force_recompile: false } + } } impl Step for Sysroot { @@ -1247,6 +1307,8 @@ impl Step for Sysroot { let sysroot_dir = |stage| { if stage == 0 { host_dir.join("stage0-sysroot") + } else if self.force_recompile && stage == compiler.stage { + host_dir.join(format!("stage{stage}-test-sysroot")) } else if builder.download_rustc() && compiler.stage != builder.top_stage { host_dir.join("ci-rustc-sysroot") } else { @@ -1286,16 +1348,26 @@ impl Step for Sysroot { // 2. The sysroot is deleted and recreated between each invocation, so running `x test // ui-fulldeps && x test ui` can't cause failures. let mut filtered_files = Vec::new(); - // Don't trim directories or files that aren't loaded per-target; they can't cause conflicts. - let suffix = format!("lib/rustlib/{}/lib", compiler.host); - for path in builder.config.rustc_dev_contents() { - let path = Path::new(&path); - if path.parent().map_or(false, |parent| parent.ends_with(&suffix)) { - filtered_files.push(path.file_name().unwrap().to_owned()); + let mut add_filtered_files = |suffix, contents| { + for path in contents { + let path = Path::new(&path); + if path.parent().map_or(false, |parent| parent.ends_with(&suffix)) { + filtered_files.push(path.file_name().unwrap().to_owned()); + } } - } - - let filtered_extensions = [OsStr::new("rmeta"), OsStr::new("rlib"), OsStr::new("so")]; + }; + let suffix = format!("lib/rustlib/{}/lib", compiler.host); + add_filtered_files(suffix.as_str(), builder.config.ci_rustc_dev_contents()); + // NOTE: we can't copy std eagerly because `stage2-test-sysroot` needs to have only the + // newly compiled std, not the downloaded std. + add_filtered_files("lib", builder.config.ci_rust_std_contents()); + + let filtered_extensions = [ + OsStr::new("rmeta"), + OsStr::new("rlib"), + // FIXME: this is wrong when compiler.host != build, but we don't support that today + OsStr::new(std::env::consts::DLL_EXTENSION), + ]; let ci_rustc_dir = builder.ci_rustc_dir(builder.config.build); builder.cp_filtered(&ci_rustc_dir, &sysroot, &|path| { if path.extension().map_or(true, |ext| !filtered_extensions.contains(&ext)) { @@ -1411,7 +1483,8 @@ impl Step for Assemble { // If we're downloading a compiler from CI, we can use the same compiler for all stages other than 0. if builder.download_rustc() { - let sysroot = builder.ensure(Sysroot { compiler: target_compiler }); + let sysroot = + builder.ensure(Sysroot { compiler: target_compiler, force_recompile: false }); // Ensure that `libLLVM.so` ends up in the newly created target directory, // so that tools using `rustc_private` can use it. dist::maybe_install_llvm_target(builder, target_compiler.host, &sysroot); diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 45ad1547eb771..c59df7eecf680 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -12,11 +12,11 @@ use std::collections::{HashMap, HashSet}; use std::env; use std::fmt; use std::fs; +use std::io::IsTerminal; use std::path::{Path, PathBuf}; use std::process::Command; use std::str::FromStr; -use crate::builder::TaskPath; use crate::cache::{Interned, INTERNER}; use crate::cc_detect::{ndk_compiler, Language}; use crate::channel::{self, GitInfo}; @@ -25,6 +25,7 @@ use crate::flags::{Color, Flags, Warnings}; use crate::util::{exe, output, t}; use build_helper::detail_exit_macro; use once_cell::sync::OnceCell; +use semver::Version; use serde::{Deserialize, Deserializer}; use serde_derive::Deserialize; @@ -78,7 +79,7 @@ pub struct Config { pub sanitizers: bool, pub profiler: bool, pub omit_git_hash: bool, - pub exclude: Vec, + pub exclude: Vec, pub include_default_paths: bool, pub rustc_error_format: Option, pub json_output: bool, @@ -893,8 +894,6 @@ define_config! { impl Config { pub fn default_opts() -> Config { - use is_terminal::IsTerminal; - let mut config = Config::default(); config.llvm_optimize = true; config.ninja_in_file = true; @@ -957,7 +956,7 @@ impl Config { // Set flags. config.paths = std::mem::take(&mut flags.paths); - config.exclude = flags.exclude.into_iter().map(|path| TaskPath::parse(path)).collect(); + config.exclude = flags.exclude; config.include_default_paths = flags.include_default_paths; config.rustc_error_format = flags.rustc_error_format; config.json_output = flags.json_output; @@ -1114,10 +1113,14 @@ impl Config { config.out = crate::util::absolute(&config.out); } - config.initial_rustc = build.rustc.map(PathBuf::from).unwrap_or_else(|| { + config.initial_rustc = if let Some(rustc) = build.rustc { + config.check_build_rustc_version(&rustc); + PathBuf::from(rustc) + } else { config.download_beta_toolchain(); config.out.join(config.build.triple).join("stage0/bin/rustc") - }); + }; + config.initial_cargo = build .cargo .map(|cargo| { @@ -1779,6 +1782,42 @@ impl Config { self.rust_codegen_backends.get(0).cloned() } + pub fn check_build_rustc_version(&self, rustc_path: &str) { + if self.dry_run() { + return; + } + + // check rustc version is same or lower with 1 apart from the building one + let mut cmd = Command::new(rustc_path); + cmd.arg("--version"); + let rustc_output = output(&mut cmd) + .lines() + .next() + .unwrap() + .split(' ') + .nth(1) + .unwrap() + .split('-') + .next() + .unwrap() + .to_owned(); + let rustc_version = Version::parse(&rustc_output.trim()).unwrap(); + let source_version = + Version::parse(&fs::read_to_string(self.src.join("src/version")).unwrap().trim()) + .unwrap(); + if !(source_version == rustc_version + || (source_version.major == rustc_version.major + && source_version.minor == rustc_version.minor + 1)) + { + let prev_version = format!("{}.{}.x", source_version.major, source_version.minor - 1); + eprintln!( + "Unexpected rustc version: {}, we should use {}/{} to build source with {}", + rustc_version, prev_version, source_version, source_version + ); + detail_exit_macro!(1); + } + } + /// Returns the commit to download, or `None` if we shouldn't download CI artifacts. fn download_ci_rustc_commit(&self, download_rustc: Option) -> Option { // If `download-rustc` is not set, default to rebuilding. diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py index 571062a3a6fd0..4481e1668b60f 100755 --- a/src/bootstrap/configure.py +++ b/src/bootstrap/configure.py @@ -45,7 +45,6 @@ def v(*args): o("llvm-link-shared", "llvm.link-shared", "prefer shared linking to LLVM (llvm-config --link-shared)") o("rpath", "rust.rpath", "build rpaths into rustc itself") o("codegen-tests", "rust.codegen-tests", "run the tests/codegen tests") -o("option-checking", None, "complain about unrecognized options in this configure script") o("ninja", "llvm.ninja", "build LLVM using the Ninja generator (for MSVC, requires building in the correct environment)") o("locked-deps", "build.locked-deps", "force Cargo.lock to be up to date") o("vendor", "build.vendor", "enable usage of vendored Rust crates") @@ -170,6 +169,9 @@ def v(*args): v("host", None, "List of GNUs ./configure syntax LLVM host triples") v("target", None, "List of GNUs ./configure syntax LLVM target triples") +# Options specific to this configure script +o("option-checking", None, "complain about unrecognized options in this configure script") +o("verbose-configure", None, "don't truncate options when printing them in this configure script") v("set", None, "set arbitrary key/value pairs in TOML configuration") @@ -211,6 +213,8 @@ def is_value_list(key): print('be passed with `--disable-foo` to forcibly disable the option') sys.exit(0) +VERBOSE = False + # Parse all command line arguments into one of these three lists, handling # boolean and value-based options separately def parse_args(args): @@ -271,6 +275,9 @@ def parse_args(args): if len(need_value_args) > 0: err("Option '{0}' needs a value ({0}=val)".format(need_value_args[0])) + global VERBOSE + VERBOSE = 'verbose-configure' in known_args + config = {} set('build.configure-args', sys.argv[1:], config) @@ -290,7 +297,7 @@ def set(key, value, config): value = [v for v in value if v] s = "{:20} := {}".format(key, value) - if len(s) < 70: + if len(s) < 70 or VERBOSE: p(s) else: p(s[:70] + " ...") @@ -371,7 +378,7 @@ def apply_args(known_args, option_checking, config): set('rust.lld', True, config) set('rust.llvm-tools', True, config) set('build.extended', True, config) - elif option.name == 'option-checking': + elif option.name in ['option-checking', 'verbose-configure']: # this was handled above pass elif option.name == 'dist-compression-formats': diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs index 3de85c91516c4..8592895423e8f 100644 --- a/src/bootstrap/doc.rs +++ b/src/bootstrap/doc.rs @@ -8,7 +8,6 @@ //! `rustdoc`. use std::fs; -use std::io; use std::path::{Path, PathBuf}; use crate::builder::crate_description; @@ -667,7 +666,7 @@ impl Step for Rustc { /// Compiler documentation is distributed separately, so we make sure /// we do not merge it with the other documentation from std, test and /// proc_macros. This is largely just a wrapper around `cargo doc`. - fn run(self, builder: &Builder<'_>) { + fn run(mut self, builder: &Builder<'_>) { let stage = self.stage; let target = self.target; @@ -680,7 +679,7 @@ impl Step for Rustc { let compiler = builder.compiler(stage, builder.config.build); builder.ensure(compile::Std::new(compiler, builder.config.build)); - let _guard = builder.msg( + let _guard = builder.msg_sysroot_tool( Kind::Doc, stage, &format!("compiler{}", crate_description(&self.crates)), @@ -694,11 +693,12 @@ impl Step for Rustc { // rustc. rustdoc needs to be able to see everything, for example when // merging the search index, or generating local (relative) links. let out_dir = builder.stage_out(compiler, Mode::Rustc).join(target.triple).join("doc"); - t!(symlink_dir_force(&builder.config, &out, &out_dir)); + t!(fs::create_dir_all(out_dir.parent().unwrap())); + symlink_dir_force(&builder.config, &out, &out_dir); // Cargo puts proc macros in `target/doc` even if you pass `--target` // explicitly (https://github.com/rust-lang/cargo/issues/7677). let proc_macro_out_dir = builder.stage_out(compiler, Mode::Rustc).join("doc"); - t!(symlink_dir_force(&builder.config, &out, &proc_macro_out_dir)); + symlink_dir_force(&builder.config, &out, &proc_macro_out_dir); // Build cargo command. let mut cargo = builder.cargo(compiler, Mode::Rustc, SourceType::InTree, target, "doc"); @@ -725,6 +725,11 @@ impl Step for Rustc { cargo.rustdocflag("ena=https://docs.rs/ena/latest/"); let mut to_open = None; + + if self.crates.is_empty() { + self.crates = INTERNER.intern_list(vec!["rustc_driver".to_owned()]); + }; + for krate in &*self.crates { // Create all crate output directories first to make sure rustdoc uses // relative links. @@ -816,7 +821,7 @@ macro_rules! tool_doc { ]; for out_dir in out_dirs { t!(fs::create_dir_all(&out_dir)); - t!(symlink_dir_force(&builder.config, &out, &out_dir)); + symlink_dir_force(&builder.config, &out, &out_dir); } // Build cargo command. @@ -834,6 +839,7 @@ macro_rules! tool_doc { cargo.arg("-Zskip-rustdoc-fingerprint"); // Only include compiler crates, no dependencies of those, such as `libc`. cargo.arg("--no-deps"); + cargo.arg("--lib"); $( cargo.arg("-p").arg($krate); )+ @@ -958,21 +964,24 @@ impl Step for UnstableBookGen { } } -fn symlink_dir_force(config: &Config, src: &Path, dst: &Path) -> io::Result<()> { +fn symlink_dir_force(config: &Config, original: &Path, link: &Path) { if config.dry_run() { - return Ok(()); + return; } - if let Ok(m) = fs::symlink_metadata(dst) { + if let Ok(m) = fs::symlink_metadata(link) { if m.file_type().is_dir() { - fs::remove_dir_all(dst)?; + t!(fs::remove_dir_all(link)); } else { // handle directory junctions on windows by falling back to // `remove_dir`. - fs::remove_file(dst).or_else(|_| fs::remove_dir(dst))?; + t!(fs::remove_file(link).or_else(|_| fs::remove_dir(link))); } } - symlink_dir(config, src, dst) + t!( + symlink_dir(config, original, link), + format!("failed to create link from {} -> {}", link.display(), original.display()) + ); } #[derive(Ord, PartialOrd, Debug, Copy, Clone, Hash, PartialEq, Eq)] diff --git a/src/bootstrap/download.rs b/src/bootstrap/download.rs index 12780df21757a..06f479808b975 100644 --- a/src/bootstrap/download.rs +++ b/src/bootstrap/download.rs @@ -271,11 +271,8 @@ impl Config { // `compile::Sysroot` needs to know the contents of the `rustc-dev` tarball to avoid adding // it to the sysroot unless it was explicitly requested. But parsing the 100 MB tarball is slow. // Cache the entries when we extract it so we only have to read it once. - let mut recorded_entries = if dst.ends_with("ci-rustc") && pattern == "rustc-dev" { - Some(BufWriter::new(t!(File::create(dst.join(".rustc-dev-contents"))))) - } else { - None - }; + let mut recorded_entries = + if dst.ends_with("ci-rustc") { recorded_entries(dst, pattern) } else { None }; for member in t!(tar.entries()) { let mut member = t!(member); @@ -332,6 +329,17 @@ impl Config { } } +fn recorded_entries(dst: &Path, pattern: &str) -> Option> { + let name = if pattern == "rustc-dev" { + ".rustc-dev-contents" + } else if pattern.starts_with("rust-std") { + ".rust-std-contents" + } else { + return None; + }; + Some(BufWriter::new(t!(File::create(dst.join(name))))) +} + enum DownloadSource { CI, Dist, @@ -382,11 +390,20 @@ impl Config { Some(rustfmt_path) } - pub(crate) fn rustc_dev_contents(&self) -> Vec { + pub(crate) fn ci_rust_std_contents(&self) -> Vec { + self.ci_component_contents(".rust-std-contents") + } + + pub(crate) fn ci_rustc_dev_contents(&self) -> Vec { + self.ci_component_contents(".rustc-dev-contents") + } + + fn ci_component_contents(&self, stamp_file: &str) -> Vec { assert!(self.download_rustc()); let ci_rustc_dir = self.out.join(&*self.build.triple).join("ci-rustc"); - let rustc_dev_contents_file = t!(File::open(ci_rustc_dir.join(".rustc-dev-contents"))); - t!(BufReader::new(rustc_dev_contents_file).lines().collect()) + let stamp_file = ci_rustc_dir.join(stamp_file); + let contents_file = t!(File::open(&stamp_file), stamp_file.display().to_string()); + t!(BufReader::new(contents_file).lines().collect()) } pub(crate) fn download_ci_rustc(&self, commit: &str) { diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index a1aaee68c625d..7a16189926bf5 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -222,6 +222,7 @@ pub struct Build { initial_cargo: PathBuf, initial_lld: PathBuf, initial_libdir: PathBuf, + initial_sysroot: PathBuf, // Runtime state filled in later on // C/C++ compilers and archiver for all targets @@ -389,13 +390,16 @@ impl Build { "/dummy".to_string() } else { output(Command::new(&config.initial_rustc).arg("--print").arg("sysroot")) - }; + } + .trim() + .to_string(); + let initial_libdir = initial_target_dir .parent() .unwrap() .parent() .unwrap() - .strip_prefix(initial_sysroot.trim()) + .strip_prefix(&initial_sysroot) .unwrap() .to_path_buf(); @@ -425,6 +429,7 @@ impl Build { initial_cargo: config.initial_cargo.clone(), initial_lld, initial_libdir, + initial_sysroot: initial_sysroot.into(), local_rebuild: config.local_rebuild, fail_fast: config.cmd.fail_fast(), doc_tests: config.cmd.doc_tests(), @@ -1045,8 +1050,8 @@ impl Build { what: impl Display, target: TargetSelection, ) -> Option { - let action = action.into(); - let msg = format!("{action:?}ing {what} for {target}"); + let action = action.into().description(); + let msg = format!("{action} {what} for {target}"); self.group(&msg) } @@ -1058,8 +1063,8 @@ impl Build { host: TargetSelection, target: TargetSelection, ) -> Option { - let action = action.into(); - let msg = |fmt| format!("{action:?}ing {what} {fmt}"); + let action = action.into().description(); + let msg = |fmt| format!("{action} {what} {fmt}"); let msg = if host == target { msg(format_args!("(stage{stage} -> stage{}, {target})", stage + 1)) } else { @@ -1069,7 +1074,6 @@ impl Build { } fn group(&self, msg: &str) -> Option { - self.info(&msg); match self.config.dry_run { DryRun::SelfCheck => None, DryRun::Disabled | DryRun::UserSelected => Some(gha::group(&msg)), diff --git a/src/bootstrap/llvm.rs b/src/bootstrap/llvm.rs index 3fd0cca40e522..19e595650cf9d 100644 --- a/src/bootstrap/llvm.rs +++ b/src/bootstrap/llvm.rs @@ -834,6 +834,31 @@ impl Step for Lld { } } + // LLD is built as an LLVM tool, but is distributed outside of the `llvm-tools` component, + // which impacts where it expects to find LLVM's shared library. This causes #80703. + // + // LLD is distributed at "$root/lib/rustlib/$host/bin/rust-lld", but the `libLLVM-*.so` it + // needs is distributed at "$root/lib". The default rpath of "$ORIGIN/../lib" points at the + // lib path for LLVM tools, not the one for rust binaries. + // + // (The `llvm-tools` component copies the .so there for the other tools, and with that + // component installed, one can successfully invoke `rust-lld` directly without rustup's + // `LD_LIBRARY_PATH` overrides) + // + if builder.config.rpath_enabled(target) + && util::use_host_linker(target) + && builder.config.llvm_link_shared() + && target.contains("linux") + { + // So we inform LLD where it can find LLVM's libraries by adding an rpath entry to the + // expected parent `lib` directory. + // + // Be careful when changing this path, we need to ensure it's quoted or escaped: + // `$ORIGIN` would otherwise be expanded when the `LdFlags` are passed verbatim to + // cmake. + ldflags.push_all("-Wl,-rpath,'$ORIGIN/../../../'"); + } + configure_cmake(builder, target, &mut cfg, true, ldflags, &[]); configure_llvm(builder, target, &mut cfg); diff --git a/src/bootstrap/render_tests.rs b/src/bootstrap/render_tests.rs index 872b75f6c1599..06ab820953d08 100644 --- a/src/bootstrap/render_tests.rs +++ b/src/bootstrap/render_tests.rs @@ -88,10 +88,10 @@ impl<'a> Renderer<'a> { } fn render_all(mut self) { - let mut line = String::new(); + let mut line = Vec::new(); loop { line.clear(); - match self.stdout.read_line(&mut line) { + match self.stdout.read_until(b'\n', &mut line) { Ok(_) => {} Err(err) if err.kind() == std::io::ErrorKind::UnexpectedEof => break, Err(err) => panic!("failed to read output of test runner: {err}"), @@ -100,12 +100,13 @@ impl<'a> Renderer<'a> { break; } - match serde_json::from_str(&line) { + match serde_json::from_slice(&line) { Ok(parsed) => self.render_message(parsed), Err(_err) => { // Handle non-JSON output, for example when --nocapture is passed. - print!("{line}"); - let _ = std::io::stdout().flush(); + let mut stdout = std::io::stdout(); + stdout.write_all(&line).unwrap(); + let _ = stdout.flush(); } } } diff --git a/src/bootstrap/setup.rs b/src/bootstrap/setup.rs index 40038df833210..c604c63a4dd76 100644 --- a/src/bootstrap/setup.rs +++ b/src/bootstrap/setup.rs @@ -20,7 +20,7 @@ pub enum Profile { Codegen, Library, Tools, - User, + Dist, None, } @@ -31,6 +31,7 @@ static SETTINGS_HASHES: &[&str] = &[ "ea67e259dedf60d4429b6c349a564ffcd1563cf41c920a856d1f5b16b4701ac8", "56e7bf011c71c5d81e0bf42e84938111847a810eee69d906bba494ea90b51922", "af1b5efe196aed007577899db9dae15d6dbc923d6fa42fa0934e68617ba9bbe0", + "3468fea433c25fff60be6b71e8a215a732a7b1268b6a83bf10d024344e140541", ]; static RUST_ANALYZER_SETTINGS: &str = include_str!("../etc/rust_analyzer_settings.json"); @@ -42,7 +43,7 @@ impl Profile { pub fn all() -> impl Iterator { use Profile::*; // N.B. these are ordered by how they are displayed, not alphabetically - [Library, Compiler, Codegen, Tools, User, None].iter().copied() + [Library, Compiler, Codegen, Tools, Dist, None].iter().copied() } pub fn purpose(&self) -> String { @@ -52,7 +53,7 @@ impl Profile { Compiler => "Contribute to the compiler itself", Codegen => "Contribute to the compiler, and also modify LLVM or codegen", Tools => "Contribute to tools which depend on the compiler, but do not modify it directly (e.g. rustdoc, clippy, miri)", - User => "Install Rust from source", + Dist => "Install Rust from source", None => "Do not modify `config.toml`" } .to_string() @@ -72,7 +73,7 @@ impl Profile { Profile::Codegen => "codegen", Profile::Library => "library", Profile::Tools => "tools", - Profile::User => "user", + Profile::Dist => "dist", Profile::None => "none", } } @@ -86,7 +87,7 @@ impl FromStr for Profile { "lib" | "library" => Ok(Profile::Library), "compiler" => Ok(Profile::Compiler), "llvm" | "codegen" => Ok(Profile::Codegen), - "maintainer" | "user" => Ok(Profile::User), + "maintainer" | "dist" => Ok(Profile::Dist), "tools" | "tool" | "rustdoc" | "clippy" | "miri" | "rustfmt" | "rls" => { Ok(Profile::Tools) } @@ -159,7 +160,7 @@ pub fn setup(config: &Config, profile: Profile) { "test src/tools/rustfmt", ], Profile::Library => &["check", "build", "test library/std", "doc"], - Profile::User => &["dist", "build"], + Profile::Dist => &["dist", "build"], }; println!(); @@ -169,7 +170,7 @@ pub fn setup(config: &Config, profile: Profile) { println!("- `x.py {}`", cmd); } - if profile != Profile::User { + if profile != Profile::Dist { println!( "For more suggestions, see https://rustc-dev-guide.rust-lang.org/building/suggested.html" ); diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index eec8c4ad69f23..f31ba946099f5 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -700,7 +700,7 @@ impl Step for CompiletestTest { /// Runs `cargo test` for compiletest. fn run(self, builder: &Builder<'_>) { let host = self.host; - let compiler = builder.compiler(1, host); + let compiler = builder.compiler(builder.top_stage, host); // We need `ToolStd` for the locally-built sysroot because // compiletest uses unstable features of the `test` crate. @@ -1424,7 +1424,15 @@ note: if you're sure you want to do this, please open an issue as to why. In the cmd.arg("--src-base").arg(builder.src.join("tests").join(suite)); cmd.arg("--build-base").arg(testdir(builder, compiler.host).join(suite)); - cmd.arg("--sysroot-base").arg(builder.sysroot(compiler)); + + // When top stage is 0, that means that we're testing an externally provided compiler. + // In that case we need to use its specific sysroot for tests to pass. + let sysroot = if builder.top_stage == 0 { + builder.initial_sysroot.clone() + } else { + builder.sysroot(compiler).to_path_buf() + }; + cmd.arg("--sysroot-base").arg(sysroot); cmd.arg("--stage-id").arg(stage_id); cmd.arg("--suite").arg(suite); cmd.arg("--mode").arg(mode); @@ -1529,7 +1537,7 @@ note: if you're sure you want to do this, please open an issue as to why. In the for exclude in &builder.config.exclude { cmd.arg("--skip"); - cmd.arg(&exclude.path); + cmd.arg(&exclude); } // Get paths from cmd args @@ -2204,7 +2212,8 @@ impl Step for Crate { let target = self.target; let mode = self.mode; - builder.ensure(compile::Std::new(compiler, target)); + // See [field@compile::Std::force_recompile]. + builder.ensure(compile::Std::force_recompile(compiler, target)); builder.ensure(RemoteCopyLibs { compiler, target }); // If we're not doing a full bootstrap but we're testing a stage2 @@ -2218,6 +2227,16 @@ impl Step for Crate { match mode { Mode::Std => { compile::std_cargo(builder, target, compiler.stage, &mut cargo); + // `std_cargo` actually does the wrong thing: it passes `--sysroot build/host/stage2`, + // but we want to use the force-recompile std we just built in `build/host/stage2-test-sysroot`. + // Override it. + if builder.download_rustc() { + let sysroot = builder + .out + .join(compiler.host.triple) + .join(format!("stage{}-test-sysroot", compiler.stage)); + cargo.env("RUSTC_SYSROOT", sysroot); + } } Mode::Rustc => { compile::rustc_cargo(builder, &mut cargo, target, compiler.stage); @@ -2269,6 +2288,11 @@ impl Step for CrateRustdoc { // isn't really necessary. builder.compiler_for(builder.top_stage, target, target) }; + // NOTE: normally `ensure(Rustc)` automatically runs `ensure(Std)` for us. However, when + // using `download-rustc`, the rustc_private artifacts may be in a *different sysroot* from + // the target rustdoc (`ci-rustc-sysroot` vs `stage2`). In that case, we need to ensure this + // explicitly to make sure it ends up in the stage2 sysroot. + builder.ensure(compile::Std::new(compiler, target)); builder.ensure(compile::Rustc::new(compiler, target)); let mut cargo = tool::prepare_tool_cargo( @@ -2320,7 +2344,13 @@ impl Step for CrateRustdoc { dylib_path.insert(0, PathBuf::from(&*libdir)); cargo.env(dylib_path_var(), env::join_paths(&dylib_path).unwrap()); - let _guard = builder.msg(builder.kind, compiler.stage, "rustdoc", compiler.host, target); + let _guard = builder.msg_sysroot_tool( + builder.kind, + compiler.stage, + "rustdoc", + compiler.host, + target, + ); run_cargo_test( cargo, &[], diff --git a/src/bootstrap/tool.rs b/src/bootstrap/tool.rs index 0f0a3bb8775db..962cbf758d4d5 100644 --- a/src/bootstrap/tool.rs +++ b/src/bootstrap/tool.rs @@ -711,7 +711,7 @@ impl Step for RustAnalyzerProcMacroSrv { tool: "rust-analyzer-proc-macro-srv", mode: Mode::ToolStd, path: "src/tools/rust-analyzer/crates/proc-macro-srv-cli", - extra_features: vec!["proc-macro-srv/sysroot-abi".to_owned()], + extra_features: vec!["sysroot-abi".to_owned()], is_optional_tool: false, source_type: SourceType::InTree, allow_features: RustAnalyzer::ALLOW_FEATURES, diff --git a/src/bootstrap/util.rs b/src/bootstrap/util.rs index e4bbccdb067c2..7e29f671f028b 100644 --- a/src/bootstrap/util.rs +++ b/src/bootstrap/util.rs @@ -134,17 +134,17 @@ pub(crate) fn program_out_of_date(stamp: &Path, key: &str) -> bool { /// Symlinks two directories, using junctions on Windows and normal symlinks on /// Unix. -pub fn symlink_dir(config: &Config, src: &Path, dest: &Path) -> io::Result<()> { +pub fn symlink_dir(config: &Config, original: &Path, link: &Path) -> io::Result<()> { if config.dry_run() { return Ok(()); } - let _ = fs::remove_dir(dest); - return symlink_dir_inner(src, dest); + let _ = fs::remove_dir(link); + return symlink_dir_inner(original, link); #[cfg(not(windows))] - fn symlink_dir_inner(src: &Path, dest: &Path) -> io::Result<()> { + fn symlink_dir_inner(original: &Path, link: &Path) -> io::Result<()> { use std::os::unix::fs; - fs::symlink(src, dest) + fs::symlink(original, link) } #[cfg(windows)] diff --git a/src/ci/docker/README.md b/src/ci/docker/README.md index e799d7c968893..852f2e2093595 100644 --- a/src/ci/docker/README.md +++ b/src/ci/docker/README.md @@ -270,7 +270,7 @@ For targets: `loongarch64-unknown-linux-gnu` - Operating System > Linux kernel version = 5.19.16 - Binary utilities > Version of binutils = 2.40 - C-library > glibc version = 2.36 -- C compiler > gcc version = 12.2.0 +- C compiler > gcc version = 13.1.0 - C compiler > C++ = ENABLE -- to cross compile LLVM ### `mips-linux-gnu.defconfig` diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version index 3ccca1e8b89bc..413b31282f7c9 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version +++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version @@ -1 +1 @@ -0.16.5 \ No newline at end of file +0.16.6 \ No newline at end of file diff --git a/src/ci/docker/scripts/crosstool-ng-git.sh b/src/ci/docker/scripts/crosstool-ng-git.sh index 449cc476f9ab5..b8d3991532717 100644 --- a/src/ci/docker/scripts/crosstool-ng-git.sh +++ b/src/ci/docker/scripts/crosstool-ng-git.sh @@ -2,7 +2,7 @@ set -ex URL=https://github.com/crosstool-ng/crosstool-ng -REV=943364711a650d9b9e84c1b42c91cc0265b6ab5c +REV=227d99d7f3115f3a078595a580d2b307dcd23e93 mkdir crosstool-ng cd crosstool-ng diff --git a/src/ci/run.sh b/src/ci/run.sh index 966af3abc6f1c..5363086f68b65 100755 --- a/src/ci/run.sh +++ b/src/ci/run.sh @@ -53,6 +53,7 @@ if ! isCI || isCiBranch auto || isCiBranch beta || isCiBranch try || isCiBranch HAS_METRICS=1 fi +RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-verbose-configure" RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-sccache" RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --disable-manage-submodules" RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-locked-deps" diff --git a/src/ci/stage-build.py b/src/ci/stage-build.py index 4141296bd422e..febc0492b9433 100644 --- a/src/ci/stage-build.py +++ b/src/ci/stage-build.py @@ -124,6 +124,12 @@ def llvm_bolt_profile_merged_file(self) -> Path: def metrics_path(self) -> Path: return self.build_root() / "build" / "metrics.json" + def executable_extension(self) -> str: + raise NotImplementedError + + def skipped_tests(self) -> Iterable[str]: + return () + class LinuxPipeline(Pipeline): def checkout_path(self) -> Path: @@ -152,6 +158,13 @@ def build_rustc_perf(self): def supports_bolt(self) -> bool: return True + def executable_extension(self) -> str: + return "" + + def skipped_tests(self) -> Iterable[str]: + # This test fails because of linker errors, as of June 2023. + yield "tests/ui/process/nofile-limit.rs" + class WindowsPipeline(Pipeline): def __init__(self): @@ -211,6 +224,13 @@ def rustc_profile_template_path(self) -> Path: def supports_bolt(self) -> bool: return False + def executable_extension(self) -> str: + return ".exe" + + def skipped_tests(self) -> Iterable[str]: + # This test fails as of June 2023 + yield "tests\\codegen\\vec-shrink-panik.rs" + def get_timestamp() -> float: return time.time() @@ -403,9 +423,9 @@ def delete_directory(path: Path): shutil.rmtree(path) -def unpack_archive(archive: Path): +def unpack_archive(archive: Path, target_dir: Optional[Path] = None): LOGGER.info(f"Unpacking archive `{archive}`") - shutil.unpack_archive(archive) + shutil.unpack_archive(str(archive), extract_dir=str(target_dir) if target_dir is not None else None) def download_file(src: str, target: Path): @@ -455,6 +475,7 @@ def cmd( ) return subprocess.run(args, env=environment, check=True) + class BenchmarkRunner: def run_rustc(self, pipeline: Pipeline): raise NotImplementedError @@ -465,6 +486,7 @@ def run_llvm(self, pipeline: Pipeline): def run_bolt(self, pipeline: Pipeline): raise NotImplementedError + class DefaultBenchmarkRunner(BenchmarkRunner): def run_rustc(self, pipeline: Pipeline): # Here we're profiling the `rustc` frontend, so we also include `Check`. @@ -478,6 +500,7 @@ def run_rustc(self, pipeline: Pipeline): LLVM_PROFILE_FILE=str(pipeline.rustc_profile_template_path()) ) ) + def run_llvm(self, pipeline: Pipeline): run_compiler_benchmarks( pipeline, @@ -494,6 +517,7 @@ def run_bolt(self, pipeline: Pipeline): crates=LLVM_BOLT_CRATES ) + def run_compiler_benchmarks( pipeline: Pipeline, profiles: List[str], @@ -596,11 +620,17 @@ def get_files(directory: Path, filter: Optional[Callable[[Path], bool]] = None) yield path -def build_rustc( +def bootstrap_build( pipeline: Pipeline, args: List[str], - env: Optional[Dict[str, str]] = None + env: Optional[Dict[str, str]] = None, + targets: Iterable[str] = ("library/std", ) ): + if env is None: + env = {} + else: + env = dict(env) + env["RUST_BACKTRACE"] = "1" arguments = [ sys.executable, pipeline.checkout_path() / "x.py", @@ -608,8 +638,7 @@ def build_rustc( "--target", PGO_HOST, "--host", PGO_HOST, "--stage", "2", - "library/std" - ] + args + ] + list(targets) + args cmd(arguments, env=env) @@ -650,10 +679,8 @@ def gather_llvm_profiles(pipeline: Pipeline, runner: BenchmarkRunner): def gather_rustc_profiles(pipeline: Pipeline, runner: BenchmarkRunner): LOGGER.info("Running benchmarks with PGO instrumented rustc") - runner.run_rustc(pipeline) - profile_path = pipeline.rustc_profile_merged_file() LOGGER.info(f"Merging Rustc PGO profiles to {profile_path}") cmd([ @@ -754,95 +781,215 @@ def record_metrics(pipeline: Pipeline, timer: Timer): if metrics is None: return llvm_steps = tuple(metrics.find_all_by_type("bootstrap::llvm::Llvm")) - assert len(llvm_steps) > 0 llvm_duration = sum(step.duration for step in llvm_steps) rustc_steps = tuple(metrics.find_all_by_type("bootstrap::compile::Rustc")) - assert len(rustc_steps) > 0 rustc_duration = sum(step.duration for step in rustc_steps) # The LLVM step is part of the Rustc step - rustc_duration -= llvm_duration + rustc_duration = max(0, rustc_duration - llvm_duration) - timer.add_duration("LLVM", llvm_duration) - timer.add_duration("Rustc", rustc_duration) + if llvm_duration > 0: + timer.add_duration("LLVM", llvm_duration) + if rustc_duration > 0: + timer.add_duration("Rustc", rustc_duration) log_metrics(metrics) -def execute_build_pipeline(timer: Timer, pipeline: Pipeline, runner: BenchmarkRunner, final_build_args: List[str]): +def run_tests(pipeline: Pipeline): + """ + After `dist` is executed, we extract its archived components into a sysroot directory, + and then use that extracted rustc as a stage0 compiler. + Then we run a subset of tests using that compiler, to have a basic smoke test which checks + whether the optimization pipeline hasn't broken something. + """ + build_dir = pipeline.build_root() / "build" + dist_dir = build_dir / "dist" + + def extract_dist_dir(name: str) -> Path: + target_dir = build_dir / "optimized-dist" + target_dir.mkdir(parents=True, exist_ok=True) + unpack_archive(dist_dir / f"{name}.tar.xz", target_dir=target_dir) + extracted_path = target_dir / name + assert extracted_path.is_dir() + return extracted_path + + # Extract rustc, libstd, cargo and src archives to create the optimized sysroot + rustc_dir = extract_dist_dir(f"rustc-nightly-{PGO_HOST}") / "rustc" + libstd_dir = extract_dist_dir(f"rust-std-nightly-{PGO_HOST}") / f"rust-std-{PGO_HOST}" + cargo_dir = extract_dist_dir(f"cargo-nightly-{PGO_HOST}") / f"cargo" + extracted_src_dir = extract_dist_dir("rust-src-nightly") / "rust-src" + + # We need to manually copy libstd to the extracted rustc sysroot + shutil.copytree( + libstd_dir / "lib" / "rustlib" / PGO_HOST / "lib", + rustc_dir / "lib" / "rustlib" / PGO_HOST / "lib" + ) + + # Extract sources - they aren't in the `rustc-nightly-{host}` tarball, so we need to manually copy libstd + # sources to the extracted sysroot. We need sources available so that `-Zsimulate-remapped-rust-src-base` + # works correctly. + shutil.copytree( + extracted_src_dir / "lib" / "rustlib" / "src", + rustc_dir / "lib" / "rustlib" / "src" + ) + + rustc_path = rustc_dir / "bin" / f"rustc{pipeline.executable_extension()}" + assert rustc_path.is_file() + cargo_path = cargo_dir / "bin" / f"cargo{pipeline.executable_extension()}" + assert cargo_path.is_file() + + config_content = f"""profile = "user" +changelog-seen = 2 + +[build] +rustc = "{rustc_path.as_posix()}" +cargo = "{cargo_path.as_posix()}" + +[llvm] +download-ci-llvm = true +""" + logging.info(f"Using following `config.toml` for running tests:\n{config_content}") + + # Simulate a stage 0 compiler with the extracted optimized dist artifacts. + with open("config.toml", "w") as f: + f.write(config_content) + + args = [ + sys.executable, + pipeline.checkout_path() / "x.py", + "test", + "--stage", "0", + "tests/assembly", + "tests/codegen", + "tests/codegen-units", + "tests/incremental", + "tests/mir-opt", + "tests/pretty", + "tests/run-pass-valgrind", + "tests/ui", + ] + for test_path in pipeline.skipped_tests(): + args.extend(["--exclude", test_path]) + cmd(args=args, env=dict( + COMPILETEST_FORCE_STAGE0="1" + )) + + +def execute_build_pipeline(timer: Timer, pipeline: Pipeline, runner: BenchmarkRunner, dist_build_args: List[str]): # Clear and prepare tmp directory shutil.rmtree(pipeline.opt_artifacts(), ignore_errors=True) os.makedirs(pipeline.opt_artifacts(), exist_ok=True) pipeline.build_rustc_perf() - # Stage 1: Build rustc + PGO instrumented LLVM - with timer.section("Stage 1 (LLVM PGO)") as stage1: - with stage1.section("Build rustc and LLVM") as rustc_build: - build_rustc(pipeline, args=[ - "--llvm-profile-generate" - ], env=dict( - LLVM_PROFILE_DIR=str(pipeline.llvm_profile_dir_root() / "prof-%p") - )) - record_metrics(pipeline, rustc_build) + """ + Stage 1: Build PGO instrumented rustc + + We use a normal build of LLVM, because gathering PGO profiles for LLVM and `rustc` at the same time + can cause issues. + """ + with timer.section("Stage 1 (rustc PGO)") as stage1: + with stage1.section("Build PGO instrumented rustc and LLVM") as rustc_pgo_instrument: + bootstrap_build(pipeline, args=[ + "--rust-profile-generate", + pipeline.rustc_profile_dir_root() + ]) + record_metrics(pipeline, rustc_pgo_instrument) with stage1.section("Gather profiles"): - gather_llvm_profiles(pipeline, runner) + gather_rustc_profiles(pipeline, runner) print_free_disk_space(pipeline) - clear_llvm_files(pipeline) - final_build_args += [ - "--llvm-profile-use", - pipeline.llvm_profile_merged_file() - ] - - # Stage 2: Build PGO instrumented rustc + LLVM - with timer.section("Stage 2 (rustc PGO)") as stage2: - with stage2.section("Build rustc and LLVM") as rustc_build: - build_rustc(pipeline, args=[ - "--rust-profile-generate", - pipeline.rustc_profile_dir_root() + with stage1.section("Build PGO optimized rustc") as rustc_pgo_use: + bootstrap_build(pipeline, args=[ + "--rust-profile-use", + pipeline.rustc_profile_merged_file() ]) - record_metrics(pipeline, rustc_build) + record_metrics(pipeline, rustc_pgo_use) + dist_build_args += [ + "--rust-profile-use", + pipeline.rustc_profile_merged_file() + ] + + """ + Stage 2: Gather LLVM PGO profiles + """ + with timer.section("Stage 2 (LLVM PGO)") as stage2: + # Clear normal LLVM artifacts + clear_llvm_files(pipeline) + + with stage2.section("Build PGO instrumented LLVM") as llvm_pgo_instrument: + bootstrap_build(pipeline, args=[ + "--llvm-profile-generate", + # We want to keep the already built PGO-optimized `rustc`. + "--keep-stage", "0", + "--keep-stage", "1" + ], env=dict( + LLVM_PROFILE_DIR=str(pipeline.llvm_profile_dir_root() / "prof-%p") + )) + record_metrics(pipeline, llvm_pgo_instrument) with stage2.section("Gather profiles"): - gather_rustc_profiles(pipeline, runner) + gather_llvm_profiles(pipeline, runner) + + dist_build_args += [ + "--llvm-profile-use", + pipeline.llvm_profile_merged_file(), + ] print_free_disk_space(pipeline) - clear_llvm_files(pipeline) - final_build_args += [ - "--rust-profile-use", - pipeline.rustc_profile_merged_file() - ] + # Clear PGO-instrumented LLVM artifacts + clear_llvm_files(pipeline) + + """ + Stage 3: Build BOLT instrumented LLVM - # Stage 3: Build rustc + BOLT instrumented LLVM + We build a PGO optimized LLVM in this step, then instrument it with BOLT and gather BOLT profiles. + Note that we don't remove LLVM artifacts after this step, so that they are reused in the final dist build. + BOLT instrumentation is performed "on-the-fly" when the LLVM library is copied to the sysroot of rustc, + therefore the LLVM artifacts on disk are not "tainted" with BOLT instrumentation and they can be reused. + """ if pipeline.supports_bolt(): with timer.section("Stage 3 (LLVM BOLT)") as stage3: - with stage3.section("Build rustc and LLVM") as rustc_build: - build_rustc(pipeline, args=[ + with stage3.section("Build BOLT instrumented LLVM") as llvm_bolt_instrument: + bootstrap_build(pipeline, args=[ "--llvm-profile-use", pipeline.llvm_profile_merged_file(), "--llvm-bolt-profile-generate", - "--rust-profile-use", - pipeline.rustc_profile_merged_file() + # We want to keep the already built PGO-optimized `rustc`. + "--keep-stage", "0", + "--keep-stage", "1" ]) - record_metrics(pipeline, rustc_build) + record_metrics(pipeline, llvm_bolt_instrument) with stage3.section("Gather profiles"): gather_llvm_bolt_profiles(pipeline, runner) - # LLVM is not being cleared here, we want to reuse the previous build - print_free_disk_space(pipeline) - final_build_args += [ - "--llvm-bolt-profile-use", - pipeline.llvm_bolt_profile_merged_file() - ] + dist_build_args += [ + "--llvm-bolt-profile-use", + pipeline.llvm_bolt_profile_merged_file() + ] + print_free_disk_space(pipeline) - # Stage 4: Build PGO optimized rustc + PGO/BOLT optimized LLVM - with timer.section("Stage 4 (final build)") as stage4: - cmd(final_build_args) - record_metrics(pipeline, stage4) + # We want to keep the already built PGO-optimized `rustc`. + dist_build_args += [ + "--keep-stage", "0", + "--keep-stage", "1" + ] + + """ + Final stage: Build PGO optimized rustc + PGO/BOLT optimized LLVM + """ + with timer.section("Final stage (dist build)") as final_stage: + cmd(dist_build_args) + record_metrics(pipeline, final_stage) + + # Try builds can be in various broken states, so we don't want to gatekeep them with tests + if not is_try_build(): + with timer.section("Run tests"): + run_tests(pipeline) def run(runner: BenchmarkRunner): diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index 73343ba9df51b..2b0431a159b1b 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -31,6 +31,7 @@ - [\*-unknown-fuchsia](platform-support/fuchsia.md) - [\*-kmc-solid_\*](platform-support/kmc-solid.md) - [loongarch\*-unknown-linux-\*](platform-support/loongarch-linux.md) + - [loongarch\*-unknown-none\*](platform-support/loongarch-none.md) - [m68k-unknown-linux-gnu](platform-support/m68k-unknown-linux-gnu.md) - [mips64-openwrt-linux-musl](platform-support/mips64-openwrt-linux-musl.md) - [mipsel-sony-psx](platform-support/mipsel-sony-psx.md) diff --git a/src/doc/rustc/src/command-line-arguments.md b/src/doc/rustc/src/command-line-arguments.md index 3be4382b0a3aa..2c7c05c0c4b88 100644 --- a/src/doc/rustc/src/command-line-arguments.md +++ b/src/doc/rustc/src/command-line-arguments.md @@ -58,8 +58,8 @@ Example: `-l static:+whole-archive=mylib`. The kind of library and the modifiers can also be specified in a [`#[link]` attribute][link-attribute]. If the kind is not specified in the `link` -attribute or on the command-line, it will link a dynamic library if available, -otherwise it will use a static library. If the kind is specified on the +attribute or on the command-line, it will link a dynamic library by default, +except when building a static executable. If the kind is specified on the command-line, it will override the kind specified in a `link` attribute. The name used in a `link` attribute may be overridden using the form `-l @@ -202,6 +202,12 @@ flag](codegen-options/index.md#extra-filename). The files are written to the current directory unless the [`--out-dir` flag](#option-out-dir) is used. Each emission type may also specify the output filename with the form `KIND=PATH`, which takes precedence over the `-o` flag. +Specifying `-o -` or `--emit KIND=-` asks rustc to emit to stdout. +Text output types (`asm`, `dep-info`, `llvm-ir` and `mir`) can be written to +stdout despite it being a tty or not. This will result in an error if any +binary output type is written to stdout that is a tty. +This will also result in an error if multiple output types +would be written to stdout, because they would be all mixed together. [LLVM bitcode]: https://llvm.org/docs/BitCodeFormat.html [LLVM IR]: https://llvm.org/docs/LangRef.html diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 3b2463aa5b2dd..f69dcd5983d33 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -267,6 +267,8 @@ target | std | host | notes `i686-uwp-windows-gnu` | ? | | `i686-uwp-windows-msvc` | ? | | `i686-wrs-vxworks` | ? | | +[`loongarch64-unknown-none`](platform-support/loongarch-none.md) | * | LoongArch64 Bare-metal (LP64D ABI) +[`loongarch64-unknown-none-softfloat`](platform-support/loongarch-none.md) | * | LoongArch64 Bare-metal (LP64S ABI) [`m68k-unknown-linux-gnu`](platform-support/m68k-unknown-linux-gnu.md) | ? | | Motorola 680x0 Linux `mips-unknown-linux-uclibc` | ✓ | | MIPS Linux with uClibc [`mips64-openwrt-linux-musl`](platform-support/mips64-openwrt-linux-musl.md) | ? | | MIPS64 for OpenWrt Linux MUSL @@ -298,6 +300,7 @@ target | std | host | notes `riscv32im-unknown-none-elf` | * | | Bare RISC-V (RV32IM ISA) [`riscv32imac-unknown-xous-elf`](platform-support/riscv32imac-unknown-xous-elf.md) | ? | | RISC-V Xous (RV32IMAC ISA) [`riscv32imc-esp-espidf`](platform-support/esp-idf.md) | ✓ | | RISC-V ESP-IDF +[`riscv32imac-esp-espidf`](platform-support/esp-idf.md) | ✓ | | RISC-V ESP-IDF `riscv64gc-unknown-freebsd` | | | RISC-V FreeBSD `riscv64gc-unknown-fuchsia` | | | RISC-V Fuchsia `riscv64gc-unknown-linux-musl` | | | RISC-V Linux (kernel 4.20, musl 1.2.0) diff --git a/src/doc/rustc/src/platform-support/armv7-sony-vita-newlibeabihf.md b/src/doc/rustc/src/platform-support/armv7-sony-vita-newlibeabihf.md index d75bd92beda73..49eed366dacf0 100644 --- a/src/doc/rustc/src/platform-support/armv7-sony-vita-newlibeabihf.md +++ b/src/doc/rustc/src/platform-support/armv7-sony-vita-newlibeabihf.md @@ -7,7 +7,7 @@ This tier supports the ARM Cortex A9 processor running on a PlayStation Vita con Rust support for this target is not affiliated with Sony, and is not derived from nor used with any official Sony SDK. -## Designated Developers +## Target maintainers * [@amg98](https://github.com/amg98) * [@nikarh](https://github.com/nikarh) @@ -27,9 +27,9 @@ In order to support some APIs, binaries must be linked against `libc` written for the target, using a linker for the target. These are provided by the VITASDK toolchain. -This target generates binaries in the ELF format. +This target generates binaries in the ELF format with thumb ISA. -## Building +## Building the target Rust does not ship pre-compiled artifacts for this target. You can use `build-std` flag to build binaries with `std`: @@ -37,43 +37,9 @@ Rust does not ship pre-compiled artifacts for this target. You can use `build-st cargo build -Z build-std=std,panic_abort --target=armv7-sony-vita-newlibeabihf --release ``` -## Cross-compilation - -This target can be cross-compiled from `x86_64` on either Windows, MacOS or Linux systems. Other hosts are not supported for cross-compilation. - -## Testing - -Currently there is no support to run the rustc test suite for this target. - -## Building and Running Rust Programs +## Building Rust programs -`std` support for this target relies on newlib. In order to work, newlib must be initialized correctly. The easiest way to achieve this with VITASDK newlib implementation is by compiling your program as a staticlib with and exposing your main function from rust to `_init` function in `crt0`. - -Add this to your `Cargo.toml`: - -```toml -[lib] -crate-type = ["staticlib"] - -[profile.release] -panic = 'abort' -lto = true -opt-level = 3 -``` - -Your entrypoint should look roughly like this, `src/lib.rs`: -```rust,ignore,no_run -#[used] -#[export_name = "_newlib_heap_size_user"] -pub static _NEWLIB_HEAP_SIZE_USER: u32 = 100 * 1024 * 1024; // Default heap size is only 32mb, increase it to something suitable for your application - -#[no_mangle] -pub extern "C" fn main() { - println!("Hello, world!"); -} -``` - -To test your developed rust programs on PlayStation Vita, first you must correctly link and package your rust staticlib. These steps can be preformed using tools available in VITASDK, and can be automated using tools like `cargo-make`. +To test your developed rust programs on PlayStation Vita, first you must correctly package your elf. These steps can be preformed using tools available in VITASDK, and can be automated using a tool like `cargo-make`. First, set up environment variables for `VITASDK`, and it's binaries: @@ -88,40 +54,21 @@ Use the example below as a template for your project: [env] TITLE = "Rust Hello World" TITLEID = "RUST00001" -# Add other libs required by your project here -LINKER_LIBS = "-lpthread -lm -lmathneon" # At least a "sce_sys" folder should be place there for app metadata (title, icons, description...) # You can find sample assets for that on $VITASDK/share/gcc-arm-vita-eabi/samples/hello_world/sce_sys/ STATIC_DIR = "static" # Folder where static assets should be placed (sce_sys folder is at $STATIC_DIR/sce_sys) CARGO_TARGET_DIR = { script = ["echo ${CARGO_TARGET_DIR:=target}"] } -RUST_TARGET = "armv7-sony-vita-newlibeabihf" CARGO_OUT_DIR = "${CARGO_TARGET_DIR}/${RUST_TARGET}/release" -TARGET_LINKER = "arm-vita-eabi-gcc" -TARGET_LINKER_FLAGS = "-Wl,-q" - [tasks.build] -description = "Build the project using `cargo` as a static lib." +description = "Build the project using `cargo`." command = "cargo" args = ["build", "-Z", "build-std=std,panic_abort", "--target=armv7-sony-vita-newlibeabihf", "--release"] -[tasks.link] -description = "Build an ELF executable using the `vitasdk` linker." -dependencies = ["build"] -script = [ - """ - ${TARGET_LINKER} ${TARGET_LINKER_FLAGS} \ - -L"${CARGO_OUT_DIR}" \ - -l"${CARGO_MAKE_CRATE_FS_NAME}" \ - ${LINKER_LIBS} \ - -o"${CARGO_OUT_DIR}/${CARGO_MAKE_CRATE_NAME}.elf" - """ -] - [tasks.strip] description = "Strip the produced ELF executable." -dependencies = ["link"] +dependencies = ["build"] command = "arm-vita-eabi-strip" args = ["-g", '${CARGO_OUT_DIR}/${CARGO_MAKE_CRATE_FS_NAME}.elf'] @@ -194,3 +141,11 @@ script = [ ``` After running the above script, you should be able to get a *.vpk file in the same folder your *.elf executable resides. Now you can pick it and install it on your own PlayStation Vita using, or you can use an [Vita3K](https://vita3k.org/) emulator. + +## Testing + +Currently there is no support to run the rustc test suite for this target. + +## Cross-compilation + +This target can be cross-compiled from `x86_64` on either Windows, MacOS or Linux systems. Other hosts are not supported for cross-compilation. diff --git a/src/doc/rustc/src/platform-support/esp-idf.md b/src/doc/rustc/src/platform-support/esp-idf.md index 8a4ca347e22f5..4bbe35709b087 100644 --- a/src/doc/rustc/src/platform-support/esp-idf.md +++ b/src/doc/rustc/src/platform-support/esp-idf.md @@ -13,11 +13,12 @@ Targets for the [ESP-IDF](https://github.com/espressif/esp-idf) development fram The target names follow this format: `$ARCH-esp-espidf`, where `$ARCH` specifies the target processor architecture. The following targets are currently defined: -| Target name | Target CPU(s) | -|--------------------------------|-----------------------| -| `riscv32imc-esp-espidf` | [ESP32-C3](https://www.espressif.com/en/products/socs/esp32-c3) | +| Target name | Target CPU(s) | Minimum ESP-IDF version | +|--------------------------------|-----------------------|-------------------------| +| `riscv32imc-esp-espidf` | [ESP32-C3](https://www.espressif.com/en/products/socs/esp32-c3) | `v4.3` | +| `riscv32imac-esp-espidf` | [ESP32-C6](https://www.espressif.com/en/products/socs/esp32-c6) | `v5.1` | -The minimum supported ESP-IDF version is `v4.3`, though it is recommended to use the latest stable release if possible. +It is recommended to use the latest ESP-IDF stable release if possible. ## Building the target diff --git a/src/doc/rustc/src/platform-support/loongarch-none.md b/src/doc/rustc/src/platform-support/loongarch-none.md new file mode 100644 index 0000000000000..d0ae3425fa8eb --- /dev/null +++ b/src/doc/rustc/src/platform-support/loongarch-none.md @@ -0,0 +1,79 @@ +# `loongarch*-unknown-none*` + +**Tier: 3** + +Freestanding/bare-metal LoongArch64 binaries in ELF format: firmware, kernels, etc. + +| Target | Descriptions | +|------------------------------------|-------------------------------------------------------| +| loongarch64-unknown-none | LoongArch 64-bit, LP64D ABI (freestanding, hardfloat) | +| loongarch64-unknown-none-softfloat | LoongArch 64-bit, LP64S ABI (freestanding, softfloat) | + +## Target maintainers + +- [WANG Rui](https://github.com/heiher) `wangrui@loongson.cn` +- [WANG Xuerui](https://github.com/xen0n) `git@xen0n.name` + +## Requirements + +This target is cross-compiled. There is no support for `std`. There is no +default allocator, but it's possible to use `alloc` by supplying an allocator. + +This allows the generated code to run in environments, such as kernels, which +may need to avoid the use of such registers or which may have special considerations +about the use of such registers (e.g. saving and restoring them to avoid breaking +userspace code using the same registers). You can change code generation to use +additional CPU features via the `-C target-feature=` codegen options to rustc, or +via the `#[target_feature]` mechanism within Rust code. + +By default, code generated with this target should run on any `loongarch` +hardware; enabling additional target features may raise this baseline. + +Code generated with this target will use the `small` code model by default. +You can change this using the `-C code-model=` option to rustc. + +On `loongarch64-unknown-none*`, `extern "C"` uses the [standard calling +convention](https://loongson.github.io/LoongArch-Documentation/LoongArch-ELF-ABI-EN.html). + +This target generates binaries in the ELF format. Any alternate formats or +special considerations for binary layout will require linker options or linker +scripts. + +## Building the target + +You can build Rust with support for the target by adding it to the `target` +list in `config.toml`: + +```toml +[build] +build-stage = 1 +target = ["loongarch64-unknown-none"] +``` + +## Building Rust programs + +```text +# target flag may be used with any cargo or rustc command +cargo build --target loongarch64-unknown-none +``` + +## Testing + +As `loongarch64-unknown-none*` supports a variety of different environments and does +not support `std`, this target does not support running the Rust test suite. + +## Cross-compilation toolchains and C code + +If you want to compile C code along with Rust (such as for Rust crates with C +dependencies), you will need an appropriate `loongarch` toolchain. + +Rust *may* be able to use an `loongarch64-unknown-linux-gnu-` toolchain with +appropriate standalone flags to build for this toolchain (depending on the assumptions +of that toolchain, see below), or you may wish to use a separate +`loongarch64-unknown-none` toolchain. + +On some `loongarch` hosts that use ELF binaries, you *may* be able to use the host +C toolchain, if it does not introduce assumptions about the host environment +that don't match the expectations of a standalone environment. Otherwise, you +may need a separate toolchain for standalone/freestanding development, just as +when cross-compiling from a non-`loongarch` platform. diff --git a/src/doc/rustc/src/platform-support/nto-qnx.md b/src/doc/rustc/src/platform-support/nto-qnx.md index 0d815c9b59807..b376c4a84ac9e 100644 --- a/src/doc/rustc/src/platform-support/nto-qnx.md +++ b/src/doc/rustc/src/platform-support/nto-qnx.md @@ -164,18 +164,12 @@ export exclude_tests=' --exclude tests/run-make-fulldeps' env $build_env \ - ./x.py test -j 1 \ + ./x.py test \ $exclude_tests \ --stage 1 \ --target x86_64-pc-nto-qnx710 ``` -Currently, only one thread can be used when testing due to limitations in `libc::fork` and `libc::posix_spawnp`. -See [fork documentation](https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/f/fork.html) -(error section) for more information. -This can be achieved by using the `-j 1` parameter in the `x.py` call. -This issue is being researched and we will try to allow parallelism in the future. - ## Building Rust programs Rust does not yet ship pre-compiled artifacts for this target. diff --git a/src/etc/gdb_load_rust_pretty_printers.py b/src/etc/gdb_load_rust_pretty_printers.py index 856b5df2de70b..491b6ba9e9e69 100644 --- a/src/etc/gdb_load_rust_pretty_printers.py +++ b/src/etc/gdb_load_rust_pretty_printers.py @@ -1,3 +1,14 @@ +# Add this folder to the python sys path; GDB Python-interpreter will now find modules in this path +import sys +from os import path +self_dir = path.dirname(path.realpath(__file__)) +sys.path.append(self_dir) + import gdb import gdb_lookup -gdb_lookup.register_printers(gdb.current_objfile()) + +# current_objfile can be none; even with `gdb foo-app`; sourcing this file after gdb init now works +try: + gdb_lookup.register_printers(gdb.current_objfile()) +except Exception: + gdb_lookup.register_printers(gdb.selected_inferior().progspace) diff --git a/src/etc/rust_analyzer_settings.json b/src/etc/rust_analyzer_settings.json index dd01bfaa7252d..d9c4645f0b3b7 100644 --- a/src/etc/rust_analyzer_settings.json +++ b/src/etc/rust_analyzer_settings.json @@ -7,7 +7,14 @@ "check", "--json-output" ], - "rust-analyzer.linkedProjects": ["src/bootstrap/Cargo.toml", "Cargo.toml"], + "rust-analyzer.linkedProjects": [ + "Cargo.toml", + "src/tools/x/Cargo.toml", + "src/bootstrap/Cargo.toml", + "src/tools/rust-analyzer/Cargo.toml", + "compiler/rustc_codegen_cranelift/Cargo.toml", + "compiler/rustc_codegen_gcc/Cargo.toml" + ], "rust-analyzer.rustfmt.overrideCommand": [ "./build/host/rustfmt/bin/rustfmt", "--edition=2021" diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index baf2b0a858529..c8a40e0150112 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -124,7 +124,7 @@ where unsafety: hir::Unsafety::Normal, generics: new_generics, trait_: Some(clean_trait_ref_with_bindings(self.cx, trait_ref, ThinVec::new())), - for_: clean_middle_ty(ty::Binder::dummy(ty), self.cx, None), + for_: clean_middle_ty(ty::Binder::dummy(ty), self.cx, None, None), items: Vec::new(), polarity, kind: ImplKind::Auto, diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs index 83887cc44b96b..a36041588510f 100644 --- a/src/librustdoc/clean/blanket_impl.rs +++ b/src/librustdoc/clean/blanket_impl.rs @@ -104,10 +104,15 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { // the post-inference `trait_ref`, as it's more accurate. trait_: Some(clean_trait_ref_with_bindings( cx, - ty::Binder::dummy(trait_ref.skip_binder()), + ty::Binder::dummy(trait_ref.subst_identity()), ThinVec::new(), )), - for_: clean_middle_ty(ty::Binder::dummy(ty.skip_binder()), cx, None), + for_: clean_middle_ty( + ty::Binder::dummy(ty.subst_identity()), + cx, + None, + None, + ), items: cx .tcx .associated_items(impl_def_id) @@ -116,9 +121,10 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { .collect::>(), polarity: ty::ImplPolarity::Positive, kind: ImplKind::Blanket(Box::new(clean_middle_ty( - ty::Binder::dummy(trait_ref.skip_binder().self_ty()), + ty::Binder::dummy(trait_ref.subst_identity().self_ty()), cx, None, + None, ))), }))), cfg: None, diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 7dc08b3b1ffa6..3aa98da1c803a 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -278,8 +278,12 @@ fn build_union(cx: &mut DocContext<'_>, did: DefId) -> clean::Union { fn build_type_alias(cx: &mut DocContext<'_>, did: DefId) -> Box { let predicates = cx.tcx.explicit_predicates_of(did); - let type_ = - clean_middle_ty(ty::Binder::dummy(cx.tcx.type_of(did).subst_identity()), cx, Some(did)); + let type_ = clean_middle_ty( + ty::Binder::dummy(cx.tcx.type_of(did).subst_identity()), + cx, + Some(did), + None, + ); Box::new(clean::Typedef { type_, @@ -386,9 +390,12 @@ pub(crate) fn build_impl( let for_ = match &impl_item { Some(impl_) => clean_ty(impl_.self_ty, cx), - None => { - clean_middle_ty(ty::Binder::dummy(tcx.type_of(did).subst_identity()), cx, Some(did)) - } + None => clean_middle_ty( + ty::Binder::dummy(tcx.type_of(did).subst_identity()), + cx, + Some(did), + None, + ), }; // Only inline impl if the implementing type is @@ -630,6 +637,7 @@ fn build_const(cx: &mut DocContext<'_>, def_id: DefId) -> clean::Constant { ty::Binder::dummy(cx.tcx.type_of(def_id).subst_identity()), cx, Some(def_id), + None, ), kind: clean::ConstantKind::Extern { def_id }, } @@ -641,6 +649,7 @@ fn build_static(cx: &mut DocContext<'_>, did: DefId, mutable: bool) -> clean::St ty::Binder::dummy(cx.tcx.type_of(did).subst_identity()), cx, Some(did), + None, ), mutability: if mutable { Mutability::Mut } else { Mutability::Not }, expr: None, diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 5fd867189fd71..0c7950bfd3ddd 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -31,6 +31,7 @@ use rustc_middle::{bug, span_bug}; use rustc_span::hygiene::{AstPass, MacroKind}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{self, ExpnKind}; +use rustc_trait_selection::traits::wf::object_region_bounds; use std::borrow::Cow; use std::collections::hash_map::Entry; @@ -253,6 +254,7 @@ pub(crate) fn clean_const<'tcx>(constant: &hir::ConstArg, cx: &mut DocContext<'t ty::Binder::dummy(cx.tcx.type_of(def_id).subst_identity()), cx, Some(def_id), + None, ), kind: ConstantKind::Anonymous { body: constant.value.body }, } @@ -264,7 +266,7 @@ pub(crate) fn clean_middle_const<'tcx>( ) -> Constant { // FIXME: instead of storing the stringified expression, store `self` directly instead. Constant { - type_: clean_middle_ty(constant.map_bound(|c| c.ty()), cx, None), + type_: clean_middle_ty(constant.map_bound(|c| c.ty()), cx, None, None), kind: ConstantKind::TyConst { expr: constant.skip_binder().to_string().into() }, } } @@ -370,7 +372,7 @@ fn clean_poly_trait_predicate<'tcx>( let poly_trait_ref = pred.map_bound(|pred| pred.trait_ref); Some(WherePredicate::BoundPredicate { - ty: clean_middle_ty(poly_trait_ref.self_ty(), cx, None), + ty: clean_middle_ty(poly_trait_ref.self_ty(), cx, None, None), bounds: vec![clean_poly_trait_ref_with_bindings(cx, poly_trait_ref, ThinVec::new())], bound_params: Vec::new(), }) @@ -396,7 +398,7 @@ fn clean_type_outlives_predicate<'tcx>( let ty::OutlivesPredicate(ty, lt) = pred.skip_binder(); Some(WherePredicate::BoundPredicate { - ty: clean_middle_ty(pred.rebind(ty), cx, None), + ty: clean_middle_ty(pred.rebind(ty), cx, None, None), bounds: vec![GenericBound::Outlives( clean_middle_region(lt).expect("failed to clean lifetimes"), )], @@ -409,7 +411,7 @@ fn clean_middle_term<'tcx>( cx: &mut DocContext<'tcx>, ) -> Term { match term.skip_binder().unpack() { - ty::TermKind::Ty(ty) => Term::Type(clean_middle_ty(term.rebind(ty), cx, None)), + ty::TermKind::Ty(ty) => Term::Type(clean_middle_ty(term.rebind(ty), cx, None, None)), ty::TermKind::Const(c) => Term::Constant(clean_middle_const(term.rebind(c), cx)), } } @@ -462,7 +464,7 @@ fn clean_projection<'tcx>( let trait_ = clean_trait_ref_with_bindings(cx, ty.map_bound(|ty| ty.trait_ref(cx.tcx)), ThinVec::new()); - let self_type = clean_middle_ty(ty.map_bound(|ty| ty.self_ty()), cx, None); + let self_type = clean_middle_ty(ty.map_bound(|ty| ty.self_ty()), cx, None, None); let self_def_id = if let Some(def_id) = def_id { cx.tcx.opt_parent(def_id).or(Some(def_id)) } else { @@ -493,8 +495,13 @@ fn projection_to_path_segment<'tcx>( PathSegment { name: item.name, args: GenericArgs::AngleBracketed { - args: substs_to_args(cx, ty.map_bound(|ty| &ty.substs[generics.parent_count..]), false) - .into(), + args: substs_to_args( + cx, + ty.map_bound(|ty| &ty.substs[generics.parent_count..]), + false, + None, + ) + .into(), bindings: Default::default(), }, } @@ -514,6 +521,7 @@ fn clean_generic_param_def<'tcx>( ty::Binder::dummy(cx.tcx.type_of(def.def_id).subst_identity()), cx, Some(def.def_id), + None, )) } else { None @@ -540,6 +548,7 @@ fn clean_generic_param_def<'tcx>( ), cx, Some(def.def_id), + None, )), default: match has_default { true => Some(Box::new( @@ -871,7 +880,7 @@ fn clean_ty_generics<'tcx>( let crate::core::ImplTraitParam::ParamIndex(idx) = param else { unreachable!() }; if let Some(proj) = impl_trait_proj.remove(&idx) { for (trait_did, name, rhs, bound_params) in proj { - let rhs = clean_middle_ty(rhs, cx, None); + let rhs = clean_middle_ty(rhs, cx, None, None); simplify::merge_bounds( cx, &mut bounds, @@ -1126,7 +1135,7 @@ fn clean_fn_decl_from_did_and_sig<'tcx>( // We assume all empty tuples are default return type. This theoretically can discard `-> ()`, // but shouldn't change any code meaning. - let output = clean_middle_ty(sig.output(), cx, None); + let output = clean_middle_ty(sig.output(), cx, None, None); FnDecl { output, @@ -1136,7 +1145,7 @@ fn clean_fn_decl_from_did_and_sig<'tcx>( .inputs() .iter() .map(|t| Argument { - type_: clean_middle_ty(t.map_bound(|t| *t), cx, None), + type_: clean_middle_ty(t.map_bound(|t| *t), cx, None, None), name: names .next() .map(|i| i.name) @@ -1190,8 +1199,12 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext hir::TraitItemKind::Type(bounds, Some(default)) => { let generics = enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx)); let bounds = bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(); - let item_type = - clean_middle_ty(ty::Binder::dummy(hir_ty_to_ty(cx.tcx, default)), cx, None); + let item_type = clean_middle_ty( + ty::Binder::dummy(hir_ty_to_ty(cx.tcx, default)), + cx, + None, + None, + ); AssocTypeItem( Box::new(Typedef { type_: clean_ty(default, cx), @@ -1224,14 +1237,18 @@ pub(crate) fn clean_impl_item<'tcx>( } hir::ImplItemKind::Fn(ref sig, body) => { let m = clean_function(cx, sig, impl_.generics, FunctionArgs::Body(body)); - let defaultness = cx.tcx.impl_defaultness(impl_.owner_id); + let defaultness = cx.tcx.defaultness(impl_.owner_id); MethodItem(m, Some(defaultness)) } hir::ImplItemKind::Type(hir_ty) => { let type_ = clean_ty(hir_ty, cx); let generics = clean_generics(impl_.generics, cx); - let item_type = - clean_middle_ty(ty::Binder::dummy(hir_ty_to_ty(cx.tcx, hir_ty)), cx, None); + let item_type = clean_middle_ty( + ty::Binder::dummy(hir_ty_to_ty(cx.tcx, hir_ty)), + cx, + None, + None, + ); AssocTypeItem( Box::new(Typedef { type_, generics, item_type: Some(item_type) }), Vec::new(), @@ -1254,11 +1271,12 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( ty::Binder::dummy(tcx.type_of(assoc_item.def_id).subst_identity()), cx, Some(assoc_item.def_id), + None, ); let provided = match assoc_item.container { ty::ImplContainer => true, - ty::TraitContainer => tcx.impl_defaultness(assoc_item.def_id).has_value(), + ty::TraitContainer => tcx.defaultness(assoc_item.def_id).has_value(), }; if provided { AssocConstItem(ty, ConstantKind::Extern { def_id: assoc_item.def_id }) @@ -1440,13 +1458,14 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( } generics.where_predicates = where_predicates; - if tcx.impl_defaultness(assoc_item.def_id).has_value() { + if tcx.defaultness(assoc_item.def_id).has_value() { AssocTypeItem( Box::new(Typedef { type_: clean_middle_ty( ty::Binder::dummy(tcx.type_of(assoc_item.def_id).subst_identity()), cx, Some(assoc_item.def_id), + None, ), generics, // FIXME: should we obtain the Type from HIR and pass it on here? @@ -1465,6 +1484,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( ty::Binder::dummy(tcx.type_of(assoc_item.def_id).subst_identity()), cx, Some(assoc_item.def_id), + None, ), generics: Generics { params: ThinVec::new(), @@ -1510,7 +1530,7 @@ fn clean_qpath<'tcx>(hir_ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type if !ty.has_escaping_bound_vars() && let Some(normalized_value) = normalize(cx, ty::Binder::dummy(ty)) { - return clean_middle_ty(normalized_value, cx, None); + return clean_middle_ty(normalized_value, cx, None, None); } let trait_segments = &p.segments[..p.segments.len() - 1]; @@ -1738,11 +1758,140 @@ fn normalize<'tcx>( } } +fn clean_trait_object_lifetime_bound<'tcx>( + region: ty::Region<'tcx>, + container: Option>, + preds: &'tcx ty::List>, + tcx: TyCtxt<'tcx>, +) -> Option { + if can_elide_trait_object_lifetime_bound(region, container, preds, tcx) { + return None; + } + + // Since there is a semantic difference between an implicitly elided (i.e. "defaulted") object + // lifetime and an explicitly elided object lifetime (`'_`), we intentionally don't hide the + // latter contrary to `clean_middle_region`. + match *region { + ty::ReStatic => Some(Lifetime::statik()), + ty::ReEarlyBound(region) if region.name != kw::Empty => Some(Lifetime(region.name)), + ty::ReLateBound(_, ty::BoundRegion { kind: ty::BrNamed(_, name), .. }) + if name != kw::Empty => + { + Some(Lifetime(name)) + } + ty::ReEarlyBound(_) + | ty::ReLateBound(..) + | ty::ReFree(_) + | ty::ReVar(_) + | ty::RePlaceholder(_) + | ty::ReErased + | ty::ReError(_) => None, + } +} + +fn can_elide_trait_object_lifetime_bound<'tcx>( + region: ty::Region<'tcx>, + container: Option>, + preds: &'tcx ty::List>, + tcx: TyCtxt<'tcx>, +) -> bool { + // Below we quote extracts from https://doc.rust-lang.org/reference/lifetime-elision.html#default-trait-object-lifetimes + + // > If the trait object is used as a type argument of a generic type then the containing type is + // > first used to try to infer a bound. + let default = container + .map_or(ObjectLifetimeDefault::Empty, |container| container.object_lifetime_default(tcx)); + + // > If there is a unique bound from the containing type then that is the default + // If there is a default object lifetime and the given region is lexically equal to it, elide it. + match default { + ObjectLifetimeDefault::Static => return *region == ty::ReStatic, + // FIXME(fmease): Don't compare lexically but respect de Bruijn indices etc. to handle shadowing correctly. + ObjectLifetimeDefault::Arg(default) => return region.get_name() == default.get_name(), + // > If there is more than one bound from the containing type then an explicit bound must be specified + // Due to ambiguity there is no default trait-object lifetime and thus elision is impossible. + // Don't elide the lifetime. + ObjectLifetimeDefault::Ambiguous => return false, + // There is no meaningful bound. Further processing is needed... + ObjectLifetimeDefault::Empty => {} + } + + // > If neither of those rules apply, then the bounds on the trait are used: + match *object_region_bounds(tcx, preds) { + // > If the trait has no lifetime bounds, then the lifetime is inferred in expressions + // > and is 'static outside of expressions. + // FIXME: If we are in an expression context (i.e. fn bodies and const exprs) then the default is + // `'_` and not `'static`. Only if we are in a non-expression one, the default is `'static`. + // Note however that at the time of this writing it should be fine to disregard this subtlety + // as we neither render const exprs faithfully anyway (hiding them in some places or using `_` instead) + // nor show the contents of fn bodies. + [] => *region == ty::ReStatic, + // > If the trait is defined with a single lifetime bound then that bound is used. + // > If 'static is used for any lifetime bound then 'static is used. + // FIXME(fmease): Don't compare lexically but respect de Bruijn indices etc. to handle shadowing correctly. + [object_region] => object_region.get_name() == region.get_name(), + // There are several distinct trait regions and none are `'static`. + // Due to ambiguity there is no default trait-object lifetime and thus elision is impossible. + // Don't elide the lifetime. + _ => false, + } +} + +#[derive(Debug)] +pub(crate) enum ContainerTy<'tcx> { + Ref(ty::Region<'tcx>), + Regular { ty: DefId, substs: ty::Binder<'tcx, &'tcx [ty::GenericArg<'tcx>]>, arg: usize }, +} + +impl<'tcx> ContainerTy<'tcx> { + fn object_lifetime_default(self, tcx: TyCtxt<'tcx>) -> ObjectLifetimeDefault<'tcx> { + match self { + Self::Ref(region) => ObjectLifetimeDefault::Arg(region), + Self::Regular { ty: container, substs, arg: index } => { + let (DefKind::Struct + | DefKind::Union + | DefKind::Enum + | DefKind::TyAlias + | DefKind::Trait + | DefKind::AssocTy + | DefKind::Variant) = tcx.def_kind(container) + else { + return ObjectLifetimeDefault::Empty; + }; + + let generics = tcx.generics_of(container); + let param = generics.params[index].def_id; + let default = tcx.object_lifetime_default(param); + + match default { + rbv::ObjectLifetimeDefault::Param(lifetime) => { + let index = generics.param_def_id_to_index[&lifetime]; + let arg = substs.skip_binder()[index as usize].expect_region(); + ObjectLifetimeDefault::Arg(arg) + } + rbv::ObjectLifetimeDefault::Empty => ObjectLifetimeDefault::Empty, + rbv::ObjectLifetimeDefault::Static => ObjectLifetimeDefault::Static, + rbv::ObjectLifetimeDefault::Ambiguous => ObjectLifetimeDefault::Ambiguous, + } + } + } + } +} + +#[derive(Debug, Clone, Copy)] +pub(crate) enum ObjectLifetimeDefault<'tcx> { + Empty, + Static, + Ambiguous, + Arg(ty::Region<'tcx>), +} + #[instrument(level = "trace", skip(cx), ret)] pub(crate) fn clean_middle_ty<'tcx>( bound_ty: ty::Binder<'tcx, Ty<'tcx>>, cx: &mut DocContext<'tcx>, parent_def_id: Option, + container: Option>, ) -> Type { let bound_ty = normalize(cx, bound_ty).unwrap_or(bound_ty); match *bound_ty.skip_binder().kind() { @@ -1753,19 +1902,24 @@ pub(crate) fn clean_middle_ty<'tcx>( ty::Uint(uint_ty) => Primitive(uint_ty.into()), ty::Float(float_ty) => Primitive(float_ty.into()), ty::Str => Primitive(PrimitiveType::Str), - ty::Slice(ty) => Slice(Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None))), + ty::Slice(ty) => Slice(Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None, None))), ty::Array(ty, mut n) => { n = n.eval(cx.tcx, ty::ParamEnv::reveal_all()); let n = print_const(cx, n); - Array(Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None)), n.into()) + Array(Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None, None)), n.into()) } ty::RawPtr(mt) => { - RawPointer(mt.mutbl, Box::new(clean_middle_ty(bound_ty.rebind(mt.ty), cx, None))) + RawPointer(mt.mutbl, Box::new(clean_middle_ty(bound_ty.rebind(mt.ty), cx, None, None))) } ty::Ref(r, ty, mutbl) => BorrowedRef { lifetime: clean_middle_region(r), mutability: mutbl, - type_: Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None)), + type_: Box::new(clean_middle_ty( + bound_ty.rebind(ty), + cx, + None, + Some(ContainerTy::Ref(r)), + )), }, ty::FnDef(..) | ty::FnPtr(_) => { // FIXME: should we merge the outer and inner binders somehow? @@ -1817,10 +1971,8 @@ pub(crate) fn clean_middle_ty<'tcx>( inline::record_extern_fqn(cx, did, ItemType::Trait); - // FIXME(fmease): Hide the trait-object lifetime bound if it coincides with its default - // to partially address #44306. Follow the rules outlined at - // https://doc.rust-lang.org/reference/lifetime-elision.html#default-trait-object-lifetimes - let lifetime = clean_middle_region(*reg); + let lifetime = clean_trait_object_lifetime_bound(*reg, container, obj, cx.tcx); + let mut bounds = dids .map(|did| { let empty = ty::Binder::dummy(InternalSubsts::empty()); @@ -1869,7 +2021,7 @@ pub(crate) fn clean_middle_ty<'tcx>( DynTrait(bounds, lifetime) } ty::Tuple(t) => { - Tuple(t.iter().map(|t| clean_middle_ty(bound_ty.rebind(t), cx, None)).collect()) + Tuple(t.iter().map(|t| clean_middle_ty(bound_ty.rebind(t), cx, None, None)).collect()) } ty::Alias(ty::Projection, ref data) => { @@ -1878,7 +2030,7 @@ pub(crate) fn clean_middle_ty<'tcx>( ty::Alias(ty::Inherent, alias_ty) => { let alias_ty = bound_ty.rebind(alias_ty); - let self_type = clean_middle_ty(alias_ty.map_bound(|ty| ty.self_ty()), cx, None); + let self_type = clean_middle_ty(alias_ty.map_bound(|ty| ty.self_ty()), cx, None, None); Type::QPath(Box::new(QPathData { assoc: PathSegment { @@ -1888,6 +2040,7 @@ pub(crate) fn clean_middle_ty<'tcx>( cx, alias_ty.map_bound(|ty| ty.substs.as_slice()), true, + None, ) .into(), bindings: Default::default(), @@ -2023,6 +2176,7 @@ pub(crate) fn clean_middle_field<'tcx>(field: &ty::FieldDef, cx: &mut DocContext ty::Binder::dummy(cx.tcx.type_of(field.did).subst_identity()), cx, Some(field.did), + None, ), cx, ) @@ -2180,7 +2334,8 @@ fn get_all_import_attributes<'hir>( // This is the "original" reexport so we get all its attributes without filtering them. attrs = import_attrs.iter().map(|attr| (Cow::Borrowed(attr), Some(def_id))).collect(); first = false; - } else { + // We don't add attributes of an intermediate re-export if it has `#[doc(hidden)]`. + } else if !cx.tcx.is_doc_hidden(def_id) { add_without_unwanted_attributes(&mut attrs, import_attrs, is_inline, Some(def_id)); } } @@ -2313,7 +2468,12 @@ fn clean_maybe_renamed_item<'tcx>( ItemKind::TyAlias(hir_ty, generics) => { *cx.current_type_aliases.entry(def_id).or_insert(0) += 1; let rustdoc_ty = clean_ty(hir_ty, cx); - let ty = clean_middle_ty(ty::Binder::dummy(hir_ty_to_ty(cx.tcx, hir_ty)), cx, None); + let ty = clean_middle_ty( + ty::Binder::dummy(hir_ty_to_ty(cx.tcx, hir_ty)), + cx, + None, + None, + ); let generics = clean_generics(generics, cx); if let Some(count) = cx.current_type_aliases.get_mut(&def_id) { *count -= 1; @@ -2417,6 +2577,7 @@ fn clean_impl<'tcx>( ty::Binder::dummy(tcx.type_of(def_id).subst_identity()), cx, Some(def_id.to_def_id()), + None, )), _ => None, }); diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 366f93952963f..294de12cea8e2 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -73,8 +73,9 @@ pub(crate) fn krate(cx: &mut DocContext<'_>) -> Crate { pub(crate) fn substs_to_args<'tcx>( cx: &mut DocContext<'tcx>, - substs: ty::Binder<'tcx, &[ty::subst::GenericArg<'tcx>]>, + substs: ty::Binder<'tcx, &'tcx [ty::subst::GenericArg<'tcx>]>, mut skip_first: bool, + container: Option, ) -> Vec { let mut ret_val = Vec::with_capacity(substs.skip_binder().len().saturating_sub(if skip_first { @@ -82,19 +83,29 @@ pub(crate) fn substs_to_args<'tcx>( } else { 0 })); - ret_val.extend(substs.iter().filter_map(|kind| match kind.skip_binder().unpack() { - GenericArgKind::Lifetime(lt) => { - Some(GenericArg::Lifetime(clean_middle_region(lt).unwrap_or(Lifetime::elided()))) - } - GenericArgKind::Type(_) if skip_first => { - skip_first = false; - None - } - GenericArgKind::Type(ty) => { - Some(GenericArg::Type(clean_middle_ty(kind.rebind(ty), cx, None))) - } - GenericArgKind::Const(ct) => { - Some(GenericArg::Const(Box::new(clean_middle_const(kind.rebind(ct), cx)))) + + ret_val.extend(substs.iter().enumerate().filter_map(|(index, kind)| { + match kind.skip_binder().unpack() { + GenericArgKind::Lifetime(lt) => { + Some(GenericArg::Lifetime(clean_middle_region(lt).unwrap_or(Lifetime::elided()))) + } + GenericArgKind::Type(_) if skip_first => { + skip_first = false; + None + } + GenericArgKind::Type(ty) => Some(GenericArg::Type(clean_middle_ty( + kind.rebind(ty), + cx, + None, + container.map(|container| crate::clean::ContainerTy::Regular { + ty: container, + substs, + arg: index, + }), + ))), + GenericArgKind::Const(ct) => { + Some(GenericArg::Const(Box::new(clean_middle_const(kind.rebind(ct), cx)))) + } } })); ret_val @@ -107,7 +118,7 @@ fn external_generic_args<'tcx>( bindings: ThinVec, substs: ty::Binder<'tcx, SubstsRef<'tcx>>, ) -> GenericArgs { - let args = substs_to_args(cx, substs.map_bound(|substs| &substs[..]), has_self); + let args = substs_to_args(cx, substs.map_bound(|substs| &substs[..]), has_self, Some(did)); if cx.tcx.fn_trait_kind_from_def_id(did).is_some() { let ty = substs @@ -118,7 +129,7 @@ fn external_generic_args<'tcx>( let inputs = // The trait's first substitution is the one after self, if there is one. match ty.skip_binder().kind() { - ty::Tuple(tys) => tys.iter().map(|t| clean_middle_ty(ty.rebind(t), cx, None)).collect::>().into(), + ty::Tuple(tys) => tys.iter().map(|t| clean_middle_ty(ty.rebind(t), cx, None, None)).collect::>().into(), _ => return GenericArgs::AngleBracketed { args: args.into(), bindings }, }; let output = bindings.into_iter().next().and_then(|binding| match binding.kind { diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index a5223bd6309d7..baffee0964da9 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -53,12 +53,15 @@ use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::def_id::{DefId, DefIdSet}; use rustc_hir::Mutability; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::traits::{Obligation, ObligationCause}; use rustc_middle::middle::stability; -use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::{ParamEnv, TyCtxt}; use rustc_span::{ symbol::{sym, Symbol}, BytePos, FileName, RealFileName, }; +use rustc_trait_selection::traits::ObligationCtxt; use serde::ser::{SerializeMap, SerializeSeq}; use serde::{Serialize, Serializer}; @@ -787,10 +790,12 @@ fn assoc_type( indent: usize, cx: &Context<'_>, ) { + let tcx = cx.tcx(); write!( w, - "{indent}type {name}{generics}", + "{indent}{vis}type {name}{generics}", indent = " ".repeat(indent), + vis = visibility_print_with_space(it.visibility(tcx), it.item_id, cx), href = assoc_href_attr(it, link, cx), name = it.name.as_ref().unwrap(), generics = generics.print(cx), @@ -1038,9 +1043,9 @@ fn render_attributes_in_pre<'a, 'b: 'a>( // When an attribute is rendered inside a tag, it is formatted using // a div to produce a newline after it. -fn render_attributes_in_code(w: &mut Buffer, it: &clean::Item, tcx: TyCtxt<'_>) { - for a in it.attributes(tcx, false) { - write!(w, "
{}
", a); +fn render_attributes_in_code(w: &mut impl fmt::Write, it: &clean::Item, tcx: TyCtxt<'_>) { + for attr in it.attributes(tcx, false) { + write!(w, "
{attr}
").unwrap(); } } @@ -1110,15 +1115,47 @@ fn render_assoc_items<'a, 'cx: 'a>( containing_item: &'a clean::Item, it: DefId, what: AssocItemRender<'a>, + aliased_type: Option, ) -> impl fmt::Display + 'a + Captures<'cx> { let mut derefs = DefIdSet::default(); derefs.insert(it); display_fn(move |f| { - render_assoc_items_inner(f, cx, containing_item, it, what, &mut derefs); + render_assoc_items_inner(f, cx, containing_item, it, what, &mut derefs, aliased_type); Ok(()) }) } +/// Check whether `impl_def_id` may apply to *some instantiation* of `item_def_id`. +fn is_valid_impl_for(tcx: TyCtxt<'_>, item_def_id: DefId, impl_def_id: DefId) -> bool { + let infcx = tcx.infer_ctxt().intercrate(true).build(); + let ocx = ObligationCtxt::new(&infcx); + let param_env = ParamEnv::empty(); + + let alias_substs = infcx.fresh_substs_for_item(rustc_span::DUMMY_SP, item_def_id); + let alias_ty = tcx.type_of(item_def_id).subst(tcx, alias_substs); + let alias_bounds = tcx.predicates_of(item_def_id).instantiate(tcx, alias_substs); + + let impl_substs = infcx.fresh_substs_for_item(rustc_span::DUMMY_SP, impl_def_id); + let impl_self_ty = tcx.type_of(impl_def_id).subst(tcx, impl_substs); + let impl_bounds = tcx.predicates_of(impl_def_id).instantiate(tcx, impl_substs); + + if ocx.eq(&ObligationCause::dummy(), param_env, impl_self_ty, alias_ty).is_err() { + return false; + } + ocx.register_obligations( + alias_bounds + .iter() + .chain(impl_bounds) + .map(|(p, _)| Obligation::new(tcx, ObligationCause::dummy(), param_env, p)), + ); + + let errors = ocx.select_where_possible(); + errors.is_empty() +} + +// If `aliased_type` is `Some`, it means `it` is a type alias and `aliased_type` is the "actual" +// type aliased behind `it`. It is used to check whether or not the implementation of the aliased +// type can be displayed on the alias doc page. fn render_assoc_items_inner( mut w: &mut dyn fmt::Write, cx: &mut Context<'_>, @@ -1126,12 +1163,28 @@ fn render_assoc_items_inner( it: DefId, what: AssocItemRender<'_>, derefs: &mut DefIdSet, + aliased_type: Option, ) { info!("Documenting associated items of {:?}", containing_item.name); let shared = Rc::clone(&cx.shared); let cache = &shared.cache; - let Some(v) = cache.impls.get(&it) else { return }; - let (non_trait, traits): (Vec<_>, _) = v.iter().partition(|i| i.inner_impl().trait_.is_none()); + let empty = Vec::new(); + let v = match cache.impls.get(&it) { + Some(v) => v, + None => &empty, + }; + let v2 = match aliased_type { + Some(aliased_type) => cache.impls.get(&aliased_type).unwrap_or(&empty), + None => &empty, + }; + if v.is_empty() && v2.is_empty() { + return; + } + let mut saw_impls = FxHashSet::default(); + let (non_trait, traits): (Vec<_>, _) = + v.iter().chain(v2).partition(|i| i.inner_impl().trait_.is_none()); + let tcx = cx.tcx(); + let is_alias = aliased_type.is_some(); if !non_trait.is_empty() { let mut tmp_buf = Buffer::html(); let (render_mode, id, class_html) = match what { @@ -1163,6 +1216,12 @@ fn render_assoc_items_inner( }; let mut impls_buf = Buffer::html(); for i in &non_trait { + if !saw_impls.insert(i.def_id()) { + continue; + } + if is_alias && !is_valid_impl_for(tcx, it, i.def_id()) { + continue; + } render_impl( &mut impls_buf, cx, @@ -1191,9 +1250,14 @@ fn render_assoc_items_inner( if !traits.is_empty() { let deref_impl = traits.iter().find(|t| t.trait_did() == cx.tcx().lang_items().deref_trait()); - if let Some(impl_) = deref_impl { + if let Some(impl_) = deref_impl && + (!is_alias || is_valid_impl_for(tcx, it, impl_.def_id())) + { let has_deref_mut = - traits.iter().any(|t| t.trait_did() == cx.tcx().lang_items().deref_mut_trait()); + traits.iter().any(|t| { + t.trait_did() == cx.tcx().lang_items().deref_mut_trait() && + (!is_alias || is_valid_impl_for(tcx, it, t.def_id())) + }); render_deref_methods(&mut w, cx, impl_, containing_item, has_deref_mut, derefs); } @@ -1203,10 +1267,14 @@ fn render_assoc_items_inner( return; } - let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) = - traits.into_iter().partition(|t| t.inner_impl().kind.is_auto()); - let (blanket_impl, concrete): (Vec<&Impl>, _) = - concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket()); + let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) = traits + .into_iter() + .filter(|t| saw_impls.insert(t.def_id())) + .partition(|t| t.inner_impl().kind.is_auto()); + let (blanket_impl, concrete): (Vec<&Impl>, _) = concrete + .into_iter() + .filter(|t| !is_alias || is_valid_impl_for(tcx, it, t.def_id())) + .partition(|t| t.inner_impl().kind.is_blanket()); render_all_impls(w, cx, containing_item, &concrete, &synthetic, &blanket_impl); } @@ -1245,10 +1313,10 @@ fn render_deref_methods( return; } } - render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs); + render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs, None); } else if let Some(prim) = target.primitive_type() { if let Some(&did) = cache.primitive_locations.get(&prim) { - render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs); + render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs, None); } } } diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 21f61acb2c53f..01089ed348bfd 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -260,7 +260,7 @@ fn item_template_render_assoc_items<'a: 'b, 'b, 'cx: 'a>( display_fn(move |f| { let (item, mut cx) = templ.item_and_mut_cx(); let def_id = item.item_id.expect_def_id(); - let v = render_assoc_items(*cx, item, def_id, AssocItemRender::All); + let v = render_assoc_items(*cx, item, def_id, AssocItemRender::All, None); write!(f, "{v}") }) } @@ -893,7 +893,11 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: } // If there are methods directly on this trait object, render them here. - write!(w, "{}", render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All)); + write!( + w, + "{}", + render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All, None) + ); let cloned_shared = Rc::clone(&cx.shared); let cache = &cloned_shared.cache; @@ -1101,7 +1105,12 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: ); } -fn item_trait_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::TraitAlias) { +fn item_trait_alias( + w: &mut impl fmt::Write, + cx: &mut Context<'_>, + it: &clean::Item, + t: &clean::TraitAlias, +) { wrap_item(w, |w| { write!( w, @@ -1111,19 +1120,29 @@ fn item_trait_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: & print_where_clause(&t.generics, cx, 0, Ending::Newline), bounds(&t.bounds, true, cx), attrs = render_attributes_in_pre(it, "", cx.tcx()), - ); + ) + .unwrap(); }); - write!(w, "{}", document(cx, it, None, HeadingOffset::H2)); - + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)).unwrap(); // Render any items associated directly to this alias, as otherwise they // won't be visible anywhere in the docs. It would be nice to also show // associated items from the aliased type (see discussion in #32077), but // we need #14072 to make sense of the generics. - write!(w, "{}", render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All)) + write!( + w, + "{}", + render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All, None) + ) + .unwrap(); } -fn item_opaque_ty(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::OpaqueTy) { +fn item_opaque_ty( + w: &mut impl fmt::Write, + cx: &mut Context<'_>, + it: &clean::Item, + t: &clean::OpaqueTy, +) { wrap_item(w, |w| { write!( w, @@ -1133,16 +1152,22 @@ fn item_opaque_ty(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &cl where_clause = print_where_clause(&t.generics, cx, 0, Ending::Newline), bounds = bounds(&t.bounds, false, cx), attrs = render_attributes_in_pre(it, "", cx.tcx()), - ); + ) + .unwrap(); }); - write!(w, "{}", document(cx, it, None, HeadingOffset::H2)); + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)).unwrap(); // Render any items associated directly to this alias, as otherwise they // won't be visible anywhere in the docs. It would be nice to also show // associated items from the aliased type (see discussion in #32077), but // we need #14072 to make sense of the generics. - write!(w, "{}", render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All)) + write!( + w, + "{}", + render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All, None) + ) + .unwrap(); } fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Typedef) { @@ -1166,11 +1191,11 @@ fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clea write!(w, "{}", document(cx, it, None, HeadingOffset::H2)); let def_id = it.item_id.expect_def_id(); - // Render any items associated directly to this alias, as otherwise they - // won't be visible anywhere in the docs. It would be nice to also show - // associated items from the aliased type (see discussion in #32077), but - // we need #14072 to make sense of the generics. - write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All)); + write!( + w, + "{}", + render_assoc_items(cx, it, def_id, AssocItemRender::All, t.type_.def_id(&cx.cache())) + ); write!(w, "{}", document_type_layout(cx, def_id)); } @@ -1410,7 +1435,7 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean:: write!(w, ""); } let def_id = it.item_id.expect_def_id(); - write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All)); + write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All, None)); write!(w, "{}", document_type_layout(cx, def_id)); } @@ -1425,37 +1450,35 @@ fn item_proc_macro( it: &clean::Item, m: &clean::ProcMacro, ) { - let mut buffer = Buffer::new(); - wrap_item(&mut buffer, |buffer| { + wrap_item(w, |buffer| { let name = it.name.expect("proc-macros always have names"); match m.kind { MacroKind::Bang => { - write!(buffer, "{}!() {{ /* proc-macro */ }}", name); + write!(buffer, "{name}!() {{ /* proc-macro */ }}").unwrap(); } MacroKind::Attr => { - write!(buffer, "#[{}]", name); + write!(buffer, "#[{name}]").unwrap(); } MacroKind::Derive => { - write!(buffer, "#[derive({})]", name); + write!(buffer, "#[derive({name})]").unwrap(); if !m.helpers.is_empty() { - buffer.push_str("\n{\n"); - buffer.push_str(" // Attributes available to this derive:\n"); + buffer.write_str("\n{\n // Attributes available to this derive:\n").unwrap(); for attr in &m.helpers { - writeln!(buffer, " #[{}]", attr); + writeln!(buffer, " #[{attr}]").unwrap(); } - buffer.push_str("}\n"); + buffer.write_str("}\n").unwrap(); } } } }); - write!(w, "{}{}", buffer.into_inner(), document(cx, it, None, HeadingOffset::H2)).unwrap(); + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)).unwrap(); } -fn item_primitive(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) { +fn item_primitive(w: &mut impl fmt::Write, cx: &mut Context<'_>, it: &clean::Item) { let def_id = it.item_id.expect_def_id(); - write!(w, "{}", document(cx, it, None, HeadingOffset::H2)); + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)).unwrap(); if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) { - write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All)); + write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All, None)).unwrap(); } else { // We handle the "reference" primitive type on its own because we only want to list // implementations on generic types. @@ -1560,13 +1583,12 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean } } let def_id = it.item_id.expect_def_id(); - write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All)); + write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All, None)); write!(w, "{}", document_type_layout(cx, def_id)); } fn item_static(w: &mut impl fmt::Write, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Static) { - let mut buffer = Buffer::new(); - wrap_item(&mut buffer, |buffer| { + wrap_item(w, |buffer| { render_attributes_in_code(buffer, it, cx.tcx()); write!( buffer, @@ -1575,31 +1597,33 @@ fn item_static(w: &mut impl fmt::Write, cx: &mut Context<'_>, it: &clean::Item, mutability = s.mutability.print_with_space(), name = it.name.unwrap(), typ = s.type_.print(cx) - ); + ) + .unwrap(); }); - write!(w, "{}", buffer.into_inner()).unwrap(); - write!(w, "{}", document(cx, it, None, HeadingOffset::H2)).unwrap(); } fn item_foreign_type(w: &mut impl fmt::Write, cx: &mut Context<'_>, it: &clean::Item) { - let mut buffer = Buffer::new(); - wrap_item(&mut buffer, |buffer| { - buffer.write_str("extern {\n"); + wrap_item(w, |buffer| { + buffer.write_str("extern {\n").unwrap(); render_attributes_in_code(buffer, it, cx.tcx()); write!( buffer, " {}type {};\n}}", visibility_print_with_space(it.visibility(cx.tcx()), it.item_id, cx), it.name.unwrap(), - ); + ) + .unwrap(); }); - write!(w, "{}{}", buffer.into_inner(), document(cx, it, None, HeadingOffset::H2)).unwrap(); - - write!(w, "{}", render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All)) - .unwrap(); + write!(w, "{}", document(cx, it, None, HeadingOffset::H2)).unwrap(); + write!( + w, + "{}", + render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All, None) + ) + .unwrap(); } fn item_keyword(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) { @@ -1673,13 +1697,14 @@ fn bounds(t_bounds: &[clean::GenericBound], trait_alias: bool, cx: &Context<'_>) bounds } -fn wrap_item(w: &mut Buffer, f: F) +fn wrap_item(w: &mut W, f: F) where - F: FnOnce(&mut Buffer), + W: fmt::Write, + F: FnOnce(&mut W), { - w.write_str(r#"
"#);
+    write!(w, r#"
"#).unwrap();
     f(w);
-    w.write_str("
"); + write!(w, "
").unwrap(); } #[derive(PartialEq, Eq)] diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 2b0fef267e9a2..c31ba3304b032 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -205,7 +205,7 @@ impl UrlFragment { &UrlFragment::Item(def_id) => { let kind = match tcx.def_kind(def_id) { DefKind::AssocFn => { - if tcx.impl_defaultness(def_id).has_value() { + if tcx.defaultness(def_id).has_value() { "method." } else { "tymethod." diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index abb9229fbd51a..22c8cc092438c 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -14,7 +14,7 @@ use rustc_span::hygiene::MacroKind; use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::Span; -use std::{iter, mem}; +use std::mem; use crate::clean::{cfg::Cfg, reexport_chain, AttributesExt, NestedAttributesExt}; use crate::core; @@ -147,9 +147,9 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { // `#[macro_export] macro_rules!` items are reexported at the top level of the // crate, regardless of where they're defined. We want to document the - // top level rexport of the macro, not its original definition, since - // the rexport defines the path that a user will actually see. Accordingly, - // we add the rexport as an item here, and then skip over the original + // top level re-export of the macro, not its original definition, since + // the re-export defines the path that a user will actually see. Accordingly, + // we add the re-export as an item here, and then skip over the original // definition in `visit_item()` below. // // We also skip `#[macro_export] macro_rules!` that have already been inserted, @@ -246,7 +246,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { glob: bool, please_inline: bool, ) -> bool { - debug!("maybe_inline_local res: {:?}", res); + debug!("maybe_inline_local (renamed: {renamed:?}) res: {res:?}"); if renamed == Some(kw::Underscore) { // We never inline `_` reexports. @@ -291,26 +291,15 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { if !please_inline { let inherits_hidden = inherits_doc_hidden(tcx, res_did, None); // Only inline if requested or if the item would otherwise be stripped. - // - // If it's a doc hidden module, we need to keep it in case some of its inner items - // are re-exported. if (!is_private && !inherits_hidden) || ( is_hidden && + // If it's a doc hidden module, we need to keep it in case some of its inner items + // are re-exported. !matches!(item, Node::Item(&hir::Item { kind: hir::ItemKind::Mod(_), .. })) - ) { - return false; - } else if let Some(item_def_id) = reexport_chain(tcx, def_id, res_did).iter() - .flat_map(|reexport| reexport.id()).map(|id| id.expect_local()) - .chain(iter::once(res_did)).nth(1) && - item_def_id != def_id && - self - .cx - .cache - .effective_visibilities - .is_directly_public(tcx, item_def_id.to_def_id()) && - !inherits_doc_hidden(tcx, item_def_id, None) - { + ) || // The imported item is public and not `doc(hidden)` so no need to inline it. + self.reexport_public_and_not_hidden(def_id, res_did) + { return false; } } @@ -358,6 +347,28 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { ret } + /// Returns `true` if the item is visible, meaning it's not `#[doc(hidden)]` or private. + /// + /// This function takes into account the entire re-export `use` chain, so it needs the + /// ID of the "leaf" `use` and the ID of the "root" item. + fn reexport_public_and_not_hidden( + &self, + import_def_id: LocalDefId, + target_def_id: LocalDefId, + ) -> bool { + let tcx = self.cx.tcx; + let item_def_id = reexport_chain(tcx, import_def_id, target_def_id) + .iter() + .flat_map(|reexport| reexport.id()) + .map(|id| id.expect_local()) + .nth(1) + .unwrap_or(target_def_id); + item_def_id != import_def_id + && self.cx.cache.effective_visibilities.is_directly_public(tcx, item_def_id.to_def_id()) + && !tcx.is_doc_hidden(item_def_id) + && !inherits_doc_hidden(tcx, item_def_id, None) + } + #[inline] fn add_to_current_mod( &mut self, diff --git a/src/llvm-project b/src/llvm-project index 533d3f338b804..22897bce7bfed 160000 --- a/src/llvm-project +++ b/src/llvm-project @@ -1 +1 @@ -Subproject commit 533d3f338b804d54e5d0ac4fba6276af23002d9c +Subproject commit 22897bce7bfedc9cd3953a33419b346936263500 diff --git a/src/tools/build_helper/src/ci.rs b/src/tools/build_helper/src/ci.rs index d2e9c324af8c6..d106e5b339b2f 100644 --- a/src/tools/build_helper/src/ci.rs +++ b/src/tools/build_helper/src/ci.rs @@ -46,6 +46,8 @@ pub mod gha { pub fn group(name: impl std::fmt::Display) -> Group { if std::env::var_os("GITHUB_ACTIONS").is_some() { eprintln!("::group::{name}"); + } else { + eprintln!("{name}") } Group(()) } diff --git a/src/tools/cargo b/src/tools/cargo index f7b95e31642e0..49b6d9e179a91 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit f7b95e31642e09c2b6eabb18ed75007dda6677a0 +Subproject commit 49b6d9e179a91cf7645142541c9563443f64bf2b diff --git a/src/tools/clippy/.github/ISSUE_TEMPLATE/new_lint.yml b/src/tools/clippy/.github/ISSUE_TEMPLATE/new_lint.yml index 0b43d8d70c007..b49493edce1ba 100644 --- a/src/tools/clippy/.github/ISSUE_TEMPLATE/new_lint.yml +++ b/src/tools/clippy/.github/ISSUE_TEMPLATE/new_lint.yml @@ -12,29 +12,6 @@ body: description: What does this lint do? validations: required: true - - type: input - id: lint-name - attributes: - label: Lint Name - description: Please provide the lint name. - - type: dropdown - id: category - attributes: - label: Category - description: > - What category should this lint go into? If you're unsure you can select - multiple categories. You can find a category description in the - `README`. - multiple: true - options: - - correctness - - suspicious - - style - - complexity - - perf - - pedantic - - restriction - - cargo - type: textarea id: advantage attributes: diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index 79f2a47110b9e..8b609b47d8192 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -6,7 +6,132 @@ document. ## Unreleased / Beta / In Rust Nightly -[149392b0...master](https://github.com/rust-lang/rust-clippy/compare/149392b0...master) +[83e42a23...master](https://github.com/rust-lang/rust-clippy/compare/83e42a23...master) + +## Rust 1.70 + +Current beta, released 2023-06-01 + +[149392b0...83e42a23](https://github.com/rust-lang/rust-clippy/compare/149392b0...83e42a23) + +### New Lints + +* [`large_futures`] + [#10414](https://github.com/rust-lang/rust-clippy/pull/10414) +* [`missing_assert_message`] + [#10362](https://github.com/rust-lang/rust-clippy/pull/10362) +* [`clear_with_drain`] + [#10528](https://github.com/rust-lang/rust-clippy/pull/10528) +* [`redundant_async_block`] + [#10448](https://github.com/rust-lang/rust-clippy/pull/10448) +* [`collection_is_never_read`] + [#10415](https://github.com/rust-lang/rust-clippy/pull/10415) +* [`let_with_type_underscore`] + [#10467](https://github.com/rust-lang/rust-clippy/pull/10467) +* [`tests_outside_test_module`] + [#10543](https://github.com/rust-lang/rust-clippy/pull/10543) +* [`allow_attributes`] + [#10481](https://github.com/rust-lang/rust-clippy/pull/10481) +* [`suspicious_doc_comments`] + [#10497](https://github.com/rust-lang/rust-clippy/pull/10497) +* [`unnecessary_box_returns`] + [#9102](https://github.com/rust-lang/rust-clippy/pull/9102) +* [`manual_main_separator_str`] + [#10483](https://github.com/rust-lang/rust-clippy/pull/10483) +* [`unnecessary_struct_initialization`] + [#10489](https://github.com/rust-lang/rust-clippy/pull/10489) +* [`manual_slice_size_calculation`] + [#10601](https://github.com/rust-lang/rust-clippy/pull/10601) +* [`lines_filter_map_ok`] + [#10534](https://github.com/rust-lang/rust-clippy/pull/10534) + +### Moves and Deprecations + +* Moved [`let_underscore_untyped`] to `restriction` + [#10442](https://github.com/rust-lang/rust-clippy/pull/10442) + +### Enhancements + +* [`extra_unused_type_parameters`]: No longer lints on public items if `avoid-breaking-exported-api` is set + [#10536](https://github.com/rust-lang/rust-clippy/pull/10536) +* [`len_without_is_empty`]: Now also detects `async` functions + [#10359](https://github.com/rust-lang/rust-clippy/pull/10359) +* [`arithmetic_side_effects`]: Now correctly handles divisions and modulo expressions if the right-hand-side + is unknown + [#10585](https://github.com/rust-lang/rust-clippy/pull/10585) +* [`nonminimal_bool`]: No longer ignores `#[allow]` attributes + [#10588](https://github.com/rust-lang/rust-clippy/pull/10588) +* [`uninit_vec`], [`uninit_assumed_init`]: Now uses a better heuristic + [#10520](https://github.com/rust-lang/rust-clippy/pull/10520) +* [`ifs_same_cond`]: Now also detects immutable method calls. + [#10350](https://github.com/rust-lang/rust-clippy/pull/10350) +* [`arithmetic_side_effects`]: No longer lints on right or left shifts with constant integers, as the + compiler warns about them + [#10309](https://github.com/rust-lang/rust-clippy/pull/10309) +* [`items_after_statements`]: `#[allow(items_after_statements)]` now works on items + [#10542](https://github.com/rust-lang/rust-clippy/pull/10542) +* [`significant_drop_tightening`]: Was optimized + [#10533](https://github.com/rust-lang/rust-clippy/pull/10533) + +### False Positive Fixes + +* [`single_component_path_imports`]: No longer lints if the import is used relative to `self` + [#10566](https://github.com/rust-lang/rust-clippy/pull/10566) +* [`derivable_impls`]: No longer suggests deriving `Default` on generics with implicit arguments + [#10399](https://github.com/rust-lang/rust-clippy/pull/10399) +* [`let_unit_value`]: No longer lints if the expression contains an `await` + [#10439](https://github.com/rust-lang/rust-clippy/pull/10439) +* [`double_must_use`]: Now ignores `async` functions + [#10589](https://github.com/rust-lang/rust-clippy/pull/10589) +* [`manual_clamp`]: No longer lints in constant context + [#10479](https://github.com/rust-lang/rust-clippy/pull/10479) +* [`almost_swapped`]: Now ignores external macros + [#10502](https://github.com/rust-lang/rust-clippy/pull/10502) +* [`nonminimal_bool`]: Now ignores macros + [#10527](https://github.com/rust-lang/rust-clippy/pull/10527) +* [`needless_return`]: No longer lints match statements with incompatible branches + [#10593](https://github.com/rust-lang/rust-clippy/pull/10593) +* [`use_self`]: Do not suggest using `Self` in const generic parameters + [#10375](https://github.com/rust-lang/rust-clippy/pull/10375) +* [`mem_replace_option_with_none`]: No longer lints on field expressions + [#10594](https://github.com/rust-lang/rust-clippy/pull/10594) +* [`items_after_statements`]: No longer lints on times from macros + [#10542](https://github.com/rust-lang/rust-clippy/pull/10542) +* [`print_literal`], [`write_literal`]: No longer lint strings coming from the `file!()` macro + [#10573](https://github.com/rust-lang/rust-clippy/pull/10573) +* [`uninit_vec`], [`uninit_assumed_init`]: Now check the types inside arrays and tuples + [#10553](https://github.com/rust-lang/rust-clippy/pull/10553) +* [`almost_swapped`]: No longer lints if a variable is assigned to itself + [#10499](https://github.com/rust-lang/rust-clippy/pull/10499) +* [`missing_docs_in_private_items`]: No longer lints on public items + [#10324](https://github.com/rust-lang/rust-clippy/pull/10324) + +### Suggestion Fixes/Improvements + +* [`extra_unused_type_parameters`]: The suggestion is now machine applicable + [#10536](https://github.com/rust-lang/rust-clippy/pull/10536) +* [`match_single_binding`]: Now adds a semicolon after the suggestion + [#10470](https://github.com/rust-lang/rust-clippy/pull/10470) +* [`missing_const_for_fn`]: Now includes a note if the change could break compatibility + [#10618](https://github.com/rust-lang/rust-clippy/pull/10618) +* [`cast_possible_truncation`]: Corrected suggestion for float and wildcard casts + [#10496](https://github.com/rust-lang/rust-clippy/pull/10496) +* [`transmutes_expressible_as_ptr_casts`]: The suggestion now includes parentheses when they are required + [#10454](https://github.com/rust-lang/rust-clippy/pull/10454) + +### ICE Fixes + +* [`needless_borrow`]: No longer panics on ambiguous projections + [#10403](https://github.com/rust-lang/rust-clippy/pull/10403) +* [`multiple_unsafe_ops_per_block`]: Fix ICE when calling a function-like object in an unsafe block + [#10405](https://github.com/rust-lang/rust-clippy/pull/10405) + +### Others + +* `clippy-driver` now searches parent directories for `clippy.toml` files + [#10592](https://github.com/rust-lang/rust-clippy/pull/10592) +* Fixed a deserialization error for the `array-size-threshold` config value + [#10423](https://github.com/rust-lang/rust-clippy/pull/10423) ## Rust 1.69 @@ -4838,6 +4963,7 @@ Released 2018-09-13 [`missing_docs_in_private_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items [`missing_enforced_import_renames`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_enforced_import_renames [`missing_errors_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_errors_doc +[`missing_fields_in_debug`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_fields_in_debug [`missing_inline_in_public_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_inline_in_public_items [`missing_panics_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_panics_doc [`missing_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc @@ -4874,6 +5000,7 @@ Released 2018-09-13 [`needless_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_collect [`needless_continue`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_continue [`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main +[`needless_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_else [`needless_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_for_each [`needless_late_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_late_init [`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes @@ -4949,6 +5076,7 @@ Released 2018-09-13 [`println_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#println_empty_string [`ptr_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_arg [`ptr_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr +[`ptr_cast_constness`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_cast_constness [`ptr_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_eq [`ptr_offset_with_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_offset_with_cast [`pub_enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_enum_variant_names diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml index 3c72bb62ed19e..ca8bf9fac91a0 100644 --- a/src/tools/clippy/Cargo.toml +++ b/src/tools/clippy/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.71" +version = "0.1.72" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" @@ -30,16 +30,11 @@ termize = "0.1" compiletest_rs = { version = "0.10", features = ["tmp"] } tester = "0.9" regex = "1.5" -toml = "0.5" +toml = "0.7.3" walkdir = "2.3" # This is used by the `collect-metadata` alias. filetime = "0.2" -# A noop dependency that changes in the Rust repository, it's a bit of a hack. -# See the `src/tools/rustc-workspace-hack/README.md` file in `rust-lang/rust` -# for more information. -rustc-workspace-hack = "1.0" - # UI test dependencies clap = { version = "4.1.4", features = ["derive"] } clippy_utils = { path = "clippy_utils" } diff --git a/src/tools/clippy/book/src/configuration.md b/src/tools/clippy/book/src/configuration.md index 1304f6a8c2f09..e8274bc4575d0 100644 --- a/src/tools/clippy/book/src/configuration.md +++ b/src/tools/clippy/book/src/configuration.md @@ -2,8 +2,14 @@ > **Note:** The configuration file is unstable and may be deprecated in the future. -Some lints can be configured in a TOML file named `clippy.toml` or `.clippy.toml`. It contains a -basic `variable = value` mapping e.g. +Some lints can be configured in a TOML file named `clippy.toml` or `.clippy.toml`, which is searched for in: + +1. The directory specified by the `CLIPPY_CONF_DIR` environment variable, or +2. The directory specified by the +[CARGO_MANIFEST_DIR](https://doc.rust-lang.org/cargo/reference/environment-variables.html) environment variable, or +3. The current directory. + +It contains a basic `variable = value` mapping e.g. ```toml avoid-breaking-exported-api = false diff --git a/src/tools/clippy/book/src/development/adding_lints.md b/src/tools/clippy/book/src/development/adding_lints.md index ccae8d3742018..c26aa883ebae2 100644 --- a/src/tools/clippy/book/src/development/adding_lints.md +++ b/src/tools/clippy/book/src/development/adding_lints.md @@ -630,8 +630,14 @@ Before submitting your PR make sure you followed all the basic requirements: ## Adding configuration to a lint -Clippy supports the configuration of lints values using a `clippy.toml` file in -the workspace directory. Adding a configuration to a lint can be useful for +Clippy supports the configuration of lints values using a `clippy.toml` file which is searched for in: + +1. The directory specified by the `CLIPPY_CONF_DIR` environment variable, or +2. The directory specified by the +[CARGO_MANIFEST_DIR](https://doc.rust-lang.org/cargo/reference/environment-variables.html) environment variable, or +3. The current directory. + +Adding a configuration to a lint can be useful for thresholds or to constrain some behavior that can be seen as a false positive for some users. Adding a configuration is done in the following steps: diff --git a/src/tools/clippy/clippy_dev/src/main.rs b/src/tools/clippy/clippy_dev/src/main.rs index e2457e5a8a5e9..c03fbe9d275f3 100644 --- a/src/tools/clippy/clippy_dev/src/main.rs +++ b/src/tools/clippy/clippy_dev/src/main.rs @@ -5,6 +5,7 @@ use clap::{Arg, ArgAction, ArgMatches, Command}; use clippy_dev::{bless, dogfood, fmt, lint, new_lint, serve, setup, update_lints}; use indoc::indoc; +use std::convert::Infallible; fn main() { let matches = get_clap_config(); @@ -180,7 +181,8 @@ fn get_clap_config() -> ArgMatches { .short('n') .long("name") .help("Name of the new lint in snake case, ex: fn_too_long") - .required(true), + .required(true) + .value_parser(|name: &str| Ok::<_, Infallible>(name.replace('-', "_"))), Arg::new("category") .short('c') .long("category") diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml index 98e69c7fd264a..be9261a47041c 100644 --- a/src/tools/clippy/clippy_lints/Cargo.toml +++ b/src/tools/clippy/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.71" +version = "0.1.72" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" @@ -20,15 +20,13 @@ quine-mc_cluskey = "0.2" regex-syntax = "0.7" serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", optional = true } -tempfile = { version = "3.2", optional = true } -toml = "0.5" +tempfile = { version = "3.3.0", optional = true } +toml = "0.7.3" unicode-normalization = "0.1" unicode-script = { version = "0.5", default-features = false } semver = "1.0" rustc-semver = "1.1" -# NOTE: cargo requires serde feat in its url dep -# see -url = { version = "2.2", features = ["serde"] } +url = "2.2" [features] deny-warnings = ["clippy_utils/deny-warnings"] diff --git a/src/tools/clippy/clippy_lints/src/allow_attributes.rs b/src/tools/clippy/clippy_lints/src/allow_attributes.rs index add73d0aeeed3..554efdc58e1cb 100644 --- a/src/tools/clippy/clippy_lints/src/allow_attributes.rs +++ b/src/tools/clippy/clippy_lints/src/allow_attributes.rs @@ -40,7 +40,7 @@ declare_clippy_lint! { /// a.len() /// } /// ``` - #[clippy::version = "1.69.0"] + #[clippy::version = "1.70.0"] pub ALLOW_ATTRIBUTES, restriction, "`#[allow]` will not trigger if a warning isn't found. `#[expect]` triggers if there are no warnings." diff --git a/src/tools/clippy/clippy_lints/src/booleans.rs b/src/tools/clippy/clippy_lints/src/booleans.rs index 455f0df7cd0ad..ff0102255a0a9 100644 --- a/src/tools/clippy/clippy_lints/src/booleans.rs +++ b/src/tools/clippy/clippy_lints/src/booleans.rs @@ -88,7 +88,6 @@ impl<'tcx> LateLintPass<'tcx> for NonminimalBool { NonminimalBoolVisitor { cx }.visit_body(body); } } - struct NonminimalBoolVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, } @@ -473,6 +472,10 @@ impl<'a, 'tcx> Visitor<'tcx> for NonminimalBoolVisitor<'a, 'tcx> { self.bool_expr(e); }, ExprKind::Unary(UnOp::Not, inner) => { + if let ExprKind::Unary(UnOp::Not, ex) = inner.kind && + !self.cx.typeck_results().node_types()[ex.hir_id].is_bool() { + return; + } if self.cx.typeck_results().node_types()[inner.hir_id].is_bool() { self.bool_expr(e); } diff --git a/src/tools/clippy/clippy_lints/src/casts/mod.rs b/src/tools/clippy/clippy_lints/src/casts/mod.rs index 0c175372aab83..b90dab07a274a 100644 --- a/src/tools/clippy/clippy_lints/src/casts/mod.rs +++ b/src/tools/clippy/clippy_lints/src/casts/mod.rs @@ -17,6 +17,7 @@ mod fn_to_numeric_cast; mod fn_to_numeric_cast_any; mod fn_to_numeric_cast_with_truncation; mod ptr_as_ptr; +mod ptr_cast_constness; mod unnecessary_cast; mod utils; @@ -363,7 +364,7 @@ declare_clippy_lint! { /// namely `*const T` to `*const U` and `*mut T` to `*mut U`. /// /// ### Why is this bad? - /// Though `as` casts between raw pointers is not terrible, `pointer::cast` is safer because + /// Though `as` casts between raw pointers are not terrible, `pointer::cast` is safer because /// it cannot accidentally change the pointer's mutability nor cast the pointer to other types like `usize`. /// /// ### Example @@ -386,6 +387,34 @@ declare_clippy_lint! { "casting using `as` from and to raw pointers that doesn't change its mutability, where `pointer::cast` could take the place of `as`" } +declare_clippy_lint! { + /// ### What it does + /// Checks for `as` casts between raw pointers which change its constness, namely `*const T` to + /// `*mut T` and `*mut T` to `*const T`. + /// + /// ### Why is this bad? + /// Though `as` casts between raw pointers are not terrible, `pointer::cast_mut` and + /// `pointer::cast_const` are safer because they cannot accidentally cast the pointer to another + /// type. + /// + /// ### Example + /// ```rust + /// let ptr: *const u32 = &42_u32; + /// let mut_ptr = ptr as *mut u32; + /// let ptr = mut_ptr as *const u32; + /// ``` + /// Use instead: + /// ```rust + /// let ptr: *const u32 = &42_u32; + /// let mut_ptr = ptr.cast_mut(); + /// let ptr = mut_ptr.cast_const(); + /// ``` + #[clippy::version = "1.71.0"] + pub PTR_CAST_CONSTNESS, + pedantic, + "casting using `as` from and to raw pointers to change constness when specialized methods apply" +} + declare_clippy_lint! { /// ### What it does /// Checks for casts from an enum type to an integral type which will definitely truncate the @@ -652,6 +681,7 @@ impl_lint_pass!(Casts => [ FN_TO_NUMERIC_CAST_WITH_TRUNCATION, CHAR_LIT_AS_U8, PTR_AS_PTR, + PTR_CAST_CONSTNESS, CAST_ENUM_TRUNCATION, CAST_ENUM_CONSTRUCTOR, CAST_ABS_TO_UNSIGNED, @@ -685,6 +715,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { return; } cast_slice_from_raw_parts::check(cx, expr, cast_expr, cast_to, &self.msrv); + ptr_cast_constness::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv); as_ptr_cast_mut::check(cx, expr, cast_expr, cast_to); fn_to_numeric_cast_any::check(cx, expr, cast_expr, cast_from, cast_to); fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to); diff --git a/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs b/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs new file mode 100644 index 0000000000000..ab015f8822e1b --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs @@ -0,0 +1,44 @@ +use clippy_utils::msrvs::POINTER_CAST_CONSTNESS; +use clippy_utils::sugg::Sugg; +use clippy_utils::{diagnostics::span_lint_and_sugg, msrvs::Msrv}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{Expr, Mutability}; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty, TypeAndMut}; + +use super::PTR_CAST_CONSTNESS; + +pub(super) fn check( + cx: &LateContext<'_>, + expr: &Expr<'_>, + cast_expr: &Expr<'_>, + cast_from: Ty<'_>, + cast_to: Ty<'_>, + msrv: &Msrv, +) { + if_chain! { + if msrv.meets(POINTER_CAST_CONSTNESS); + if let ty::RawPtr(TypeAndMut { mutbl: from_mutbl, .. }) = cast_from.kind(); + if let ty::RawPtr(TypeAndMut { mutbl: to_mutbl, .. }) = cast_to.kind(); + if matches!((from_mutbl, to_mutbl), + (Mutability::Not, Mutability::Mut) | (Mutability::Mut, Mutability::Not)); + then { + let sugg = Sugg::hir(cx, cast_expr, "_"); + let constness = match *to_mutbl { + Mutability::Not => "const", + Mutability::Mut => "mut", + }; + + span_lint_and_sugg( + cx, + PTR_CAST_CONSTNESS, + expr.span, + "`as` casting between raw pointers while changing its constness", + &format!("try `pointer::cast_{constness}`, a safer alternative"), + format!("{}.cast_{constness}()", sugg.maybe_par()), + Applicability::MachineApplicable, + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs b/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs index 5e2eb5789f627..ac5ac542cf944 100644 --- a/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs +++ b/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs @@ -37,7 +37,7 @@ declare_clippy_lint! { /// println!("{sample}"); /// } /// ``` - #[clippy::version = "1.69.0"] + #[clippy::version = "1.70.0"] pub COLLECTION_IS_NEVER_READ, nursery, "a collection is never queried" diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index 212e809d4c564..f1d1355123ab6 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -89,6 +89,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::casts::FN_TO_NUMERIC_CAST_ANY_INFO, crate::casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION_INFO, crate::casts::PTR_AS_PTR_INFO, + crate::casts::PTR_CAST_CONSTNESS_INFO, crate::casts::UNNECESSARY_CAST_INFO, crate::checked_conversions::CHECKED_CONVERSIONS_INFO, crate::cognitive_complexity::COGNITIVE_COMPLEXITY_INFO, @@ -135,7 +136,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::double_parens::DOUBLE_PARENS_INFO, crate::drop_forget_ref::DROP_NON_DROP_INFO, crate::drop_forget_ref::FORGET_NON_DROP_INFO, - crate::drop_forget_ref::UNDROPPED_MANUALLY_DROPS_INFO, crate::duplicate_mod::DUPLICATE_MOD_INFO, crate::else_if_without_else::ELSE_IF_WITHOUT_ELSE_INFO, crate::empty_drop::EMPTY_DROP_INFO, @@ -427,6 +427,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::missing_const_for_fn::MISSING_CONST_FOR_FN_INFO, crate::missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS_INFO, crate::missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES_INFO, + crate::missing_fields_in_debug::MISSING_FIELDS_IN_DEBUG_INFO, crate::missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS_INFO, crate::missing_trait_methods::MISSING_TRAIT_METHODS_INFO, crate::mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION_INFO, @@ -447,6 +448,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::needless_bool::NEEDLESS_BOOL_ASSIGN_INFO, crate::needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE_INFO, crate::needless_continue::NEEDLESS_CONTINUE_INFO, + crate::needless_else::NEEDLESS_ELSE_INFO, crate::needless_for_each::NEEDLESS_FOR_EACH_INFO, crate::needless_late_init::NEEDLESS_LATE_INIT_INFO, crate::needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS_INFO, @@ -474,7 +476,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::operators::ARITHMETIC_SIDE_EFFECTS_INFO, crate::operators::ASSIGN_OP_PATTERN_INFO, crate::operators::BAD_BIT_MASK_INFO, - crate::operators::CMP_NAN_INFO, crate::operators::CMP_OWNED_INFO, crate::operators::DOUBLE_COMPARISONS_INFO, crate::operators::DURATION_SUBSEC_INFO, diff --git a/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs b/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs index 9bd7a0dc0f3b8..fb037bbcbf3eb 100644 --- a/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs +++ b/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs @@ -1,4 +1,4 @@ -use clippy_utils::{diagnostics::span_lint_and_sugg, match_def_path, paths}; +use clippy_utils::{diagnostics::span_lint_and_sugg, is_ty_alias, match_def_path, paths}; use hir::{def::Res, ExprKind}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -43,12 +43,23 @@ declare_clippy_lint! { } declare_lint_pass!(DefaultConstructedUnitStructs => [DEFAULT_CONSTRUCTED_UNIT_STRUCTS]); +fn is_alias(ty: hir::Ty<'_>) -> bool { + if let hir::TyKind::Path(ref qpath) = ty.kind { + is_ty_alias(qpath) + } else { + false + } +} + impl LateLintPass<'_> for DefaultConstructedUnitStructs { fn check_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { if_chain!( // make sure we have a call to `Default::default` if let hir::ExprKind::Call(fn_expr, &[]) = expr.kind; - if let ExprKind::Path(ref qpath@ hir::QPath::TypeRelative(_,_)) = fn_expr.kind; + if let ExprKind::Path(ref qpath @ hir::QPath::TypeRelative(base, _)) = fn_expr.kind; + // make sure this isn't a type alias: + // `::Assoc` cannot be used as a constructor + if !is_alias(*base); if let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id); if match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD); // make sure we have a struct with no fields (unit struct) diff --git a/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs b/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs index 9c60edb179415..7a4b9a87aeb47 100644 --- a/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs +++ b/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note}; +use clippy_utils::diagnostics::span_lint_and_note; use clippy_utils::get_parent_node; use clippy_utils::is_must_use_func_call; use clippy_utils::ty::{is_copy, is_must_use_ty, is_type_lang_item}; @@ -47,35 +47,6 @@ declare_clippy_lint! { "call to `std::mem::forget` with a value which does not implement `Drop`" } -declare_clippy_lint! { - /// ### What it does - /// Prevents the safe `std::mem::drop` function from being called on `std::mem::ManuallyDrop`. - /// - /// ### Why is this bad? - /// The safe `drop` function does not drop the inner value of a `ManuallyDrop`. - /// - /// ### Known problems - /// Does not catch cases if the user binds `std::mem::drop` - /// to a different name and calls it that way. - /// - /// ### Example - /// ```rust - /// struct S; - /// drop(std::mem::ManuallyDrop::new(S)); - /// ``` - /// Use instead: - /// ```rust - /// struct S; - /// unsafe { - /// std::mem::ManuallyDrop::drop(&mut std::mem::ManuallyDrop::new(S)); - /// } - /// ``` - #[clippy::version = "1.49.0"] - pub UNDROPPED_MANUALLY_DROPS, - correctness, - "use of safe `std::mem::drop` function to drop a std::mem::ManuallyDrop, which will not drop the inner value" -} - const DROP_NON_DROP_SUMMARY: &str = "call to `std::mem::drop` with a value that does not implement `Drop`. \ Dropping such a type only extends its contained lifetimes"; const FORGET_NON_DROP_SUMMARY: &str = "call to `std::mem::forget` with a value that does not implement `Drop`. \ @@ -84,7 +55,6 @@ const FORGET_NON_DROP_SUMMARY: &str = "call to `std::mem::forget` with a value t declare_lint_pass!(DropForgetRef => [ DROP_NON_DROP, FORGET_NON_DROP, - UNDROPPED_MANUALLY_DROPS ]); impl<'tcx> LateLintPass<'tcx> for DropForgetRef { @@ -103,17 +73,7 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef { sym::mem_forget if arg_ty.is_ref() => return, sym::mem_drop if is_copy && !drop_is_single_call_in_arm => return, sym::mem_forget if is_copy => return, - sym::mem_drop if is_type_lang_item(cx, arg_ty, LangItem::ManuallyDrop) => { - span_lint_and_help( - cx, - UNDROPPED_MANUALLY_DROPS, - expr.span, - "the inner value of this ManuallyDrop will not be dropped", - None, - "to drop a `ManuallyDrop`, use std::mem::ManuallyDrop::drop", - ); - return; - } + sym::mem_drop if is_type_lang_item(cx, arg_ty, LangItem::ManuallyDrop) => return, sym::mem_drop if !(arg_ty.needs_drop(cx.tcx, cx.param_env) || is_must_use_func_call(cx, arg) diff --git a/src/tools/clippy/clippy_lints/src/large_futures.rs b/src/tools/clippy/clippy_lints/src/large_futures.rs index 0ca31033b169b..087c4a65250a9 100644 --- a/src/tools/clippy/clippy_lints/src/large_futures.rs +++ b/src/tools/clippy/clippy_lints/src/large_futures.rs @@ -38,7 +38,7 @@ declare_clippy_lint! { /// wait(fut).await; /// } /// ``` - #[clippy::version = "1.68.0"] + #[clippy::version = "1.70.0"] pub LARGE_FUTURES, pedantic, "large future may lead to unexpected stack overflows" diff --git a/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs b/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs index 32c6312e06946..0a5901bce046e 100644 --- a/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs +++ b/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs @@ -38,7 +38,7 @@ impl_lint_pass!(LargeStackArrays => [LARGE_STACK_ARRAYS]); impl<'tcx> LateLintPass<'tcx> for LargeStackArrays { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - if let ExprKind::Repeat(_, _) = expr.kind + if let ExprKind::Repeat(_, _) | ExprKind::Array(_) = expr.kind && let ty::Array(element_type, cst) = cx.typeck_results().expr_ty(expr).kind() && let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind() && let Ok(element_count) = element_count.try_to_target_usize(cx.tcx) diff --git a/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs b/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs index c01e3882d529f..2f10e3d258133 100644 --- a/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs +++ b/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs @@ -17,7 +17,7 @@ declare_clippy_lint! { /// ```rust,ignore /// let my_number = 1; /// ``` - #[clippy::version = "1.69.0"] + #[clippy::version = "1.70.0"] pub LET_WITH_TYPE_UNDERSCORE, complexity, "unneeded underscore type (`_`) in a variable declaration" diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index fcca595c2bc4c..4a23edb58aaa4 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -202,6 +202,7 @@ mod missing_assert_message; mod missing_const_for_fn; mod missing_doc; mod missing_enforced_import_rename; +mod missing_fields_in_debug; mod missing_inline; mod missing_trait_methods; mod mixed_read_write_in_expression; @@ -217,6 +218,7 @@ mod needless_arbitrary_self_type; mod needless_bool; mod needless_borrowed_ref; mod needless_continue; +mod needless_else; mod needless_for_each; mod needless_late_init; mod needless_parens_on_range_literals; @@ -333,7 +335,7 @@ mod zero_sized_map_values; pub use crate::utils::conf::{lookup_conf_file, Conf}; use crate::utils::{ - conf::{format_error, metadata::get_configuration_metadata, TryConf}, + conf::{metadata::get_configuration_metadata, TryConf}, FindAll, }; @@ -369,23 +371,36 @@ pub fn read_conf(sess: &Session, path: &io::Result<(Option, Vec }, }; - let TryConf { conf, errors, warnings } = utils::conf::read(file_name); + let TryConf { conf, errors, warnings } = utils::conf::read(sess, file_name); // all conf errors are non-fatal, we just use the default conf in case of error for error in errors { - sess.err(format!( - "error reading Clippy's configuration file `{}`: {}", - file_name.display(), - format_error(error) - )); + if let Some(span) = error.span { + sess.span_err( + span, + format!("error reading Clippy's configuration file: {}", error.message), + ); + } else { + sess.err(format!( + "error reading Clippy's configuration file `{}`: {}", + file_name.display(), + error.message + )); + } } for warning in warnings { - sess.struct_warn(format!( - "error reading Clippy's configuration file `{}`: {}", - file_name.display(), - format_error(warning) - )) - .emit(); + if let Some(span) = warning.span { + sess.span_warn( + span, + format!("error reading Clippy's configuration file: {}", warning.message), + ); + } else { + sess.warn(format!( + "error reading Clippy's configuration file `{}`: {}", + file_name.display(), + warning.message + )); + } } conf @@ -990,6 +1005,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(items_after_test_module::ItemsAfterTestModule)); store.register_early_pass(|| Box::new(ref_patterns::RefPatterns)); store.register_late_pass(|_| Box::new(default_constructed_unit_structs::DefaultConstructedUnitStructs)); + store.register_early_pass(|| Box::new(needless_else::NeedlessElse)); + store.register_late_pass(|_| Box::new(missing_fields_in_debug::MissingFieldsInDebug)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/src/tools/clippy/clippy_lints/src/manual_let_else.rs b/src/tools/clippy/clippy_lints/src/manual_let_else.rs index 3f8b42ffe8053..389b0a4a62dc5 100644 --- a/src/tools/clippy/clippy_lints/src/manual_let_else.rs +++ b/src/tools/clippy/clippy_lints/src/manual_let_else.rs @@ -77,53 +77,54 @@ impl<'tcx> LateLintPass<'tcx> for ManualLetElse { local.els.is_none() && local.ty.is_none() && init.span.ctxt() == stmt.span.ctxt() && - let Some(if_let_or_match) = IfLetOrMatch::parse(cx, init) { - match if_let_or_match { - IfLetOrMatch::IfLet(if_let_expr, let_pat, if_then, if_else) => if_chain! { - if expr_is_simple_identity(let_pat, if_then); - if let Some(if_else) = if_else; - if expr_diverges(cx, if_else); - then { - emit_manual_let_else(cx, stmt.span, if_let_expr, local.pat, let_pat, if_else); - } - }, - IfLetOrMatch::Match(match_expr, arms, source) => { - if self.matches_behaviour == MatchLintBehaviour::Never { - return; - } - if source != MatchSource::Normal { - return; - } - // Any other number than two arms doesn't (necessarily) - // have a trivial mapping to let else. - if arms.len() != 2 { - return; - } - // Guards don't give us an easy mapping either - if arms.iter().any(|arm| arm.guard.is_some()) { - return; - } - let check_types = self.matches_behaviour == MatchLintBehaviour::WellKnownTypes; - let diverging_arm_opt = arms - .iter() - .enumerate() - .find(|(_, arm)| expr_diverges(cx, arm.body) && pat_allowed_for_else(cx, arm.pat, check_types)); - let Some((idx, diverging_arm)) = diverging_arm_opt else { return; }; - // If the non-diverging arm is the first one, its pattern can be reused in a let/else statement. - // However, if it arrives in second position, its pattern may cover some cases already covered - // by the diverging one. - // TODO: accept the non-diverging arm as a second position if patterns are disjointed. - if idx == 0 { - return; - } - let pat_arm = &arms[1 - idx]; - if !expr_is_simple_identity(pat_arm.pat, pat_arm.body) { - return; - } + let Some(if_let_or_match) = IfLetOrMatch::parse(cx, init) + { + match if_let_or_match { + IfLetOrMatch::IfLet(if_let_expr, let_pat, if_then, if_else) => if_chain! { + if expr_is_simple_identity(let_pat, if_then); + if let Some(if_else) = if_else; + if expr_diverges(cx, if_else); + then { + emit_manual_let_else(cx, stmt.span, if_let_expr, local.pat, let_pat, if_else); + } + }, + IfLetOrMatch::Match(match_expr, arms, source) => { + if self.matches_behaviour == MatchLintBehaviour::Never { + return; + } + if source != MatchSource::Normal { + return; + } + // Any other number than two arms doesn't (necessarily) + // have a trivial mapping to let else. + if arms.len() != 2 { + return; + } + // Guards don't give us an easy mapping either + if arms.iter().any(|arm| arm.guard.is_some()) { + return; + } + let check_types = self.matches_behaviour == MatchLintBehaviour::WellKnownTypes; + let diverging_arm_opt = arms + .iter() + .enumerate() + .find(|(_, arm)| expr_diverges(cx, arm.body) && pat_allowed_for_else(cx, arm.pat, check_types)); + let Some((idx, diverging_arm)) = diverging_arm_opt else { return; }; + // If the non-diverging arm is the first one, its pattern can be reused in a let/else statement. + // However, if it arrives in second position, its pattern may cover some cases already covered + // by the diverging one. + // TODO: accept the non-diverging arm as a second position if patterns are disjointed. + if idx == 0 { + return; + } + let pat_arm = &arms[1 - idx]; + if !expr_is_simple_identity(pat_arm.pat, pat_arm.body) { + return; + } - emit_manual_let_else(cx, stmt.span, match_expr, local.pat, pat_arm.pat, diverging_arm.body); - }, - } + emit_manual_let_else(cx, stmt.span, match_expr, local.pat, pat_arm.pat, diverging_arm.body); + }, + } }; } @@ -145,10 +146,9 @@ fn emit_manual_let_else( "this could be rewritten as `let...else`", |diag| { // This is far from perfect, for example there needs to be: - // * mut additions for the bindings - // * renamings of the bindings for `PatKind::Or` + // * tracking for multi-binding cases: let (foo, bar) = if let (Some(foo), Ok(bar)) = ... + // * renamings of the bindings for many `PatKind`s like structs, slices, etc. // * unused binding collision detection with existing ones - // * putting patterns with at the top level | inside () // for this to be machine applicable. let mut app = Applicability::HasPlaceholders; let (sn_expr, _) = snippet_with_context(cx, expr.span, span.ctxt(), "", &mut app); @@ -159,28 +159,62 @@ fn emit_manual_let_else( } else { format!("{{ {sn_else} }}") }; - let sn_bl = match pat.kind { - PatKind::Or(..) => { - let (sn_pat, _) = snippet_with_context(cx, pat.span, span.ctxt(), "", &mut app); - format!("({sn_pat})") - }, - // Replace the variable name iff `TupleStruct` has one argument like `Variant(v)`. - PatKind::TupleStruct(ref w, args, ..) if args.len() == 1 => { - let sn_wrapper = cx.sess().source_map().span_to_snippet(w.span()).unwrap_or_default(); - let (sn_inner, _) = snippet_with_context(cx, local.span, span.ctxt(), "", &mut app); - format!("{sn_wrapper}({sn_inner})") - }, - _ => { - let (sn_pat, _) = snippet_with_context(cx, pat.span, span.ctxt(), "", &mut app); - sn_pat.into_owned() - }, - }; + let sn_bl = replace_in_pattern(cx, span, local, pat, &mut app); let sugg = format!("let {sn_bl} = {sn_expr} else {else_bl};"); diag.span_suggestion(span, "consider writing", sugg, app); }, ); } +// replaces the locals in the pattern +fn replace_in_pattern( + cx: &LateContext<'_>, + span: Span, + local: &Pat<'_>, + pat: &Pat<'_>, + app: &mut Applicability, +) -> String { + let mut bindings_count = 0; + pat.each_binding_or_first(&mut |_, _, _, _| bindings_count += 1); + // If the pattern creates multiple bindings, exit early, + // as otherwise we might paste the pattern to the positions of multiple bindings. + if bindings_count > 1 { + let (sn_pat, _) = snippet_with_context(cx, pat.span, span.ctxt(), "", app); + return sn_pat.into_owned(); + } + + match pat.kind { + PatKind::Binding(..) => { + let (sn_bdg, _) = snippet_with_context(cx, local.span, span.ctxt(), "", app); + return sn_bdg.to_string(); + }, + PatKind::Or(pats) => { + let patterns = pats + .iter() + .map(|pat| replace_in_pattern(cx, span, local, pat, app)) + .collect::>(); + let or_pat = patterns.join(" | "); + return format!("({or_pat})"); + }, + // Replace the variable name iff `TupleStruct` has one argument like `Variant(v)`. + PatKind::TupleStruct(ref w, args, dot_dot_pos) => { + let mut args = args + .iter() + .map(|pat| replace_in_pattern(cx, span, local, pat, app)) + .collect::>(); + if let Some(pos) = dot_dot_pos.as_opt_usize() { + args.insert(pos, "..".to_owned()); + } + let args = args.join(", "); + let sn_wrapper = cx.sess().source_map().span_to_snippet(w.span()).unwrap_or_default(); + return format!("{sn_wrapper}({args})"); + }, + _ => {}, + } + let (sn_pat, _) = snippet_with_context(cx, pat.span, span.ctxt(), "", app); + sn_pat.into_owned() +} + /// Check whether an expression is divergent. May give false negatives. fn expr_diverges(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { struct V<'cx, 'tcx> { diff --git a/src/tools/clippy/clippy_lints/src/matches/match_wild_err_arm.rs b/src/tools/clippy/clippy_lints/src/matches/match_wild_err_arm.rs index 42f1e2629d41a..6424ac31d9f68 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_wild_err_arm.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_wild_err_arm.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_note; use clippy_utils::macros::{is_panic, root_macro_call}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::is_local_used; -use clippy_utils::{is_wild, peel_blocks_with_stmt}; +use clippy_utils::{in_constant, is_wild, peel_blocks_with_stmt}; use rustc_hir::{Arm, Expr, PatKind}; use rustc_lint::LateContext; use rustc_span::symbol::{kw, sym}; @@ -10,6 +10,11 @@ use rustc_span::symbol::{kw, sym}; use super::MATCH_WILD_ERR_ARM; pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm<'tcx>]) { + // `unwrap`/`expect` is not (yet) const, so we want to allow this in const contexts for now + if in_constant(cx, ex.hir_id) { + return; + } + let ex_ty = cx.typeck_results().expr_ty(ex).peel_refs(); if is_type_diagnostic_item(cx, ex_ty, sym::Result) { for arm in arms { diff --git a/src/tools/clippy/clippy_lints/src/matches/mod.rs b/src/tools/clippy/clippy_lints/src/matches/mod.rs index 55ec9d4474f59..0d91051632a18 100644 --- a/src/tools/clippy/clippy_lints/src/matches/mod.rs +++ b/src/tools/clippy/clippy_lints/src/matches/mod.rs @@ -25,7 +25,7 @@ mod wild_in_or_pats; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{snippet_opt, walk_span_to_context}; -use clippy_utils::{higher, in_constant, is_span_match, tokenize_with_text}; +use clippy_utils::{higher, in_constant, is_direct_expn_of, is_span_match, tokenize_with_text}; use rustc_hir::{Arm, Expr, ExprKind, Local, MatchSource, Pat}; use rustc_lexer::TokenKind; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -974,12 +974,16 @@ impl_lint_pass!(Matches => [ impl<'tcx> LateLintPass<'tcx> for Matches { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if in_external_macro(cx.sess(), expr.span) { + if is_direct_expn_of(expr.span, "matches").is_none() && in_external_macro(cx.sess(), expr.span) { return; } let from_expansion = expr.span.from_expansion(); if let ExprKind::Match(ex, arms, source) = expr.kind { + if is_direct_expn_of(expr.span, "matches").is_some() { + redundant_pattern_match::check_match(cx, expr, ex, arms); + } + if source == MatchSource::Normal && !is_span_match(cx, expr.span) { return; } diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs index e81e09da42547..479cfd835126c 100644 --- a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs @@ -1,10 +1,10 @@ use super::REDUNDANT_PATTERN_MATCHING; -use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, walk_span_to_context}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_type_diagnostic_item, needs_ordered_drop}; use clippy_utils::visitors::any_temporaries_need_ordered_drop; -use clippy_utils::{higher, is_trait_method}; +use clippy_utils::{higher, is_expn_of, is_trait_method}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -190,24 +190,19 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op let node_pair = (&arms[0].pat.kind, &arms[1].pat.kind); if let Some(good_method) = found_good_method(cx, arms, node_pair) { - let span = expr.span.to(op.span); + let span = is_expn_of(expr.span, "matches").unwrap_or(expr.span.to(op.span)); let result_expr = match &op.kind { ExprKind::AddrOf(_, _, borrowed) => borrowed, _ => op, }; - span_lint_and_then( + span_lint_and_sugg( cx, REDUNDANT_PATTERN_MATCHING, - expr.span, + span, &format!("redundant pattern matching, consider using `{good_method}`"), - |diag| { - diag.span_suggestion( - span, - "try this", - format!("{}.{good_method}", snippet(cx, result_expr.span, "_")), - Applicability::MaybeIncorrect, // snippet - ); - }, + "try this", + format!("{}.{good_method}", snippet(cx, result_expr.span, "_")), + Applicability::MachineApplicable, ); } } diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index 9a594d964ab22..88cbefbb5d3d5 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -3191,7 +3191,7 @@ declare_clippy_lint! { /// let mut v = vec![1, 2, 3]; /// v.clear(); /// ``` - #[clippy::version = "1.69.0"] + #[clippy::version = "1.70.0"] pub CLEAR_WITH_DRAIN, nursery, "calling `drain` in order to `clear` a container" diff --git a/src/tools/clippy/clippy_lints/src/missing_assert_message.rs b/src/tools/clippy/clippy_lints/src/missing_assert_message.rs index 2214a568d9c6d..4dbb79334caf3 100644 --- a/src/tools/clippy/clippy_lints/src/missing_assert_message.rs +++ b/src/tools/clippy/clippy_lints/src/missing_assert_message.rs @@ -36,7 +36,7 @@ declare_clippy_lint! { /// assert!(service.ready, "`service.poll_ready()` must be called first to ensure that service is ready to receive requests"); /// } /// ``` - #[clippy::version = "1.69.0"] + #[clippy::version = "1.70.0"] pub MISSING_ASSERT_MESSAGE, restriction, "checks assertions without a custom panic message" diff --git a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs new file mode 100644 index 0000000000000..b6f0de7e504f0 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs @@ -0,0 +1,234 @@ +use std::ops::ControlFlow; + +use clippy_utils::{ + diagnostics::span_lint_and_then, + is_path_lang_item, paths, + ty::match_type, + visitors::{for_each_expr, Visitable}, +}; +use rustc_ast::LitKind; +use rustc_data_structures::fx::FxHashSet; +use rustc_hir::Block; +use rustc_hir::{ + def::{DefKind, Res}, + Expr, ImplItemKind, LangItem, Node, +}; +use rustc_hir::{ExprKind, Impl, ItemKind, QPath, TyKind}; +use rustc_hir::{ImplItem, Item, VariantData}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::Ty; +use rustc_middle::ty::TypeckResults; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{sym, Span, Symbol}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for manual [`core::fmt::Debug`](https://doc.rust-lang.org/core/fmt/trait.Debug.html) implementations that do not use all fields. + /// + /// ### Why is this bad? + /// A common mistake is to forget to update manual `Debug` implementations when adding a new field + /// to a struct or a new variant to an enum. + /// + /// At the same time, it also acts as a style lint to suggest using [`core::fmt::DebugStruct::finish_non_exhaustive`](https://doc.rust-lang.org/core/fmt/struct.DebugStruct.html#method.finish_non_exhaustive) + /// for the times when the user intentionally wants to leave out certain fields (e.g. to hide implementation details). + /// + /// ### Known problems + /// This lint works based on the `DebugStruct` helper types provided by the `Formatter`, + /// so this won't detect `Debug` impls that use the `write!` macro. + /// Oftentimes there is more logic to a `Debug` impl if it uses `write!` macro, so it tries + /// to be on the conservative side and not lint in those cases in an attempt to prevent false positives. + /// + /// This lint also does not look through function calls, so calling a function does not consider fields + /// used inside of that function as used by the `Debug` impl. + /// + /// Lastly, it also ignores tuple structs as their `DebugTuple` formatter does not have a `finish_non_exhaustive` + /// method, as well as enums because their exhaustiveness is already checked by the compiler when matching on the enum, + /// making it much less likely to accidentally forget to update the `Debug` impl when adding a new variant. + /// + /// ### Example + /// ```rust + /// use std::fmt; + /// struct Foo { + /// data: String, + /// // implementation detail + /// hidden_data: i32 + /// } + /// impl fmt::Debug for Foo { + /// fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + /// formatter + /// .debug_struct("Foo") + /// .field("data", &self.data) + /// .finish() + /// } + /// } + /// ``` + /// Use instead: + /// ```rust + /// use std::fmt; + /// struct Foo { + /// data: String, + /// // implementation detail + /// hidden_data: i32 + /// } + /// impl fmt::Debug for Foo { + /// fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + /// formatter + /// .debug_struct("Foo") + /// .field("data", &self.data) + /// .finish_non_exhaustive() + /// } + /// } + /// ``` + #[clippy::version = "1.70.0"] + pub MISSING_FIELDS_IN_DEBUG, + pedantic, + "missing fields in manual `Debug` implementation" +} +declare_lint_pass!(MissingFieldsInDebug => [MISSING_FIELDS_IN_DEBUG]); + +fn report_lints(cx: &LateContext<'_>, span: Span, span_notes: Vec<(Span, &'static str)>) { + span_lint_and_then( + cx, + MISSING_FIELDS_IN_DEBUG, + span, + "manual `Debug` impl does not include all fields", + |diag| { + for (span, note) in span_notes { + diag.span_note(span, note); + } + diag.help("consider including all fields in this `Debug` impl") + .help("consider calling `.finish_non_exhaustive()` if you intend to ignore fields"); + }, + ); +} + +/// Checks if we should lint in a block of code +/// +/// The way we check for this condition is by checking if there is +/// a call to `Formatter::debug_struct` but no call to `.finish_non_exhaustive()`. +fn should_lint<'tcx>( + cx: &LateContext<'tcx>, + typeck_results: &TypeckResults<'tcx>, + block: impl Visitable<'tcx>, +) -> bool { + // Is there a call to `DebugStruct::finish_non_exhaustive`? Don't lint if there is. + let mut has_finish_non_exhaustive = false; + // Is there a call to `DebugStruct::debug_struct`? Do lint if there is. + let mut has_debug_struct = false; + + for_each_expr(block, |expr| { + if let ExprKind::MethodCall(path, recv, ..) = &expr.kind { + let recv_ty = typeck_results.expr_ty(recv).peel_refs(); + + if path.ident.name == sym::debug_struct && match_type(cx, recv_ty, &paths::FORMATTER) { + has_debug_struct = true; + } else if path.ident.name == sym!(finish_non_exhaustive) && match_type(cx, recv_ty, &paths::DEBUG_STRUCT) { + has_finish_non_exhaustive = true; + } + } + ControlFlow::::Continue(()) + }); + + !has_finish_non_exhaustive && has_debug_struct +} + +/// Checks if the given expression is a call to `DebugStruct::field` +/// and the first argument to it is a string literal and if so, returns it +/// +/// Example: `.field("foo", ....)` returns `Some("foo")` +fn as_field_call<'tcx>( + cx: &LateContext<'tcx>, + typeck_results: &TypeckResults<'tcx>, + expr: &Expr<'_>, +) -> Option { + if let ExprKind::MethodCall(path, recv, [debug_field, _], _) = &expr.kind + && let recv_ty = typeck_results.expr_ty(recv).peel_refs() + && match_type(cx, recv_ty, &paths::DEBUG_STRUCT) + && path.ident.name == sym::field + && let ExprKind::Lit(lit) = &debug_field.kind + && let LitKind::Str(sym, ..) = lit.node + { + Some(sym) + } else { + None + } +} + +/// Attempts to find unused fields assuming that the item is a struct +fn check_struct<'tcx>( + cx: &LateContext<'tcx>, + typeck_results: &TypeckResults<'tcx>, + block: &'tcx Block<'tcx>, + self_ty: Ty<'tcx>, + item: &'tcx Item<'tcx>, + data: &VariantData<'_>, +) { + // Is there a "direct" field access anywhere (i.e. self.foo)? + // We don't want to lint if there is not, because the user might have + // a newtype struct and use fields from the wrapped type only. + let mut has_direct_field_access = false; + let mut field_accesses = FxHashSet::default(); + + for_each_expr(block, |expr| { + if let ExprKind::Field(target, ident) = expr.kind + && let target_ty = typeck_results.expr_ty_adjusted(target).peel_refs() + && target_ty == self_ty + { + field_accesses.insert(ident.name); + has_direct_field_access = true; + } else if let Some(sym) = as_field_call(cx, typeck_results, expr) { + field_accesses.insert(sym); + } + ControlFlow::::Continue(()) + }); + + let span_notes = data + .fields() + .iter() + .filter_map(|field| { + if field_accesses.contains(&field.ident.name) || is_path_lang_item(cx, field.ty, LangItem::PhantomData) { + None + } else { + Some((field.span, "this field is unused")) + } + }) + .collect::>(); + + // only lint if there's also at least one direct field access to allow patterns + // where one might have a newtype struct and uses fields from the wrapped type + if !span_notes.is_empty() && has_direct_field_access { + report_lints(cx, item.span, span_notes); + } +} + +impl<'tcx> LateLintPass<'tcx> for MissingFieldsInDebug { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx rustc_hir::Item<'tcx>) { + // is this an `impl Debug for X` block? + if let ItemKind::Impl(Impl { of_trait: Some(trait_ref), self_ty, items, .. }) = item.kind + && let Res::Def(DefKind::Trait, trait_def_id) = trait_ref.path.res + && let TyKind::Path(QPath::Resolved(_, self_path)) = &self_ty.kind + && cx.match_def_path(trait_def_id, &[sym::core, sym::fmt, sym::Debug]) + // don't trigger if this impl was derived + && !cx.tcx.has_attr(item.owner_id, sym::automatically_derived) + && !item.span.from_expansion() + // find `Debug::fmt` function + && let Some(fmt_item) = items.iter().find(|i| i.ident.name == sym::fmt) + && let ImplItem { kind: ImplItemKind::Fn(_, body_id), .. } = cx.tcx.hir().impl_item(fmt_item.id) + && let body = cx.tcx.hir().body(*body_id) + && let ExprKind::Block(block, _) = body.value.kind + // inspect `self` + && let self_ty = cx.tcx.type_of(self_path.res.def_id()).skip_binder().peel_refs() + && let Some(self_adt) = self_ty.ty_adt_def() + && let Some(self_def_id) = self_adt.did().as_local() + && let Some(Node::Item(self_item)) = cx.tcx.hir().find_by_def_id(self_def_id) + // NB: can't call cx.typeck_results() as we are not in a body + && let typeck_results = cx.tcx.typeck_body(*body_id) + && should_lint(cx, typeck_results, block) + { + // we intentionally only lint structs, see lint description + if let ItemKind::Struct(data, _) = &self_item.kind { + check_struct(cx, typeck_results, block, self_ty, item, data); + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/missing_inline.rs b/src/tools/clippy/clippy_lints/src/missing_inline.rs index 5a459548153aa..a41d5a9ce8d2f 100644 --- a/src/tools/clippy/clippy_lints/src/missing_inline.rs +++ b/src/tools/clippy/clippy_lints/src/missing_inline.rs @@ -105,7 +105,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { match tit_.kind { hir::TraitItemKind::Const(..) | hir::TraitItemKind::Type(..) => {}, hir::TraitItemKind::Fn(..) => { - if cx.tcx.impl_defaultness(tit.id.owner_id).has_value() { + if cx.tcx.defaultness(tit.id.owner_id).has_value() { // trait method with default body needs inline in case // an impl is not provided let desc = "a default trait method"; diff --git a/src/tools/clippy/clippy_lints/src/needless_else.rs b/src/tools/clippy/clippy_lints/src/needless_else.rs new file mode 100644 index 0000000000000..4ff1bf7ffc0de --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/needless_else.rs @@ -0,0 +1,61 @@ +use clippy_utils::source::snippet_opt; +use clippy_utils::{diagnostics::span_lint_and_sugg, source::trim_span}; +use rustc_ast::ast::{Expr, ExprKind}; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for empty `else` branches. + /// + /// ### Why is this bad? + /// An empty else branch does nothing and can be removed. + /// + /// ### Example + /// ```rust + ///# fn check() -> bool { true } + /// if check() { + /// println!("Check successful!"); + /// } else { + /// } + /// ``` + /// Use instead: + /// ```rust + ///# fn check() -> bool { true } + /// if check() { + /// println!("Check successful!"); + /// } + /// ``` + #[clippy::version = "1.71.0"] + pub NEEDLESS_ELSE, + style, + "empty else branch" +} +declare_lint_pass!(NeedlessElse => [NEEDLESS_ELSE]); + +impl EarlyLintPass for NeedlessElse { + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + if let ExprKind::If(_, then_block, Some(else_clause)) = &expr.kind + && let ExprKind::Block(block, _) = &else_clause.kind + && !expr.span.from_expansion() + && !else_clause.span.from_expansion() + && block.stmts.is_empty() + && let Some(trimmed) = expr.span.trim_start(then_block.span) + && let span = trim_span(cx.sess().source_map(), trimmed) + && let Some(else_snippet) = snippet_opt(cx, span) + // Ignore else blocks that contain comments or #[cfg]s + && !else_snippet.contains(['/', '#']) + { + span_lint_and_sugg( + cx, + NEEDLESS_ELSE, + span, + "this else branch is empty", + "you can remove it", + String::new(), + Applicability::MachineApplicable, + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs b/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs index 6c909e5ed73ea..2d79a5c90081c 100644 --- a/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs +++ b/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs @@ -241,7 +241,7 @@ impl<'de> Deserialize<'de> for MacroMatcher { V: de::MapAccess<'de>, { let mut name = None; - let mut brace: Option<&str> = None; + let mut brace: Option = None; while let Some(key) = map.next_key()? { match key { Field::Name => { diff --git a/src/tools/clippy/clippy_lints/src/operators/cmp_nan.rs b/src/tools/clippy/clippy_lints/src/operators/cmp_nan.rs deleted file mode 100644 index e18064b7061bf..0000000000000 --- a/src/tools/clippy/clippy_lints/src/operators/cmp_nan.rs +++ /dev/null @@ -1,30 +0,0 @@ -use clippy_utils::consts::{constant, Constant}; -use clippy_utils::diagnostics::span_lint; -use clippy_utils::in_constant; -use rustc_hir::{BinOpKind, Expr}; -use rustc_lint::LateContext; - -use super::CMP_NAN; - -pub(super) fn check(cx: &LateContext<'_>, e: &Expr<'_>, op: BinOpKind, lhs: &Expr<'_>, rhs: &Expr<'_>) { - if op.is_comparison() && !in_constant(cx, e.hir_id) && (is_nan(cx, lhs) || is_nan(cx, rhs)) { - span_lint( - cx, - CMP_NAN, - e.span, - "doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead", - ); - } -} - -fn is_nan(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { - if let Some(value) = constant(cx, cx.typeck_results(), e) { - match value { - Constant::F32(num) => num.is_nan(), - Constant::F64(num) => num.is_nan(), - _ => false, - } - } else { - false - } -} diff --git a/src/tools/clippy/clippy_lints/src/operators/mod.rs b/src/tools/clippy/clippy_lints/src/operators/mod.rs index d63a836e73d6f..2cf15adda01a2 100644 --- a/src/tools/clippy/clippy_lints/src/operators/mod.rs +++ b/src/tools/clippy/clippy_lints/src/operators/mod.rs @@ -1,7 +1,6 @@ mod absurd_extreme_comparisons; mod assign_op_pattern; mod bit_mask; -mod cmp_nan; mod cmp_owned; mod double_comparison; mod duration_subsec; @@ -485,31 +484,6 @@ declare_clippy_lint! { "integer division may cause loss of precision" } -declare_clippy_lint! { - /// ### What it does - /// Checks for comparisons to NaN. - /// - /// ### Why is this bad? - /// NaN does not compare meaningfully to anything – not - /// even itself – so those comparisons are simply wrong. - /// - /// ### Example - /// ```rust - /// # let x = 1.0; - /// if x == f32::NAN { } - /// ``` - /// - /// Use instead: - /// ```rust - /// # let x = 1.0f32; - /// if x.is_nan() { } - /// ``` - #[clippy::version = "pre 1.29.0"] - pub CMP_NAN, - correctness, - "comparisons to `NAN`, which will always return false, probably not intended" -} - declare_clippy_lint! { /// ### What it does /// Checks for conversions to owned values just for the sake @@ -775,7 +749,6 @@ impl_lint_pass!(Operators => [ FLOAT_EQUALITY_WITHOUT_ABS, IDENTITY_OP, INTEGER_DIVISION, - CMP_NAN, CMP_OWNED, FLOAT_CMP, FLOAT_CMP_CONST, @@ -816,7 +789,6 @@ impl<'tcx> LateLintPass<'tcx> for Operators { duration_subsec::check(cx, e, op.node, lhs, rhs); float_equality_without_abs::check(cx, e, op.node, lhs, rhs); integer_division::check(cx, e, op.node, lhs, rhs); - cmp_nan::check(cx, e, op.node, lhs, rhs); cmp_owned::check(cx, op.node, lhs, rhs); float_cmp::check(cx, e, op.node, lhs, rhs); modulo_one::check(cx, e, op.node, rhs); diff --git a/src/tools/clippy/clippy_lints/src/redundant_async_block.rs b/src/tools/clippy/clippy_lints/src/redundant_async_block.rs index a0f831764d071..05e52e6b38b12 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_async_block.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_async_block.rs @@ -35,7 +35,7 @@ declare_clippy_lint! { /// }; /// let fut = f; /// ``` - #[clippy::version = "1.69.0"] + #[clippy::version = "1.70.0"] pub REDUNDANT_ASYNC_BLOCK, complexity, "`async { future.await }` can be replaced by `future`" diff --git a/src/tools/clippy/clippy_lints/src/regex.rs b/src/tools/clippy/clippy_lints/src/regex.rs index ef19c6f461729..674f8bf4c0f30 100644 --- a/src/tools/clippy/clippy_lints/src/regex.rs +++ b/src/tools/clippy/clippy_lints/src/regex.rs @@ -177,7 +177,7 @@ fn check_set<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) { } fn check_regex<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) { - let mut parser = regex_syntax::ParserBuilder::new().unicode(true).utf8(!utf8).build(); + let mut parser = regex_syntax::ParserBuilder::new().unicode(true).utf8(utf8).build(); if let ExprKind::Lit(lit) = expr.kind { if let LitKind::Str(ref r, style) = lit.node { diff --git a/src/tools/clippy/clippy_lints/src/renamed_lints.rs b/src/tools/clippy/clippy_lints/src/renamed_lints.rs index 4cb4183002339..cbcd11debfd76 100644 --- a/src/tools/clippy/clippy_lints/src/renamed_lints.rs +++ b/src/tools/clippy/clippy_lints/src/renamed_lints.rs @@ -33,6 +33,7 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[ ("clippy::zero_width_space", "clippy::invisible_characters"), ("clippy::cast_ref_to_mut", "cast_ref_to_mut"), ("clippy::clone_double_ref", "suspicious_double_ref_op"), + ("clippy::cmp_nan", "invalid_nan_comparisons"), ("clippy::drop_bounds", "drop_bounds"), ("clippy::drop_copy", "dropping_copy_types"), ("clippy::drop_ref", "dropping_references"), @@ -50,6 +51,7 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[ ("clippy::panic_params", "non_fmt_panics"), ("clippy::positional_named_format_parameters", "named_arguments_used_positionally"), ("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr"), + ("clippy::undropped_manually_drops", "undropped_manually_drops"), ("clippy::unknown_clippy_lints", "unknown_lints"), ("clippy::unused_label", "unused_labels"), ]; diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs index df126d7617ebe..631ecf1428d6a 100644 --- a/src/tools/clippy/clippy_lints/src/returns.rs +++ b/src/tools/clippy/clippy_lints/src/returns.rs @@ -70,7 +70,7 @@ declare_clippy_lint! { "using a return statement like `return expr;` where an expression would suffice" } -#[derive(PartialEq, Eq, Clone)] +#[derive(PartialEq, Eq)] enum RetReplacement<'tcx> { Empty, Block, @@ -80,7 +80,7 @@ enum RetReplacement<'tcx> { } impl<'tcx> RetReplacement<'tcx> { - fn sugg_help(self) -> &'static str { + fn sugg_help(&self) -> &'static str { match self { Self::Empty | Self::Expr(..) => "remove `return`", Self::Block => "replace `return` with an empty block", @@ -88,10 +88,11 @@ impl<'tcx> RetReplacement<'tcx> { Self::IfSequence(..) => "remove `return` and wrap the sequence with parentheses", } } - fn applicability(&self) -> Option { + + fn applicability(&self) -> Applicability { match self { - Self::Expr(_, ap) | Self::IfSequence(_, ap) => Some(*ap), - _ => None, + Self::Expr(_, ap) | Self::IfSequence(_, ap) => *ap, + _ => Applicability::MachineApplicable, } } } @@ -271,7 +272,7 @@ fn check_final_expr<'tcx>( return; } - emit_return_lint(cx, ret_span, semi_spans, replacement); + emit_return_lint(cx, ret_span, semi_spans, &replacement); }, ExprKind::If(_, then, else_clause_opt) => { check_block_return(cx, &then.kind, peeled_drop_expr.span, semi_spans.clone()); @@ -306,20 +307,17 @@ fn expr_contains_conjunctive_ifs<'tcx>(expr: &'tcx Expr<'tcx>) -> bool { contains_if(expr, false) } -fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, semi_spans: Vec, replacement: RetReplacement<'_>) { +fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, semi_spans: Vec, replacement: &RetReplacement<'_>) { if ret_span.from_expansion() { return; } - let applicability = replacement.applicability().unwrap_or(Applicability::MachineApplicable); - let return_replacement = replacement.to_string(); - let sugg_help = replacement.sugg_help(); span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| { - diag.span_suggestion_hidden(ret_span, sugg_help, return_replacement, applicability); - // for each parent statement, we need to remove the semicolon - for semi_stmt_span in semi_spans { - diag.tool_only_span_suggestion(semi_stmt_span, "remove this semicolon", "", applicability); - } + let suggestions = std::iter::once((ret_span, replacement.to_string())) + .chain(semi_spans.into_iter().map(|span| (span, String::new()))) + .collect(); + + diag.multipart_suggestion_verbose(replacement.sugg_help(), suggestions, replacement.applicability()); }); } diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs index 483f860a8b5e2..8658009eba43f 100644 --- a/src/tools/clippy/clippy_lints/src/strings.rs +++ b/src/tools/clippy/clippy_lints/src/strings.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sug use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::ty::is_type_lang_item; use clippy_utils::{get_expr_use_or_unification_node, peel_blocks, SpanlessEq}; -use clippy_utils::{get_parent_expr, is_lint_allowed, match_function_call, method_calls, paths}; +use clippy_utils::{get_parent_expr, is_lint_allowed, is_path_diagnostic_item, method_calls}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; @@ -255,7 +255,8 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { if_chain! { // Find std::str::converts::from_utf8 - if let Some(args) = match_function_call(cx, e, &paths::STR_FROM_UTF8); + if let ExprKind::Call(fun, args) = e.kind; + if is_path_diagnostic_item(cx, fun, sym::str_from_utf8); // Find string::as_bytes if let ExprKind::AddrOf(BorrowKind::Ref, _, args) = args[0].kind; diff --git a/src/tools/clippy/clippy_lints/src/unused_async.rs b/src/tools/clippy/clippy_lints/src/unused_async.rs index 55651a28be924..117dda092223e 100644 --- a/src/tools/clippy/clippy_lints/src/unused_async.rs +++ b/src/tools/clippy/clippy_lints/src/unused_async.rs @@ -1,5 +1,5 @@ -use clippy_utils::diagnostics::span_lint_and_help; -use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, Visitor}; +use clippy_utils::diagnostics::span_lint_and_then; +use rustc_hir::intravisit::{walk_body, walk_expr, walk_fn, FnKind, Visitor}; use rustc_hir::{Body, Expr, ExprKind, FnDecl, YieldSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; @@ -42,6 +42,10 @@ declare_lint_pass!(UnusedAsync => [UNUSED_ASYNC]); struct AsyncFnVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, found_await: bool, + /// Also keep track of `await`s in nested async blocks so we can mention + /// it in a note + await_in_async_block: Option, + async_depth: usize, } impl<'a, 'tcx> Visitor<'tcx> for AsyncFnVisitor<'a, 'tcx> { @@ -49,7 +53,11 @@ impl<'a, 'tcx> Visitor<'tcx> for AsyncFnVisitor<'a, 'tcx> { fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { if let ExprKind::Yield(_, YieldSource::Await { .. }) = ex.kind { - self.found_await = true; + if self.async_depth == 1 { + self.found_await = true; + } else if self.await_in_async_block.is_none() { + self.await_in_async_block = Some(ex.span); + } } walk_expr(self, ex); } @@ -57,6 +65,20 @@ impl<'a, 'tcx> Visitor<'tcx> for AsyncFnVisitor<'a, 'tcx> { fn nested_visit_map(&mut self) -> Self::Map { self.cx.tcx.hir() } + + fn visit_body(&mut self, b: &'tcx Body<'tcx>) { + let is_async_block = matches!(b.generator_kind, Some(rustc_hir::GeneratorKind::Async(_))); + + if is_async_block { + self.async_depth += 1; + } + + walk_body(self, b); + + if is_async_block { + self.async_depth -= 1; + } + } } impl<'tcx> LateLintPass<'tcx> for UnusedAsync { @@ -70,16 +92,30 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync { def_id: LocalDefId, ) { if !span.from_expansion() && fn_kind.asyncness().is_async() { - let mut visitor = AsyncFnVisitor { cx, found_await: false }; + let mut visitor = AsyncFnVisitor { + cx, + found_await: false, + async_depth: 0, + await_in_async_block: None, + }; walk_fn(&mut visitor, fn_kind, fn_decl, body.id(), def_id); if !visitor.found_await { - span_lint_and_help( + span_lint_and_then( cx, UNUSED_ASYNC, span, "unused `async` for function with no await statements", - None, - "consider removing the `async` from this function", + |diag| { + diag.help("consider removing the `async` from this function"); + + if let Some(span) = visitor.await_in_async_block { + diag.span_note( + span, + "`await` used in an async block, which does not require \ + the enclosing function to be `async`", + ); + } + }, ); } } diff --git a/src/tools/clippy/clippy_lints/src/utils/conf.rs b/src/tools/clippy/clippy_lints/src/utils/conf.rs index f6de66bb5145b..35f40830681d6 100644 --- a/src/tools/clippy/clippy_lints/src/utils/conf.rs +++ b/src/tools/clippy/clippy_lints/src/utils/conf.rs @@ -2,12 +2,15 @@ #![allow(clippy::module_name_repetitions)] +use rustc_session::Session; +use rustc_span::{BytePos, Pos, SourceFile, Span, SyntaxContext}; use serde::de::{Deserializer, IgnoredAny, IntoDeserializer, MapAccess, Visitor}; use serde::Deserialize; -use std::error::Error; +use std::fmt::{Debug, Display, Formatter}; +use std::ops::Range; use std::path::{Path, PathBuf}; use std::str::FromStr; -use std::{cmp, env, fmt, fs, io, iter}; +use std::{cmp, env, fmt, fs, io}; #[rustfmt::skip] const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[ @@ -67,33 +70,70 @@ impl DisallowedPath { #[derive(Default)] pub struct TryConf { pub conf: Conf, - pub errors: Vec>, - pub warnings: Vec>, + pub errors: Vec, + pub warnings: Vec, } impl TryConf { - fn from_error(error: impl Error + 'static) -> Self { + fn from_toml_error(file: &SourceFile, error: &toml::de::Error) -> Self { + ConfError::from_toml(file, error).into() + } +} + +impl From for TryConf { + fn from(value: ConfError) -> Self { Self { conf: Conf::default(), - errors: vec![Box::new(error)], + errors: vec![value], warnings: vec![], } } } +impl From for TryConf { + fn from(value: io::Error) -> Self { + ConfError::from(value).into() + } +} + #[derive(Debug)] -struct ConfError(String); +pub struct ConfError { + pub message: String, + pub span: Option, +} + +impl ConfError { + fn from_toml(file: &SourceFile, error: &toml::de::Error) -> Self { + if let Some(span) = error.span() { + Self::spanned(file, error.message(), span) + } else { + Self { + message: error.message().to_string(), + span: None, + } + } + } -impl fmt::Display for ConfError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - ::fmt(&self.0, f) + fn spanned(file: &SourceFile, message: impl Into, span: Range) -> Self { + Self { + message: message.into(), + span: Some(Span::new( + file.start_pos + BytePos::from_usize(span.start), + file.start_pos + BytePos::from_usize(span.end), + SyntaxContext::root(), + None, + )), + } } } -impl Error for ConfError {} - -fn conf_error(s: impl Into) -> Box { - Box::new(ConfError(s.into())) +impl From for ConfError { + fn from(value: io::Error) -> Self { + Self { + message: value.to_string(), + span: None, + } + } } macro_rules! define_Conf { @@ -117,20 +157,14 @@ macro_rules! define_Conf { } } - impl<'de> Deserialize<'de> for TryConf { - fn deserialize(deserializer: D) -> Result where D: Deserializer<'de> { - deserializer.deserialize_map(ConfVisitor) - } - } - #[derive(Deserialize)] #[serde(field_identifier, rename_all = "kebab-case")] #[allow(non_camel_case_types)] enum Field { $($name,)* third_party, } - struct ConfVisitor; + struct ConfVisitor<'a>(&'a SourceFile); - impl<'de> Visitor<'de> for ConfVisitor { + impl<'de> Visitor<'de> for ConfVisitor<'_> { type Value = TryConf; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -141,32 +175,38 @@ macro_rules! define_Conf { let mut errors = Vec::new(); let mut warnings = Vec::new(); $(let mut $name = None;)* - // could get `Field` here directly, but get `str` first for diagnostics - while let Some(name) = map.next_key::<&str>()? { - match Field::deserialize(name.into_deserializer())? { - $(Field::$name => { - $(warnings.push(conf_error(format!("deprecated field `{}`. {}", name, $dep)));)? - match map.next_value() { - Err(e) => errors.push(conf_error(e.to_string())), + // could get `Field` here directly, but get `String` first for diagnostics + while let Some(name) = map.next_key::>()? { + match Field::deserialize(name.get_ref().as_str().into_deserializer()) { + Err(e) => { + let e: FieldError = e; + errors.push(ConfError::spanned(self.0, e.0, name.span())); + } + $(Ok(Field::$name) => { + $(warnings.push(ConfError::spanned(self.0, format!("deprecated field `{}`. {}", name.get_ref(), $dep), name.span()));)? + let raw_value = map.next_value::>()?; + let value_span = raw_value.span(); + match <$ty>::deserialize(raw_value.into_inner()) { + Err(e) => errors.push(ConfError::spanned(self.0, e.to_string().replace('\n', " ").trim(), value_span)), Ok(value) => match $name { - Some(_) => errors.push(conf_error(format!("duplicate field `{}`", name))), + Some(_) => errors.push(ConfError::spanned(self.0, format!("duplicate field `{}`", name.get_ref()), name.span())), None => { $name = Some(value); // $new_conf is the same as one of the defined `$name`s, so // this variable is defined in line 2 of this function. $(match $new_conf { - Some(_) => errors.push(conf_error(concat!( + Some(_) => errors.push(ConfError::spanned(self.0, concat!( "duplicate field `", stringify!($new_conf), "` (provided as `", stringify!($name), "`)" - ))), + ), name.span())), None => $new_conf = $name.clone(), })? }, } } })* - // white-listed; ignore - Field::third_party => drop(map.next_value::()) + // ignore contents of the third_party key + Ok(Field::third_party) => drop(map.next_value::()) } } let conf = Conf { $($name: $name.unwrap_or_else(defaults::$name),)* }; @@ -486,7 +526,7 @@ pub fn lookup_conf_file() -> io::Result<(Option, Vec)> { const CONFIG_FILE_NAMES: [&str; 2] = [".clippy.toml", "clippy.toml"]; // Start looking for a config file in CLIPPY_CONF_DIR, or failing that, CARGO_MANIFEST_DIR. - // If neither of those exist, use ".". + // If neither of those exist, use ".". (Update documentation if this priority changes) let mut current = env::var_os("CLIPPY_CONF_DIR") .or_else(|| env::var_os("CARGO_MANIFEST_DIR")) .map_or_else(|| PathBuf::from("."), PathBuf::from) @@ -532,19 +572,19 @@ pub fn lookup_conf_file() -> io::Result<(Option, Vec)> { /// Read the `toml` configuration file. /// /// In case of error, the function tries to continue as much as possible. -pub fn read(path: &Path) -> TryConf { - let content = match fs::read_to_string(path) { - Err(e) => return TryConf::from_error(e), - Ok(content) => content, +pub fn read(sess: &Session, path: &Path) -> TryConf { + let file = match sess.source_map().load_file(path) { + Err(e) => return e.into(), + Ok(file) => file, }; - match toml::from_str::(&content) { + match toml::de::Deserializer::new(file.src.as_ref().unwrap()).deserialize_map(ConfVisitor(&file)) { Ok(mut conf) => { extend_vec_if_indicator_present(&mut conf.conf.doc_valid_idents, DEFAULT_DOC_VALID_IDENTS); extend_vec_if_indicator_present(&mut conf.conf.disallowed_names, DEFAULT_DISALLOWED_NAMES); conf }, - Err(e) => TryConf::from_error(e), + Err(e) => TryConf::from_toml_error(&file, &e), } } @@ -556,65 +596,42 @@ fn extend_vec_if_indicator_present(vec: &mut Vec, default: &[&str]) { const SEPARATOR_WIDTH: usize = 4; -// Check whether the error is "unknown field" and, if so, list the available fields sorted and at -// least one per line, more if `CLIPPY_TERMINAL_WIDTH` is set and allows it. -pub fn format_error(error: Box) -> String { - let s = error.to_string(); - - if_chain! { - if error.downcast::().is_ok(); - if let Some((prefix, mut fields, suffix)) = parse_unknown_field_message(&s); - then { - use fmt::Write; - - fields.sort_unstable(); - - let (rows, column_widths) = calculate_dimensions(&fields); - - let mut msg = String::from(prefix); - for row in 0..rows { - writeln!(msg).unwrap(); - for (column, column_width) in column_widths.iter().copied().enumerate() { - let index = column * rows + row; - let field = fields.get(index).copied().unwrap_or_default(); - write!( - msg, - "{:SEPARATOR_WIDTH$}{field:column_width$}", - " " - ) - .unwrap(); - } - } - write!(msg, "\n{suffix}").unwrap(); - msg - } else { - s - } +#[derive(Debug)] +struct FieldError(String); + +impl std::error::Error for FieldError {} + +impl Display for FieldError { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.pad(&self.0) } } -// `parse_unknown_field_message` will become unnecessary if -// https://github.com/alexcrichton/toml-rs/pull/364 is merged. -fn parse_unknown_field_message(s: &str) -> Option<(&str, Vec<&str>, &str)> { - // An "unknown field" message has the following form: - // unknown field `UNKNOWN`, expected one of `FIELD0`, `FIELD1`, ..., `FIELDN` at line X column Y - // ^^ ^^^^ ^^ - if_chain! { - if s.starts_with("unknown field"); - let slices = s.split("`, `").collect::>(); - let n = slices.len(); - if n >= 2; - if let Some((prefix, first_field)) = slices[0].rsplit_once(" `"); - if let Some((last_field, suffix)) = slices[n - 1].split_once("` "); - then { - let fields = iter::once(first_field) - .chain(slices[1..n - 1].iter().copied()) - .chain(iter::once(last_field)) - .collect::>(); - Some((prefix, fields, suffix)) - } else { - None +impl serde::de::Error for FieldError { + fn custom(msg: T) -> Self { + Self(msg.to_string()) + } + + fn unknown_field(field: &str, expected: &'static [&'static str]) -> Self { + // List the available fields sorted and at least one per line, more if `CLIPPY_TERMINAL_WIDTH` is + // set and allows it. + use fmt::Write; + + let mut expected = expected.to_vec(); + expected.sort_unstable(); + + let (rows, column_widths) = calculate_dimensions(&expected); + + let mut msg = format!("unknown field `{field}`, expected one of"); + for row in 0..rows { + writeln!(msg).unwrap(); + for (column, column_width) in column_widths.iter().copied().enumerate() { + let index = column * rows + row; + let field = expected.get(index).copied().unwrap_or_default(); + write!(msg, "{:SEPARATOR_WIDTH$}{field:column_width$}", " ").unwrap(); + } } + Self(msg) } } diff --git a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs index a9089fba3c539..2a3d86988bb04 100644 --- a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs +++ b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs @@ -65,8 +65,9 @@ declare_clippy_lint! { /// This can lead to confusing error messages at best and to unexpected behavior at worst. /// /// ### Exceptions - /// Wildcard imports are allowed from modules named `prelude`. Many crates (including the standard library) - /// provide modules named "prelude" specifically designed for wildcard import. + /// Wildcard imports are allowed from modules that their name contains `prelude`. Many crates + /// (including the standard library) provide modules named "prelude" specifically designed + /// for wildcard import. /// /// `use super::*` is allowed in test modules. This is defined as any module with "test" in the name. /// @@ -159,7 +160,7 @@ impl LateLintPass<'_> for WildcardImports { ) }; - let mut imports = used_imports.items().map(ToString::to_string).into_sorted_stable_ord(false); + let mut imports = used_imports.items().map(ToString::to_string).into_sorted_stable_ord(); let imports_string = if imports.len() == 1 { imports.pop().unwrap() } else if braced_glob { @@ -212,7 +213,9 @@ impl WildcardImports { // Allow "...prelude::..::*" imports. // Many crates have a prelude, and it is imported as a glob by design. fn is_prelude_import(segments: &[PathSegment<'_>]) -> bool { - segments.iter().any(|ps| ps.ident.name == sym::prelude) + segments + .iter() + .any(|ps| ps.ident.name.as_str().contains(sym::prelude.as_str())) } // Allow "super::*" imports in tests. diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml index 66a5079fa85ef..cfe686eb9b01d 100644 --- a/src/tools/clippy/clippy_utils/Cargo.toml +++ b/src/tools/clippy/clippy_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_utils" -version = "0.1.71" +version = "0.1.72" edition = "2021" publish = false diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 575c29a6b6f97..8c883445a7984 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -287,7 +287,7 @@ pub fn is_wild(pat: &Pat<'_>) -> bool { /// Checks if the given `QPath` belongs to a type alias. pub fn is_ty_alias(qpath: &QPath<'_>) -> bool { match *qpath { - QPath::Resolved(_, path) => matches!(path.res, Res::Def(DefKind::TyAlias, ..)), + QPath::Resolved(_, path) => matches!(path.res, Res::Def(DefKind::TyAlias | DefKind::AssocTy, ..)), QPath::TypeRelative(ty, _) if let TyKind::Path(qpath) = ty.kind => { is_ty_alias(&qpath) }, _ => false, } diff --git a/src/tools/clippy/clippy_utils/src/msrvs.rs b/src/tools/clippy/clippy_utils/src/msrvs.rs index e05de2dc99c05..6f102308f0bf6 100644 --- a/src/tools/clippy/clippy_utils/src/msrvs.rs +++ b/src/tools/clippy/clippy_utils/src/msrvs.rs @@ -20,7 +20,7 @@ macro_rules! msrv_aliases { // names may refer to stabilized feature flags or library items msrv_aliases! { 1,68,0 { PATH_MAIN_SEPARATOR_STR } - 1,65,0 { LET_ELSE } + 1,65,0 { LET_ELSE, POINTER_CAST_CONSTNESS } 1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE } 1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY } 1,55,0 { SEEK_REWIND } diff --git a/src/tools/clippy/clippy_utils/src/paths.rs b/src/tools/clippy/clippy_utils/src/paths.rs index 0f0792fdaa963..38ee84fb76c7a 100644 --- a/src/tools/clippy/clippy_utils/src/paths.rs +++ b/src/tools/clippy/clippy_utils/src/paths.rs @@ -125,8 +125,6 @@ pub const STRING_NEW: [&str; 4] = ["alloc", "string", "String", "new"]; pub const STR_BYTES: [&str; 4] = ["core", "str", "", "bytes"]; pub const STR_CHARS: [&str; 4] = ["core", "str", "", "chars"]; pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "", "ends_with"]; -pub const STR_FROM_UTF8: [&str; 4] = ["core", "str", "converts", "from_utf8"]; -pub const STR_FROM_UTF8_UNCHECKED: [&str; 4] = ["core", "str", "converts", "from_utf8_unchecked"]; pub const STR_LEN: [&str; 4] = ["core", "str", "", "len"]; pub const STR_STARTS_WITH: [&str; 4] = ["core", "str", "", "starts_with"]; #[cfg(feature = "internal")] @@ -163,3 +161,5 @@ pub const VEC_IS_EMPTY: [&str; 4] = ["alloc", "vec", "Vec", "is_empty"]; pub const VEC_POP: [&str; 4] = ["alloc", "vec", "Vec", "pop"]; pub const OPTION_UNWRAP: [&str; 4] = ["core", "option", "Option", "unwrap"]; pub const OPTION_EXPECT: [&str; 4] = ["core", "option", "Option", "expect"]; +pub const FORMATTER: [&str; 3] = ["core", "fmt", "Formatter"]; +pub const DEBUG_STRUCT: [&str; 4] = ["core", "fmt", "builders", "DebugStruct"]; diff --git a/src/tools/clippy/clippy_utils/src/source.rs b/src/tools/clippy/clippy_utils/src/source.rs index 0f60290644a18..582337b47e813 100644 --- a/src/tools/clippy/clippy_utils/src/source.rs +++ b/src/tools/clippy/clippy_utils/src/source.rs @@ -4,7 +4,7 @@ use rustc_data_structures::sync::Lrc; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind}; +use rustc_hir::{BlockCheckMode, Expr, ExprKind, UnsafeSource}; use rustc_lint::{LateContext, LintContext}; use rustc_session::Session; use rustc_span::source_map::{original_sp, SourceMap}; @@ -71,11 +71,16 @@ pub fn expr_block( app: &mut Applicability, ) -> String { let (code, from_macro) = snippet_block_with_context(cx, expr.span, outer, default, indent_relative_to, app); - if from_macro { - format!("{{ {code} }}") - } else if let ExprKind::Block(_, _) = expr.kind { + if !from_macro && + let ExprKind::Block(block, _) = expr.kind && + block.rules != BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) + { format!("{code}") } else { + // FIXME: add extra indent for the unsafe blocks: + // original code: unsafe { ... } + // result code: { unsafe { ... } } + // desired code: {\n unsafe { ... }\n} format!("{{ {code} }}") } } diff --git a/src/tools/clippy/declare_clippy_lint/Cargo.toml b/src/tools/clippy/declare_clippy_lint/Cargo.toml index 139102798c42a..4dc906d00db16 100644 --- a/src/tools/clippy/declare_clippy_lint/Cargo.toml +++ b/src/tools/clippy/declare_clippy_lint/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "declare_clippy_lint" -version = "0.1.71" +version = "0.1.72" edition = "2021" publish = false diff --git a/src/tools/clippy/lintcheck/Cargo.toml b/src/tools/clippy/lintcheck/Cargo.toml index 27d32f390035d..a828d12370467 100644 --- a/src/tools/clippy/lintcheck/Cargo.toml +++ b/src/tools/clippy/lintcheck/Cargo.toml @@ -22,7 +22,7 @@ rayon = "1.5.1" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.85" tar = "0.4" -toml = "0.5" +toml = "0.7.3" ureq = "2.2" walkdir = "2.3" diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain index bc7fb711ed8b8..0d2e1eee64384 100644 --- a/src/tools/clippy/rust-toolchain +++ b/src/tools/clippy/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2023-05-20" +channel = "nightly-2023-06-02" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs index 59bf447a7cd07..3c5b6e12b9688 100644 --- a/src/tools/clippy/src/driver.rs +++ b/src/tools/clippy/src/driver.rs @@ -70,7 +70,7 @@ fn track_clippy_args(parse_sess: &mut ParseSess, args_env_var: &Option) /// Track files that may be accessed at runtime in `file_depinfo` so that cargo will re-run clippy /// when any of them are modified -fn track_files(parse_sess: &mut ParseSess, conf_path_string: Option) { +fn track_files(parse_sess: &mut ParseSess) { let file_depinfo = parse_sess.file_depinfo.get_mut(); // Used by `clippy::cargo` lints and to determine the MSRV. `cargo clippy` executes `clippy-driver` @@ -79,10 +79,7 @@ fn track_files(parse_sess: &mut ParseSess, conf_path_string: Option) { file_depinfo.insert(Symbol::intern("Cargo.toml")); } - // `clippy.toml` - if let Some(path) = conf_path_string { - file_depinfo.insert(Symbol::intern(&path)); - } + // `clippy.toml` will be automatically tracked as it's loaded with `sess.source_map().load_file()` // During development track the `clippy-driver` executable so that cargo will re-run clippy whenever // it is rebuilt @@ -126,17 +123,11 @@ impl rustc_driver::Callbacks for ClippyCallbacks { #[allow(rustc::bad_opt_access)] fn config(&mut self, config: &mut interface::Config) { let conf_path = clippy_lints::lookup_conf_file(); - let conf_path_string = if let Ok((Some(path), _)) = &conf_path { - path.to_str().map(String::from) - } else { - None - }; - let previous = config.register_lints.take(); let clippy_args_var = self.clippy_args_var.take(); config.parse_sess_created = Some(Box::new(move |parse_sess| { track_clippy_args(parse_sess, &clippy_args_var); - track_files(parse_sess, conf_path_string); + track_files(parse_sess); })); config.register_lints = Some(Box::new(move |sess, lint_store| { // technically we're ~guaranteed that this is none but might as well call anything that diff --git a/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.lock b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.lock index 7e96aa36feb45..e4de82ad3b83e 100644 --- a/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.lock +++ b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.lock @@ -1,5 +1,7 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "ansi_term" version = "0.11.0" @@ -9,71 +11,14 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "bitflags" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" - -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - -[[package]] -name = "ctrlc" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "653abc99aa905f693d89df4797fadc08085baee379db92be9f2496cefe8a6f2c" -dependencies = [ - "kernel32-sys", - "nix", - "winapi 0.2.8", -] - -[[package]] -name = "kernel32-sys" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -dependencies = [ - "winapi 0.2.8", - "winapi-build", -] - -[[package]] -name = "libc" -version = "0.2.71" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49" - [[package]] name = "multiple_crate_versions" version = "0.1.0" dependencies = [ "ansi_term", - "ctrlc", -] - -[[package]] -name = "nix" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2c5afeb0198ec7be8569d666644b574345aad2e95a53baf3a532da3e0f3fb32" -dependencies = [ - "bitflags", - "cfg-if", - "libc", - "void", + "winapi 0.2.8", ] -[[package]] -name = "void" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" - [[package]] name = "winapi" version = "0.2.8" @@ -90,12 +35,6 @@ dependencies = [ "winapi-x86_64-pc-windows-gnu", ] -[[package]] -name = "winapi-build" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" - [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" diff --git a/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml index 4f97b01133402..79317659ac072 100644 --- a/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml +++ b/src/tools/clippy/tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml @@ -6,5 +6,5 @@ publish = false [workspace] [dependencies] -ctrlc = "=3.1.0" +winapi = "0.2" ansi_term = "=0.11.0" diff --git a/src/tools/clippy/tests/ui-internal/custom_ice_message.rs b/src/tools/clippy/tests/ui-internal/custom_ice_message.rs index 99ce702839094..9b0db660c9975 100644 --- a/src/tools/clippy/tests/ui-internal/custom_ice_message.rs +++ b/src/tools/clippy/tests/ui-internal/custom_ice_message.rs @@ -3,7 +3,7 @@ //@normalize-stderr-test: "produce_ice.rs:\d*:\d*" -> "produce_ice.rs" //@normalize-stderr-test: "', .*clippy_lints" -> "', clippy_lints" //@normalize-stderr-test: "'rustc'" -> "''" -//@normalize-stderr-test: "running on .*" -> "running on " +//@normalize-stderr-test: "rustc 1\.\d+.* running on .*" -> "rustc running on " //@normalize-stderr-test: "(?ms)query stack during panic:\n.*end of query stack\n" -> "" #![deny(clippy::internal)] diff --git a/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr b/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr index 0fc385cd6935a..b9ea5a64de75e 100644 --- a/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr +++ b/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr @@ -5,10 +5,9 @@ error: the compiler unexpectedly panicked. this is a bug. note: we would appreciate a bug report: https://github.com/rust-lang/rust-clippy/issues/new -note: rustc 1.71.0-nightly (521f4dae1 2023-05-19) running on +note: rustc running on note: compiler flags: -C prefer-dynamic -Z ui-testing -note: Clippy version: foo - -thread panicked while panicking. aborting. +query stack during panic: +thread panicked while processing panic. aborting. diff --git a/src/tools/clippy/tests/ui-toml/bad_toml/conf_bad_toml.stderr b/src/tools/clippy/tests/ui-toml/bad_toml/conf_bad_toml.stderr index 28c1a568a632b..5b7e8c0db744d 100644 --- a/src/tools/clippy/tests/ui-toml/bad_toml/conf_bad_toml.stderr +++ b/src/tools/clippy/tests/ui-toml/bad_toml/conf_bad_toml.stderr @@ -1,4 +1,8 @@ -error: error reading Clippy's configuration file `$DIR/clippy.toml`: expected an equals, found an identifier at line 1 column 4 +error: error reading Clippy's configuration file: expected `.`, `=` + --> $DIR/clippy.toml:1:4 + | +LL | fn this_is_obviously(not: a, toml: file) { + | ^ error: aborting due to previous error diff --git a/src/tools/clippy/tests/ui-toml/bad_toml_type/conf_bad_type.stderr b/src/tools/clippy/tests/ui-toml/bad_toml_type/conf_bad_type.stderr index e3ec60192040e..386e1135df90d 100644 --- a/src/tools/clippy/tests/ui-toml/bad_toml_type/conf_bad_type.stderr +++ b/src/tools/clippy/tests/ui-toml/bad_toml_type/conf_bad_type.stderr @@ -1,4 +1,8 @@ -error: error reading Clippy's configuration file `$DIR/clippy.toml`: invalid type: integer `42`, expected a sequence for key `disallowed-names` +error: error reading Clippy's configuration file: invalid type: integer `42`, expected a sequence + --> $DIR/clippy.toml:1:20 + | +LL | disallowed-names = 42 + | ^^ error: aborting due to previous error diff --git a/src/tools/clippy/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr b/src/tools/clippy/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr index 630bad07c5b71..123ad94dd09da 100644 --- a/src/tools/clippy/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr +++ b/src/tools/clippy/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr @@ -1,6 +1,14 @@ -warning: error reading Clippy's configuration file `$DIR/clippy.toml`: deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead +warning: error reading Clippy's configuration file: deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead + --> $DIR/clippy.toml:2:1 + | +LL | cyclomatic-complexity-threshold = 2 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: error reading Clippy's configuration file `$DIR/clippy.toml`: deprecated field `blacklisted-names`. Please use `disallowed-names` instead +warning: error reading Clippy's configuration file: deprecated field `blacklisted-names`. Please use `disallowed-names` instead + --> $DIR/clippy.toml:3:1 + | +LL | blacklisted-names = [ "..", "wibble" ] + | ^^^^^^^^^^^^^^^^^ error: the function has a cognitive complexity of (3/2) --> $DIR/conf_deprecated_key.rs:6:4 diff --git a/src/tools/clippy/tests/ui-toml/duplicated_keys/clippy.toml b/src/tools/clippy/tests/ui-toml/duplicated_keys/clippy.toml index 63a893cc6c795..55789afc1b71c 100644 --- a/src/tools/clippy/tests/ui-toml/duplicated_keys/clippy.toml +++ b/src/tools/clippy/tests/ui-toml/duplicated_keys/clippy.toml @@ -1,5 +1,2 @@ cognitive-complexity-threshold = 2 -# This is the deprecated name for the same key -cyclomatic-complexity-threshold = 3 -# Check we get duplication warning regardless of order cognitive-complexity-threshold = 4 diff --git a/src/tools/clippy/tests/ui-toml/duplicated_keys/duplicated_keys.stderr b/src/tools/clippy/tests/ui-toml/duplicated_keys/duplicated_keys.stderr index d99490a242d4f..54997735274e4 100644 --- a/src/tools/clippy/tests/ui-toml/duplicated_keys/duplicated_keys.stderr +++ b/src/tools/clippy/tests/ui-toml/duplicated_keys/duplicated_keys.stderr @@ -1,8 +1,8 @@ -error: error reading Clippy's configuration file `$DIR/clippy.toml`: duplicate field `cognitive_complexity_threshold` (provided as `cyclomatic_complexity_threshold`) +error: error reading Clippy's configuration file: duplicate key `cognitive-complexity-threshold` in document root + --> $DIR/clippy.toml:2:1 + | +LL | cognitive-complexity-threshold = 4 + | ^ -error: error reading Clippy's configuration file `$DIR/clippy.toml`: duplicate field `cognitive-complexity-threshold` - -warning: error reading Clippy's configuration file `$DIR/clippy.toml`: deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead - -error: aborting due to 2 previous errors; 1 warning emitted +error: aborting due to previous error diff --git a/src/tools/clippy/tests/ui-toml/duplicated_keys_deprecated/clippy.toml b/src/tools/clippy/tests/ui-toml/duplicated_keys_deprecated/clippy.toml new file mode 100644 index 0000000000000..7932c43ebd217 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/duplicated_keys_deprecated/clippy.toml @@ -0,0 +1,3 @@ +cognitive-complexity-threshold = 2 +# This is the deprecated name for the same key +cyclomatic-complexity-threshold = 3 diff --git a/src/tools/clippy/tests/ui-toml/duplicated_keys_deprecated/duplicated_keys.rs b/src/tools/clippy/tests/ui-toml/duplicated_keys_deprecated/duplicated_keys.rs new file mode 100644 index 0000000000000..f328e4d9d04c3 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/duplicated_keys_deprecated/duplicated_keys.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/src/tools/clippy/tests/ui-toml/duplicated_keys_deprecated/duplicated_keys.stderr b/src/tools/clippy/tests/ui-toml/duplicated_keys_deprecated/duplicated_keys.stderr new file mode 100644 index 0000000000000..2ae7848f183b5 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/duplicated_keys_deprecated/duplicated_keys.stderr @@ -0,0 +1,14 @@ +error: error reading Clippy's configuration file: duplicate field `cognitive_complexity_threshold` (provided as `cyclomatic_complexity_threshold`) + --> $DIR/clippy.toml:3:1 + | +LL | cyclomatic-complexity-threshold = 3 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: error reading Clippy's configuration file: deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead + --> $DIR/clippy.toml:3:1 + | +LL | cyclomatic-complexity-threshold = 3 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error; 1 warning emitted + diff --git a/src/tools/clippy/tests/ui-toml/duplicated_keys_deprecated_2/clippy.toml b/src/tools/clippy/tests/ui-toml/duplicated_keys_deprecated_2/clippy.toml new file mode 100644 index 0000000000000..53c634b727ed5 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/duplicated_keys_deprecated_2/clippy.toml @@ -0,0 +1,4 @@ +# This is the deprecated name for cognitive-complexity-threshold +cyclomatic-complexity-threshold = 3 +# Check we get duplication warning regardless of order +cognitive-complexity-threshold = 4 diff --git a/src/tools/clippy/tests/ui-toml/duplicated_keys_deprecated_2/duplicated_keys.rs b/src/tools/clippy/tests/ui-toml/duplicated_keys_deprecated_2/duplicated_keys.rs new file mode 100644 index 0000000000000..f328e4d9d04c3 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/duplicated_keys_deprecated_2/duplicated_keys.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/src/tools/clippy/tests/ui-toml/duplicated_keys_deprecated_2/duplicated_keys.stderr b/src/tools/clippy/tests/ui-toml/duplicated_keys_deprecated_2/duplicated_keys.stderr new file mode 100644 index 0000000000000..53ad427124691 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/duplicated_keys_deprecated_2/duplicated_keys.stderr @@ -0,0 +1,14 @@ +error: error reading Clippy's configuration file: duplicate field `cognitive-complexity-threshold` + --> $DIR/clippy.toml:4:1 + | +LL | cognitive-complexity-threshold = 4 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: error reading Clippy's configuration file: deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead + --> $DIR/clippy.toml:2:1 + | +LL | cyclomatic-complexity-threshold = 3 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error; 1 warning emitted + diff --git a/src/tools/clippy/tests/ui-toml/ifs_same_cond/ifs_same_cond.rs b/src/tools/clippy/tests/ui-toml/ifs_same_cond/ifs_same_cond.rs index d623ac7e02008..4882416c414ac 100644 --- a/src/tools/clippy/tests/ui-toml/ifs_same_cond/ifs_same_cond.rs +++ b/src/tools/clippy/tests/ui-toml/ifs_same_cond/ifs_same_cond.rs @@ -1,5 +1,5 @@ #![warn(clippy::ifs_same_cond)] -#![allow(clippy::if_same_then_else, clippy::comparison_chain)] +#![allow(clippy::if_same_then_else, clippy::comparison_chain, clippy::needless_else)] fn main() {} diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_key/clippy.toml b/src/tools/clippy/tests/ui-toml/toml_unknown_key/clippy.toml index 554b87cc50be1..b77b4580051ef 100644 --- a/src/tools/clippy/tests/ui-toml/toml_unknown_key/clippy.toml +++ b/src/tools/clippy/tests/ui-toml/toml_unknown_key/clippy.toml @@ -1,6 +1,8 @@ # that one is an error foobar = 42 +# so is this one +barfoo = 53 -# that one is white-listed +# that one is ignored [third-party] clippy-feature = "nightly" diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 44710b09648ee..b6038f031f3c3 100644 --- a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -1,4 +1,4 @@ -error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of +error: error reading Clippy's configuration file: unknown field `foobar`, expected one of allow-dbg-in-tests allow-expect-in-tests allow-mixed-uninlined-format-args @@ -54,7 +54,71 @@ error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown fie vec-box-size-threshold verbose-bit-mask-threshold warn-on-all-wildcard-imports - at line 5 column 1 + --> $DIR/clippy.toml:2:1 + | +LL | foobar = 42 + | ^^^^^^ -error: aborting due to previous error +error: error reading Clippy's configuration file: unknown field `barfoo`, expected one of + allow-dbg-in-tests + allow-expect-in-tests + allow-mixed-uninlined-format-args + allow-print-in-tests + allow-unwrap-in-tests + allowed-scripts + arithmetic-side-effects-allowed + arithmetic-side-effects-allowed-binary + arithmetic-side-effects-allowed-unary + array-size-threshold + avoid-breaking-exported-api + await-holding-invalid-types + blacklisted-names + cargo-ignore-publish + cognitive-complexity-threshold + cyclomatic-complexity-threshold + disallowed-macros + disallowed-methods + disallowed-names + disallowed-types + doc-valid-idents + enable-raw-pointer-heuristic-for-send + enforced-import-renames + enum-variant-name-threshold + enum-variant-size-threshold + future-size-threshold + ignore-interior-mutability + large-error-threshold + literal-representation-threshold + matches-for-let-else + max-fn-params-bools + max-include-file-size + max-struct-bools + max-suggested-slice-pattern-length + max-trait-bounds + missing-docs-in-crate-items + msrv + pass-by-value-size-limit + semicolon-inside-block-ignore-singleline + semicolon-outside-block-ignore-multiline + single-char-binding-names-threshold + standard-macro-braces + suppress-restriction-lint-in-const + third-party + too-large-for-stack + too-many-arguments-threshold + too-many-lines-threshold + trivial-copy-size-limit + type-complexity-threshold + unnecessary-box-size + unreadable-literal-lint-fractions + upper-case-acronyms-aggressive + vec-box-size-threshold + verbose-bit-mask-threshold + warn-on-all-wildcard-imports + --> $DIR/clippy.toml:4:1 + | +LL | barfoo = 53 + | ^^^^^^ + +error: aborting due to 2 previous errors diff --git a/src/tools/clippy/tests/ui/auxiliary/wildcard_imports_helper.rs b/src/tools/clippy/tests/ui/auxiliary/wildcard_imports_helper.rs index d75cdd625f9ec..44f49c080cdd6 100644 --- a/src/tools/clippy/tests/ui/auxiliary/wildcard_imports_helper.rs +++ b/src/tools/clippy/tests/ui/auxiliary/wildcard_imports_helper.rs @@ -25,3 +25,9 @@ pub mod prelude { pub struct PreludeModAnywhere; } } + +pub mod extern_prelude { + pub mod v1 { + pub struct ExternPreludeModAnywhere; + } +} diff --git a/src/tools/clippy/tests/ui/branches_sharing_code/valid_if_blocks.rs b/src/tools/clippy/tests/ui/branches_sharing_code/valid_if_blocks.rs index 2d6055eb6c422..5780ea0893777 100644 --- a/src/tools/clippy/tests/ui/branches_sharing_code/valid_if_blocks.rs +++ b/src/tools/clippy/tests/ui/branches_sharing_code/valid_if_blocks.rs @@ -1,6 +1,10 @@ #![deny(clippy::branches_sharing_code, clippy::if_same_then_else)] #![allow(dead_code)] -#![allow(clippy::mixed_read_write_in_expression, clippy::uninlined_format_args)] +#![allow( + clippy::mixed_read_write_in_expression, + clippy::uninlined_format_args, + clippy::needless_else +)] // This tests valid if blocks that shouldn't trigger the lint diff --git a/src/tools/clippy/tests/ui/branches_sharing_code/valid_if_blocks.stderr b/src/tools/clippy/tests/ui/branches_sharing_code/valid_if_blocks.stderr index ce7fff0122f1f..a7e72b780affc 100644 --- a/src/tools/clippy/tests/ui/branches_sharing_code/valid_if_blocks.stderr +++ b/src/tools/clippy/tests/ui/branches_sharing_code/valid_if_blocks.stderr @@ -1,5 +1,5 @@ error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:105:14 + --> $DIR/valid_if_blocks.rs:109:14 | LL | if false { | ______________^ @@ -7,7 +7,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/valid_if_blocks.rs:106:12 + --> $DIR/valid_if_blocks.rs:110:12 | LL | } else { | ____________^ @@ -20,7 +20,7 @@ LL | #![deny(clippy::branches_sharing_code, clippy::if_same_then_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:116:15 + --> $DIR/valid_if_blocks.rs:120:15 | LL | if x == 0 { | _______________^ @@ -31,7 +31,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/valid_if_blocks.rs:120:12 + --> $DIR/valid_if_blocks.rs:124:12 | LL | } else { | ____________^ @@ -42,19 +42,19 @@ LL | | } | |_____^ error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:127:23 + --> $DIR/valid_if_blocks.rs:131:23 | LL | let _ = if x == 6 { 7 } else { 7 }; | ^^^^^ | note: same as this - --> $DIR/valid_if_blocks.rs:127:34 + --> $DIR/valid_if_blocks.rs:131:34 | LL | let _ = if x == 6 { 7 } else { 7 }; | ^^^^^ error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:133:23 + --> $DIR/valid_if_blocks.rs:137:23 | LL | } else if x == 68 { | _______________________^ @@ -66,7 +66,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/valid_if_blocks.rs:138:12 + --> $DIR/valid_if_blocks.rs:142:12 | LL | } else { | ____________^ @@ -78,7 +78,7 @@ LL | | }; | |_____^ error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:147:23 + --> $DIR/valid_if_blocks.rs:151:23 | LL | } else if x == 68 { | _______________________^ @@ -88,7 +88,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/valid_if_blocks.rs:150:12 + --> $DIR/valid_if_blocks.rs:154:12 | LL | } else { | ____________^ diff --git a/src/tools/clippy/tests/ui/cmp_nan.rs b/src/tools/clippy/tests/ui/cmp_nan.rs deleted file mode 100644 index 64ca52b010a7e..0000000000000 --- a/src/tools/clippy/tests/ui/cmp_nan.rs +++ /dev/null @@ -1,34 +0,0 @@ -const NAN_F32: f32 = f32::NAN; -const NAN_F64: f64 = f64::NAN; - -#[warn(clippy::cmp_nan)] -#[allow(clippy::float_cmp, clippy::no_effect, clippy::unnecessary_operation)] -fn main() { - let x = 5f32; - x == f32::NAN; - x != f32::NAN; - x < f32::NAN; - x > f32::NAN; - x <= f32::NAN; - x >= f32::NAN; - x == NAN_F32; - x != NAN_F32; - x < NAN_F32; - x > NAN_F32; - x <= NAN_F32; - x >= NAN_F32; - - let y = 0f64; - y == f64::NAN; - y != f64::NAN; - y < f64::NAN; - y > f64::NAN; - y <= f64::NAN; - y >= f64::NAN; - y == NAN_F64; - y != NAN_F64; - y < NAN_F64; - y > NAN_F64; - y <= NAN_F64; - y >= NAN_F64; -} diff --git a/src/tools/clippy/tests/ui/cmp_nan.stderr b/src/tools/clippy/tests/ui/cmp_nan.stderr deleted file mode 100644 index 867516661a539..0000000000000 --- a/src/tools/clippy/tests/ui/cmp_nan.stderr +++ /dev/null @@ -1,148 +0,0 @@ -error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead - --> $DIR/cmp_nan.rs:8:5 - | -LL | x == f32::NAN; - | ^^^^^^^^^^^^^ - | - = note: `-D clippy::cmp-nan` implied by `-D warnings` - -error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead - --> $DIR/cmp_nan.rs:9:5 - | -LL | x != f32::NAN; - | ^^^^^^^^^^^^^ - -error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead - --> $DIR/cmp_nan.rs:10:5 - | -LL | x < f32::NAN; - | ^^^^^^^^^^^^ - -error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead - --> $DIR/cmp_nan.rs:11:5 - | -LL | x > f32::NAN; - | ^^^^^^^^^^^^ - -error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead - --> $DIR/cmp_nan.rs:12:5 - | -LL | x <= f32::NAN; - | ^^^^^^^^^^^^^ - -error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead - --> $DIR/cmp_nan.rs:13:5 - | -LL | x >= f32::NAN; - | ^^^^^^^^^^^^^ - -error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead - --> $DIR/cmp_nan.rs:14:5 - | -LL | x == NAN_F32; - | ^^^^^^^^^^^^ - -error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead - --> $DIR/cmp_nan.rs:15:5 - | -LL | x != NAN_F32; - | ^^^^^^^^^^^^ - -error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead - --> $DIR/cmp_nan.rs:16:5 - | -LL | x < NAN_F32; - | ^^^^^^^^^^^ - -error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead - --> $DIR/cmp_nan.rs:17:5 - | -LL | x > NAN_F32; - | ^^^^^^^^^^^ - -error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead - --> $DIR/cmp_nan.rs:18:5 - | -LL | x <= NAN_F32; - | ^^^^^^^^^^^^ - -error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead - --> $DIR/cmp_nan.rs:19:5 - | -LL | x >= NAN_F32; - | ^^^^^^^^^^^^ - -error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead - --> $DIR/cmp_nan.rs:22:5 - | -LL | y == f64::NAN; - | ^^^^^^^^^^^^^ - -error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead - --> $DIR/cmp_nan.rs:23:5 - | -LL | y != f64::NAN; - | ^^^^^^^^^^^^^ - -error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead - --> $DIR/cmp_nan.rs:24:5 - | -LL | y < f64::NAN; - | ^^^^^^^^^^^^ - -error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead - --> $DIR/cmp_nan.rs:25:5 - | -LL | y > f64::NAN; - | ^^^^^^^^^^^^ - -error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead - --> $DIR/cmp_nan.rs:26:5 - | -LL | y <= f64::NAN; - | ^^^^^^^^^^^^^ - -error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead - --> $DIR/cmp_nan.rs:27:5 - | -LL | y >= f64::NAN; - | ^^^^^^^^^^^^^ - -error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead - --> $DIR/cmp_nan.rs:28:5 - | -LL | y == NAN_F64; - | ^^^^^^^^^^^^ - -error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead - --> $DIR/cmp_nan.rs:29:5 - | -LL | y != NAN_F64; - | ^^^^^^^^^^^^ - -error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead - --> $DIR/cmp_nan.rs:30:5 - | -LL | y < NAN_F64; - | ^^^^^^^^^^^ - -error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead - --> $DIR/cmp_nan.rs:31:5 - | -LL | y > NAN_F64; - | ^^^^^^^^^^^ - -error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead - --> $DIR/cmp_nan.rs:32:5 - | -LL | y <= NAN_F64; - | ^^^^^^^^^^^^ - -error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead - --> $DIR/cmp_nan.rs:33:5 - | -LL | y >= NAN_F64; - | ^^^^^^^^^^^^ - -error: aborting due to 24 previous errors - diff --git a/src/tools/clippy/tests/ui/crashes/ice-7410.rs b/src/tools/clippy/tests/ui/crashes/ice-7410.rs index ffe20ab1c3101..a5373cdcae120 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-7410.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-7410.rs @@ -6,6 +6,7 @@ #![no_std] #![allow(clippy::if_same_then_else)] #![allow(clippy::redundant_pattern_matching)] +#![allow(clippy::needless_else)] use core::panic::PanicInfo; diff --git a/src/tools/clippy/tests/ui/crashes/mut_mut_macro.rs b/src/tools/clippy/tests/ui/crashes/mut_mut_macro.rs index a238e7896fc6b..92821b6ecbbab 100644 --- a/src/tools/clippy/tests/ui/crashes/mut_mut_macro.rs +++ b/src/tools/clippy/tests/ui/crashes/mut_mut_macro.rs @@ -1,4 +1,4 @@ -#![deny(clippy::mut_mut, clippy::zero_ptr, clippy::cmp_nan)] +#![deny(clippy::mut_mut, clippy::zero_ptr)] #![allow(dead_code)] // FIXME: compiletest + extern crates doesn't work together. To make this test work, it would need @@ -8,13 +8,12 @@ // extern crate lazy_static; // use std::collections::HashMap; -/// ensure that we don't suggest `is_nan` and `is_null` inside constants +/// ensure that we don't suggest `is_null` inside constants /// FIXME: once const fn is stable, suggest these functions again in constants const BAA: *const i32 = 0 as *const i32; static mut BAR: *const i32 = BAA; static mut FOO: *const i32 = 0 as *const i32; -static mut BUH: bool = 42.0 < f32::NAN; #[allow(unused_variables, unused_mut)] fn main() { diff --git a/src/tools/clippy/tests/ui/default_constructed_unit_structs.fixed b/src/tools/clippy/tests/ui/default_constructed_unit_structs.fixed index e1012f38bba2a..ac5fe38ff4439 100644 --- a/src/tools/clippy/tests/ui/default_constructed_unit_structs.fixed +++ b/src/tools/clippy/tests/ui/default_constructed_unit_structs.fixed @@ -101,6 +101,28 @@ struct EmptyStruct {} #[non_exhaustive] struct NonExhaustiveStruct; +mod issue_10755 { + struct Sqlite {} + + trait HasArguments<'q> { + type Arguments; + } + + impl<'q> HasArguments<'q> for Sqlite { + type Arguments = std::marker::PhantomData<&'q ()>; + } + + type SqliteArguments<'q> = >::Arguments; + + fn foo() { + // should not lint + // type alias cannot be used as a constructor + let _ = ::Arguments::default(); + + let _ = SqliteArguments::default(); + } +} + fn main() { // should lint let _ = PhantomData::; diff --git a/src/tools/clippy/tests/ui/default_constructed_unit_structs.rs b/src/tools/clippy/tests/ui/default_constructed_unit_structs.rs index c7b4313dbf0c9..de7f14ffbd95c 100644 --- a/src/tools/clippy/tests/ui/default_constructed_unit_structs.rs +++ b/src/tools/clippy/tests/ui/default_constructed_unit_structs.rs @@ -101,6 +101,28 @@ struct EmptyStruct {} #[non_exhaustive] struct NonExhaustiveStruct; +mod issue_10755 { + struct Sqlite {} + + trait HasArguments<'q> { + type Arguments; + } + + impl<'q> HasArguments<'q> for Sqlite { + type Arguments = std::marker::PhantomData<&'q ()>; + } + + type SqliteArguments<'q> = >::Arguments; + + fn foo() { + // should not lint + // type alias cannot be used as a constructor + let _ = ::Arguments::default(); + + let _ = SqliteArguments::default(); + } +} + fn main() { // should lint let _ = PhantomData::::default(); diff --git a/src/tools/clippy/tests/ui/default_constructed_unit_structs.stderr b/src/tools/clippy/tests/ui/default_constructed_unit_structs.stderr index 61a32fb10e53b..13abb9149da24 100644 --- a/src/tools/clippy/tests/ui/default_constructed_unit_structs.stderr +++ b/src/tools/clippy/tests/ui/default_constructed_unit_structs.stderr @@ -13,25 +13,25 @@ LL | inner: PhantomData::default(), | ^^^^^^^^^^^ help: remove this call to `default` error: use of `default` to create a unit struct - --> $DIR/default_constructed_unit_structs.rs:106:33 + --> $DIR/default_constructed_unit_structs.rs:128:33 | LL | let _ = PhantomData::::default(); | ^^^^^^^^^^^ help: remove this call to `default` error: use of `default` to create a unit struct - --> $DIR/default_constructed_unit_structs.rs:107:42 + --> $DIR/default_constructed_unit_structs.rs:129:42 | LL | let _: PhantomData = PhantomData::default(); | ^^^^^^^^^^^ help: remove this call to `default` error: use of `default` to create a unit struct - --> $DIR/default_constructed_unit_structs.rs:108:55 + --> $DIR/default_constructed_unit_structs.rs:130:55 | LL | let _: PhantomData = std::marker::PhantomData::default(); | ^^^^^^^^^^^ help: remove this call to `default` error: use of `default` to create a unit struct - --> $DIR/default_constructed_unit_structs.rs:109:23 + --> $DIR/default_constructed_unit_structs.rs:131:23 | LL | let _ = UnitStruct::default(); | ^^^^^^^^^^^ help: remove this call to `default` diff --git a/src/tools/clippy/tests/ui/ifs_same_cond.rs b/src/tools/clippy/tests/ui/ifs_same_cond.rs index 9ce9a87626a79..f62da157d1b94 100644 --- a/src/tools/clippy/tests/ui/ifs_same_cond.rs +++ b/src/tools/clippy/tests/ui/ifs_same_cond.rs @@ -1,5 +1,5 @@ #![warn(clippy::ifs_same_cond)] -#![allow(clippy::if_same_then_else, clippy::comparison_chain)] // all empty blocks +#![allow(clippy::if_same_then_else, clippy::comparison_chain, clippy::needless_else)] // all empty blocks fn ifs_same_cond() { let a = 0; diff --git a/src/tools/clippy/tests/ui/large_stack_arrays.rs b/src/tools/clippy/tests/ui/large_stack_arrays.rs index 99787ffd3d395..3e9d5e6a4ca32 100644 --- a/src/tools/clippy/tests/ui/large_stack_arrays.rs +++ b/src/tools/clippy/tests/ui/large_stack_arrays.rs @@ -18,6 +18,19 @@ pub static DOESNOTLINT2: [u8; 512_001] = { [x; 512_001] }; +fn issue_10741() { + #[derive(Copy, Clone)] + struct Large([u32; 100_000]); + + fn build() -> Large { + Large([0; 100_000]) + } + + let _x = [build(); 3]; + + let _y = [build(), build(), build()]; +} + fn main() { let bad = ( [0u32; 20_000_000], diff --git a/src/tools/clippy/tests/ui/large_stack_arrays.stderr b/src/tools/clippy/tests/ui/large_stack_arrays.stderr index 24e90094982aa..118d39566abee 100644 --- a/src/tools/clippy/tests/ui/large_stack_arrays.stderr +++ b/src/tools/clippy/tests/ui/large_stack_arrays.stderr @@ -1,14 +1,30 @@ error: allocating a local array larger than 512000 bytes - --> $DIR/large_stack_arrays.rs:23:9 + --> $DIR/large_stack_arrays.rs:29:14 + | +LL | let _x = [build(); 3]; + | ^^^^^^^^^^^^ + | + = help: consider allocating on the heap with `vec![build(); 3].into_boxed_slice()` + = note: `-D clippy::large-stack-arrays` implied by `-D warnings` + +error: allocating a local array larger than 512000 bytes + --> $DIR/large_stack_arrays.rs:31:14 + | +LL | let _y = [build(), build(), build()]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider allocating on the heap with `vec![build(), build(), build()].into_boxed_slice()` + +error: allocating a local array larger than 512000 bytes + --> $DIR/large_stack_arrays.rs:36:9 | LL | [0u32; 20_000_000], | ^^^^^^^^^^^^^^^^^^ | = help: consider allocating on the heap with `vec![0u32; 20_000_000].into_boxed_slice()` - = note: `-D clippy::large-stack-arrays` implied by `-D warnings` error: allocating a local array larger than 512000 bytes - --> $DIR/large_stack_arrays.rs:24:9 + --> $DIR/large_stack_arrays.rs:37:9 | LL | [S { data: [0; 32] }; 5000], | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,7 +32,7 @@ LL | [S { data: [0; 32] }; 5000], = help: consider allocating on the heap with `vec![S { data: [0; 32] }; 5000].into_boxed_slice()` error: allocating a local array larger than 512000 bytes - --> $DIR/large_stack_arrays.rs:25:9 + --> $DIR/large_stack_arrays.rs:38:9 | LL | [Some(""); 20_000_000], | ^^^^^^^^^^^^^^^^^^^^^^ @@ -24,7 +40,7 @@ LL | [Some(""); 20_000_000], = help: consider allocating on the heap with `vec![Some(""); 20_000_000].into_boxed_slice()` error: allocating a local array larger than 512000 bytes - --> $DIR/large_stack_arrays.rs:26:9 + --> $DIR/large_stack_arrays.rs:39:9 | LL | [E::T(0); 5000], | ^^^^^^^^^^^^^^^ @@ -32,12 +48,12 @@ LL | [E::T(0); 5000], = help: consider allocating on the heap with `vec![E::T(0); 5000].into_boxed_slice()` error: allocating a local array larger than 512000 bytes - --> $DIR/large_stack_arrays.rs:27:9 + --> $DIR/large_stack_arrays.rs:40:9 | LL | [0u8; usize::MAX], | ^^^^^^^^^^^^^^^^^ | = help: consider allocating on the heap with `vec![0u8; usize::MAX].into_boxed_slice()` -error: aborting due to 5 previous errors +error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/manual_let_else.rs b/src/tools/clippy/tests/ui/manual_let_else.rs index 3996d775f55f4..351ea0e4f509e 100644 --- a/src/tools/clippy/tests/ui/manual_let_else.rs +++ b/src/tools/clippy/tests/ui/manual_let_else.rs @@ -146,10 +146,20 @@ fn fire() { Variant::A(0, 0) } - // Should not be renamed let v = if let Variant::A(a, 0) = e() { a } else { return }; - // Should be renamed - let v = if let Variant::B(b) = e() { b } else { return }; + + // `mut v` is inserted into the pattern + let mut v = if let Variant::B(b) = e() { b } else { return }; + + // Nesting works + let nested = Ok(Some(e())); + let v = if let Ok(Some(Variant::B(b))) | Err(Some(Variant::A(b, _))) = nested { + b + } else { + return; + }; + // dot dot works + let v = if let Variant::A(.., a) = e() { a } else { return }; } fn not_fire() { diff --git a/src/tools/clippy/tests/ui/manual_let_else.stderr b/src/tools/clippy/tests/ui/manual_let_else.stderr index f6f56f7b00e51..0e87679713474 100644 --- a/src/tools/clippy/tests/ui/manual_let_else.stderr +++ b/src/tools/clippy/tests/ui/manual_let_else.stderr @@ -260,19 +260,42 @@ LL | create_binding_if_some!(w, g()); = note: this error originates in the macro `create_binding_if_some` (in Nightly builds, run with -Z macro-backtrace for more info) error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:150:5 + --> $DIR/manual_let_else.rs:149:5 | LL | let v = if let Variant::A(a, 0) = e() { a } else { return }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Variant::A(a, 0) = e() else { return };` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Variant::A(v, 0) = e() else { return };` error: this could be rewritten as `let...else` --> $DIR/manual_let_else.rs:152:5 | -LL | let v = if let Variant::B(b) = e() { b } else { return }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Variant::B(v) = e() else { return };` +LL | let mut v = if let Variant::B(b) = e() { b } else { return }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Variant::B(mut v) = e() else { return };` error: this could be rewritten as `let...else` - --> $DIR/manual_let_else.rs:262:5 + --> $DIR/manual_let_else.rs:156:5 + | +LL | / let v = if let Ok(Some(Variant::B(b))) | Err(Some(Variant::A(b, _))) = nested { +LL | | b +LL | | } else { +LL | | return; +LL | | }; + | |______^ + | +help: consider writing + | +LL ~ let (Ok(Some(Variant::B(v))) | Err(Some(Variant::A(v, _)))) = nested else { +LL + return; +LL + }; + | + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:162:5 + | +LL | let v = if let Variant::A(.., a) = e() { a } else { return }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Variant::A(.., v) = e() else { return };` + +error: this could be rewritten as `let...else` + --> $DIR/manual_let_else.rs:272:5 | LL | / let _ = match ff { LL | | Some(value) => value, @@ -280,5 +303,5 @@ LL | | _ => macro_call!(), LL | | }; | |______^ help: consider writing: `let Some(_) = ff else { macro_call!() };` -error: aborting due to 20 previous errors +error: aborting due to 22 previous errors diff --git a/src/tools/clippy/tests/ui/manual_let_else_match.rs b/src/tools/clippy/tests/ui/manual_let_else_match.rs index 73b7467912597..dfca3b023cd58 100644 --- a/src/tools/clippy/tests/ui/manual_let_else_match.rs +++ b/src/tools/clippy/tests/ui/manual_let_else_match.rs @@ -68,7 +68,7 @@ fn fire() { let f = Variant::Bar(1); let _value = match f { - Variant::Bar(_) | Variant::Baz(_) => (), + Variant::Bar(v) | Variant::Baz(v) => v, _ => return, }; diff --git a/src/tools/clippy/tests/ui/manual_let_else_match.stderr b/src/tools/clippy/tests/ui/manual_let_else_match.stderr index bacc14dc96720..13ed35bc1d5db 100644 --- a/src/tools/clippy/tests/ui/manual_let_else_match.stderr +++ b/src/tools/clippy/tests/ui/manual_let_else_match.stderr @@ -58,10 +58,10 @@ error: this could be rewritten as `let...else` --> $DIR/manual_let_else_match.rs:70:5 | LL | / let _value = match f { -LL | | Variant::Bar(_) | Variant::Baz(_) => (), +LL | | Variant::Bar(v) | Variant::Baz(v) => v, LL | | _ => return, LL | | }; - | |______^ help: consider writing: `let (Variant::Bar(_) | Variant::Baz(_)) = f else { return };` + | |______^ help: consider writing: `let (Variant::Bar(_value) | Variant::Baz(_value)) = f else { return };` error: this could be rewritten as `let...else` --> $DIR/manual_let_else_match.rs:76:5 diff --git a/src/tools/clippy/tests/ui/match_wild_err_arm.rs b/src/tools/clippy/tests/ui/match_wild_err_arm.rs index 823be65efe065..5a552e4ae51a5 100644 --- a/src/tools/clippy/tests/ui/match_wild_err_arm.rs +++ b/src/tools/clippy/tests/ui/match_wild_err_arm.rs @@ -1,7 +1,20 @@ #![feature(exclusive_range_pattern)] -#![allow(clippy::match_same_arms)] +#![allow(clippy::match_same_arms, dead_code)] #![warn(clippy::match_wild_err_arm)] +fn issue_10635() { + enum Error { + A, + B, + } + + // Don't trigger in const contexts. Const unwrap is not yet stable + const X: () = match Ok::<_, Error>(()) { + Ok(x) => x, + Err(_) => panic!(), + }; +} + fn match_wild_err_arm() { let x: Result = Ok(3); diff --git a/src/tools/clippy/tests/ui/match_wild_err_arm.stderr b/src/tools/clippy/tests/ui/match_wild_err_arm.stderr index b016d682698c8..a9f54feacdb58 100644 --- a/src/tools/clippy/tests/ui/match_wild_err_arm.stderr +++ b/src/tools/clippy/tests/ui/match_wild_err_arm.stderr @@ -1,5 +1,5 @@ error: `Err(_)` matches all errors - --> $DIR/match_wild_err_arm.rs:11:9 + --> $DIR/match_wild_err_arm.rs:24:9 | LL | Err(_) => panic!("err"), | ^^^^^^ @@ -8,7 +8,7 @@ LL | Err(_) => panic!("err"), = note: `-D clippy::match-wild-err-arm` implied by `-D warnings` error: `Err(_)` matches all errors - --> $DIR/match_wild_err_arm.rs:17:9 + --> $DIR/match_wild_err_arm.rs:30:9 | LL | Err(_) => panic!(), | ^^^^^^ @@ -16,7 +16,7 @@ LL | Err(_) => panic!(), = note: match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable error: `Err(_)` matches all errors - --> $DIR/match_wild_err_arm.rs:23:9 + --> $DIR/match_wild_err_arm.rs:36:9 | LL | Err(_) => { | ^^^^^^ @@ -24,7 +24,7 @@ LL | Err(_) => { = note: match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable error: `Err(_e)` matches all errors - --> $DIR/match_wild_err_arm.rs:31:9 + --> $DIR/match_wild_err_arm.rs:44:9 | LL | Err(_e) => panic!(), | ^^^^^^^ diff --git a/src/tools/clippy/tests/ui/missing_fields_in_debug.rs b/src/tools/clippy/tests/ui/missing_fields_in_debug.rs new file mode 100644 index 0000000000000..c156d394eceaa --- /dev/null +++ b/src/tools/clippy/tests/ui/missing_fields_in_debug.rs @@ -0,0 +1,191 @@ +#![allow(unused)] +#![warn(clippy::missing_fields_in_debug)] + +use std::fmt; +use std::marker::PhantomData; +use std::ops::Deref; + +struct NamedStruct1Ignored { + data: u8, + hidden: u32, +} + +impl fmt::Debug for NamedStruct1Ignored { + // unused field: hidden + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter + .debug_struct("NamedStruct1Ignored") + .field("data", &self.data) + .finish() + } +} + +struct NamedStructMultipleIgnored { + data: u8, + hidden: u32, + hidden2: String, + hidden3: Vec>, + hidden4: ((((u8), u16), u32), u64), +} + +impl fmt::Debug for NamedStructMultipleIgnored { + // unused fields: hidden, hidden2, hidden4 + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter + .debug_struct("NamedStructMultipleIgnored") + .field("data", &self.data) + .field("hidden3", &self.hidden3) + .finish() + } +} + +struct Unit; + +// ok +impl fmt::Debug for Unit { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.debug_struct("Unit").finish() + } +} + +struct UnnamedStruct1Ignored(String); + +impl fmt::Debug for UnnamedStruct1Ignored { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.debug_tuple("UnnamedStruct1Ignored").finish() + } +} + +struct UnnamedStructMultipleIgnored(String, Vec, i32); + +// tuple structs are not linted +impl fmt::Debug for UnnamedStructMultipleIgnored { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter + .debug_tuple("UnnamedStructMultipleIgnored") + .field(&self.1) + .finish() + } +} + +struct NamedStructNonExhaustive { + a: u8, + b: String, +} + +// ok +impl fmt::Debug for NamedStructNonExhaustive { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter + .debug_struct("NamedStructNonExhaustive") + .field("a", &self.a) + .finish_non_exhaustive() // should not warn here + } +} + +struct MultiExprDebugImpl { + a: u8, + b: String, +} + +// ok +impl fmt::Debug for MultiExprDebugImpl { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut f = formatter.debug_struct("MultiExprDebugImpl"); + f.field("a", &self.a); + f.finish() + } +} + +#[derive(Debug)] +struct DerivedStruct { + a: u8, + b: i32, +} + +// https://github.com/rust-lang/rust-clippy/pull/10616#discussion_r1166846953 + +struct Inner { + a: usize, + b: usize, +} + +struct HasInner { + inner: Inner, +} + +impl HasInner { + fn get(&self) -> &Inner { + &self.inner + } +} + +impl fmt::Debug for HasInner { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let inner = self.get(); + + f.debug_struct("HasInner") + .field("a", &inner.a) + .field("b", &inner.b) + .finish() + } +} + +// https://github.com/rust-lang/rust-clippy/pull/10616#discussion_r1170306053 +struct Foo { + a: u8, + b: u8, +} + +impl fmt::Debug for Foo { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Foo").field("a", &self.a).field("b", &()).finish() + } +} + +// https://github.com/rust-lang/rust-clippy/pull/10616#discussion_r1175473620 +mod comment1175473620 { + use super::*; + + struct Inner { + a: usize, + b: usize, + } + struct Wrapper(Inner); + + impl Deref for Wrapper { + type Target = Inner; + + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + impl fmt::Debug for Wrapper { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Wrapper") + .field("a", &self.a) + .field("b", &self.b) + .finish() + } + } +} + +// https://github.com/rust-lang/rust-clippy/pull/10616#discussion_r1175488757 +// PhantomData is an exception and does not need to be included +struct WithPD { + a: u8, + b: u8, + c: PhantomData, +} + +impl fmt::Debug for WithPD { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("WithPD") + .field("a", &self.a) + .field("b", &self.b) + .finish() + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/missing_fields_in_debug.stderr b/src/tools/clippy/tests/ui/missing_fields_in_debug.stderr new file mode 100644 index 0000000000000..ef9d02abab7db --- /dev/null +++ b/src/tools/clippy/tests/ui/missing_fields_in_debug.stderr @@ -0,0 +1,73 @@ +error: manual `Debug` impl does not include all fields + --> $DIR/missing_fields_in_debug.rs:13:1 + | +LL | / impl fmt::Debug for NamedStruct1Ignored { +LL | | // unused field: hidden +LL | | fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { +LL | | formatter +... | +LL | | } +LL | | } + | |_^ + | +note: this field is unused + --> $DIR/missing_fields_in_debug.rs:10:5 + | +LL | hidden: u32, + | ^^^^^^^^^^^ + = help: consider including all fields in this `Debug` impl + = help: consider calling `.finish_non_exhaustive()` if you intend to ignore fields + = note: `-D clippy::missing-fields-in-debug` implied by `-D warnings` + +error: manual `Debug` impl does not include all fields + --> $DIR/missing_fields_in_debug.rs:31:1 + | +LL | / impl fmt::Debug for NamedStructMultipleIgnored { +LL | | // unused fields: hidden, hidden2, hidden4 +LL | | fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { +LL | | formatter +... | +LL | | } +LL | | } + | |_^ + | +note: this field is unused + --> $DIR/missing_fields_in_debug.rs:25:5 + | +LL | hidden: u32, + | ^^^^^^^^^^^ +note: this field is unused + --> $DIR/missing_fields_in_debug.rs:26:5 + | +LL | hidden2: String, + | ^^^^^^^^^^^^^^^ +note: this field is unused + --> $DIR/missing_fields_in_debug.rs:28:5 + | +LL | hidden4: ((((u8), u16), u32), u64), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: consider including all fields in this `Debug` impl + = help: consider calling `.finish_non_exhaustive()` if you intend to ignore fields + +error: manual `Debug` impl does not include all fields + --> $DIR/missing_fields_in_debug.rs:92:1 + | +LL | / impl fmt::Debug for MultiExprDebugImpl { +LL | | fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { +LL | | let mut f = formatter.debug_struct("MultiExprDebugImpl"); +LL | | f.field("a", &self.a); +LL | | f.finish() +LL | | } +LL | | } + | |_^ + | +note: this field is unused + --> $DIR/missing_fields_in_debug.rs:88:5 + | +LL | b: String, + | ^^^^^^^^^ + = help: consider including all fields in this `Debug` impl + = help: consider calling `.finish_non_exhaustive()` if you intend to ignore fields + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/needless_else.fixed b/src/tools/clippy/tests/ui/needless_else.fixed new file mode 100644 index 0000000000000..06a16162790cc --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_else.fixed @@ -0,0 +1,57 @@ +//@run-rustfix +#![allow(unused)] +#![warn(clippy::needless_else)] +#![allow(clippy::suspicious_else_formatting)] + +macro_rules! mac { + ($test:expr) => { + if $test { + println!("Test successful!"); + } else { + } + }; +} + +macro_rules! empty_expansion { + () => {}; +} + +fn main() { + let b = std::hint::black_box(true); + + if b { + println!("Foobar"); + } + + if b { + println!("Foobar"); + } else { + // Do not lint because this comment might be important + } + + if b { + println!("Foobar"); + } else + /* Do not lint because this comment might be important */ + { + } + + // Do not lint because of the expression + let _ = if b { 1 } else { 2 }; + + // Do not lint because inside a macro + mac!(b); + + if b { + println!("Foobar"); + } else { + #[cfg(foo)] + "Do not lint cfg'd out code" + } + + if b { + println!("Foobar"); + } else { + empty_expansion!(); + } +} diff --git a/src/tools/clippy/tests/ui/needless_else.rs b/src/tools/clippy/tests/ui/needless_else.rs new file mode 100644 index 0000000000000..728032c47a66b --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_else.rs @@ -0,0 +1,58 @@ +//@run-rustfix +#![allow(unused)] +#![warn(clippy::needless_else)] +#![allow(clippy::suspicious_else_formatting)] + +macro_rules! mac { + ($test:expr) => { + if $test { + println!("Test successful!"); + } else { + } + }; +} + +macro_rules! empty_expansion { + () => {}; +} + +fn main() { + let b = std::hint::black_box(true); + + if b { + println!("Foobar"); + } else { + } + + if b { + println!("Foobar"); + } else { + // Do not lint because this comment might be important + } + + if b { + println!("Foobar"); + } else + /* Do not lint because this comment might be important */ + { + } + + // Do not lint because of the expression + let _ = if b { 1 } else { 2 }; + + // Do not lint because inside a macro + mac!(b); + + if b { + println!("Foobar"); + } else { + #[cfg(foo)] + "Do not lint cfg'd out code" + } + + if b { + println!("Foobar"); + } else { + empty_expansion!(); + } +} diff --git a/src/tools/clippy/tests/ui/needless_else.stderr b/src/tools/clippy/tests/ui/needless_else.stderr new file mode 100644 index 0000000000000..ea69308516449 --- /dev/null +++ b/src/tools/clippy/tests/ui/needless_else.stderr @@ -0,0 +1,12 @@ +error: this else branch is empty + --> $DIR/needless_else.rs:24:7 + | +LL | } else { + | _______^ +LL | | } + | |_____^ help: you can remove it + | + = note: `-D clippy::needless-else` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/src/tools/clippy/tests/ui/needless_return.fixed b/src/tools/clippy/tests/ui/needless_return.fixed index d49ae5d8636f5..4dabf313963de 100644 --- a/src/tools/clippy/tests/ui/needless_return.fixed +++ b/src/tools/clippy/tests/ui/needless_return.fixed @@ -7,7 +7,8 @@ clippy::if_same_then_else, clippy::single_match, clippy::needless_bool, - clippy::equatable_if_let + clippy::equatable_if_let, + clippy::needless_else )] #![warn(clippy::needless_return)] diff --git a/src/tools/clippy/tests/ui/needless_return.rs b/src/tools/clippy/tests/ui/needless_return.rs index 367638261746c..542f562b31496 100644 --- a/src/tools/clippy/tests/ui/needless_return.rs +++ b/src/tools/clippy/tests/ui/needless_return.rs @@ -7,7 +7,8 @@ clippy::if_same_then_else, clippy::single_match, clippy::needless_bool, - clippy::equatable_if_let + clippy::equatable_if_let, + clippy::needless_else )] #![warn(clippy::needless_return)] diff --git a/src/tools/clippy/tests/ui/needless_return.stderr b/src/tools/clippy/tests/ui/needless_return.stderr index 05f6038cd2553..1d9d23d30083c 100644 --- a/src/tools/clippy/tests/ui/needless_return.stderr +++ b/src/tools/clippy/tests/ui/needless_return.stderr @@ -1,390 +1,582 @@ error: unneeded `return` statement - --> $DIR/needless_return.rs:27:5 + --> $DIR/needless_return.rs:28:5 | LL | return true; | ^^^^^^^^^^^ | = note: `-D clippy::needless-return` implied by `-D warnings` - = help: remove `return` +help: remove `return` + | +LL - return true; +LL + true + | error: unneeded `return` statement - --> $DIR/needless_return.rs:31:5 + --> $DIR/needless_return.rs:32:5 | LL | return true; | ^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL - return true; +LL + true + | error: unneeded `return` statement - --> $DIR/needless_return.rs:36:5 + --> $DIR/needless_return.rs:37:5 | LL | return true;;; | ^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL - return true;;; +LL + true + | error: unneeded `return` statement - --> $DIR/needless_return.rs:41:5 + --> $DIR/needless_return.rs:42:5 | LL | return true;; ; ; | ^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL - return true;; ; ; +LL + true + | error: unneeded `return` statement - --> $DIR/needless_return.rs:46:9 + --> $DIR/needless_return.rs:47:9 | LL | return true; | ^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL - return true; +LL + true + | error: unneeded `return` statement - --> $DIR/needless_return.rs:48:9 + --> $DIR/needless_return.rs:49:9 | LL | return false; | ^^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL - return false; +LL + false + | error: unneeded `return` statement - --> $DIR/needless_return.rs:54:17 + --> $DIR/needless_return.rs:55:17 | LL | true => return false, | ^^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL | true => false, + | ~~~~~ error: unneeded `return` statement - --> $DIR/needless_return.rs:56:13 + --> $DIR/needless_return.rs:57:13 | LL | return true; | ^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL - return true; +LL + true + | error: unneeded `return` statement - --> $DIR/needless_return.rs:63:9 + --> $DIR/needless_return.rs:64:9 | LL | return true; | ^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL - return true; +LL + true + | error: unneeded `return` statement - --> $DIR/needless_return.rs:65:16 + --> $DIR/needless_return.rs:66:16 | LL | let _ = || return true; | ^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL | let _ = || true; + | ~~~~ error: unneeded `return` statement - --> $DIR/needless_return.rs:69:5 + --> $DIR/needless_return.rs:70:5 | LL | return the_answer!(); | ^^^^^^^^^^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL - return the_answer!(); +LL + the_answer!() + | error: unneeded `return` statement - --> $DIR/needless_return.rs:72:21 + --> $DIR/needless_return.rs:73:21 | LL | fn test_void_fun() { | _____________________^ LL | | return; | |__________^ | - = help: remove `return` +help: remove `return` + | +LL - fn test_void_fun() { +LL - return; +LL + fn test_void_fun() { + | error: unneeded `return` statement - --> $DIR/needless_return.rs:77:11 + --> $DIR/needless_return.rs:78:11 | LL | if b { | ___________^ LL | | return; | |______________^ | - = help: remove `return` +help: remove `return` + | +LL - if b { +LL - return; +LL + if b { + | error: unneeded `return` statement - --> $DIR/needless_return.rs:79:13 + --> $DIR/needless_return.rs:80:13 | LL | } else { | _____________^ LL | | return; | |______________^ | - = help: remove `return` +help: remove `return` + | +LL - } else { +LL - return; +LL + } else { + | error: unneeded `return` statement - --> $DIR/needless_return.rs:87:14 + --> $DIR/needless_return.rs:88:14 | LL | _ => return, | ^^^^^^ | - = help: replace `return` with a unit value +help: replace `return` with a unit value + | +LL | _ => (), + | ~~ error: unneeded `return` statement - --> $DIR/needless_return.rs:95:24 + --> $DIR/needless_return.rs:96:24 | LL | let _ = 42; | ________________________^ LL | | return; | |__________________^ | - = help: remove `return` +help: remove `return` + | +LL - let _ = 42; +LL - return; +LL + let _ = 42; + | error: unneeded `return` statement - --> $DIR/needless_return.rs:98:14 + --> $DIR/needless_return.rs:99:14 | LL | _ => return, | ^^^^^^ | - = help: replace `return` with a unit value +help: replace `return` with a unit value + | +LL | _ => (), + | ~~ error: unneeded `return` statement - --> $DIR/needless_return.rs:111:9 + --> $DIR/needless_return.rs:112:9 | LL | return String::from("test"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL - return String::from("test"); +LL + String::from("test") + | error: unneeded `return` statement - --> $DIR/needless_return.rs:113:9 + --> $DIR/needless_return.rs:114:9 | LL | return String::new(); | ^^^^^^^^^^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL - return String::new(); +LL + String::new() + | error: unneeded `return` statement - --> $DIR/needless_return.rs:135:32 + --> $DIR/needless_return.rs:136:32 | LL | bar.unwrap_or_else(|_| return) | ^^^^^^ | - = help: replace `return` with an empty block +help: replace `return` with an empty block + | +LL | bar.unwrap_or_else(|_| {}) + | ~~ error: unneeded `return` statement - --> $DIR/needless_return.rs:139:21 + --> $DIR/needless_return.rs:140:21 | LL | let _ = || { | _____________________^ LL | | return; | |__________________^ | - = help: remove `return` +help: remove `return` + | +LL - let _ = || { +LL - return; +LL + let _ = || { + | error: unneeded `return` statement - --> $DIR/needless_return.rs:142:20 + --> $DIR/needless_return.rs:143:20 | LL | let _ = || return; | ^^^^^^ | - = help: replace `return` with an empty block +help: replace `return` with an empty block + | +LL | let _ = || {}; + | ~~ error: unneeded `return` statement - --> $DIR/needless_return.rs:148:32 + --> $DIR/needless_return.rs:149:32 | LL | res.unwrap_or_else(|_| return Foo) | ^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL | res.unwrap_or_else(|_| Foo) + | ~~~ error: unneeded `return` statement - --> $DIR/needless_return.rs:157:5 + --> $DIR/needless_return.rs:158:5 | LL | return true; | ^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL - return true; +LL + true + | error: unneeded `return` statement - --> $DIR/needless_return.rs:161:5 + --> $DIR/needless_return.rs:162:5 | LL | return true; | ^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL - return true; +LL + true + | error: unneeded `return` statement - --> $DIR/needless_return.rs:166:9 + --> $DIR/needless_return.rs:167:9 | LL | return true; | ^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL - return true; +LL + true + | error: unneeded `return` statement - --> $DIR/needless_return.rs:168:9 + --> $DIR/needless_return.rs:169:9 | LL | return false; | ^^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL - return false; +LL + false + | error: unneeded `return` statement - --> $DIR/needless_return.rs:174:17 + --> $DIR/needless_return.rs:175:17 | LL | true => return false, | ^^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL | true => false, + | ~~~~~ error: unneeded `return` statement - --> $DIR/needless_return.rs:176:13 + --> $DIR/needless_return.rs:177:13 | LL | return true; | ^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL - return true; +LL + true + | error: unneeded `return` statement - --> $DIR/needless_return.rs:183:9 + --> $DIR/needless_return.rs:184:9 | LL | return true; | ^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL - return true; +LL + true + | error: unneeded `return` statement - --> $DIR/needless_return.rs:185:16 + --> $DIR/needless_return.rs:186:16 | LL | let _ = || return true; | ^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL | let _ = || true; + | ~~~~ error: unneeded `return` statement - --> $DIR/needless_return.rs:189:5 + --> $DIR/needless_return.rs:190:5 | LL | return the_answer!(); | ^^^^^^^^^^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL - return the_answer!(); +LL + the_answer!() + | error: unneeded `return` statement - --> $DIR/needless_return.rs:192:33 + --> $DIR/needless_return.rs:193:33 | LL | async fn async_test_void_fun() { | _________________________________^ LL | | return; | |__________^ | - = help: remove `return` +help: remove `return` + | +LL - async fn async_test_void_fun() { +LL - return; +LL + async fn async_test_void_fun() { + | error: unneeded `return` statement - --> $DIR/needless_return.rs:197:11 + --> $DIR/needless_return.rs:198:11 | LL | if b { | ___________^ LL | | return; | |______________^ | - = help: remove `return` +help: remove `return` + | +LL - if b { +LL - return; +LL + if b { + | error: unneeded `return` statement - --> $DIR/needless_return.rs:199:13 + --> $DIR/needless_return.rs:200:13 | LL | } else { | _____________^ LL | | return; | |______________^ | - = help: remove `return` +help: remove `return` + | +LL - } else { +LL - return; +LL + } else { + | error: unneeded `return` statement - --> $DIR/needless_return.rs:207:14 + --> $DIR/needless_return.rs:208:14 | LL | _ => return, | ^^^^^^ | - = help: replace `return` with a unit value +help: replace `return` with a unit value + | +LL | _ => (), + | ~~ error: unneeded `return` statement - --> $DIR/needless_return.rs:220:9 + --> $DIR/needless_return.rs:221:9 | LL | return String::from("test"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL - return String::from("test"); +LL + String::from("test") + | error: unneeded `return` statement - --> $DIR/needless_return.rs:222:9 + --> $DIR/needless_return.rs:223:9 | LL | return String::new(); | ^^^^^^^^^^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL - return String::new(); +LL + String::new() + | error: unneeded `return` statement - --> $DIR/needless_return.rs:238:5 + --> $DIR/needless_return.rs:239:5 | LL | return format!("Hello {}", "world!"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL - return format!("Hello {}", "world!"); +LL + format!("Hello {}", "world!") + | error: unneeded `return` statement - --> $DIR/needless_return.rs:250:9 + --> $DIR/needless_return.rs:251:9 | LL | return true; | ^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL ~ true +LL | } else { +LL | return false; +LL ~ } + | error: unneeded `return` statement - --> $DIR/needless_return.rs:252:9 + --> $DIR/needless_return.rs:253:9 | LL | return false; | ^^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL ~ false +LL ~ } + | error: unneeded `return` statement - --> $DIR/needless_return.rs:259:13 + --> $DIR/needless_return.rs:260:13 | LL | return 10; | ^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL ~ 10 +LL | }, + ... +LL | }, +LL ~ } + | error: unneeded `return` statement - --> $DIR/needless_return.rs:262:13 + --> $DIR/needless_return.rs:263:13 | LL | return 100; | ^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL ~ 100 +LL | }, +LL ~ } + | error: unneeded `return` statement - --> $DIR/needless_return.rs:270:9 + --> $DIR/needless_return.rs:271:9 | LL | return 0; | ^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL ~ 0 +LL ~ } + | error: unneeded `return` statement - --> $DIR/needless_return.rs:277:13 + --> $DIR/needless_return.rs:278:13 | LL | return *(x as *const isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL ~ *(x as *const isize) +LL | } else { +LL | return !*(x as *const isize); +LL ~ } +LL ~ } + | error: unneeded `return` statement - --> $DIR/needless_return.rs:279:13 + --> $DIR/needless_return.rs:280:13 | LL | return !*(x as *const isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL ~ !*(x as *const isize) +LL ~ } +LL ~ } + | error: unneeded `return` statement - --> $DIR/needless_return.rs:286:20 + --> $DIR/needless_return.rs:287:20 | LL | let _ = 42; | ____________________^ @@ -392,47 +584,73 @@ LL | | LL | | return; | |______________^ | - = help: remove `return` +help: remove `return` + | +LL - let _ = 42; +LL - +LL - return; +LL + let _ = 42; + | error: unneeded `return` statement - --> $DIR/needless_return.rs:293:20 + --> $DIR/needless_return.rs:294:20 | LL | let _ = 42; return; | ^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL - let _ = 42; return; +LL + let _ = 42; + | error: unneeded `return` statement - --> $DIR/needless_return.rs:305:9 + --> $DIR/needless_return.rs:306:9 | LL | return Ok(format!("ok!")); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL - return Ok(format!("ok!")); +LL + Ok(format!("ok!")) + | error: unneeded `return` statement - --> $DIR/needless_return.rs:307:9 + --> $DIR/needless_return.rs:308:9 | LL | return Err(format!("err!")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL - return Err(format!("err!")); +LL + Err(format!("err!")) + | error: unneeded `return` statement - --> $DIR/needless_return.rs:313:9 + --> $DIR/needless_return.rs:314:9 | LL | return if true { 1 } else { 2 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: remove `return` +help: remove `return` + | +LL - return if true { 1 } else { 2 }; +LL + if true { 1 } else { 2 } + | error: unneeded `return` statement - --> $DIR/needless_return.rs:317:9 + --> $DIR/needless_return.rs:318:9 | LL | return if b1 { 0 } else { 1 } | if b2 { 2 } else { 3 } | if b3 { 4 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: remove `return` and wrap the sequence with parentheses +help: remove `return` and wrap the sequence with parentheses + | +LL - return if b1 { 0 } else { 1 } | if b2 { 2 } else { 3 } | if b3 { 4 } else { 5 }; +LL + (if b1 { 0 } else { 1 } | if b2 { 2 } else { 3 } | if b3 { 4 } else { 5 }) + | error: aborting due to 52 previous errors diff --git a/src/tools/clippy/tests/ui/nonminimal_bool.rs b/src/tools/clippy/tests/ui/nonminimal_bool.rs index 80cc7c60f56e5..fec6b7713eef8 100644 --- a/src/tools/clippy/tests/ui/nonminimal_bool.rs +++ b/src/tools/clippy/tests/ui/nonminimal_bool.rs @@ -110,3 +110,17 @@ fn issue_10435() { println!("{}", line!()); } } + +fn issue10836() { + struct Foo(bool); + impl std::ops::Not for Foo { + type Output = bool; + + fn not(self) -> Self::Output { + !self.0 + } + } + + // Should not lint + let _: bool = !!Foo(true); +} diff --git a/src/tools/clippy/tests/ui/ptr_cast_constness.fixed b/src/tools/clippy/tests/ui/ptr_cast_constness.fixed new file mode 100644 index 0000000000000..24de573d0838e --- /dev/null +++ b/src/tools/clippy/tests/ui/ptr_cast_constness.fixed @@ -0,0 +1,55 @@ +//@run-rustfix +//@aux-build:proc_macros.rs + +#![warn(clippy::ptr_cast_constness)] + +extern crate proc_macros; +use proc_macros::{external, inline_macros}; + +#[inline_macros] +fn main() { + let ptr: *const u32 = &42_u32; + let mut_ptr: *mut u32 = &mut 42_u32; + + let _ = ptr as *const i32; + let _ = mut_ptr as *mut i32; + + // Make sure the lint can handle the difference in their operator precedences. + unsafe { + let ptr_ptr: *const *const u32 = &ptr; + let _ = (*ptr_ptr).cast_mut(); + } + + let _ = ptr.cast_mut(); + let _ = mut_ptr.cast_const(); + + // Lint this, since pointer::cast_mut and pointer::cast_const have ?Sized + let ptr_of_array: *const [u32; 4] = &[1, 2, 3, 4]; + let _ = ptr_of_array as *const [u32]; + let _ = ptr_of_array as *const dyn std::fmt::Debug; + + // Make sure the lint is triggered inside a macro + let _ = inline!($ptr as *const i32); + + // Do not lint inside macros from external crates + let _ = external!($ptr as *const i32); +} + +#[clippy::msrv = "1.64"] +fn _msrv_1_64() { + let ptr: *const u32 = &42_u32; + let mut_ptr: *mut u32 = &mut 42_u32; + + // `pointer::cast_const` and `pointer::cast_mut` were stabilized in 1.65. Do not lint this + let _ = ptr as *mut i32; + let _ = mut_ptr as *const i32; +} + +#[clippy::msrv = "1.65"] +fn _msrv_1_65() { + let ptr: *const u32 = &42_u32; + let mut_ptr: *mut u32 = &mut 42_u32; + + let _ = ptr.cast_mut(); + let _ = mut_ptr.cast_const(); +} diff --git a/src/tools/clippy/tests/ui/ptr_cast_constness.rs b/src/tools/clippy/tests/ui/ptr_cast_constness.rs new file mode 100644 index 0000000000000..63d973a9fca87 --- /dev/null +++ b/src/tools/clippy/tests/ui/ptr_cast_constness.rs @@ -0,0 +1,55 @@ +//@run-rustfix +//@aux-build:proc_macros.rs + +#![warn(clippy::ptr_cast_constness)] + +extern crate proc_macros; +use proc_macros::{external, inline_macros}; + +#[inline_macros] +fn main() { + let ptr: *const u32 = &42_u32; + let mut_ptr: *mut u32 = &mut 42_u32; + + let _ = ptr as *const i32; + let _ = mut_ptr as *mut i32; + + // Make sure the lint can handle the difference in their operator precedences. + unsafe { + let ptr_ptr: *const *const u32 = &ptr; + let _ = *ptr_ptr as *mut i32; + } + + let _ = ptr as *mut i32; + let _ = mut_ptr as *const i32; + + // Lint this, since pointer::cast_mut and pointer::cast_const have ?Sized + let ptr_of_array: *const [u32; 4] = &[1, 2, 3, 4]; + let _ = ptr_of_array as *const [u32]; + let _ = ptr_of_array as *const dyn std::fmt::Debug; + + // Make sure the lint is triggered inside a macro + let _ = inline!($ptr as *const i32); + + // Do not lint inside macros from external crates + let _ = external!($ptr as *const i32); +} + +#[clippy::msrv = "1.64"] +fn _msrv_1_64() { + let ptr: *const u32 = &42_u32; + let mut_ptr: *mut u32 = &mut 42_u32; + + // `pointer::cast_const` and `pointer::cast_mut` were stabilized in 1.65. Do not lint this + let _ = ptr as *mut i32; + let _ = mut_ptr as *const i32; +} + +#[clippy::msrv = "1.65"] +fn _msrv_1_65() { + let ptr: *const u32 = &42_u32; + let mut_ptr: *mut u32 = &mut 42_u32; + + let _ = ptr as *mut i32; + let _ = mut_ptr as *const i32; +} diff --git a/src/tools/clippy/tests/ui/ptr_cast_constness.stderr b/src/tools/clippy/tests/ui/ptr_cast_constness.stderr new file mode 100644 index 0000000000000..43816c87c1907 --- /dev/null +++ b/src/tools/clippy/tests/ui/ptr_cast_constness.stderr @@ -0,0 +1,34 @@ +error: `as` casting between raw pointers while changing its constness + --> $DIR/ptr_cast_constness.rs:20:17 + | +LL | let _ = *ptr_ptr as *mut i32; + | ^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast_mut`, a safer alternative: `(*ptr_ptr).cast_mut()` + | + = note: `-D clippy::ptr-cast-constness` implied by `-D warnings` + +error: `as` casting between raw pointers while changing its constness + --> $DIR/ptr_cast_constness.rs:23:13 + | +LL | let _ = ptr as *mut i32; + | ^^^^^^^^^^^^^^^ help: try `pointer::cast_mut`, a safer alternative: `ptr.cast_mut()` + +error: `as` casting between raw pointers while changing its constness + --> $DIR/ptr_cast_constness.rs:24:13 + | +LL | let _ = mut_ptr as *const i32; + | ^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast_const`, a safer alternative: `mut_ptr.cast_const()` + +error: `as` casting between raw pointers while changing its constness + --> $DIR/ptr_cast_constness.rs:53:13 + | +LL | let _ = ptr as *mut i32; + | ^^^^^^^^^^^^^^^ help: try `pointer::cast_mut`, a safer alternative: `ptr.cast_mut()` + +error: `as` casting between raw pointers while changing its constness + --> $DIR/ptr_cast_constness.rs:54:13 + | +LL | let _ = mut_ptr as *const i32; + | ^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast_const`, a safer alternative: `mut_ptr.cast_const()` + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.fixed b/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.fixed index bebdf89716fa2..481c9b263fbb8 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.fixed +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.fixed @@ -2,7 +2,7 @@ // Issue #5746 #![warn(clippy::redundant_pattern_matching)] -#![allow(clippy::if_same_then_else, clippy::equatable_if_let)] +#![allow(clippy::if_same_then_else, clippy::equatable_if_let, clippy::needless_else)] use std::task::Poll::{Pending, Ready}; fn main() { diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.rs b/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.rs index 8fb6ed5f7eca0..86e46d41e65a1 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.rs +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_drop_order.rs @@ -2,7 +2,7 @@ // Issue #5746 #![warn(clippy::redundant_pattern_matching)] -#![allow(clippy::if_same_then_else, clippy::equatable_if_let)] +#![allow(clippy::if_same_then_else, clippy::equatable_if_let, clippy::needless_else)] use std::task::Poll::{Pending, Ready}; fn main() { diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed index accdf1da9ddc9..dae931541d454 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed @@ -47,6 +47,7 @@ fn main() { issue6067(); issue10726(); + issue10803(); let _ = if gen_opt().is_some() { 1 @@ -107,3 +108,14 @@ fn issue10726() { _ => false, }; } + +fn issue10803() { + let x = Some(42); + + let _ = x.is_some(); + + let _ = x.is_none(); + + // Don't lint + let _ = matches!(x, Some(16)); +} diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs index ec684bdf71c11..3f2fa3f53ce99 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs @@ -56,6 +56,7 @@ fn main() { issue6067(); issue10726(); + issue10803(); let _ = if let Some(_) = gen_opt() { 1 @@ -134,3 +135,14 @@ fn issue10726() { _ => false, }; } + +fn issue10803() { + let x = Some(42); + + let _ = matches!(x, Some(_)); + + let _ = matches!(x, None); + + // Don't lint + let _ = matches!(x, Some(16)); +} diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr index a69eb3905205f..93760ce97a8f6 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr @@ -77,49 +77,49 @@ LL | let _ = if let Some(_) = opt { true } else { false }; | -------^^^^^^^------ help: try this: `if opt.is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:60:20 + --> $DIR/redundant_pattern_matching_option.rs:61:20 | LL | let _ = if let Some(_) = gen_opt() { | -------^^^^^^^------------ help: try this: `if gen_opt().is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:62:19 + --> $DIR/redundant_pattern_matching_option.rs:63:19 | LL | } else if let None = gen_opt() { | -------^^^^------------ help: try this: `if gen_opt().is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:68:12 + --> $DIR/redundant_pattern_matching_option.rs:69:12 | LL | if let Some(..) = gen_opt() {} | -------^^^^^^^^------------ help: try this: `if gen_opt().is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:83:12 + --> $DIR/redundant_pattern_matching_option.rs:84:12 | LL | if let Some(_) = Some(42) {} | -------^^^^^^^----------- help: try this: `if Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:85:12 + --> $DIR/redundant_pattern_matching_option.rs:86:12 | LL | if let None = None::<()> {} | -------^^^^------------- help: try this: `if None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:87:15 + --> $DIR/redundant_pattern_matching_option.rs:88:15 | LL | while let Some(_) = Some(42) {} | ----------^^^^^^^----------- help: try this: `while Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:89:15 + --> $DIR/redundant_pattern_matching_option.rs:90:15 | LL | while let None = None::<()> {} | ----------^^^^------------- help: try this: `while None::<()>.is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:91:5 + --> $DIR/redundant_pattern_matching_option.rs:92:5 | LL | / match Some(42) { LL | | Some(_) => true, @@ -128,7 +128,7 @@ LL | | }; | |_____^ help: try this: `Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:96:5 + --> $DIR/redundant_pattern_matching_option.rs:97:5 | LL | / match None::<()> { LL | | Some(_) => false, @@ -137,19 +137,19 @@ LL | | }; | |_____^ help: try this: `None::<()>.is_none()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:104:12 + --> $DIR/redundant_pattern_matching_option.rs:105:12 | LL | if let None = *(&None::<()>) {} | -------^^^^----------------- help: try this: `if (&None::<()>).is_none()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:105:12 + --> $DIR/redundant_pattern_matching_option.rs:106:12 | LL | if let None = *&None::<()> {} | -------^^^^--------------- help: try this: `if (&None::<()>).is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:111:5 + --> $DIR/redundant_pattern_matching_option.rs:112:5 | LL | / match x { LL | | Some(_) => true, @@ -158,7 +158,7 @@ LL | | }; | |_____^ help: try this: `x.is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:116:5 + --> $DIR/redundant_pattern_matching_option.rs:117:5 | LL | / match x { LL | | None => true, @@ -167,7 +167,7 @@ LL | | }; | |_____^ help: try this: `x.is_none()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching_option.rs:121:5 + --> $DIR/redundant_pattern_matching_option.rs:122:5 | LL | / match x { LL | | Some(_) => false, @@ -176,7 +176,7 @@ LL | | }; | |_____^ help: try this: `x.is_none()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_option.rs:126:5 + --> $DIR/redundant_pattern_matching_option.rs:127:5 | LL | / match x { LL | | None => false, @@ -184,5 +184,17 @@ LL | | _ => true, LL | | }; | |_____^ help: try this: `x.is_some()` -error: aborting due to 26 previous errors +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching_option.rs:142:13 + | +LL | let _ = matches!(x, Some(_)); + | ^^^^^^^^^^^^^^^^^^^^ help: try this: `x.is_some()` + +error: redundant pattern matching, consider using `is_none()` + --> $DIR/redundant_pattern_matching_option.rs:144:13 + | +LL | let _ = matches!(x, None); + | ^^^^^^^^^^^^^^^^^ help: try this: `x.is_none()` + +error: aborting due to 28 previous errors diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.fixed b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.fixed index e4032ae44b715..d77a2af761642 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.fixed +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.fixed @@ -44,6 +44,7 @@ fn main() { issue6067(); issue6065(); issue10726(); + issue10803(); let _ = if gen_res().is_ok() { 1 @@ -133,3 +134,17 @@ fn issue10726() { _ => true, }; } + +fn issue10803() { + let x: Result = Ok(42); + + let _ = x.is_ok(); + + let _ = x.is_err(); + + // Don't lint + let _ = matches!(x, Ok(16)); + + // Don't lint + let _ = matches!(x, Err(16)); +} diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.rs b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.rs index 39eb10df8789e..aa884ac6bb1ee 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.rs +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.rs @@ -56,6 +56,7 @@ fn main() { issue6067(); issue6065(); issue10726(); + issue10803(); let _ = if let Ok(_) = gen_res() { 1 @@ -163,3 +164,17 @@ fn issue10726() { _ => true, }; } + +fn issue10803() { + let x: Result = Ok(42); + + let _ = matches!(x, Ok(_)); + + let _ = matches!(x, Err(_)); + + // Don't lint + let _ = matches!(x, Ok(16)); + + // Don't lint + let _ = matches!(x, Err(16)); +} diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.stderr b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.stderr index 5893ae4dcc492..b462f7f41b9cc 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.stderr +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.stderr @@ -73,67 +73,67 @@ LL | let _ = if let Ok(_) = Ok::(4) { true } else { false }; | -------^^^^^--------------------- help: try this: `if Ok::(4).is_ok()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:60:20 + --> $DIR/redundant_pattern_matching_result.rs:61:20 | LL | let _ = if let Ok(_) = gen_res() { | -------^^^^^------------ help: try this: `if gen_res().is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:62:19 + --> $DIR/redundant_pattern_matching_result.rs:63:19 | LL | } else if let Err(_) = gen_res() { | -------^^^^^^------------ help: try this: `if gen_res().is_err()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_result.rs:85:19 + --> $DIR/redundant_pattern_matching_result.rs:86:19 | LL | while let Some(_) = r#try!(result_opt()) {} | ----------^^^^^^^----------------------- help: try this: `while r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_result.rs:86:16 + --> $DIR/redundant_pattern_matching_result.rs:87:16 | LL | if let Some(_) = r#try!(result_opt()) {} | -------^^^^^^^----------------------- help: try this: `if r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_result.rs:92:12 + --> $DIR/redundant_pattern_matching_result.rs:93:12 | LL | if let Some(_) = m!() {} | -------^^^^^^^------- help: try this: `if m!().is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_result.rs:93:15 + --> $DIR/redundant_pattern_matching_result.rs:94:15 | LL | while let Some(_) = m!() {} | ----------^^^^^^^------- help: try this: `while m!().is_some()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:111:12 + --> $DIR/redundant_pattern_matching_result.rs:112:12 | LL | if let Ok(_) = Ok::(42) {} | -------^^^^^--------------------- help: try this: `if Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:113:12 + --> $DIR/redundant_pattern_matching_result.rs:114:12 | LL | if let Err(_) = Err::(42) {} | -------^^^^^^---------------------- help: try this: `if Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:115:15 + --> $DIR/redundant_pattern_matching_result.rs:116:15 | LL | while let Ok(_) = Ok::(10) {} | ----------^^^^^--------------------- help: try this: `while Ok::(10).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:117:15 + --> $DIR/redundant_pattern_matching_result.rs:118:15 | LL | while let Err(_) = Ok::(10) {} | ----------^^^^^^--------------------- help: try this: `while Ok::(10).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:119:5 + --> $DIR/redundant_pattern_matching_result.rs:120:5 | LL | / match Ok::(42) { LL | | Ok(_) => true, @@ -142,7 +142,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:124:5 + --> $DIR/redundant_pattern_matching_result.rs:125:5 | LL | / match Err::(42) { LL | | Ok(_) => false, @@ -151,7 +151,7 @@ LL | | }; | |_____^ help: try this: `Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:134:5 + --> $DIR/redundant_pattern_matching_result.rs:135:5 | LL | / match x { LL | | Ok(_) => true, @@ -160,7 +160,7 @@ LL | | }; | |_____^ help: try this: `x.is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:139:5 + --> $DIR/redundant_pattern_matching_result.rs:140:5 | LL | / match x { LL | | Ok(_) => false, @@ -169,7 +169,7 @@ LL | | }; | |_____^ help: try this: `x.is_err()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:144:5 + --> $DIR/redundant_pattern_matching_result.rs:145:5 | LL | / match x { LL | | Err(_) => true, @@ -178,7 +178,7 @@ LL | | }; | |_____^ help: try this: `x.is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:149:5 + --> $DIR/redundant_pattern_matching_result.rs:150:5 | LL | / match x { LL | | Err(_) => false, @@ -186,5 +186,17 @@ LL | | _ => true, LL | | }; | |_____^ help: try this: `x.is_ok()` -error: aborting due to 26 previous errors +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching_result.rs:171:13 + | +LL | let _ = matches!(x, Ok(_)); + | ^^^^^^^^^^^^^^^^^^ help: try this: `x.is_ok()` + +error: redundant pattern matching, consider using `is_err()` + --> $DIR/redundant_pattern_matching_result.rs:173:13 + | +LL | let _ = matches!(x, Err(_)); + | ^^^^^^^^^^^^^^^^^^^ help: try this: `x.is_err()` + +error: aborting due to 28 previous errors diff --git a/src/tools/clippy/tests/ui/regex.rs b/src/tools/clippy/tests/ui/regex.rs index a5f79b139bc2a..1c8e47ab59494 100644 --- a/src/tools/clippy/tests/ui/regex.rs +++ b/src/tools/clippy/tests/ui/regex.rs @@ -42,6 +42,11 @@ fn syntax_error() { let escaped_string_span = Regex::new("\\b\\c"); let aux_span = Regex::new("(?ixi)"); + + let should_not_lint = Regex::new("(?u)."); + let should_not_lint = BRegex::new("(?u)."); + let invalid_utf8_should_not_lint = BRegex::new("(?-u)."); + let invalid_utf8_should_lint = Regex::new("(?-u)."); } fn trivial_regex() { @@ -71,6 +76,8 @@ fn trivial_regex() { // non-trivial regexes let non_trivial_dot = Regex::new("a.b"); let non_trivial_dot_builder = RegexBuilder::new("a.b"); + let non_trivial_dot = Regex::new("."); + let non_trivial_dot = BRegex::new("."); let non_trivial_eq = Regex::new("^foo|bar$"); let non_trivial_starts_with = Regex::new("^foo|bar"); let non_trivial_ends_with = Regex::new("^foo|bar"); diff --git a/src/tools/clippy/tests/ui/regex.stderr b/src/tools/clippy/tests/ui/regex.stderr index 6b8a772e7f0d5..1e8a21283cd95 100644 --- a/src/tools/clippy/tests/ui/regex.stderr +++ b/src/tools/clippy/tests/ui/regex.stderr @@ -99,8 +99,14 @@ error: regex syntax error: duplicate flag LL | let aux_span = Regex::new("(?ixi)"); | ^ ^ +error: regex syntax error: pattern can match invalid UTF-8 + --> $DIR/regex.rs:49:53 + | +LL | let invalid_utf8_should_lint = Regex::new("(?-u)."); + | ^ + error: trivial regex - --> $DIR/regex.rs:48:33 + --> $DIR/regex.rs:53:33 | LL | let trivial_eq = Regex::new("^foobar$"); | ^^^^^^^^^^ @@ -108,7 +114,7 @@ LL | let trivial_eq = Regex::new("^foobar$"); = help: consider using `==` on `str`s error: trivial regex - --> $DIR/regex.rs:50:48 + --> $DIR/regex.rs:55:48 | LL | let trivial_eq_builder = RegexBuilder::new("^foobar$"); | ^^^^^^^^^^ @@ -116,7 +122,7 @@ LL | let trivial_eq_builder = RegexBuilder::new("^foobar$"); = help: consider using `==` on `str`s error: trivial regex - --> $DIR/regex.rs:52:42 + --> $DIR/regex.rs:57:42 | LL | let trivial_starts_with = Regex::new("^foobar"); | ^^^^^^^^^ @@ -124,7 +130,7 @@ LL | let trivial_starts_with = Regex::new("^foobar"); = help: consider using `str::starts_with` error: trivial regex - --> $DIR/regex.rs:54:40 + --> $DIR/regex.rs:59:40 | LL | let trivial_ends_with = Regex::new("foobar$"); | ^^^^^^^^^ @@ -132,7 +138,7 @@ LL | let trivial_ends_with = Regex::new("foobar$"); = help: consider using `str::ends_with` error: trivial regex - --> $DIR/regex.rs:56:39 + --> $DIR/regex.rs:61:39 | LL | let trivial_contains = Regex::new("foobar"); | ^^^^^^^^ @@ -140,7 +146,7 @@ LL | let trivial_contains = Regex::new("foobar"); = help: consider using `str::contains` error: trivial regex - --> $DIR/regex.rs:58:39 + --> $DIR/regex.rs:63:39 | LL | let trivial_contains = Regex::new(NOT_A_REAL_REGEX); | ^^^^^^^^^^^^^^^^ @@ -148,7 +154,7 @@ LL | let trivial_contains = Regex::new(NOT_A_REAL_REGEX); = help: consider using `str::contains` error: trivial regex - --> $DIR/regex.rs:60:40 + --> $DIR/regex.rs:65:40 | LL | let trivial_backslash = Regex::new("a/.b"); | ^^^^^^^ @@ -156,7 +162,7 @@ LL | let trivial_backslash = Regex::new("a/.b"); = help: consider using `str::contains` error: trivial regex - --> $DIR/regex.rs:63:36 + --> $DIR/regex.rs:68:36 | LL | let trivial_empty = Regex::new(""); | ^^ @@ -164,7 +170,7 @@ LL | let trivial_empty = Regex::new(""); = help: the regex is unlikely to be useful as it is error: trivial regex - --> $DIR/regex.rs:65:36 + --> $DIR/regex.rs:70:36 | LL | let trivial_empty = Regex::new("^"); | ^^^ @@ -172,7 +178,7 @@ LL | let trivial_empty = Regex::new("^"); = help: the regex is unlikely to be useful as it is error: trivial regex - --> $DIR/regex.rs:67:36 + --> $DIR/regex.rs:72:36 | LL | let trivial_empty = Regex::new("^$"); | ^^^^ @@ -180,12 +186,12 @@ LL | let trivial_empty = Regex::new("^$"); = help: consider using `str::is_empty` error: trivial regex - --> $DIR/regex.rs:69:44 + --> $DIR/regex.rs:74:44 | LL | let binary_trivial_empty = BRegex::new("^$"); | ^^^^ | = help: consider using `str::is_empty` -error: aborting due to 23 previous errors +error: aborting due to 24 previous errors diff --git a/src/tools/clippy/tests/ui/rename.fixed b/src/tools/clippy/tests/ui/rename.fixed index f7854b89ee4ea..f1b25dc094ef6 100644 --- a/src/tools/clippy/tests/ui/rename.fixed +++ b/src/tools/clippy/tests/ui/rename.fixed @@ -28,6 +28,7 @@ #![allow(clippy::module_name_repetitions)] #![allow(clippy::recursive_format_impl)] #![allow(clippy::invisible_characters)] +#![allow(cast_ref_to_mut)] #![allow(suspicious_double_ref_op)] #![allow(cast_ref_to_mut)] #![allow(drop_bounds)] @@ -40,12 +41,14 @@ #![allow(invalid_atomic_ordering)] #![allow(invalid_value)] #![allow(invalid_from_utf8_unchecked)] +#![allow(invalid_nan_comparisons)] #![allow(let_underscore_drop)] #![allow(enum_intrinsics_non_enums)] #![allow(non_fmt_panics)] #![allow(named_arguments_used_positionally)] #![allow(suspicious_double_ref_op)] #![allow(temporary_cstring_as_ptr)] +#![allow(undropped_manually_drops)] #![allow(unknown_lints)] #![allow(unused_labels)] #![warn(clippy::almost_complete_range)] @@ -53,6 +56,7 @@ #![warn(clippy::blocks_in_if_conditions)] #![warn(clippy::blocks_in_if_conditions)] #![warn(clippy::box_collection)] +#![warn(invalid_nan_comparisons)] #![warn(clippy::redundant_static_lifetimes)] #![warn(clippy::cognitive_complexity)] #![warn(clippy::derived_hash_with_manual_eq)] @@ -96,6 +100,7 @@ #![warn(non_fmt_panics)] #![warn(named_arguments_used_positionally)] #![warn(temporary_cstring_as_ptr)] +#![warn(undropped_manually_drops)] #![warn(unknown_lints)] #![warn(unused_labels)] diff --git a/src/tools/clippy/tests/ui/rename.rs b/src/tools/clippy/tests/ui/rename.rs index fa347d395ef5d..4af511c344c4d 100644 --- a/src/tools/clippy/tests/ui/rename.rs +++ b/src/tools/clippy/tests/ui/rename.rs @@ -28,6 +28,7 @@ #![allow(clippy::module_name_repetitions)] #![allow(clippy::recursive_format_impl)] #![allow(clippy::invisible_characters)] +#![allow(cast_ref_to_mut)] #![allow(suspicious_double_ref_op)] #![allow(cast_ref_to_mut)] #![allow(drop_bounds)] @@ -40,12 +41,14 @@ #![allow(invalid_atomic_ordering)] #![allow(invalid_value)] #![allow(invalid_from_utf8_unchecked)] +#![allow(invalid_nan_comparisons)] #![allow(let_underscore_drop)] #![allow(enum_intrinsics_non_enums)] #![allow(non_fmt_panics)] #![allow(named_arguments_used_positionally)] #![allow(suspicious_double_ref_op)] #![allow(temporary_cstring_as_ptr)] +#![allow(undropped_manually_drops)] #![allow(unknown_lints)] #![allow(unused_labels)] #![warn(clippy::almost_complete_letter_range)] @@ -53,6 +56,7 @@ #![warn(clippy::block_in_if_condition_expr)] #![warn(clippy::block_in_if_condition_stmt)] #![warn(clippy::box_vec)] +#![warn(clippy::cmp_nan)] #![warn(clippy::const_static_lifetime)] #![warn(clippy::cyclomatic_complexity)] #![warn(clippy::derive_hash_xor_eq)] @@ -96,6 +100,7 @@ #![warn(clippy::panic_params)] #![warn(clippy::positional_named_format_parameters)] #![warn(clippy::temporary_cstring_as_ptr)] +#![warn(clippy::undropped_manually_drops)] #![warn(clippy::unknown_clippy_lints)] #![warn(clippy::unused_label)] diff --git a/src/tools/clippy/tests/ui/rename.stderr b/src/tools/clippy/tests/ui/rename.stderr index 9dffe51e5d7d3..156a8f96b5026 100644 --- a/src/tools/clippy/tests/ui/rename.stderr +++ b/src/tools/clippy/tests/ui/rename.stderr @@ -1,5 +1,5 @@ error: lint `clippy::almost_complete_letter_range` has been renamed to `clippy::almost_complete_range` - --> $DIR/rename.rs:51:9 + --> $DIR/rename.rs:54:9 | LL | #![warn(clippy::almost_complete_letter_range)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::almost_complete_range` @@ -7,298 +7,310 @@ LL | #![warn(clippy::almost_complete_letter_range)] = note: `-D renamed-and-removed-lints` implied by `-D warnings` error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_names` - --> $DIR/rename.rs:52:9 + --> $DIR/rename.rs:55:9 | LL | #![warn(clippy::blacklisted_name)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_names` error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions` - --> $DIR/rename.rs:53:9 + --> $DIR/rename.rs:56:9 | LL | #![warn(clippy::block_in_if_condition_expr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions` - --> $DIR/rename.rs:54:9 + --> $DIR/rename.rs:57:9 | LL | #![warn(clippy::block_in_if_condition_stmt)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` error: lint `clippy::box_vec` has been renamed to `clippy::box_collection` - --> $DIR/rename.rs:55:9 + --> $DIR/rename.rs:58:9 | LL | #![warn(clippy::box_vec)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` +error: lint `clippy::cmp_nan` has been renamed to `invalid_nan_comparisons` + --> $DIR/rename.rs:59:9 + | +LL | #![warn(clippy::cmp_nan)] + | ^^^^^^^^^^^^^^^ help: use the new name: `invalid_nan_comparisons` + error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` - --> $DIR/rename.rs:56:9 + --> $DIR/rename.rs:60:9 | LL | #![warn(clippy::const_static_lifetime)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes` error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity` - --> $DIR/rename.rs:57:9 + --> $DIR/rename.rs:61:9 | LL | #![warn(clippy::cyclomatic_complexity)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` error: lint `clippy::derive_hash_xor_eq` has been renamed to `clippy::derived_hash_with_manual_eq` - --> $DIR/rename.rs:58:9 + --> $DIR/rename.rs:62:9 | LL | #![warn(clippy::derive_hash_xor_eq)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::derived_hash_with_manual_eq` error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods` - --> $DIR/rename.rs:59:9 + --> $DIR/rename.rs:63:9 | LL | #![warn(clippy::disallowed_method)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types` - --> $DIR/rename.rs:60:9 + --> $DIR/rename.rs:64:9 | LL | #![warn(clippy::disallowed_type)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression` - --> $DIR/rename.rs:61:9 + --> $DIR/rename.rs:65:9 | LL | #![warn(clippy::eval_order_dependence)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression` error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` - --> $DIR/rename.rs:62:9 + --> $DIR/rename.rs:66:9 | LL | #![warn(clippy::identity_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` - --> $DIR/rename.rs:63:9 + --> $DIR/rename.rs:67:9 | LL | #![warn(clippy::if_let_some_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` error: lint `clippy::integer_arithmetic` has been renamed to `clippy::arithmetic_side_effects` - --> $DIR/rename.rs:64:9 + --> $DIR/rename.rs:68:9 | LL | #![warn(clippy::integer_arithmetic)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::arithmetic_side_effects` error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr` - --> $DIR/rename.rs:65:9 + --> $DIR/rename.rs:69:9 | LL | #![warn(clippy::logic_bug)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr` error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` - --> $DIR/rename.rs:66:9 + --> $DIR/rename.rs:70:9 | LL | #![warn(clippy::new_without_default_derive)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` - --> $DIR/rename.rs:67:9 + --> $DIR/rename.rs:71:9 | LL | #![warn(clippy::option_and_then_some)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map` error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used` - --> $DIR/rename.rs:68:9 + --> $DIR/rename.rs:72:9 | LL | #![warn(clippy::option_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:69:9 + --> $DIR/rename.rs:73:9 | LL | #![warn(clippy::option_map_unwrap_or)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:70:9 + --> $DIR/rename.rs:74:9 | LL | #![warn(clippy::option_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` - --> $DIR/rename.rs:71:9 + --> $DIR/rename.rs:75:9 | LL | #![warn(clippy::option_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` - --> $DIR/rename.rs:72:9 + --> $DIR/rename.rs:76:9 | LL | #![warn(clippy::ref_in_deref)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` - --> $DIR/rename.rs:73:9 + --> $DIR/rename.rs:77:9 | LL | #![warn(clippy::result_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:74:9 + --> $DIR/rename.rs:78:9 | LL | #![warn(clippy::result_map_unwrap_or_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` - --> $DIR/rename.rs:75:9 + --> $DIR/rename.rs:79:9 | LL | #![warn(clippy::result_unwrap_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` - --> $DIR/rename.rs:76:9 + --> $DIR/rename.rs:80:9 | LL | #![warn(clippy::single_char_push_str)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` - --> $DIR/rename.rs:77:9 + --> $DIR/rename.rs:81:9 | LL | #![warn(clippy::stutter)] | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` - --> $DIR/rename.rs:78:9 + --> $DIR/rename.rs:82:9 | LL | #![warn(clippy::to_string_in_display)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` - --> $DIR/rename.rs:79:9 + --> $DIR/rename.rs:83:9 | LL | #![warn(clippy::zero_width_space)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` error: lint `clippy::cast_ref_to_mut` has been renamed to `cast_ref_to_mut` - --> $DIR/rename.rs:80:9 + --> $DIR/rename.rs:84:9 | LL | #![warn(clippy::cast_ref_to_mut)] | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `cast_ref_to_mut` error: lint `clippy::clone_double_ref` has been renamed to `suspicious_double_ref_op` - --> $DIR/rename.rs:81:9 + --> $DIR/rename.rs:85:9 | LL | #![warn(clippy::clone_double_ref)] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `suspicious_double_ref_op` error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` - --> $DIR/rename.rs:82:9 + --> $DIR/rename.rs:86:9 | LL | #![warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` error: lint `clippy::drop_copy` has been renamed to `dropping_copy_types` - --> $DIR/rename.rs:83:9 + --> $DIR/rename.rs:87:9 | LL | #![warn(clippy::drop_copy)] | ^^^^^^^^^^^^^^^^^ help: use the new name: `dropping_copy_types` error: lint `clippy::drop_ref` has been renamed to `dropping_references` - --> $DIR/rename.rs:84:9 + --> $DIR/rename.rs:88:9 | LL | #![warn(clippy::drop_ref)] | ^^^^^^^^^^^^^^^^ help: use the new name: `dropping_references` error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:85:9 + --> $DIR/rename.rs:89:9 | LL | #![warn(clippy::for_loop_over_option)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:86:9 + --> $DIR/rename.rs:90:9 | LL | #![warn(clippy::for_loop_over_result)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles` - --> $DIR/rename.rs:87:9 + --> $DIR/rename.rs:91:9 | LL | #![warn(clippy::for_loops_over_fallibles)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles` error: lint `clippy::forget_copy` has been renamed to `forgetting_copy_types` - --> $DIR/rename.rs:88:9 + --> $DIR/rename.rs:92:9 | LL | #![warn(clippy::forget_copy)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_copy_types` error: lint `clippy::forget_ref` has been renamed to `forgetting_references` - --> $DIR/rename.rs:89:9 + --> $DIR/rename.rs:93:9 | LL | #![warn(clippy::forget_ref)] | ^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_references` error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` - --> $DIR/rename.rs:90:9 + --> $DIR/rename.rs:94:9 | LL | #![warn(clippy::into_iter_on_array)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` - --> $DIR/rename.rs:91:9 + --> $DIR/rename.rs:95:9 | LL | #![warn(clippy::invalid_atomic_ordering)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` error: lint `clippy::invalid_ref` has been renamed to `invalid_value` - --> $DIR/rename.rs:92:9 + --> $DIR/rename.rs:96:9 | LL | #![warn(clippy::invalid_ref)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` error: lint `clippy::invalid_utf8_in_unchecked` has been renamed to `invalid_from_utf8_unchecked` - --> $DIR/rename.rs:93:9 + --> $DIR/rename.rs:97:9 | LL | #![warn(clippy::invalid_utf8_in_unchecked)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_from_utf8_unchecked` error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop` - --> $DIR/rename.rs:94:9 + --> $DIR/rename.rs:98:9 | LL | #![warn(clippy::let_underscore_drop)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop` error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` - --> $DIR/rename.rs:95:9 + --> $DIR/rename.rs:99:9 | LL | #![warn(clippy::mem_discriminant_non_enum)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` - --> $DIR/rename.rs:96:9 + --> $DIR/rename.rs:100:9 | LL | #![warn(clippy::panic_params)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally` - --> $DIR/rename.rs:97:9 + --> $DIR/rename.rs:101:9 | LL | #![warn(clippy::positional_named_format_parameters)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally` error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr` - --> $DIR/rename.rs:98:9 + --> $DIR/rename.rs:102:9 | LL | #![warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr` +error: lint `clippy::undropped_manually_drops` has been renamed to `undropped_manually_drops` + --> $DIR/rename.rs:103:9 + | +LL | #![warn(clippy::undropped_manually_drops)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `undropped_manually_drops` + error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` - --> $DIR/rename.rs:99:9 + --> $DIR/rename.rs:104:9 | LL | #![warn(clippy::unknown_clippy_lints)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` error: lint `clippy::unused_label` has been renamed to `unused_labels` - --> $DIR/rename.rs:100:9 + --> $DIR/rename.rs:105:9 | LL | #![warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` -error: aborting due to 50 previous errors +error: aborting due to 52 previous errors diff --git a/src/tools/clippy/tests/ui/same_functions_in_if_condition.rs b/src/tools/clippy/tests/ui/same_functions_in_if_condition.rs index aea1507cc5bd9..08916398cbb28 100644 --- a/src/tools/clippy/tests/ui/same_functions_in_if_condition.rs +++ b/src/tools/clippy/tests/ui/same_functions_in_if_condition.rs @@ -10,6 +10,8 @@ clippy::uninlined_format_args )] +use std::marker::ConstParamTy; + fn function() -> bool { true } @@ -96,7 +98,7 @@ fn main() { }; println!("{}", os); - #[derive(PartialEq, Eq)] + #[derive(PartialEq, Eq, ConstParamTy)] enum E { A, B, diff --git a/src/tools/clippy/tests/ui/same_functions_in_if_condition.stderr b/src/tools/clippy/tests/ui/same_functions_in_if_condition.stderr index aade3b1fa4571..6aacc73b90dc1 100644 --- a/src/tools/clippy/tests/ui/same_functions_in_if_condition.stderr +++ b/src/tools/clippy/tests/ui/same_functions_in_if_condition.stderr @@ -1,11 +1,11 @@ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:37:15 + --> $DIR/same_functions_in_if_condition.rs:39:15 | LL | } else if function() { | ^^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:36:8 + --> $DIR/same_functions_in_if_condition.rs:38:8 | LL | if function() { | ^^^^^^^^^^ @@ -16,61 +16,61 @@ LL | #![deny(clippy::same_functions_in_if_condition)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:42:15 + --> $DIR/same_functions_in_if_condition.rs:44:15 | LL | } else if fn_arg(a) { | ^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:41:8 + --> $DIR/same_functions_in_if_condition.rs:43:8 | LL | if fn_arg(a) { | ^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:47:15 + --> $DIR/same_functions_in_if_condition.rs:49:15 | LL | } else if obj.method() { | ^^^^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:46:8 + --> $DIR/same_functions_in_if_condition.rs:48:8 | LL | if obj.method() { | ^^^^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:52:15 + --> $DIR/same_functions_in_if_condition.rs:54:15 | LL | } else if obj.method_arg(a) { | ^^^^^^^^^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:51:8 + --> $DIR/same_functions_in_if_condition.rs:53:8 | LL | if obj.method_arg(a) { | ^^^^^^^^^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:59:15 + --> $DIR/same_functions_in_if_condition.rs:61:15 | LL | } else if v.pop().is_none() { | ^^^^^^^^^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:57:8 + --> $DIR/same_functions_in_if_condition.rs:59:8 | LL | if v.pop().is_none() { | ^^^^^^^^^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:64:15 + --> $DIR/same_functions_in_if_condition.rs:66:15 | LL | } else if v.len() == 42 { | ^^^^^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:62:8 + --> $DIR/same_functions_in_if_condition.rs:64:8 | LL | if v.len() == 42 { | ^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/single_match.fixed b/src/tools/clippy/tests/ui/single_match.fixed new file mode 100644 index 0000000000000..77a2cf3b991f6 --- /dev/null +++ b/src/tools/clippy/tests/ui/single_match.fixed @@ -0,0 +1,209 @@ +//@run-rustfix +#![warn(clippy::single_match)] +#![allow(unused, clippy::uninlined_format_args, clippy::redundant_pattern_matching)] +fn dummy() {} + +fn single_match() { + let x = Some(1u8); + + if let Some(y) = x { + println!("{:?}", y); + }; + + let x = Some(1u8); + if let Some(y) = x { println!("{:?}", y) } + + let z = (1u8, 1u8); + if let (2..=3, 7..=9) = z { dummy() }; + + // Not linted (pattern guards used) + match x { + Some(y) if y == 0 => println!("{:?}", y), + _ => (), + } + + // Not linted (no block with statements in the single arm) + match z { + (2..=3, 7..=9) => println!("{:?}", z), + _ => println!("nope"), + } +} + +enum Foo { + Bar, + Baz(u8), +} +use std::borrow::Cow; +use Foo::*; + +fn single_match_know_enum() { + let x = Some(1u8); + let y: Result<_, i8> = Ok(1i8); + + if let Some(y) = x { dummy() }; + + if let Ok(y) = y { dummy() }; + + let c = Cow::Borrowed(""); + + if let Cow::Borrowed(..) = c { dummy() }; + + let z = Foo::Bar; + // no warning + match z { + Bar => println!("42"), + Baz(_) => (), + } + + match z { + Baz(_) => println!("42"), + Bar => (), + } +} + +// issue #173 +fn if_suggestion() { + let x = "test"; + if x == "test" { println!() } + + #[derive(PartialEq, Eq)] + enum Foo { + A, + B, + C(u32), + } + + let x = Foo::A; + if x == Foo::A { println!() } + + const FOO_C: Foo = Foo::C(0); + if x == FOO_C { println!() } + + if x == Foo::A { println!() } + + let x = &x; + if x == &Foo::A { println!() } + + enum Bar { + A, + B, + } + impl PartialEq for Bar { + fn eq(&self, rhs: &Self) -> bool { + matches!((self, rhs), (Self::A, Self::A) | (Self::B, Self::B)) + } + } + impl Eq for Bar {} + + let x = Bar::A; + if let Bar::A = x { println!() } + + // issue #7038 + struct X; + let x = Some(X); + if let None = x { println!() }; +} + +// See: issue #8282 +fn ranges() { + enum E { + V, + } + let x = (Some(E::V), Some(42)); + + // Don't lint, because the `E` enum can be extended with additional fields later. Thus, the + // proposed replacement to `if let Some(E::V)` may hide non-exhaustive warnings that appeared + // because of `match` construction. + match x { + (Some(E::V), _) => {}, + (None, _) => {}, + } + + // lint + if let (Some(_), _) = x {} + + // lint + if let (Some(E::V), _) = x { todo!() } + + // lint + if let (.., Some(E::V), _) = (Some(42), Some(E::V), Some(42)) {} + + // Don't lint, see above. + match (Some(E::V), Some(E::V), Some(E::V)) { + (.., Some(E::V), _) => {}, + (.., None, _) => {}, + } + + // Don't lint, see above. + match (Some(E::V), Some(E::V), Some(E::V)) { + (Some(E::V), ..) => {}, + (None, ..) => {}, + } + + // Don't lint, see above. + match (Some(E::V), Some(E::V), Some(E::V)) { + (_, Some(E::V), ..) => {}, + (_, None, ..) => {}, + } +} + +fn skip_type_aliases() { + enum OptionEx { + Some(i32), + None, + } + enum ResultEx { + Err(i32), + Ok(i32), + } + + use OptionEx::{None, Some}; + use ResultEx::{Err, Ok}; + + // don't lint + match Err(42) { + Ok(_) => dummy(), + Err(_) => (), + }; + + // don't lint + match Some(1i32) { + Some(_) => dummy(), + None => (), + }; +} + +macro_rules! single_match { + ($num:literal) => { + match $num { + 15 => println!("15"), + _ => (), + } + }; +} + +fn main() { + single_match!(5); + + // Don't lint + let _ = match Some(0) { + #[cfg(feature = "foo")] + Some(10) => 11, + Some(x) => x, + _ => 0, + }; +} + +fn issue_10808(bar: Option) { + if let Some(v) = bar { unsafe { + let r = &v as *const i32; + println!("{}", *r); + } } + + if let Some(v) = bar { + unsafe { + let r = &v as *const i32; + println!("{}", *r); + } + } +} diff --git a/src/tools/clippy/tests/ui/single_match.rs b/src/tools/clippy/tests/ui/single_match.rs index d0c9b7b5663e7..8d0ab5b99ad51 100644 --- a/src/tools/clippy/tests/ui/single_match.rs +++ b/src/tools/clippy/tests/ui/single_match.rs @@ -1,6 +1,6 @@ +//@run-rustfix #![warn(clippy::single_match)] -#![allow(clippy::uninlined_format_args)] - +#![allow(unused, clippy::uninlined_format_args, clippy::redundant_pattern_matching)] fn dummy() {} fn single_match() { @@ -244,3 +244,24 @@ fn main() { _ => 0, }; } + +fn issue_10808(bar: Option) { + match bar { + Some(v) => unsafe { + let r = &v as *const i32; + println!("{}", *r); + }, + _ => {}, + } + + match bar { + #[rustfmt::skip] + Some(v) => { + unsafe { + let r = &v as *const i32; + println!("{}", *r); + } + }, + _ => {}, + } +} diff --git a/src/tools/clippy/tests/ui/single_match.stderr b/src/tools/clippy/tests/ui/single_match.stderr index 7cecc1b739506..dad66e2ab2ec3 100644 --- a/src/tools/clippy/tests/ui/single_match.stderr +++ b/src/tools/clippy/tests/ui/single_match.stderr @@ -155,5 +155,47 @@ LL | | (..) => {}, LL | | } | |_____^ help: try this: `if let (.., Some(E::V), _) = (Some(42), Some(E::V), Some(42)) {}` -error: aborting due to 16 previous errors +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match.rs:249:5 + | +LL | / match bar { +LL | | Some(v) => unsafe { +LL | | let r = &v as *const i32; +LL | | println!("{}", *r); +LL | | }, +LL | | _ => {}, +LL | | } + | |_____^ + | +help: try this + | +LL ~ if let Some(v) = bar { unsafe { +LL + let r = &v as *const i32; +LL + println!("{}", *r); +LL + } } + | + +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match.rs:257:5 + | +LL | / match bar { +LL | | #[rustfmt::skip] +LL | | Some(v) => { +LL | | unsafe { +... | +LL | | _ => {}, +LL | | } + | |_____^ + | +help: try this + | +LL ~ if let Some(v) = bar { +LL + unsafe { +LL + let r = &v as *const i32; +LL + println!("{}", *r); +LL + } +LL + } + | + +error: aborting due to 18 previous errors diff --git a/src/tools/clippy/tests/ui/single_match_else.fixed b/src/tools/clippy/tests/ui/single_match_else.fixed new file mode 100644 index 0000000000000..f88498655a418 --- /dev/null +++ b/src/tools/clippy/tests/ui/single_match_else.fixed @@ -0,0 +1,173 @@ +//@run-rustfix +//@aux-build: proc_macros.rs +#![warn(clippy::single_match_else)] +#![allow(unused, clippy::needless_return, clippy::no_effect, clippy::uninlined_format_args)] +extern crate proc_macros; +use proc_macros::with_span; + +enum ExprNode { + ExprAddrOf, + Butterflies, + Unicorns, +} + +static NODE: ExprNode = ExprNode::Unicorns; + +fn unwrap_addr() -> Option<&'static ExprNode> { + let _ = if let ExprNode::ExprAddrOf = ExprNode::Butterflies { Some(&NODE) } else { + let x = 5; + None + }; + + // Don't lint + with_span!(span match ExprNode::Butterflies { + ExprNode::ExprAddrOf => Some(&NODE), + _ => { + let x = 5; + None + }, + }) +} + +macro_rules! unwrap_addr { + ($expression:expr) => { + match $expression { + ExprNode::ExprAddrOf => Some(&NODE), + _ => { + let x = 5; + None + }, + } + }; +} + +#[rustfmt::skip] +fn main() { + unwrap_addr!(ExprNode::Unicorns); + + // + // don't lint single exprs/statements + // + + // don't lint here + match Some(1) { + Some(a) => println!("${:?}", a), + None => return, + } + + // don't lint here + match Some(1) { + Some(a) => println!("${:?}", a), + None => { + return + }, + } + + // don't lint here + match Some(1) { + Some(a) => println!("${:?}", a), + None => { + return; + }, + } + + // + // lint multiple exprs/statements "else" blocks + // + + // lint here + if let Some(a) = Some(1) { println!("${:?}", a) } else { + println!("else block"); + return + } + + // lint here + if let Some(a) = Some(1) { println!("${:?}", a) } else { + println!("else block"); + return; + } + + // lint here + use std::convert::Infallible; + if let Ok(a) = Result::::Ok(1) { println!("${:?}", a) } else { + println!("else block"); + return; + } + + use std::borrow::Cow; + if let Cow::Owned(a) = Cow::from("moo") { println!("${:?}", a) } else { + println!("else block"); + return; + } +} + +fn issue_10808(bar: Option) { + if let Some(v) = bar { unsafe { + let r = &v as *const i32; + println!("{}", *r); + } } else { + println!("None1"); + println!("None2"); + } + + if let Some(v) = bar { + println!("Some"); + println!("{v}"); + } else { unsafe { + let v = 0; + let r = &v as *const i32; + println!("{}", *r); + } } + + if let Some(v) = bar { unsafe { + let r = &v as *const i32; + println!("{}", *r); + } } else { unsafe { + let v = 0; + let r = &v as *const i32; + println!("{}", *r); + } } + + if let Some(v) = bar { + unsafe { + let r = &v as *const i32; + println!("{}", *r); + } + } else { + println!("None"); + println!("None"); + } + + match bar { + Some(v) => { + println!("Some"); + println!("{v}"); + }, + #[rustfmt::skip] + None => { + unsafe { + let v = 0; + let r = &v as *const i32; + println!("{}", *r); + } + }, + } + + match bar { + #[rustfmt::skip] + Some(v) => { + unsafe { + let r = &v as *const i32; + println!("{}", *r); + } + }, + #[rustfmt::skip] + None => { + unsafe { + let v = 0; + let r = &v as *const i32; + println!("{}", *r); + } + }, + } +} diff --git a/src/tools/clippy/tests/ui/single_match_else.rs b/src/tools/clippy/tests/ui/single_match_else.rs index c8ac768b60f68..b34b95539190c 100644 --- a/src/tools/clippy/tests/ui/single_match_else.rs +++ b/src/tools/clippy/tests/ui/single_match_else.rs @@ -1,7 +1,7 @@ +//@run-rustfix //@aux-build: proc_macros.rs #![warn(clippy::single_match_else)] -#![allow(clippy::needless_return, clippy::no_effect, clippy::uninlined_format_args)] - +#![allow(unused, clippy::needless_return, clippy::no_effect, clippy::uninlined_format_args)] extern crate proc_macros; use proc_macros::with_span; @@ -115,3 +115,87 @@ fn main() { } } } + +fn issue_10808(bar: Option) { + match bar { + Some(v) => unsafe { + let r = &v as *const i32; + println!("{}", *r); + }, + None => { + println!("None1"); + println!("None2"); + }, + } + + match bar { + Some(v) => { + println!("Some"); + println!("{v}"); + }, + None => unsafe { + let v = 0; + let r = &v as *const i32; + println!("{}", *r); + }, + } + + match bar { + Some(v) => unsafe { + let r = &v as *const i32; + println!("{}", *r); + }, + None => unsafe { + let v = 0; + let r = &v as *const i32; + println!("{}", *r); + }, + } + + match bar { + #[rustfmt::skip] + Some(v) => { + unsafe { + let r = &v as *const i32; + println!("{}", *r); + } + }, + None => { + println!("None"); + println!("None"); + }, + } + + match bar { + Some(v) => { + println!("Some"); + println!("{v}"); + }, + #[rustfmt::skip] + None => { + unsafe { + let v = 0; + let r = &v as *const i32; + println!("{}", *r); + } + }, + } + + match bar { + #[rustfmt::skip] + Some(v) => { + unsafe { + let r = &v as *const i32; + println!("{}", *r); + } + }, + #[rustfmt::skip] + None => { + unsafe { + let v = 0; + let r = &v as *const i32; + println!("{}", *r); + } + }, + } +} diff --git a/src/tools/clippy/tests/ui/single_match_else.stderr b/src/tools/clippy/tests/ui/single_match_else.stderr index 62876a55dc616..228236f3bb8e8 100644 --- a/src/tools/clippy/tests/ui/single_match_else.stderr +++ b/src/tools/clippy/tests/ui/single_match_else.stderr @@ -100,5 +100,101 @@ LL + return; LL + } | -error: aborting due to 5 previous errors +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match_else.rs:120:5 + | +LL | / match bar { +LL | | Some(v) => unsafe { +LL | | let r = &v as *const i32; +LL | | println!("{}", *r); +... | +LL | | }, +LL | | } + | |_____^ + | +help: try this + | +LL ~ if let Some(v) = bar { unsafe { +LL + let r = &v as *const i32; +LL + println!("{}", *r); +LL + } } else { +LL + println!("None1"); +LL + println!("None2"); +LL + } + | + +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match_else.rs:131:5 + | +LL | / match bar { +LL | | Some(v) => { +LL | | println!("Some"); +LL | | println!("{v}"); +... | +LL | | }, +LL | | } + | |_____^ + | +help: try this + | +LL ~ if let Some(v) = bar { +LL + println!("Some"); +LL + println!("{v}"); +LL + } else { unsafe { +LL + let v = 0; +LL + let r = &v as *const i32; +LL + println!("{}", *r); +LL + } } + | + +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match_else.rs:143:5 + | +LL | / match bar { +LL | | Some(v) => unsafe { +LL | | let r = &v as *const i32; +LL | | println!("{}", *r); +... | +LL | | }, +LL | | } + | |_____^ + | +help: try this + | +LL ~ if let Some(v) = bar { unsafe { +LL + let r = &v as *const i32; +LL + println!("{}", *r); +LL + } } else { unsafe { +LL + let v = 0; +LL + let r = &v as *const i32; +LL + println!("{}", *r); +LL + } } + | + +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match_else.rs:155:5 + | +LL | / match bar { +LL | | #[rustfmt::skip] +LL | | Some(v) => { +LL | | unsafe { +... | +LL | | }, +LL | | } + | |_____^ + | +help: try this + | +LL ~ if let Some(v) = bar { +LL + unsafe { +LL + let r = &v as *const i32; +LL + println!("{}", *r); +LL + } +LL + } else { +LL + println!("None"); +LL + println!("None"); +LL + } + | + +error: aborting due to 9 previous errors diff --git a/src/tools/clippy/tests/ui/suspicious_else_formatting.rs b/src/tools/clippy/tests/ui/suspicious_else_formatting.rs index e0153cdd8cdcf..4823d9092089b 100644 --- a/src/tools/clippy/tests/ui/suspicious_else_formatting.rs +++ b/src/tools/clippy/tests/ui/suspicious_else_formatting.rs @@ -1,7 +1,7 @@ //@aux-build:proc_macro_suspicious_else_formatting.rs #![warn(clippy::suspicious_else_formatting)] -#![allow(clippy::if_same_then_else, clippy::let_unit_value)] +#![allow(clippy::if_same_then_else, clippy::let_unit_value, clippy::needless_else)] extern crate proc_macro_suspicious_else_formatting; use proc_macro_suspicious_else_formatting::DeriveBadSpan; diff --git a/src/tools/clippy/tests/ui/undropped_manually_drops.rs b/src/tools/clippy/tests/ui/undropped_manually_drops.rs deleted file mode 100644 index f4cfc92e1cd02..0000000000000 --- a/src/tools/clippy/tests/ui/undropped_manually_drops.rs +++ /dev/null @@ -1,26 +0,0 @@ -#![warn(clippy::undropped_manually_drops)] - -struct S; - -fn main() { - let f = std::mem::drop; - let g = std::mem::ManuallyDrop::drop; - let mut manual1 = std::mem::ManuallyDrop::new(S); - let mut manual2 = std::mem::ManuallyDrop::new(S); - let mut manual3 = std::mem::ManuallyDrop::new(S); - let mut manual4 = std::mem::ManuallyDrop::new(S); - - // These lines will not drop `S` and should be linted - drop(std::mem::ManuallyDrop::new(S)); - drop(manual1); - - // FIXME: this line is not linted, though it should be - f(manual2); - - // These lines will drop `S` and should be okay. - unsafe { - std::mem::ManuallyDrop::drop(&mut std::mem::ManuallyDrop::new(S)); - std::mem::ManuallyDrop::drop(&mut manual3); - g(&mut manual4); - } -} diff --git a/src/tools/clippy/tests/ui/undropped_manually_drops.stderr b/src/tools/clippy/tests/ui/undropped_manually_drops.stderr deleted file mode 100644 index 92611a9b7df4a..0000000000000 --- a/src/tools/clippy/tests/ui/undropped_manually_drops.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error: the inner value of this ManuallyDrop will not be dropped - --> $DIR/undropped_manually_drops.rs:14:5 - | -LL | drop(std::mem::ManuallyDrop::new(S)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: to drop a `ManuallyDrop`, use std::mem::ManuallyDrop::drop - = note: `-D clippy::undropped-manually-drops` implied by `-D warnings` - -error: the inner value of this ManuallyDrop will not be dropped - --> $DIR/undropped_manually_drops.rs:15:5 - | -LL | drop(manual1); - | ^^^^^^^^^^^^^ - | - = help: to drop a `ManuallyDrop`, use std::mem::ManuallyDrop::drop - -error: aborting due to 2 previous errors - diff --git a/src/tools/clippy/tests/ui/unknown_clippy_lints.fixed b/src/tools/clippy/tests/ui/unknown_clippy_lints.fixed index 49c0e4dc7eb10..debc7e152e739 100644 --- a/src/tools/clippy/tests/ui/unknown_clippy_lints.fixed +++ b/src/tools/clippy/tests/ui/unknown_clippy_lints.fixed @@ -3,7 +3,7 @@ #![warn(clippy::pedantic)] // Should suggest lowercase #![allow(clippy::all)] -#![warn(clippy::cmp_nan)] +#![warn(clippy::cmp_owned)] // Should suggest similar clippy lint name #[warn(clippy::if_not_else)] diff --git a/src/tools/clippy/tests/ui/unknown_clippy_lints.rs b/src/tools/clippy/tests/ui/unknown_clippy_lints.rs index b60042923ea18..16140fd107917 100644 --- a/src/tools/clippy/tests/ui/unknown_clippy_lints.rs +++ b/src/tools/clippy/tests/ui/unknown_clippy_lints.rs @@ -3,7 +3,7 @@ #![warn(clippy::pedantic)] // Should suggest lowercase #![allow(clippy::All)] -#![warn(clippy::CMP_NAN)] +#![warn(clippy::CMP_OWNED)] // Should suggest similar clippy lint name #[warn(clippy::if_not_els)] diff --git a/src/tools/clippy/tests/ui/unknown_clippy_lints.stderr b/src/tools/clippy/tests/ui/unknown_clippy_lints.stderr index 584c428932fee..880673eef3e40 100644 --- a/src/tools/clippy/tests/ui/unknown_clippy_lints.stderr +++ b/src/tools/clippy/tests/ui/unknown_clippy_lints.stderr @@ -6,11 +6,11 @@ LL | #![allow(clippy::All)] | = note: `-D unknown-lints` implied by `-D warnings` -error: unknown lint: `clippy::CMP_NAN` +error: unknown lint: `clippy::CMP_OWNED` --> $DIR/unknown_clippy_lints.rs:6:9 | -LL | #![warn(clippy::CMP_NAN)] - | ^^^^^^^^^^^^^^^ help: did you mean: `clippy::cmp_nan` +LL | #![warn(clippy::CMP_OWNED)] + | ^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::cmp_owned` error: unknown lint: `clippy::if_not_els` --> $DIR/unknown_clippy_lints.rs:9:8 diff --git a/src/tools/clippy/tests/ui/unused_async.rs b/src/tools/clippy/tests/ui/unused_async.rs index 4ca7f29b34cd0..bfaa5dadfa59e 100644 --- a/src/tools/clippy/tests/ui/unused_async.rs +++ b/src/tools/clippy/tests/ui/unused_async.rs @@ -3,6 +3,26 @@ use std::future::Future; use std::pin::Pin; +mod issue10800 { + #![allow(dead_code, unused_must_use, clippy::no_effect)] + + use std::future::ready; + + async fn async_block_await() { + async { + ready(()).await; + }; + } + + async fn normal_block_await() { + { + { + ready(()).await; + } + } + } +} + async fn foo() -> i32 { 4 } diff --git a/src/tools/clippy/tests/ui/unused_async.stderr b/src/tools/clippy/tests/ui/unused_async.stderr index cff3eccbd32bb..8ac2066a6b24d 100644 --- a/src/tools/clippy/tests/ui/unused_async.stderr +++ b/src/tools/clippy/tests/ui/unused_async.stderr @@ -1,5 +1,23 @@ error: unused `async` for function with no await statements - --> $DIR/unused_async.rs:6:1 + --> $DIR/unused_async.rs:11:5 + | +LL | / async fn async_block_await() { +LL | | async { +LL | | ready(()).await; +LL | | }; +LL | | } + | |_____^ + | + = help: consider removing the `async` from this function +note: `await` used in an async block, which does not require the enclosing function to be `async` + --> $DIR/unused_async.rs:13:23 + | +LL | ready(()).await; + | ^^^^^ + = note: `-D clippy::unused-async` implied by `-D warnings` + +error: unused `async` for function with no await statements + --> $DIR/unused_async.rs:26:1 | LL | / async fn foo() -> i32 { LL | | 4 @@ -7,10 +25,9 @@ LL | | } | |_^ | = help: consider removing the `async` from this function - = note: `-D clippy::unused-async` implied by `-D warnings` error: unused `async` for function with no await statements - --> $DIR/unused_async.rs:17:5 + --> $DIR/unused_async.rs:37:5 | LL | / async fn unused(&self) -> i32 { LL | | 1 @@ -19,5 +36,5 @@ LL | | } | = help: consider removing the `async` from this function -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/wildcard_imports.fixed b/src/tools/clippy/tests/ui/wildcard_imports.fixed index bd845361fa89d..733bbcfbcefab 100644 --- a/src/tools/clippy/tests/ui/wildcard_imports.fixed +++ b/src/tools/clippy/tests/ui/wildcard_imports.fixed @@ -24,6 +24,7 @@ use wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar; use wildcard_imports_helper::{ExternA, extern_foo}; use std::io::prelude::*; +use wildcard_imports_helper::extern_prelude::v1::*; use wildcard_imports_helper::prelude::v1::*; struct ReadFoo; @@ -81,6 +82,7 @@ fn main() { let _ = inner_struct_mod::C; let _ = ExternA; let _ = PreludeModAnywhere; + let _ = ExternPreludeModAnywhere; double_struct_import_test!(); double_struct_import_test!(); diff --git a/src/tools/clippy/tests/ui/wildcard_imports.rs b/src/tools/clippy/tests/ui/wildcard_imports.rs index fb51f7bdfcc6b..4acdd374bdeca 100644 --- a/src/tools/clippy/tests/ui/wildcard_imports.rs +++ b/src/tools/clippy/tests/ui/wildcard_imports.rs @@ -24,6 +24,7 @@ use wildcard_imports_helper::inner::inner_for_self_import::*; use wildcard_imports_helper::*; use std::io::prelude::*; +use wildcard_imports_helper::extern_prelude::v1::*; use wildcard_imports_helper::prelude::v1::*; struct ReadFoo; @@ -81,6 +82,7 @@ fn main() { let _ = inner_struct_mod::C; let _ = ExternA; let _ = PreludeModAnywhere; + let _ = ExternPreludeModAnywhere; double_struct_import_test!(); double_struct_import_test!(); diff --git a/src/tools/clippy/tests/ui/wildcard_imports.stderr b/src/tools/clippy/tests/ui/wildcard_imports.stderr index 6b469cdfc4449..235be2d57081b 100644 --- a/src/tools/clippy/tests/ui/wildcard_imports.stderr +++ b/src/tools/clippy/tests/ui/wildcard_imports.stderr @@ -37,55 +37,55 @@ LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:95:13 + --> $DIR/wildcard_imports.rs:97:13 | LL | use crate::fn_mod::*; | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:101:75 + --> $DIR/wildcard_imports.rs:103:75 | LL | use wildcard_imports_helper::inner::inner_for_self_import::{self, *}; | ^ help: try: `inner_extern_foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:102:13 + --> $DIR/wildcard_imports.rs:104:13 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:113:20 + --> $DIR/wildcard_imports.rs:115:20 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^ help: try: `inner::inner_foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:113:30 + --> $DIR/wildcard_imports.rs:115:30 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^^ help: try: `inner2::inner_bar` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:120:13 + --> $DIR/wildcard_imports.rs:122:13 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:149:9 + --> $DIR/wildcard_imports.rs:151:9 | LL | use crate::in_fn_test::*; | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:158:9 + --> $DIR/wildcard_imports.rs:160:9 | LL | use crate:: in_fn_test:: * ; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:159:9 + --> $DIR/wildcard_imports.rs:161:9 | LL | use crate:: fn_mod:: | _________^ @@ -93,37 +93,37 @@ LL | | *; | |_________^ help: try: `crate:: fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:170:13 + --> $DIR/wildcard_imports.rs:172:13 | LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:205:17 + --> $DIR/wildcard_imports.rs:207:17 | LL | use super::*; | ^^^^^^^^ help: try: `super::insidefoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:213:13 + --> $DIR/wildcard_imports.rs:215:13 | LL | use super_imports::*; | ^^^^^^^^^^^^^^^^ help: try: `super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:222:17 + --> $DIR/wildcard_imports.rs:224:17 | LL | use super::super::*; | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:231:13 + --> $DIR/wildcard_imports.rs:233:13 | LL | use super::super::super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:239:13 + --> $DIR/wildcard_imports.rs:241:13 | LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index f796c89873111..96fe720630ccd 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -108,6 +108,7 @@ string_enum! { Polonius => "polonius", Chalk => "chalk", NextSolver => "next-solver", + NextSolverCoherence => "next-solver-coherence", SplitDwarf => "split-dwarf", SplitDwarfSingle => "split-dwarf-single", } diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 923b2e63f2ee5..6582b534488a8 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -2127,6 +2127,9 @@ impl<'test> TestCx<'test> { Some(CompareMode::NextSolver) => { rustc.args(&["-Ztrait-solver=next"]); } + Some(CompareMode::NextSolverCoherence) => { + rustc.args(&["-Ztrait-solver=next-coherence"]); + } Some(CompareMode::SplitDwarf) if self.config.target.contains("windows") => { rustc.args(&["-Csplit-debuginfo=unpacked", "-Zunstable-options"]); } diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index ee815ae6f8fc0..918c738ed1fe5 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -33c3d101280c8eb3cd8af421bfb56a8afcc3881d +37998ab508d5d9fa0d465d7b535dc673087dda8f diff --git a/src/tools/miri/tests/compiletest.rs b/src/tools/miri/tests/compiletest.rs index 71959fa115892..9e6e37031535e 100644 --- a/src/tools/miri/tests/compiletest.rs +++ b/src/tools/miri/tests/compiletest.rs @@ -51,18 +51,9 @@ fn test_config(target: &str, path: &str, mode: Mode, with_dependencies: bool) -> let mut program = CommandBuilder::rustc(); program.program = miri_path(); - let in_rustc_test_suite = option_env!("RUSTC_STAGE").is_some(); - // Add some flags we always want. - if in_rustc_test_suite { - // Less aggressive warnings to make the rustc toolstate management less painful. - // (We often get warnings when e.g. a feature gets stabilized or some lint gets added/improved.) - program.args.push("-Astable-features".into()); - program.args.push("-Aunused".into()); - } else { - program.args.push("-Dwarnings".into()); - program.args.push("-Dunused".into()); - } + program.args.push("-Dwarnings".into()); + program.args.push("-Dunused".into()); if let Ok(extra_flags) = env::var("MIRIFLAGS") { for flag in extra_flags.split_whitespace() { program.args.push(flag.into()); diff --git a/src/tools/rust-analyzer/.github/actions/github-release/README.md b/src/tools/rust-analyzer/.github/actions/github-release/README.md index 7b50d002001ee..c8ff3ec6e52c8 100644 --- a/src/tools/rust-analyzer/.github/actions/github-release/README.md +++ b/src/tools/rust-analyzer/.github/actions/github-release/README.md @@ -10,7 +10,7 @@ perform github releases but they all tend to have their set of drawbacks. Additionally nothing handles deleting releases which we need for our rolling `dev` release. -To handle all this this action rolls-its-own implementation using the +To handle all this, this action rolls its own implementation using the actions/toolkit repository and packages published there. These run in a Docker container and take various inputs to orchestrate the release from the build. diff --git a/src/tools/rust-analyzer/.github/workflows/autopublish.yaml b/src/tools/rust-analyzer/.github/workflows/autopublish.yaml index 279f86b458dff..7090c94d93cce 100644 --- a/src/tools/rust-analyzer/.github/workflows/autopublish.yaml +++ b/src/tools/rust-analyzer/.github/workflows/autopublish.yaml @@ -32,7 +32,7 @@ jobs: shell: bash run: | git config --global user.email "runner@gha.local" - git config --global user.name "Github Action" + 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 diff --git a/src/tools/rust-analyzer/.github/workflows/ci.yaml b/src/tools/rust-analyzer/.github/workflows/ci.yaml index bb77324378a40..622da105fdd44 100644 --- a/src/tools/rust-analyzer/.github/workflows/ci.yaml +++ b/src/tools/rust-analyzer/.github/workflows/ci.yaml @@ -18,12 +18,35 @@ env: RUSTUP_MAX_RETRIES: 10 jobs: + changes: + runs-on: ubuntu-latest + permissions: + pull-requests: read + outputs: + typescript: ${{ steps.filter.outputs.typescript }} + steps: + - uses: actions/checkout@v3 + - uses: dorny/paths-filter@4067d885736b84de7c414f582ac45897079b0a78 + id: filter + with: + filters: | + typescript: + - 'editors/code/**' + proc_macros: + - 'crates/proc-macro-api/**' + - 'crates/proc-macro-srv/**' + - 'crates/proc-macro-srv-cli/**' + - 'crates/proc-macro-test/**' + rust: + needs: changes if: github.repository == 'rust-lang/rust-analyzer' name: Rust runs-on: ${{ matrix.os }} env: CC: deny_c + RUST_CHANNEL: "${{ needs.changes.outputs.proc_macros == 'true' && 'nightly' || 'stable'}}" + USE_SYSROOT_ABI: "${{ needs.changes.outputs.proc_macros == 'true' && '--features sysroot-abi' || ''}}" strategy: fail-fast: false @@ -35,30 +58,31 @@ jobs: uses: actions/checkout@v3 with: ref: ${{ github.event.pull_request.head.sha }} - fetch-depth: 20 - name: Install Rust toolchain run: | - rustup update --no-self-update stable + rustup update --no-self-update ${{ env.RUST_CHANNEL }} rustup component add rustfmt rust-src - name: Cache Dependencies - uses: Swatinem/rust-cache@76686c56f2b581d1bb5bda44b51f7e24bd9b8b8e + uses: Swatinem/rust-cache@988c164c3d0e93c4dbab36aaf5bbeb77425b2894 + with: + key: ${{ env.RUST_CHANNEL }} - name: Bump opt-level if: matrix.os == 'ubuntu-latest' run: sed -i '/\[profile.dev]/a opt-level=1' Cargo.toml - name: Compile (tests) - run: cargo test --no-run --locked + run: cargo test --no-run --locked ${{ env.USE_SYSROOT_ABI }} # It's faster to `test` before `build` ¯\_(ツ)_/¯ - name: Compile (rust-analyzer) if: matrix.os == 'ubuntu-latest' - run: cargo build --quiet + run: cargo build --quiet ${{ env.USE_SYSROOT_ABI }} - name: Test - run: cargo test -- --nocapture --quiet + run: cargo test ${{ env.USE_SYSROOT_ABI }} -- --nocapture --quiet - name: Run analysis-stats on rust-analyzer if: matrix.os == 'ubuntu-latest' @@ -90,7 +114,7 @@ jobs: rustup target add ${{ env.targets }} ${{ env.targets_ide }} - name: Cache Dependencies - uses: Swatinem/rust-cache@76686c56f2b581d1bb5bda44b51f7e24bd9b8b8e + uses: Swatinem/rust-cache@988c164c3d0e93c4dbab36aaf5bbeb77425b2894 - name: Check run: | @@ -102,6 +126,7 @@ jobs: done typescript: + needs: changes if: github.repository == 'rust-lang/rust-analyzer' name: TypeScript strategy: @@ -114,18 +139,21 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v3 + if: needs.changes.outputs.typescript == 'true' - name: Install Nodejs uses: actions/setup-node@v3 with: node-version: 16 + if: needs.changes.outputs.typescript == 'true' - name: Install xvfb - if: matrix.os == 'ubuntu-latest' + if: matrix.os == 'ubuntu-latest' && needs.changes.outputs.typescript == 'true' run: sudo apt-get install -y xvfb - run: npm ci working-directory: ./editors/code + if: needs.changes.outputs.typescript == 'true' # - run: npm audit || { sleep 10 && npm audit; } || { sleep 30 && npm audit; } # if: runner.os == 'Linux' @@ -133,16 +161,17 @@ jobs: - run: npm run lint working-directory: ./editors/code + if: needs.changes.outputs.typescript == 'true' - name: Run VS Code tests (Linux) - if: matrix.os == 'ubuntu-latest' + if: matrix.os == 'ubuntu-latest' && needs.changes.outputs.typescript == 'true' env: VSCODE_CLI: 1 run: xvfb-run npm test working-directory: ./editors/code - name: Run VS Code tests (Windows) - if: matrix.os == 'windows-latest' + if: matrix.os == 'windows-latest' && needs.changes.outputs.typescript == 'true' env: VSCODE_CLI: 1 run: npm test @@ -150,9 +179,11 @@ jobs: - run: npm run pretest working-directory: ./editors/code + if: needs.changes.outputs.typescript == 'true' - run: npm run package --scripts-prepend-node-path working-directory: ./editors/code + if: needs.changes.outputs.typescript == 'true' end-success: name: bors build finished @@ -165,7 +196,7 @@ jobs: end-failure: name: bors build finished - if: github.event.pusher.name == 'bors' && (failure() || cancelled()) + if: github.event.pusher.name == 'bors' && !success() runs-on: ubuntu-latest needs: [rust, rust-cross, typescript] steps: diff --git a/src/tools/rust-analyzer/.github/workflows/publish-libs.yaml b/src/tools/rust-analyzer/.github/workflows/publish-libs.yaml index 1b843fff1a4a1..6d026c9ad910b 100644 --- a/src/tools/rust-analyzer/.github/workflows/publish-libs.yaml +++ b/src/tools/rust-analyzer/.github/workflows/publish-libs.yaml @@ -3,9 +3,9 @@ on: workflow_dispatch: push: branches: - - main + - master paths: - - 'lib/**' + - "lib/**" jobs: publish-libs: @@ -29,7 +29,7 @@ jobs: shell: bash run: | git config --global user.email "runner@gha.local" - git config --global user.name "Github Action" + git config --global user.name "GitHub Action" # Remove r-a crates from the workspaces so we don't auto-publish them as well sed -i 's/ "crates\/\*"//' ./Cargo.toml cargo workspaces publish --yes --exact --from-git --no-git-commit --allow-dirty diff --git a/src/tools/rust-analyzer/.github/workflows/release.yaml b/src/tools/rust-analyzer/.github/workflows/release.yaml index 48f4c6b55ed90..43681c785fdc0 100644 --- a/src/tools/rust-analyzer/.github/workflows/release.yaml +++ b/src/tools/rust-analyzer/.github/workflows/release.yaml @@ -270,7 +270,7 @@ 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 + run: npx vsce publish --pat ${{ secrets.MARKETPLACE_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix --pre-release - name: Publish Extension (OpenVSX, nightly) if: github.ref != 'refs/heads/release' && (github.repository == 'rust-analyzer/rust-analyzer' || github.repository == 'rust-lang/rust-analyzer') diff --git a/src/tools/rust-analyzer/.vscode/launch.json b/src/tools/rust-analyzer/.vscode/launch.json index 1e21214ffc4bc..c353737a35a83 100644 --- a/src/tools/rust-analyzer/.vscode/launch.json +++ b/src/tools/rust-analyzer/.vscode/launch.json @@ -72,7 +72,7 @@ }, { // Used for testing the extension with a local build of the LSP server (in `target/release`) - // with all other extendions loaded. + // with all other extensions loaded. "name": "Run With Extensions", "type": "extensionHost", "request": "launch", diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 25242c6028a47..e36aef6a6aa8d 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -19,18 +19,18 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "always-assert" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbf688625d06217d5b1bb0ea9d9c44a1635fd0ee3534466388d18203174f4d11" +checksum = "4436e0292ab1bb631b42973c61205e704475fe8126af845c8d923c0996328127" dependencies = [ "log", ] [[package]] name = "anyhow" -version = "1.0.68" +version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" +checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" [[package]] name = "anymap" @@ -40,9 +40,9 @@ checksum = "8f1f8f5a6f3d50d89e3797d7593a50f96bb2aaa20ca0cc7be1fb673232c91d72" [[package]] name = "arbitrary" -version = "1.2.2" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0224938f92e7aef515fac2ff2d18bd1115c1394ddf4a092e0c87e8be9499ee5" +checksum = "e2d098ff73c1ca148721f37baad5ea6a465a13f9573aba8641fbbbae8164a54e" [[package]] name = "arrayvec" @@ -87,12 +87,14 @@ name = "base-db" version = "0.0.0" dependencies = [ "cfg", + "la-arena", "profile", "rustc-hash", "salsa", "stdx", "syntax", "test-utils", + "triomphe", "tt", "vfs", ] @@ -103,6 +105,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c70beb79cbb5ce9c4f8e20849978f34225931f665bb49efa6982875a4d5facb3" + [[package]] name = "byteorder" version = "1.4.3" @@ -111,9 +119,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "camino" -version = "1.1.2" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77df041dc383319cc661b428b6961a005db4d6808d5e12536931b1ca9556055" +checksum = "c530edf18f37068ac2d977409ed5cd50d53d73bc653c7647b48eb78976ac9ae2" dependencies = [ "serde", ] @@ -129,9 +137,9 @@ dependencies = [ [[package]] name = "cargo_metadata" -version = "0.15.2" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "982a0cf6a99c350d7246035613882e376d58cebe571785abc5da4f648d53ac0a" +checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a" dependencies = [ "camino", "cargo-platform", @@ -143,9 +151,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.78" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" [[package]] name = "cfg" @@ -185,7 +193,7 @@ version = "0.89.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "473b480241695428c14e8f84f1c9a47ef232450a50faf3a4041e5c9dc11e0a3b" dependencies = [ - "bitflags", + "bitflags 1.3.2", "chalk-derive", "lazy_static", ] @@ -221,9 +229,9 @@ dependencies = [ [[package]] name = "command-group" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "026c3922235f9f7d78f21251a026f3acdeb7cce3deba107fe09a4bfa63d850a2" +checksum = "5080df6b0f0ecb76cab30808f00d937ba725cebe266a3da8cd89dff92f2a9916" dependencies = [ "nix", "winapi", @@ -257,9 +265,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.6" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" dependencies = [ "cfg-if", "crossbeam-utils", @@ -267,9 +275,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" dependencies = [ "cfg-if", "crossbeam-epoch", @@ -278,22 +286,22 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.13" +version = "0.9.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a" +checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" dependencies = [ "autocfg", "cfg-if", "crossbeam-utils", - "memoffset 0.7.1", + "memoffset", "scopeguard", ] [[package]] name = "crossbeam-utils" -version = "0.8.14" +version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" +checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" dependencies = [ "cfg-if", ] @@ -313,9 +321,9 @@ dependencies = [ [[package]] name = "derive_arbitrary" -version = "1.2.2" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf460bbff5f571bfc762da5102729f59f338be7db17a21fade44c5c4f5005350" +checksum = "f3cdeb9ec472d588e539a818b2dee436825730da08ad0017c4b1a17676bdc8b7" dependencies = [ "proc-macro2", "quote", @@ -342,24 +350,24 @@ checksum = "9bda8e21c04aca2ae33ffc2fd8c23134f3cac46db123ba97bd9d3f3b8a4a85e1" [[package]] name = "either" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "ena" -version = "0.14.0" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7402b94a93c24e742487327a7cd839dc9d36fec9de9fb25b09f2dae459f36c3" +checksum = "c533630cf40e9caa44bd91aadc88a75d75a4c3a12b4cfde353cbed41daa1e1f1" dependencies = [ "log", ] [[package]] name = "expect-test" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d4661aca38d826eb7c72fe128e4238220616de4c0cc00db7bfc38e2e1364dd3" +checksum = "30d9eafeadd538e68fb28016364c9732d78e420b9ff8853fa5e4058861e9f8d3" dependencies = [ "dissimilar", "once_cell", @@ -400,7 +408,6 @@ dependencies = [ "cargo_metadata", "command-group", "crossbeam-channel", - "jod-thread", "paths", "rustc-hash", "serde", @@ -419,12 +426,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "fs_extra" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394" - [[package]] name = "fsevent-sys" version = "4.1.0" @@ -442,9 +443,9 @@ checksum = "7ab85b9b05e3978cc9a9cf8fea7f01b494e1a09ed3037e16ba39edc7a29eb61a" [[package]] name = "gimli" -version = "0.27.0" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dec7af912d60cdbd3677c1af9352ebae6fb8394d165568a2234df0fa00f87793" +checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" [[package]] name = "hashbrown" @@ -497,6 +498,7 @@ dependencies = [ "smallvec", "stdx", "syntax", + "triomphe", "tt", ] @@ -507,7 +509,7 @@ dependencies = [ "anymap", "arrayvec", "base-db", - "bitflags", + "bitflags 2.1.0", "cfg", "cov-mark", "dashmap", @@ -533,6 +535,7 @@ dependencies = [ "syntax", "test-utils", "tracing", + "triomphe", "tt", ] @@ -557,6 +560,7 @@ dependencies = [ "stdx", "syntax", "tracing", + "triomphe", "tt", ] @@ -566,7 +570,7 @@ version = "0.0.0" dependencies = [ "arrayvec", "base-db", - "bitflags", + "bitflags 2.1.0", "chalk-derive", "chalk-ir", "chalk-recursive", @@ -582,6 +586,7 @@ dependencies = [ "itertools", "la-arena", "limit", + "nohash-hasher", "once_cell", "profile", "project-model", @@ -594,6 +599,7 @@ dependencies = [ "tracing", "tracing-subscriber", "tracing-tree", + "triomphe", "typed-arena", ] @@ -603,7 +609,7 @@ version = "0.0.20221221" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adabaadad9aa7576f97af02241cdf5554d62fb3d51a84cb05d77ba28edd3013f" dependencies = [ - "bitflags", + "bitflags 1.3.2", "hkalbasi-rustc-ap-rustc_index", "tracing", ] @@ -644,6 +650,7 @@ dependencies = [ "ide-diagnostics", "ide-ssr", "itertools", + "nohash-hasher", "oorandom", "profile", "pulldown-cmark", @@ -655,6 +662,7 @@ dependencies = [ "text-edit", "toolchain", "tracing", + "triomphe", "url", ] @@ -710,7 +718,9 @@ dependencies = [ "indexmap", "itertools", "limit", + "line-index", "memchr", + "nohash-hasher", "once_cell", "oorandom", "parser", @@ -723,6 +733,7 @@ dependencies = [ "test-utils", "text-edit", "tracing", + "triomphe", "xshell", ] @@ -755,11 +766,13 @@ dependencies = [ "hir", "ide-db", "itertools", + "nohash-hasher", "parser", "stdx", "syntax", "test-utils", "text-edit", + "triomphe", ] [[package]] @@ -774,9 +787,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.2" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown", @@ -788,7 +801,7 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" dependencies = [ - "bitflags", + "bitflags 1.3.2", "inotify-sys", "libc", ] @@ -819,6 +832,7 @@ dependencies = [ "hashbrown", "once_cell", "rustc-hash", + "triomphe", ] [[package]] @@ -832,9 +846,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" [[package]] name = "jod-thread" @@ -858,7 +872,7 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8367585489f01bc55dd27404dcf56b95e6da061a256a666ab23be9ba96a2e587" dependencies = [ - "bitflags", + "bitflags 1.3.2", "libc", ] @@ -874,9 +888,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.139" +version = "0.2.141" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" [[package]] name = "libloading" @@ -890,9 +904,9 @@ dependencies = [ [[package]] name = "libmimalloc-sys" -version = "0.1.30" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8c7cbf8b89019683667e347572e6d55a7df7ea36b0c4ce69961b0cde67b174" +checksum = "43a558e3d911bc3c7bfc8c78bc580b404d6e51c1cefbf656e176a94b49b0df40" dependencies = [ "cc", "libc", @@ -902,6 +916,14 @@ dependencies = [ name = "limit" version = "0.0.0" +[[package]] +name = "line-index" +version = "0.1.0-pre.1" +dependencies = [ + "nohash-hasher", + "text-size", +] + [[package]] name = "lock_api" version = "0.4.9" @@ -938,7 +960,7 @@ version = "0.94.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b63735a13a1f9cd4f4835223d828ed9c2e35c8c5e61837774399f558b6a1237" dependencies = [ - "bitflags", + "bitflags 1.3.2", "serde", "serde_json", "serde_repr", @@ -977,36 +999,27 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memmap2" -version = "0.5.8" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b182332558b18d807c4ce1ca8ca983b34c3ee32765e47b3f0f69b90355cc1dc" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" dependencies = [ "libc", ] [[package]] name = "memoffset" -version = "0.6.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" -dependencies = [ - "autocfg", -] - -[[package]] -name = "memoffset" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" dependencies = [ "autocfg", ] [[package]] name = "mimalloc" -version = "0.1.34" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dcb174b18635f7561a0c6c9fc2ce57218ac7523cf72c50af80e2d79ab8f3ba1" +checksum = "3d88dad3f985ec267a3fcb7a1726f5cb1a7e8cad8b646e70a84f967210df23da" dependencies = [ "libmimalloc-sys", ] @@ -1047,19 +1060,25 @@ version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if", "libc", "static_assertions", ] +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + [[package]] name = "notify" -version = "5.0.0" +version = "5.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2c66da08abae1c024c01d635253e402341b4060a12e99b31c7594063bf490a" +checksum = "58ea850aa68a06e48fdb069c0ec44d0d64c8dbffa49bf3b6f7f0a901fdea1ba9" dependencies = [ - "bitflags", + "bitflags 1.3.2", "crossbeam-channel", "filetime", "fsevent-sys", @@ -1068,7 +1087,7 @@ dependencies = [ "libc", "mio", "walkdir", - "winapi", + "windows-sys", ] [[package]] @@ -1093,18 +1112,18 @@ dependencies = [ [[package]] name = "object" -version = "0.30.2" +version = "0.30.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b8c786513eb403643f2a88c244c2aaa270ef2153f55094587d0c48a3cf22a83" +checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.17.0" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "oorandom" @@ -1173,16 +1192,16 @@ dependencies = [ "drop_bomb", "expect-test", "limit", - "rustc-ap-rustc_lexer", + "ra-ap-rustc_lexer", "sourcegen", "stdx", ] [[package]] name = "paste" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba" +checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" [[package]] name = "paths" @@ -1242,6 +1261,7 @@ dependencies = [ "snap", "stdx", "tracing", + "triomphe", "tt", ] @@ -1257,6 +1277,7 @@ dependencies = [ "paths", "proc-macro-api", "proc-macro-test", + "stdx", "tt", ] @@ -1264,6 +1285,7 @@ dependencies = [ name = "proc-macro-srv-cli" version = "0.0.0" dependencies = [ + "proc-macro-api", "proc-macro-srv", ] @@ -1282,9 +1304,9 @@ version = "0.0.0" [[package]] name = "proc-macro2" -version = "1.0.50" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" dependencies = [ "unicode-ident", ] @@ -1312,6 +1334,7 @@ dependencies = [ "cargo_metadata", "cfg", "expect-test", + "itertools", "la-arena", "paths", "profile", @@ -1322,6 +1345,7 @@ dependencies = [ "stdx", "toolchain", "tracing", + "triomphe", ] [[package]] @@ -1350,7 +1374,7 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d9cc634bc78768157b5cbfe988ffcd1dcba95cd2b2f03a88316c08c6d00ed63" dependencies = [ - "bitflags", + "bitflags 1.3.2", "memchr", "unicase", ] @@ -1366,18 +1390,28 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.23" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" dependencies = [ "proc-macro2", ] +[[package]] +name = "ra-ap-rustc_lexer" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1c145702ed3f237918e512685185dc8a4d0edc3a5326c63d20361d8ba9b45b3" +dependencies = [ + "unic-emoji-char", + "unicode-xid", +] + [[package]] name = "rayon" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" dependencies = [ "either", "rayon-core", @@ -1385,9 +1419,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" dependencies = [ "crossbeam-channel", "crossbeam-deque", @@ -1401,14 +1435,14 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] name = "regex" -version = "1.7.1" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d" dependencies = [ "regex-syntax", ] @@ -1424,19 +1458,19 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.28" +version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "rowan" -version = "0.15.10" +version = "0.15.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5811547e7ba31e903fe48c8ceab10d40d70a101f3d15523c847cce91aa71f332" +checksum = "64449cfef9483a475ed56ae30e2da5ee96448789fb2aa240a04beb6a055078bf" dependencies = [ "countme", "hashbrown", - "memoffset 0.6.5", + "memoffset", "rustc-hash", "text-size", ] @@ -1451,6 +1485,7 @@ dependencies = [ "crossbeam-channel", "dissimilar", "expect-test", + "filetime", "flycheck", "hir", "hir-def", @@ -1459,16 +1494,18 @@ dependencies = [ "ide-db", "ide-ssr", "itertools", - "jod-thread", "lsp-server", "lsp-types", "mbe", "mimalloc", + "mio", + "nohash-hasher", "num_cpus", "oorandom", "parking_lot 0.12.1", + "parking_lot_core 0.9.6", "proc-macro-api", - "proc-macro-srv", + "proc-macro-srv-cli", "profile", "project-model", "rayon", @@ -1476,17 +1513,19 @@ dependencies = [ "scip", "serde", "serde_json", + "serde_repr", "sourcegen", "stdx", "syntax", "test-utils", - "threadpool", + "thiserror", "tikv-jemallocator", "toolchain", "tracing", "tracing-log", "tracing-subscriber", "tracing-tree", + "triomphe", "tt", "vfs", "vfs-notify", @@ -1495,20 +1534,11 @@ dependencies = [ "xshell", ] -[[package]] -name = "rustc-ap-rustc_lexer" -version = "727.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f40f26e7abdcd3b982f36c09a634cc6187988fbf6ec466c91f8d30a12ac0237" -dependencies = [ - "unicode-xid", -] - [[package]] name = "rustc-demangle" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" +checksum = "d4a36c42d1873f9a77c53bde094f9664d9891bc604a45b4798fd2c389ed12e5b" [[package]] name = "rustc-hash" @@ -1518,9 +1548,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "ryu" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" [[package]] name = "salsa" @@ -1583,27 +1613,27 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "semver" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" dependencies = [ "serde", ] [[package]] name = "serde" -version = "1.0.152" +version = "1.0.156" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +checksum = "314b5b092c0ade17c00142951e50ced110ec27cea304b1037c6969246c2469a4" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.152" +version = "1.0.156" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +checksum = "d7e29c4601e36bcec74a223228dce795f4cd3616341a4af93520ca1a837c087d" dependencies = [ "proc-macro2", "quote", @@ -1612,9 +1642,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.91" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" +checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" dependencies = [ "indexmap", "itoa", @@ -1624,9 +1654,9 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a5ec9fa74a20ebbe5d9ac23dac1fc96ba0ecfe9f50f2843b52e537b10fbcb4e" +checksum = "395627de918015623b32e7669714206363a7fc00382bf477e72c1f7533e8eafc" dependencies = [ "proc-macro2", "quote", @@ -1650,9 +1680,9 @@ checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "smol_str" -version = "0.1.23" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7475118a28b7e3a2e157ce0131ba8c5526ea96e90ee601d9f6bb2e286a35ab44" +checksum = "74212e6bbe9a4352329b2f68ba3130c15a3f26fe88ff22dbdc6cdd58fa85e99c" dependencies = [ "serde", ] @@ -1682,6 +1712,8 @@ version = "0.0.0" dependencies = [ "always-assert", "backtrace", + "crossbeam-channel", + "jod-thread", "libc", "miow", "winapi", @@ -1689,9 +1721,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.107" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", @@ -1724,15 +1756,16 @@ dependencies = [ "proc-macro2", "profile", "quote", + "ra-ap-rustc_lexer", "rayon", "rowan", - "rustc-ap-rustc_lexer", "rustc-hash", "smol_str", "sourcegen", "stdx", "test-utils", "text-edit", + "triomphe", "ungrammar", ] @@ -1763,18 +1796,18 @@ checksum = "288cb548dbe72b652243ea797201f3d481a0609a967980fcc5b2315ea811560a" [[package]] name = "thiserror" -version = "1.0.38" +version = "1.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.38" +version = "1.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e" dependencies = [ "proc-macro2", "quote", @@ -1783,22 +1816,14 @@ dependencies = [ [[package]] name = "thread_local" -version = "1.1.4" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" dependencies = [ + "cfg-if", "once_cell", ] -[[package]] -name = "threadpool" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" -dependencies = [ - "num_cpus", -] - [[package]] name = "tikv-jemalloc-ctl" version = "0.5.0" @@ -1812,12 +1837,11 @@ dependencies = [ [[package]] name = "tikv-jemalloc-sys" -version = "0.5.2+5.3.0-patched" +version = "0.5.3+5.3.0-patched" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec45c14da997d0925c7835883e4d5c181f196fa142f8c19d7643d1e9af2592c3" +checksum = "a678df20055b43e57ef8cddde41cdfda9a3c1a060b67f4c5836dfb1d78543ba8" dependencies = [ "cc", - "fs_extra", "libc", ] @@ -1833,9 +1857,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.17" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" +checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" dependencies = [ "serde", "time-core", @@ -1858,9 +1882,9 @@ dependencies = [ [[package]] name = "tinyvec_macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "toolchain" @@ -1942,6 +1966,12 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "triomphe" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1ee9bd9239c339d714d657fac840c6d2a4f9c45f4f9ec7b0975113458be78db" + [[package]] name = "tt" version = "0.0.0" @@ -1962,6 +1992,47 @@ version = "1.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e5df347f0bf3ec1d670aad6ca5c6a1859cd9ea61d2113125794654ccced68f" +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-emoji-char" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b07221e68897210270a38bde4babb655869637af0f69407f96053a34f76494d" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", +] + [[package]] name = "unicase" version = "2.6.0" @@ -1973,15 +2044,15 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.10" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.6" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" [[package]] name = "unicode-normalization" @@ -1994,9 +2065,9 @@ dependencies = [ [[package]] name = "unicode-segmentation" -version = "1.10.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" [[package]] name = "unicode-xid" @@ -2034,6 +2105,7 @@ version = "0.0.0" dependencies = [ "fst", "indexmap", + "nohash-hasher", "paths", "rustc-hash", "stdx", @@ -2044,9 +2116,9 @@ name = "vfs-notify" version = "0.0.0" dependencies = [ "crossbeam-channel", - "jod-thread", "notify", "paths", + "stdx", "tracing", "vfs", "walkdir", @@ -2054,12 +2126,11 @@ dependencies = [ [[package]] name = "walkdir" -version = "2.3.2" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" dependencies = [ "same-file", - "winapi", "winapi-util", ] @@ -2117,45 +2188,45 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" [[package]] name = "windows_aarch64_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" [[package]] name = "windows_i686_gnu" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_x86_64_gnu" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" [[package]] name = "windows_x86_64_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] name = "write-json" diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index 333f03ce2ffe5..3050cf764a4c5 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -1,6 +1,7 @@ [workspace] members = ["xtask/", "lib/*", "crates/*"] exclude = ["crates/proc-macro-test/imp"] +resolver = "2" [workspace.package] rust-version = "1.66" @@ -74,5 +75,20 @@ toolchain = { path = "./crates/toolchain", version = "0.0.0" } tt = { path = "./crates/tt", version = "0.0.0" } vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" } vfs = { path = "./crates/vfs", version = "0.0.0" } +line-index = { version = "0.1.0-pre.1", path = "./lib/line-index" } + # non-local crates -smallvec = { version = "1.10.0", features = ["const_new", "union", "const_generics"] } +smallvec = { version = "1.10.0", features = [ + "const_new", + "union", + "const_generics", +] } +smol_str = "0.2.0" +nohash-hasher = "0.2.0" +text-size = "1.1.0" +# the following crates are pinned to prevent us from pulling in syn 2 until all our dependencies have moved +serde = { version = "=1.0.156", features = ["derive"] } +serde_json = "1.0.94" +triomphe = { version = "0.1.8", default-features = false, features = ["std"] } + +rustc_lexer = { version = "0.1.0", package = "ra-ap-rustc_lexer" } diff --git a/src/tools/rust-analyzer/bench_data/glorious_old_parser b/src/tools/rust-analyzer/bench_data/glorious_old_parser index 764893daa12ad..f593f2b2955ac 100644 --- a/src/tools/rust-analyzer/bench_data/glorious_old_parser +++ b/src/tools/rust-analyzer/bench_data/glorious_old_parser @@ -3808,7 +3808,7 @@ impl<'a> Parser<'a> { if self.eat_keyword(keywords::Else) || !cond.returns() { let sp = self.sess.source_map().next_point(lo); let mut err = self.diagnostic() - .struct_span_err(sp, "missing condition for `if` statemement"); + .struct_span_err(sp, "missing condition for `if` statement"); err.span_label(sp, "expected if condition here"); return Err(err) } diff --git a/src/tools/rust-analyzer/crates/base-db/Cargo.toml b/src/tools/rust-analyzer/crates/base-db/Cargo.toml index f6a1075c190a8..6001772c86ee4 100644 --- a/src/tools/rust-analyzer/crates/base-db/Cargo.toml +++ b/src/tools/rust-analyzer/crates/base-db/Cargo.toml @@ -15,6 +15,10 @@ doctest = false salsa = "0.17.0-pre.2" rustc-hash = "1.1.0" +triomphe.workspace = true + +la-arena = { version = "0.3.0", path = "../../lib/la-arena" } + # local deps cfg.workspace = true profile.workspace = true diff --git a/src/tools/rust-analyzer/crates/base-db/src/change.rs b/src/tools/rust-analyzer/crates/base-db/src/change.rs index b57f2345767d0..6a3b36b231280 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/change.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/change.rs @@ -1,19 +1,21 @@ //! Defines a unit of change that can applied to the database to get the next //! state. Changes are transactional. -use std::{fmt, sync::Arc}; +use std::fmt; use salsa::Durability; +use triomphe::Arc; use vfs::FileId; -use crate::{CrateGraph, SourceDatabaseExt, SourceRoot, SourceRootId}; +use crate::{CrateGraph, ProcMacros, SourceDatabaseExt, SourceRoot, SourceRootId}; /// Encapsulate a bunch of raw `.set` calls on the database. #[derive(Default)] pub struct Change { pub roots: Option>, - pub files_changed: Vec<(FileId, Option>)>, + pub files_changed: Vec<(FileId, Option>)>, pub crate_graph: Option, + pub proc_macros: Option, } impl fmt::Debug for Change { @@ -33,7 +35,7 @@ impl fmt::Debug for Change { } impl Change { - pub fn new() -> Change { + pub fn new() -> Self { Change::default() } @@ -41,7 +43,7 @@ impl Change { self.roots = Some(roots); } - pub fn change_file(&mut self, file_id: FileId, new_text: Option>) { + pub fn change_file(&mut self, file_id: FileId, new_text: Option>) { self.files_changed.push((file_id, new_text)) } @@ -49,6 +51,10 @@ impl Change { self.crate_graph = Some(graph); } + pub fn set_proc_macros(&mut self, proc_macros: ProcMacros) { + self.proc_macros = Some(proc_macros); + } + pub fn apply(self, db: &mut dyn SourceDatabaseExt) { let _p = profile::span("RootDatabase::apply_change"); if let Some(roots) = self.roots { @@ -67,11 +73,14 @@ impl Change { let source_root = db.source_root(source_root_id); let durability = durability(&source_root); // XXX: can't actually remove the file, just reset the text - let text = text.unwrap_or_default(); + let text = text.unwrap_or_else(|| Arc::from("")); db.set_file_text_with_durability(file_id, text, durability) } if let Some(crate_graph) = self.crate_graph { - db.set_crate_graph_with_durability(Arc::new(crate_graph), Durability::HIGH) + db.set_crate_graph_with_durability(Arc::new(crate_graph), Durability::HIGH); + } + if let Some(proc_macros) = self.proc_macros { + db.set_proc_macros_with_durability(Arc::new(proc_macros), Durability::HIGH); } } } diff --git a/src/tools/rust-analyzer/crates/base-db/src/fixture.rs b/src/tools/rust-analyzer/crates/base-db/src/fixture.rs index 8a7e9dfadfed2..5b11343173b30 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/fixture.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/fixture.rs @@ -1,24 +1,27 @@ //! A set of high-level utility fixture methods to use in tests. -use std::{mem, str::FromStr, sync::Arc}; +use std::{mem, str::FromStr, sync}; use cfg::CfgOptions; use rustc_hash::FxHashMap; use test_utils::{ - extract_range_or_offset, Fixture, RangeOrOffset, CURSOR_MARKER, ESCAPED_CURSOR_MARKER, + extract_range_or_offset, Fixture, FixtureWithProjectMeta, RangeOrOffset, CURSOR_MARKER, + ESCAPED_CURSOR_MARKER, }; +use triomphe::Arc; use tt::token_id::{Leaf, Subtree, TokenTree}; use vfs::{file_set::FileSet, VfsPath}; use crate::{ input::{CrateName, CrateOrigin, LangCrateOrigin}, Change, CrateDisplayName, CrateGraph, CrateId, Dependency, Edition, Env, FileId, FilePosition, - FileRange, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, SourceDatabaseExt, - SourceRoot, SourceRootId, + FileRange, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacros, ReleaseChannel, + SourceDatabaseExt, SourceRoot, SourceRootId, }; pub const WORKSPACE: SourceRootId = SourceRootId(0); pub trait WithFixture: Default + SourceDatabaseExt + 'static { + #[track_caller] fn with_single_file(ra_fixture: &str) -> (Self, FileId) { let fixture = ChangeFixture::parse(ra_fixture); let mut db = Self::default(); @@ -27,6 +30,7 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static { (db, fixture.files[0]) } + #[track_caller] fn with_many_files(ra_fixture: &str) -> (Self, Vec) { let fixture = ChangeFixture::parse(ra_fixture); let mut db = Self::default(); @@ -35,6 +39,7 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static { (db, fixture.files) } + #[track_caller] fn with_files(ra_fixture: &str) -> Self { let fixture = ChangeFixture::parse(ra_fixture); let mut db = Self::default(); @@ -43,6 +48,7 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static { db } + #[track_caller] fn with_files_extra_proc_macros( ra_fixture: &str, proc_macros: Vec<(String, ProcMacro)>, @@ -54,18 +60,21 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static { db } + #[track_caller] fn with_position(ra_fixture: &str) -> (Self, FilePosition) { let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture); let offset = range_or_offset.expect_offset(); (db, FilePosition { file_id, offset }) } + #[track_caller] fn with_range(ra_fixture: &str) -> (Self, FileRange) { let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture); let range = range_or_offset.expect_range(); (db, FileRange { file_id, range }) } + #[track_caller] fn with_range_or_offset(ra_fixture: &str) -> (Self, FileId, RangeOrOffset) { let fixture = ChangeFixture::parse(ra_fixture); let mut db = Self::default(); @@ -100,9 +109,16 @@ impl ChangeFixture { pub fn parse_with_proc_macros( ra_fixture: &str, - mut proc_macros: Vec<(String, ProcMacro)>, + mut proc_macro_defs: Vec<(String, ProcMacro)>, ) -> ChangeFixture { - let (mini_core, proc_macro_names, fixture) = Fixture::parse(ra_fixture); + let FixtureWithProjectMeta { fixture, mini_core, proc_macro_names, toolchain } = + FixtureWithProjectMeta::parse(ra_fixture); + let toolchain = toolchain + .map(|it| { + ReleaseChannel::from_str(&it) + .unwrap_or_else(|| panic!("unknown release channel found: {it}")) + }) + .unwrap_or(ReleaseChannel::Stable); let mut change = Change::new(); let mut files = Vec::new(); @@ -157,16 +173,16 @@ impl ChangeFixture { meta.edition, Some(crate_name.clone().into()), version, - meta.cfg.clone(), meta.cfg, + Default::default(), meta.env, - Ok(Vec::new()), false, origin, meta.target_data_layout .as_deref() .map(Arc::from) .ok_or_else(|| "target_data_layout unset".into()), + Some(toolchain), ); let prev = crates.insert(crate_name.clone(), crate_id); assert!(prev.is_none()); @@ -182,7 +198,7 @@ impl ChangeFixture { default_target_data_layout = meta.target_data_layout; } - change.change_file(file_id, Some(Arc::new(text))); + change.change_file(file_id, Some(Arc::from(text))); let path = VfsPath::new_virtual_path(meta.path); file_set.insert(file_id, path); files.push(file_id); @@ -197,15 +213,15 @@ impl ChangeFixture { Edition::CURRENT, Some(CrateName::new("test").unwrap().into()), None, - default_cfg.clone(), default_cfg, + Default::default(), Env::default(), - Ok(Vec::new()), false, - CrateOrigin::CratesIo { repo: None, name: None }, + CrateOrigin::Local { repo: None, name: None }, default_target_data_layout .map(|x| x.into()) .ok_or_else(|| "target_data_layout unset".into()), + Some(toolchain), ); } else { for (from, to, prelude) in crate_deps { @@ -232,7 +248,7 @@ impl ChangeFixture { fs.insert(core_file, VfsPath::new_virtual_path("/sysroot/core/lib.rs".to_string())); roots.push(SourceRoot::new_library(fs)); - change.change_file(core_file, Some(Arc::new(mini_core.source_code()))); + change.change_file(core_file, Some(Arc::from(mini_core.source_code()))); let all_crates = crate_graph.crates_in_topological_order(); @@ -241,13 +257,13 @@ impl ChangeFixture { Edition::Edition2021, Some(CrateDisplayName::from_canonical_name("core".to_string())), None, - CfgOptions::default(), - CfgOptions::default(), + Default::default(), + Default::default(), Env::default(), - Ok(Vec::new()), false, CrateOrigin::Lang(LangCrateOrigin::Core), target_layout.clone(), + Some(toolchain), ); for krate in all_crates { @@ -257,12 +273,13 @@ impl ChangeFixture { } } + let mut proc_macros = ProcMacros::default(); if !proc_macro_names.is_empty() { let proc_lib_file = file_id; file_id.0 += 1; - proc_macros.extend(default_test_proc_macros()); - let (proc_macro, source) = filter_test_proc_macros(&proc_macro_names, proc_macros); + proc_macro_defs.extend(default_test_proc_macros()); + let (proc_macro, source) = filter_test_proc_macros(&proc_macro_names, proc_macro_defs); let mut fs = FileSet::default(); fs.insert( proc_lib_file, @@ -270,7 +287,7 @@ impl ChangeFixture { ); roots.push(SourceRoot::new_library(fs)); - change.change_file(proc_lib_file, Some(Arc::new(source))); + change.change_file(proc_lib_file, Some(Arc::from(source))); let all_crates = crate_graph.crates_in_topological_order(); @@ -279,14 +296,15 @@ impl ChangeFixture { Edition::Edition2021, Some(CrateDisplayName::from_canonical_name("proc_macros".to_string())), None, - CfgOptions::default(), - CfgOptions::default(), + Default::default(), + Default::default(), Env::default(), - Ok(proc_macro), true, - CrateOrigin::CratesIo { repo: None, name: None }, + CrateOrigin::Local { repo: None, name: None }, target_layout, + Some(toolchain), ); + proc_macros.insert(proc_macros_crate, Ok(proc_macro)); for krate in all_crates { crate_graph @@ -305,6 +323,7 @@ impl ChangeFixture { roots.push(root); change.set_roots(roots); change.set_crate_graph(crate_graph); + change.set_proc_macros(proc_macros); ChangeFixture { file_position, files, change } } @@ -323,7 +342,7 @@ pub fn identity(_attr: TokenStream, item: TokenStream) -> TokenStream { ProcMacro { name: "identity".into(), kind: crate::ProcMacroKind::Attr, - expander: Arc::new(IdentityProcMacroExpander), + expander: sync::Arc::new(IdentityProcMacroExpander), }, ), ( @@ -337,7 +356,7 @@ pub fn derive_identity(item: TokenStream) -> TokenStream { ProcMacro { name: "DeriveIdentity".into(), kind: crate::ProcMacroKind::CustomDerive, - expander: Arc::new(IdentityProcMacroExpander), + expander: sync::Arc::new(IdentityProcMacroExpander), }, ), ( @@ -351,7 +370,7 @@ pub fn input_replace(attr: TokenStream, _item: TokenStream) -> TokenStream { ProcMacro { name: "input_replace".into(), kind: crate::ProcMacroKind::Attr, - expander: Arc::new(AttributeInputReplaceProcMacroExpander), + expander: sync::Arc::new(AttributeInputReplaceProcMacroExpander), }, ), ( @@ -365,7 +384,7 @@ pub fn mirror(input: TokenStream) -> TokenStream { ProcMacro { name: "mirror".into(), kind: crate::ProcMacroKind::FuncLike, - expander: Arc::new(MirrorProcMacroExpander), + expander: sync::Arc::new(MirrorProcMacroExpander), }, ), ( @@ -379,7 +398,7 @@ pub fn shorten(input: TokenStream) -> TokenStream { ProcMacro { name: "shorten".into(), kind: crate::ProcMacroKind::FuncLike, - expander: Arc::new(ShortenProcMacroExpander), + expander: sync::Arc::new(ShortenProcMacroExpander), }, ), ] @@ -428,7 +447,7 @@ fn parse_crate(crate_str: String) -> (String, CrateOrigin, Option) { let (version, origin) = match b.split_once(':') { Some(("CratesIo", data)) => match data.split_once(',') { Some((version, url)) => { - (version, CrateOrigin::CratesIo { repo: Some(url.to_owned()), name: None }) + (version, CrateOrigin::Local { repo: Some(url.to_owned()), name: None }) } _ => panic!("Bad crates.io parameter: {data}"), }, @@ -436,10 +455,9 @@ fn parse_crate(crate_str: String) -> (String, CrateOrigin, Option) { }; (a.to_owned(), origin, Some(version.to_string())) } else { - let crate_origin = match &*crate_str { - "std" => CrateOrigin::Lang(LangCrateOrigin::Std), - "core" => CrateOrigin::Lang(LangCrateOrigin::Core), - _ => CrateOrigin::CratesIo { repo: None, name: None }, + let crate_origin = match LangCrateOrigin::from(&*crate_str) { + LangCrateOrigin::Other => CrateOrigin::Local { repo: None, name: None }, + origin => CrateOrigin::Lang(origin), }; (crate_str, crate_origin, None) } diff --git a/src/tools/rust-analyzer/crates/base-db/src/input.rs b/src/tools/rust-analyzer/crates/base-db/src/input.rs index 43388e915b5d3..e8d521b42f868 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/input.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/input.rs @@ -6,14 +6,20 @@ //! 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, ops, panic::RefUnwindSafe, str::FromStr, sync::Arc}; +use std::{fmt, mem, ops, panic::RefUnwindSafe, str::FromStr, sync}; use cfg::CfgOptions; -use rustc_hash::FxHashMap; -use stdx::hash::{NoHashHashMap, NoHashHashSet}; +use la_arena::{Arena, Idx}; +use rustc_hash::{FxHashMap, FxHashSet}; use syntax::SmolStr; +use triomphe::Arc; use tt::token_id::Subtree; -use vfs::{file_set::FileSet, AnchoredPath, FileId, VfsPath}; +use vfs::{file_set::FileSet, AbsPathBuf, AnchoredPath, FileId, VfsPath}; + +// Map from crate id to the name of the crate and path of the proc-macro. If the value is `None`, +// then the crate for the proc-macro hasn't been build yet as the build data is missing. +pub type ProcMacroPaths = FxHashMap, AbsPathBuf), String>>; +pub type ProcMacros = FxHashMap; /// Files are grouped into source roots. A source root is a directory on the /// file systems which is watched for changes. Typically it corresponds to a @@ -79,17 +85,22 @@ impl SourceRoot { /// /// `CrateGraph` is `!Serialize` by design, see /// -#[derive(Debug, Clone, Default /* Serialize, Deserialize */)] +#[derive(Clone, Default)] pub struct CrateGraph { - arena: NoHashHashMap, + arena: Arena, } -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct CrateId(pub u32); +impl fmt::Debug for CrateGraph { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_map() + .entries(self.arena.iter().map(|(id, data)| (u32::from(id.into_raw()), data))) + .finish() + } +} -impl stdx::hash::NoHashHashable for CrateId {} +pub type CrateId = Idx; -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct CrateName(SmolStr); impl CrateName { @@ -130,8 +141,12 @@ impl ops::Deref for CrateName { /// Origin of the crates. It is used in emitting monikers. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum CrateOrigin { - /// Crates that are from crates.io official registry, - CratesIo { repo: Option, name: Option }, + /// Crates that are from the rustc workspace + Rustc { name: String }, + /// Crates that are workspace members, + Local { repo: Option, name: Option }, + /// Crates that are non member libraries. + Library { repo: Option, name: String }, /// Crates that are provided by the language, like std, core, proc-macro, ... Lang(LangCrateOrigin), } @@ -173,7 +188,7 @@ impl fmt::Display for LangCrateOrigin { } } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct CrateDisplayName { // The name we use to display various paths (with `_`). crate_name: CrateName, @@ -249,10 +264,36 @@ pub type TargetLayoutLoadResult = Result, Arc>; pub struct ProcMacro { pub name: SmolStr, pub kind: ProcMacroKind, - pub expander: Arc, + pub expander: sync::Arc, } -#[derive(Debug, Clone)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum ReleaseChannel { + Stable, + Beta, + Nightly, +} + +impl ReleaseChannel { + pub fn as_str(self) -> &'static str { + match self { + ReleaseChannel::Stable => "stable", + ReleaseChannel::Beta => "beta", + ReleaseChannel::Nightly => "nightly", + } + } + + pub fn from_str(str: &str) -> Option { + Some(match str { + "" => ReleaseChannel::Stable, + "nightly" => ReleaseChannel::Nightly, + _ if str.starts_with("beta") => ReleaseChannel::Beta, + _ => return None, + }) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] pub struct CrateData { pub root_file_id: FileId, pub edition: Edition, @@ -265,13 +306,15 @@ pub struct CrateData { /// `Dependency` matters), this name should only be used for UI. pub display_name: Option, pub cfg_options: CfgOptions, - pub potential_cfg_options: CfgOptions, - pub target_layout: TargetLayoutLoadResult, + /// The cfg options that could be used by the crate + pub potential_cfg_options: Option, pub env: Env, pub dependencies: Vec, - pub proc_macro: ProcMacroLoadResult, pub origin: CrateOrigin, pub is_proc_macro: bool, + // FIXME: These things should not be per crate! These are more per workspace crate graph level things + pub target_layout: TargetLayoutLoadResult, + pub channel: Option, } #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -290,7 +333,7 @@ pub struct Env { entries: FxHashMap, } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Dependency { pub crate_id: CrateId, pub name: CrateName, @@ -320,12 +363,12 @@ impl CrateGraph { display_name: Option, version: Option, cfg_options: CfgOptions, - potential_cfg_options: CfgOptions, + potential_cfg_options: Option, env: Env, - proc_macro: ProcMacroLoadResult, is_proc_macro: bool, origin: CrateOrigin, target_layout: Result, Arc>, + channel: Option, ) -> CrateId { let data = CrateData { root_file_id, @@ -335,16 +378,44 @@ impl CrateGraph { cfg_options, potential_cfg_options, env, - proc_macro, dependencies: Vec::new(), origin, target_layout, is_proc_macro, + channel, }; - let crate_id = CrateId(self.arena.len() as u32); - let prev = self.arena.insert(crate_id, data); - assert!(prev.is_none()); - crate_id + self.arena.alloc(data) + } + + /// Remove the crate from crate graph. If any crates depend on this crate, the dependency would be replaced + /// with the second input. + pub fn remove_and_replace( + &mut self, + id: CrateId, + replace_with: CrateId, + ) -> Result<(), CyclicDependenciesError> { + for (x, data) in self.arena.iter() { + if x == id { + continue; + } + for edge in &data.dependencies { + if edge.crate_id == id { + self.check_cycle_after_dependency(edge.crate_id, replace_with)?; + } + } + } + // if everything was ok, start to replace + for (x, data) in self.arena.iter_mut() { + if x == id { + continue; + } + for edge in &mut data.dependencies { + if edge.crate_id == id { + edge.crate_id = replace_with; + } + } + } + Ok(()) } pub fn add_dep( @@ -354,17 +425,26 @@ impl CrateGraph { ) -> Result<(), CyclicDependenciesError> { let _p = profile::span("add_dep"); - // Check if adding a dep from `from` to `to` creates a cycle. To figure - // that out, look for a path in the *opposite* direction, from `to` to - // `from`. - if let Some(path) = self.find_path(&mut NoHashHashSet::default(), dep.crate_id, from) { + self.check_cycle_after_dependency(from, dep.crate_id)?; + + self.arena[from].add_dep(dep); + Ok(()) + } + + /// Check if adding a dep from `from` to `to` creates a cycle. To figure + /// that out, look for a path in the *opposite* direction, from `to` to + /// `from`. + fn check_cycle_after_dependency( + &self, + from: CrateId, + to: CrateId, + ) -> Result<(), CyclicDependenciesError> { + if let Some(path) = self.find_path(&mut FxHashSet::default(), to, from) { let path = path.into_iter().map(|it| (it, self[it].display_name.clone())).collect(); let err = CyclicDependenciesError { path }; - assert!(err.from().0 == from && err.to().0 == dep.crate_id); + assert!(err.from().0 == from && err.to().0 == to); return Err(err); } - - self.arena.get_mut(&from).unwrap().add_dep(dep); Ok(()) } @@ -373,14 +453,14 @@ impl CrateGraph { } pub fn iter(&self) -> impl Iterator + '_ { - self.arena.keys().copied() + self.arena.iter().map(|(idx, _)| idx) } /// Returns an iterator over all transitive dependencies of the given crate, /// including the crate itself. pub fn transitive_deps(&self, of: CrateId) -> impl Iterator { let mut worklist = vec![of]; - let mut deps = NoHashHashSet::default(); + let mut deps = FxHashSet::default(); while let Some(krate) = worklist.pop() { if !deps.insert(krate) { @@ -397,11 +477,11 @@ impl CrateGraph { /// including the crate itself. pub fn transitive_rev_deps(&self, of: CrateId) -> impl Iterator { let mut worklist = vec![of]; - let mut rev_deps = NoHashHashSet::default(); + let mut rev_deps = FxHashSet::default(); rev_deps.insert(of); - let mut inverted_graph = NoHashHashMap::<_, Vec<_>>::default(); - self.arena.iter().for_each(|(&krate, data)| { + let mut inverted_graph = FxHashMap::<_, Vec<_>>::default(); + self.arena.iter().for_each(|(krate, data)| { data.dependencies .iter() .for_each(|dep| inverted_graph.entry(dep.crate_id).or_default().push(krate)) @@ -424,9 +504,9 @@ impl CrateGraph { /// come before the crate itself). pub fn crates_in_topological_order(&self) -> Vec { let mut res = Vec::new(); - let mut visited = NoHashHashSet::default(); + let mut visited = FxHashSet::default(); - for krate in self.arena.keys().copied() { + for krate in self.iter() { go(self, &mut visited, &mut res, krate); } @@ -434,7 +514,7 @@ impl CrateGraph { fn go( graph: &CrateGraph, - visited: &mut NoHashHashSet, + visited: &mut FxHashSet, res: &mut Vec, source: CrateId, ) { @@ -450,31 +530,56 @@ impl CrateGraph { // FIXME: this only finds one crate with the given root; we could have multiple pub fn crate_id_for_crate_root(&self, file_id: FileId) -> Option { - let (&crate_id, _) = + let (crate_id, _) = self.arena.iter().find(|(_crate_id, data)| data.root_file_id == file_id)?; Some(crate_id) } + pub fn sort_deps(&mut self) { + self.arena + .iter_mut() + .for_each(|(_, data)| data.dependencies.sort_by_key(|dep| dep.crate_id)); + } + /// Extends this crate graph by adding a complete disjoint second crate - /// graph. + /// graph and adjust the ids in the [`ProcMacroPaths`] accordingly. /// - /// The ids of the crates in the `other` graph are shifted by the return - /// amount. - pub fn extend(&mut self, other: CrateGraph) -> u32 { - let start = self.arena.len() as u32; - self.arena.extend(other.arena.into_iter().map(|(id, mut data)| { - let new_id = id.shift(start); - for dep in &mut data.dependencies { - dep.crate_id = dep.crate_id.shift(start); + /// This will deduplicate the crates of the graph where possible. + /// Note that for deduplication to fully work, `self`'s crate dependencies must be sorted by crate id. + /// If the crate dependencies were sorted, the resulting graph from this `extend` call will also have the crate dependencies sorted. + pub fn extend(&mut self, mut other: CrateGraph, proc_macros: &mut ProcMacroPaths) { + let topo = other.crates_in_topological_order(); + let mut id_map: FxHashMap = FxHashMap::default(); + + for topo in topo { + let crate_data = &mut other.arena[topo]; + crate_data.dependencies.iter_mut().for_each(|dep| dep.crate_id = id_map[&dep.crate_id]); + crate_data.dependencies.sort_by_key(|dep| dep.crate_id); + + let res = self.arena.iter().find_map( + |(id, data)| { + if data == crate_data { + Some(id) + } else { + None + } + }, + ); + if let Some(res) = res { + id_map.insert(topo, res); + } else { + let id = self.arena.alloc(crate_data.clone()); + id_map.insert(topo, id); } - (new_id, data) - })); - start + } + + *proc_macros = + mem::take(proc_macros).into_iter().map(|(id, macros)| (id_map[&id], macros)).collect(); } fn find_path( &self, - visited: &mut NoHashHashSet, + visited: &mut FxHashSet, from: CrateId, to: CrateId, ) -> Option> { @@ -500,14 +605,14 @@ impl CrateGraph { // Work around for https://github.com/rust-lang/rust-analyzer/issues/6038. // As hacky as it gets. pub fn patch_cfg_if(&mut self) -> bool { - let cfg_if = self.hacky_find_crate("cfg_if"); - let std = self.hacky_find_crate("std"); + // we stupidly max by version in an attempt to have all duplicated std's depend on the same cfg_if so that deduplication still works + let cfg_if = + self.hacky_find_crate("cfg_if").max_by_key(|&it| self.arena[it].version.clone()); + let std = self.hacky_find_crate("std").next(); match (cfg_if, std) { (Some(cfg_if), Some(std)) => { - self.arena.get_mut(&cfg_if).unwrap().dependencies.clear(); - self.arena - .get_mut(&std) - .unwrap() + self.arena[cfg_if].dependencies.clear(); + self.arena[std] .dependencies .push(Dependency::new(CrateName::new("cfg_if").unwrap(), cfg_if)); true @@ -516,21 +621,15 @@ impl CrateGraph { } } - fn hacky_find_crate(&self, display_name: &str) -> Option { - self.iter().find(|it| self[*it].display_name.as_deref() == Some(display_name)) + fn hacky_find_crate<'a>(&'a self, display_name: &'a str) -> impl Iterator + 'a { + self.iter().filter(move |it| self[*it].display_name.as_deref() == Some(display_name)) } } impl ops::Index for CrateGraph { type Output = CrateData; fn index(&self, crate_id: CrateId) -> &CrateData { - &self.arena[&crate_id] - } -} - -impl CrateId { - fn shift(self, amount: u32) -> CrateId { - CrateId(self.0 + amount) + &self.arena[crate_id] } } @@ -632,7 +731,7 @@ impl fmt::Display for CyclicDependenciesError { mod tests { use crate::CrateOrigin; - use super::{CfgOptions, CrateGraph, CrateName, Dependency, Edition::Edition2018, Env, FileId}; + use super::{CrateGraph, CrateName, Dependency, Edition::Edition2018, Env, FileId}; #[test] fn detect_cyclic_dependency_indirect() { @@ -642,39 +741,39 @@ mod tests { Edition2018, None, None, - CfgOptions::default(), - CfgOptions::default(), + Default::default(), + Default::default(), Env::default(), - Ok(Vec::new()), false, - CrateOrigin::CratesIo { repo: None, name: None }, + CrateOrigin::Local { repo: None, name: None }, Err("".into()), + None, ); let crate2 = graph.add_crate_root( FileId(2u32), Edition2018, None, None, - CfgOptions::default(), - CfgOptions::default(), + Default::default(), + Default::default(), Env::default(), - Ok(Vec::new()), false, - CrateOrigin::CratesIo { repo: None, name: None }, + CrateOrigin::Local { repo: None, name: None }, Err("".into()), + None, ); let crate3 = graph.add_crate_root( FileId(3u32), Edition2018, None, None, - CfgOptions::default(), - CfgOptions::default(), + Default::default(), + Default::default(), Env::default(), - Ok(Vec::new()), false, - CrateOrigin::CratesIo { repo: None, name: None }, + CrateOrigin::Local { repo: None, name: None }, Err("".into()), + None, ); assert!(graph .add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2)) @@ -695,26 +794,26 @@ mod tests { Edition2018, None, None, - CfgOptions::default(), - CfgOptions::default(), + Default::default(), + Default::default(), Env::default(), - Ok(Vec::new()), false, - CrateOrigin::CratesIo { repo: None, name: None }, + CrateOrigin::Local { repo: None, name: None }, Err("".into()), + None, ); let crate2 = graph.add_crate_root( FileId(2u32), Edition2018, None, None, - CfgOptions::default(), - CfgOptions::default(), + Default::default(), + Default::default(), Env::default(), - Ok(Vec::new()), false, - CrateOrigin::CratesIo { repo: None, name: None }, + CrateOrigin::Local { repo: None, name: None }, Err("".into()), + None, ); assert!(graph .add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2)) @@ -732,39 +831,39 @@ mod tests { Edition2018, None, None, - CfgOptions::default(), - CfgOptions::default(), + Default::default(), + Default::default(), Env::default(), - Ok(Vec::new()), false, - CrateOrigin::CratesIo { repo: None, name: None }, + CrateOrigin::Local { repo: None, name: None }, Err("".into()), + None, ); let crate2 = graph.add_crate_root( FileId(2u32), Edition2018, None, None, - CfgOptions::default(), - CfgOptions::default(), + Default::default(), + Default::default(), Env::default(), - Ok(Vec::new()), false, - CrateOrigin::CratesIo { repo: None, name: None }, + CrateOrigin::Local { repo: None, name: None }, Err("".into()), + None, ); let crate3 = graph.add_crate_root( FileId(3u32), Edition2018, None, None, - CfgOptions::default(), - CfgOptions::default(), + Default::default(), + Default::default(), Env::default(), - Ok(Vec::new()), false, - CrateOrigin::CratesIo { repo: None, name: None }, + CrateOrigin::Local { repo: None, name: None }, Err("".into()), + None, ); assert!(graph .add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2)) @@ -782,26 +881,26 @@ mod tests { Edition2018, None, None, - CfgOptions::default(), - CfgOptions::default(), + Default::default(), + Default::default(), Env::default(), - Ok(Vec::new()), false, - CrateOrigin::CratesIo { repo: None, name: None }, + CrateOrigin::Local { repo: None, name: None }, Err("".into()), + None, ); let crate2 = graph.add_crate_root( FileId(2u32), Edition2018, None, None, - CfgOptions::default(), - CfgOptions::default(), + Default::default(), + Default::default(), Env::default(), - Ok(Vec::new()), false, - CrateOrigin::CratesIo { repo: None, name: None }, + CrateOrigin::Local { repo: None, name: None }, Err("".into()), + None, ); assert!(graph .add_dep( diff --git a/src/tools/rust-analyzer/crates/base-db/src/lib.rs b/src/tools/rust-analyzer/crates/base-db/src/lib.rs index 9720db9d8ace3..af204e44e6ee1 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/lib.rs @@ -6,18 +6,19 @@ mod input; mod change; pub mod fixture; -use std::{panic, sync::Arc}; +use std::panic; -use stdx::hash::NoHashHashSet; +use rustc_hash::FxHashSet; use syntax::{ast, Parse, SourceFile, TextRange, TextSize}; +use triomphe::Arc; pub use crate::{ change::Change, input::{ CrateData, CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, Edition, Env, LangCrateOrigin, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, - ProcMacroId, ProcMacroKind, ProcMacroLoadResult, SourceRoot, SourceRootId, - TargetLayoutLoadResult, + ProcMacroId, ProcMacroKind, ProcMacroLoadResult, ProcMacroPaths, ProcMacros, + ReleaseChannel, SourceRoot, SourceRootId, TargetLayoutLoadResult, }, }; pub use salsa::{self, Cancelled}; @@ -53,13 +54,13 @@ pub struct FileRange { pub range: TextRange, } -pub const DEFAULT_LRU_CAP: usize = 128; +pub const DEFAULT_PARSE_LRU_CAP: usize = 128; pub trait FileLoader { /// Text of the file. - fn file_text(&self, file_id: FileId) -> Arc; + fn file_text(&self, file_id: FileId) -> Arc; fn resolve_path(&self, path: AnchoredPath<'_>) -> Option; - fn relevant_crates(&self, file_id: FileId) -> Arc>; + fn relevant_crates(&self, file_id: FileId) -> Arc>; } /// Database which stores all significant input facts: source code and project @@ -73,6 +74,10 @@ pub trait SourceDatabase: FileLoader + std::fmt::Debug { /// The crate graph. #[salsa::input] fn crate_graph(&self) -> Arc; + + /// The crate graph. + #[salsa::input] + fn proc_macros(&self) -> Arc; } fn parse_query(db: &dyn SourceDatabase, file_id: FileId) -> Parse { @@ -86,7 +91,7 @@ fn parse_query(db: &dyn SourceDatabase, file_id: FileId) -> Parse Arc; + fn file_text(&self, file_id: FileId) -> Arc; /// Path to a file, relative to the root of its source root. /// Source root of the file. #[salsa::input] @@ -95,10 +100,10 @@ pub trait SourceDatabaseExt: SourceDatabase { #[salsa::input] fn source_root(&self, id: SourceRootId) -> Arc; - fn source_root_crates(&self, id: SourceRootId) -> Arc>; + fn source_root_crates(&self, id: SourceRootId) -> Arc>; } -fn source_root_crates(db: &dyn SourceDatabaseExt, id: SourceRootId) -> Arc> { +fn source_root_crates(db: &dyn SourceDatabaseExt, id: SourceRootId) -> Arc> { let graph = db.crate_graph(); let res = graph .iter() @@ -114,7 +119,7 @@ fn source_root_crates(db: &dyn SourceDatabaseExt, id: SourceRootId) -> Arc(pub T); impl FileLoader for FileLoaderDelegate<&'_ T> { - fn file_text(&self, file_id: FileId) -> Arc { + fn file_text(&self, file_id: FileId) -> Arc { SourceDatabaseExt::file_text(self.0, file_id) } fn resolve_path(&self, path: AnchoredPath<'_>) -> Option { @@ -124,7 +129,7 @@ impl FileLoader for FileLoaderDelegate<&'_ T> { source_root.resolve_path(path) } - fn relevant_crates(&self, file_id: FileId) -> Arc> { + fn relevant_crates(&self, file_id: FileId) -> Arc> { let _p = profile::span("relevant_crates"); let source_root = self.0.file_source_root(file_id); self.0.source_root_crates(source_root) diff --git a/src/tools/rust-analyzer/crates/cfg/src/lib.rs b/src/tools/rust-analyzer/crates/cfg/src/lib.rs index 30709c968dacf..495119d5519c3 100644 --- a/src/tools/rust-analyzer/crates/cfg/src/lib.rs +++ b/src/tools/rust-analyzer/crates/cfg/src/lib.rs @@ -86,7 +86,7 @@ impl CfgOptions { } } -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Default, Clone, Debug, PartialEq, Eq)] pub struct CfgDiff { // Invariants: No duplicates, no atom that's both in `enable` and `disable`. enable: Vec, diff --git a/src/tools/rust-analyzer/crates/flycheck/Cargo.toml b/src/tools/rust-analyzer/crates/flycheck/Cargo.toml index 609d18c4eea33..3f6671b1c43dd 100644 --- a/src/tools/rust-analyzer/crates/flycheck/Cargo.toml +++ b/src/tools/rust-analyzer/crates/flycheck/Cargo.toml @@ -16,9 +16,8 @@ crossbeam-channel = "0.5.5" tracing = "0.1.37" cargo_metadata = "0.15.0" rustc-hash = "1.1.0" -serde = { version = "1.0.137", features = ["derive"] } -serde_json = "1.0.86" -jod-thread = "0.1.2" +serde_json.workspace = true +serde.workspace = true command-group = "2.0.1" # local deps diff --git a/src/tools/rust-analyzer/crates/flycheck/src/lib.rs b/src/tools/rust-analyzer/crates/flycheck/src/lib.rs index accb14a51deb9..fbb943ccb99dd 100644 --- a/src/tools/rust-analyzer/crates/flycheck/src/lib.rs +++ b/src/tools/rust-analyzer/crates/flycheck/src/lib.rs @@ -77,7 +77,7 @@ impl fmt::Display for FlycheckConfig { pub struct FlycheckHandle { // XXX: drop order is significant sender: Sender, - _thread: jod_thread::JoinHandle, + _thread: stdx::thread::JoinHandle, id: usize, } @@ -90,7 +90,7 @@ impl FlycheckHandle { ) -> FlycheckHandle { let actor = FlycheckActor::new(id, sender, config, workspace_root); let (sender, receiver) = unbounded::(); - let thread = jod_thread::Builder::new() + let thread = stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker) .name("Flycheck".to_owned()) .spawn(move || actor.run(receiver)) .expect("failed to spawn thread"); @@ -395,7 +395,7 @@ struct CargoHandle { /// The handle to the actual cargo process. As we cannot cancel directly from with /// a read syscall dropping and therefore terminating the process is our best option. child: JodGroupChild, - thread: jod_thread::JoinHandle>, + thread: stdx::thread::JoinHandle>, receiver: Receiver, } @@ -409,7 +409,7 @@ impl CargoHandle { let (sender, receiver) = unbounded(); let actor = CargoActor::new(sender, stdout, stderr); - let thread = jod_thread::Builder::new() + let thread = stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker) .name("CargoHandle".to_owned()) .spawn(move || actor.run()) .expect("failed to spawn thread"); @@ -485,7 +485,7 @@ impl CargoActor { error.push_str(line); error.push('\n'); - return false; + false }; let output = streaming_output( self.stdout, diff --git a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml index 31d4018d2b6ab..83c7051646ed8 100644 --- a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml @@ -14,7 +14,7 @@ doctest = false [dependencies] anymap = "1.0.0-beta.2" arrayvec = "0.7.2" -bitflags = "1.3.2" +bitflags = "2.1.0" cov-mark = "2.0.0-pre.1" # We need to freeze the version of the crate, as the raw-api feature is considered unstable dashmap = { version = "=5.4.0", features = ["raw-api"] } @@ -29,6 +29,7 @@ once_cell = "1.17.0" rustc-hash = "1.1.0" smallvec.workspace = true tracing = "0.1.35" +triomphe.workspace = true rustc_abi = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_abi", default-features = false } rustc_index = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_index", default-features = false } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs index 200072c172ebe..bab3bbc2329a9 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs @@ -1,6 +1,11 @@ //! A higher level attributes based on TokenTree, with also some shortcuts. -use std::{hash::Hash, ops, sync::Arc}; +pub mod builtin; + +#[cfg(test)] +mod tests; + +use std::{hash::Hash, ops}; use base_db::CrateId; use cfg::{CfgExpr, CfgOptions}; @@ -16,14 +21,16 @@ use syntax::{ ast::{self, HasAttrs, IsString}, AstPtr, AstToken, SmolStr, TextRange, TextSize, }; +use triomphe::Arc; use crate::{ db::DefDatabase, item_tree::{AttrOwner, Fields, ItemTreeId, ItemTreeNode}, + lang_item::LangItem, nameres::{ModuleOrigin, ModuleSource}, src::{HasChildSource, HasSource}, - AdtId, AttrDefId, EnumId, GenericParamId, LocalEnumVariantId, LocalFieldId, Lookup, MacroId, - VariantId, + AdtId, AssocItemLoc, AttrDefId, EnumId, GenericParamId, ItemLoc, LocalEnumVariantId, + LocalFieldId, Lookup, MacroId, VariantId, }; /// Holds documentation @@ -88,6 +95,7 @@ impl Attrs { db: &dyn DefDatabase, e: EnumId, ) -> Arc> { + let _p = profile::span("variants_attrs_query"); // FIXME: There should be some proper form of mapping between item tree enum variant ids and hir enum variant ids let mut res = ArenaMap::default(); @@ -114,6 +122,7 @@ impl Attrs { db: &dyn DefDatabase, v: VariantId, ) -> Arc> { + let _p = profile::span("fields_attrs_query"); // FIXME: There should be some proper form of mapping between item tree field ids and hir field ids let mut res = ArenaMap::default(); @@ -175,13 +184,13 @@ impl Attrs { Arc::new(res) } +} +impl Attrs { pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> { AttrQuery { attrs: self, key } } -} -impl Attrs { pub fn cfg(&self) -> Option { let mut cfgs = self.by_key("cfg").tt_values().map(CfgExpr::parse); let first = cfgs.next()?; @@ -193,6 +202,7 @@ impl Attrs { None => Some(first), } } + pub(crate) fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> bool { match self.cfg() { None => true, @@ -204,6 +214,10 @@ impl Attrs { self.by_key("lang").string_value() } + pub fn lang_item(&self) -> Option { + self.by_key("lang").string_value().and_then(|it| LangItem::from_str(it)) + } + pub fn docs(&self) -> Option { let docs = self.by_key("doc").attrs().filter_map(|attr| attr.string_value()); let indent = doc_indent(self); @@ -238,6 +252,14 @@ impl Attrs { }) } + pub fn doc_exprs(&self) -> impl Iterator + '_ { + self.by_key("doc").tt_values().map(DocExpr::parse) + } + + pub fn doc_aliases(&self) -> impl Iterator + '_ { + self.doc_exprs().flat_map(|doc_expr| doc_expr.aliases().to_vec()) + } + pub fn is_proc_macro(&self) -> bool { self.by_key("proc_macro").exists() } @@ -249,10 +271,120 @@ impl Attrs { pub fn is_proc_macro_derive(&self) -> bool { self.by_key("proc_macro_derive").exists() } + + pub fn is_unstable(&self) -> bool { + self.by_key("unstable").exists() + } +} + +use std::slice::Iter as SliceIter; +#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] +pub enum DocAtom { + /// eg. `#[doc(hidden)]` + Flag(SmolStr), + /// eg. `#[doc(alias = "x")]` + /// + /// Note that a key can have multiple values that are all considered "active" at the same time. + /// For example, `#[doc(alias = "x")]` and `#[doc(alias = "y")]`. + KeyValue { key: SmolStr, value: SmolStr }, +} + +// Adapted from `CfgExpr` parsing code +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +// #[cfg_attr(test, derive(derive_arbitrary::Arbitrary))] +pub enum DocExpr { + Invalid, + /// eg. `#[doc(hidden)]`, `#[doc(alias = "x")]` + Atom(DocAtom), + /// eg. `#[doc(alias("x", "y"))]` + Alias(Vec), +} + +impl From for DocExpr { + fn from(atom: DocAtom) -> Self { + DocExpr::Atom(atom) + } +} + +impl DocExpr { + fn parse(tt: &tt::Subtree) -> DocExpr { + next_doc_expr(&mut tt.token_trees.iter()).unwrap_or(DocExpr::Invalid) + } + + pub fn aliases(&self) -> &[SmolStr] { + match self { + DocExpr::Atom(DocAtom::KeyValue { key, value }) if key == "alias" => { + std::slice::from_ref(value) + } + DocExpr::Alias(aliases) => aliases, + _ => &[], + } + } +} + +fn next_doc_expr(it: &mut SliceIter<'_, tt::TokenTree>) -> Option { + let name = match it.next() { + None => return None, + Some(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) => ident.text.clone(), + Some(_) => return Some(DocExpr::Invalid), + }; + + // Peek + let ret = match it.as_slice().first() { + Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) if punct.char == '=' => { + match it.as_slice().get(1) { + Some(tt::TokenTree::Leaf(tt::Leaf::Literal(literal))) => { + it.next(); + it.next(); + // FIXME: escape? raw string? + let value = + SmolStr::new(literal.text.trim_start_matches('"').trim_end_matches('"')); + DocAtom::KeyValue { key: name, value }.into() + } + _ => return Some(DocExpr::Invalid), + } + } + Some(tt::TokenTree::Subtree(subtree)) => { + it.next(); + let subs = parse_comma_sep(subtree); + match name.as_str() { + "alias" => DocExpr::Alias(subs), + _ => DocExpr::Invalid, + } + } + _ => DocAtom::Flag(name).into(), + }; + + // Eat comma separator + if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) = it.as_slice().first() { + if punct.char == ',' { + it.next(); + } + } + Some(ret) +} + +fn parse_comma_sep(subtree: &tt::Subtree) -> Vec { + subtree + .token_trees + .iter() + .filter_map(|tt| match tt { + tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => { + // FIXME: escape? raw string? + Some(SmolStr::new(lit.text.trim_start_matches('"').trim_end_matches('"'))) + } + _ => None, + }) + .collect() } impl AttrsWithOwner { - pub(crate) fn attrs_query(db: &dyn DefDatabase, def: AttrDefId) -> Self { + pub(crate) fn attrs_with_owner(db: &dyn DefDatabase, owner: AttrDefId) -> Self { + Self { attrs: db.attrs(owner), owner } + } + + pub(crate) fn attrs_query(db: &dyn DefDatabase, def: AttrDefId) -> Attrs { + let _p = profile::span("attrs_query"); // FIXME: this should use `Trace` to avoid duplication in `source_map` below let raw_attrs = match def { AttrDefId::ModuleId(module) => { @@ -286,31 +418,29 @@ impl AttrsWithOwner { } } AttrDefId::FieldId(it) => { - return Self { attrs: db.fields_attrs(it.parent)[it.local_id].clone(), owner: def }; + return db.fields_attrs(it.parent)[it.local_id].clone(); } AttrDefId::EnumVariantId(it) => { - return Self { - attrs: db.variants_attrs(it.parent)[it.local_id].clone(), - owner: def, - }; + return db.variants_attrs(it.parent)[it.local_id].clone(); } + // FIXME: DRY this up AttrDefId::AdtId(it) => match it { - AdtId::StructId(it) => attrs_from_item_tree(it.lookup(db).id, db), - AdtId::EnumId(it) => attrs_from_item_tree(it.lookup(db).id, db), - AdtId::UnionId(it) => attrs_from_item_tree(it.lookup(db).id, db), + AdtId::StructId(it) => attrs_from_item_tree_loc(db, it), + AdtId::EnumId(it) => attrs_from_item_tree_loc(db, it), + AdtId::UnionId(it) => attrs_from_item_tree_loc(db, it), }, - AttrDefId::TraitId(it) => attrs_from_item_tree(it.lookup(db).id, db), - AttrDefId::TraitAliasId(it) => attrs_from_item_tree(it.lookup(db).id, db), + AttrDefId::TraitId(it) => attrs_from_item_tree_loc(db, it), + AttrDefId::TraitAliasId(it) => attrs_from_item_tree_loc(db, it), AttrDefId::MacroId(it) => match it { - MacroId::Macro2Id(it) => attrs_from_item_tree(it.lookup(db).id, db), - MacroId::MacroRulesId(it) => attrs_from_item_tree(it.lookup(db).id, db), - MacroId::ProcMacroId(it) => attrs_from_item_tree(it.lookup(db).id, db), + MacroId::Macro2Id(it) => attrs_from_item_tree(db, it.lookup(db).id), + MacroId::MacroRulesId(it) => attrs_from_item_tree(db, it.lookup(db).id), + MacroId::ProcMacroId(it) => attrs_from_item_tree(db, it.lookup(db).id), }, - AttrDefId::ImplId(it) => attrs_from_item_tree(it.lookup(db).id, db), - AttrDefId::ConstId(it) => attrs_from_item_tree(it.lookup(db).id, db), - AttrDefId::StaticId(it) => attrs_from_item_tree(it.lookup(db).id, db), - AttrDefId::FunctionId(it) => attrs_from_item_tree(it.lookup(db).id, db), - AttrDefId::TypeAliasId(it) => attrs_from_item_tree(it.lookup(db).id, db), + AttrDefId::ImplId(it) => attrs_from_item_tree_loc(db, it), + AttrDefId::ConstId(it) => attrs_from_item_tree_assoc(db, it), + AttrDefId::StaticId(it) => attrs_from_item_tree_assoc(db, it), + AttrDefId::FunctionId(it) => attrs_from_item_tree_assoc(db, it), + AttrDefId::TypeAliasId(it) => attrs_from_item_tree_assoc(db, it), AttrDefId::GenericParamId(it) => match it { GenericParamId::ConstParamId(it) => { let src = it.parent().child_source(db); @@ -331,11 +461,11 @@ impl AttrsWithOwner { RawAttrs::from_attrs_owner(db.upcast(), src.with_value(&src.value[it.local_id])) } }, - AttrDefId::ExternBlockId(it) => attrs_from_item_tree(it.lookup(db).id, db), + AttrDefId::ExternBlockId(it) => attrs_from_item_tree_loc(db, it), }; let attrs = raw_attrs.filter(db.upcast(), def.krate(db)); - Self { attrs: Attrs(attrs), owner: def } + Attrs(attrs) } pub fn source_map(&self, db: &dyn DefDatabase) -> AttrSourceMap { @@ -371,7 +501,7 @@ impl AttrsWithOwner { AttrDefId::FieldId(id) => { let map = db.fields_attrs_source_map(id.parent); let file_id = id.parent.file_id(db); - let root = db.parse_or_expand(file_id).unwrap(); + let root = db.parse_or_expand(file_id); let owner = match &map[id.local_id] { Either::Left(it) => ast::AnyHasAttrs::new(it.to_node(&root)), Either::Right(it) => ast::AnyHasAttrs::new(it.to_node(&root)), @@ -379,28 +509,28 @@ impl AttrsWithOwner { InFile::new(file_id, owner) } AttrDefId::AdtId(adt) => match adt { - AdtId::StructId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new), - AdtId::UnionId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new), - AdtId::EnumId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new), + AdtId::StructId(id) => any_has_attrs(db, id), + AdtId::UnionId(id) => any_has_attrs(db, id), + AdtId::EnumId(id) => any_has_attrs(db, id), }, - AttrDefId::FunctionId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new), + AttrDefId::FunctionId(id) => any_has_attrs(db, id), AttrDefId::EnumVariantId(id) => { let map = db.variants_attrs_source_map(id.parent); let file_id = id.parent.lookup(db).id.file_id(); - let root = db.parse_or_expand(file_id).unwrap(); + let root = db.parse_or_expand(file_id); InFile::new(file_id, ast::AnyHasAttrs::new(map[id.local_id].to_node(&root))) } - AttrDefId::StaticId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new), - AttrDefId::ConstId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new), - AttrDefId::TraitId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new), - AttrDefId::TraitAliasId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new), - AttrDefId::TypeAliasId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new), + AttrDefId::StaticId(id) => any_has_attrs(db, id), + AttrDefId::ConstId(id) => any_has_attrs(db, id), + AttrDefId::TraitId(id) => any_has_attrs(db, id), + AttrDefId::TraitAliasId(id) => any_has_attrs(db, id), + AttrDefId::TypeAliasId(id) => any_has_attrs(db, id), AttrDefId::MacroId(id) => match id { - MacroId::Macro2Id(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new), - MacroId::MacroRulesId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new), - MacroId::ProcMacroId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new), + MacroId::Macro2Id(id) => any_has_attrs(db, id), + MacroId::MacroRulesId(id) => any_has_attrs(db, id), + MacroId::ProcMacroId(id) => any_has_attrs(db, id), }, - AttrDefId::ImplId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new), + AttrDefId::ImplId(id) => any_has_attrs(db, id), AttrDefId::GenericParamId(id) => match id { GenericParamId::ConstParamId(id) => id .parent() @@ -415,7 +545,7 @@ impl AttrsWithOwner { .child_source(db) .map(|source| ast::AnyHasAttrs::new(source[id.local_id].clone())), }, - AttrDefId::ExternBlockId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new), + AttrDefId::ExternBlockId(id) => any_has_attrs(db, id), }; AttrSourceMap::new(owner.as_ref().map(|node| node as &dyn HasAttrs)) @@ -635,19 +765,42 @@ impl<'attr> AttrQuery<'attr> { .nth(2); match name { - Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal{ref text, ..}))) => Some(text), + Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal{ ref text, ..}))) => Some(text), _ => None } }) } } -fn attrs_from_item_tree(id: ItemTreeId, db: &dyn DefDatabase) -> RawAttrs { +fn any_has_attrs( + db: &dyn DefDatabase, + id: impl Lookup>, +) -> InFile { + id.lookup(db).source(db).map(ast::AnyHasAttrs::new) +} + +fn attrs_from_item_tree(db: &dyn DefDatabase, id: ItemTreeId) -> RawAttrs { let tree = id.item_tree(db); let mod_item = N::id_to_mod_item(id.value); tree.raw_attrs(mod_item.into()).clone() } +fn attrs_from_item_tree_loc( + db: &dyn DefDatabase, + lookup: impl Lookup>, +) -> RawAttrs { + let id = lookup.lookup(db).id; + attrs_from_item_tree(db, id) +} + +fn attrs_from_item_tree_assoc( + db: &dyn DefDatabase, + lookup: impl Lookup>, +) -> RawAttrs { + let id = lookup.lookup(db).id; + attrs_from_item_tree(db, id) +} + pub(crate) fn variants_attrs_source_map( db: &dyn DefDatabase, def: EnumId, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/builtin_attr.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs similarity index 83% rename from src/tools/rust-analyzer/crates/hir-def/src/builtin_attr.rs rename to src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs index 142b12290194e..cead64a33749f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/builtin_attr.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs @@ -2,7 +2,7 @@ //! //! The actual definitions were copied from rustc's `compiler/rustc_feature/src/builtin_attrs.rs`. //! -//! It was last synchronized with upstream commit c1a2db3372a4d6896744919284f3287650a38ab7. +//! It was last synchronized with upstream commit e29821ff85a2a3000d226f99f62f89464028d5d6. //! //! The macros were adjusted to only expand to the attribute name, since that is all we need to do //! name resolution, and `BUILTIN_ATTRIBUTES` is almost entirely unchanged from the original, to @@ -108,7 +108,7 @@ macro_rules! experimental { }; } -/// "Inert" built-in attributes that have a special meaning to rustc or rustdoc. +/// Attributes that have a special meaning to rustc or rustdoc. #[rustfmt::skip] pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ // ========================================================================== @@ -123,7 +123,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ ungated!(ignore, Normal, template!(Word, NameValueStr: "reason"), WarnFollowing), ungated!( should_panic, Normal, - template!(Word, List: r#"expected = "reason"#, NameValueStr: "reason"), FutureWarnFollowing, + template!(Word, List: r#"expected = "reason""#, NameValueStr: "reason"), FutureWarnFollowing, ), // FIXME(Centril): This can be used on stable but shouldn't. ungated!(reexport_test_harness_main, CrateLevel, template!(NameValueStr: "name"), ErrorFollowing), @@ -142,20 +142,24 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ // Lints: ungated!( - warn, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk + warn, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), + DuplicatesOk, @only_local: true, ), ungated!( - allow, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk + allow, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), + DuplicatesOk, @only_local: true, ), gated!( expect, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk, lint_reasons, experimental!(expect) ), ungated!( - forbid, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk + forbid, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), + DuplicatesOk, @only_local: true, ), ungated!( - deny, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk + deny, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), + DuplicatesOk, @only_local: true, ), ungated!(must_use, Normal, template!(Word, NameValueStr: "reason"), FutureWarnFollowing), gated!( @@ -181,16 +185,17 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ // ABI, linking, symbols, and FFI ungated!( link, Normal, - template!(List: r#"name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...""#), + template!(List: r#"name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...", /*opt*/ import_name_type = "decorated|noprefix|undecorated""#), DuplicatesOk, ), ungated!(link_name, Normal, template!(NameValueStr: "name"), FutureWarnPreceding), ungated!(no_link, Normal, template!(Word), WarnFollowing), - ungated!(repr, Normal, template!(List: "C"), DuplicatesOk), + ungated!(repr, Normal, template!(List: "C"), DuplicatesOk, @only_local: true), ungated!(export_name, Normal, template!(NameValueStr: "name"), FutureWarnPreceding), ungated!(link_section, Normal, template!(NameValueStr: "name"), FutureWarnPreceding), ungated!(no_mangle, Normal, template!(Word), WarnFollowing, @only_local: true), ungated!(used, Normal, template!(Word, List: "compiler|linker"), WarnFollowing, @only_local: true), + ungated!(link_ordinal, Normal, template!(List: "ordinal"), ErrorPreceding), // Limits: ungated!(recursion_limit, CrateLevel, template!(NameValueStr: "N"), FutureWarnFollowing), @@ -201,6 +206,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ ), // Entry point: + gated!(unix_sigpipe, Normal, template!(Word, NameValueStr: "inherit|sig_ign|sig_dfl"), ErrorFollowing, experimental!(unix_sigpipe)), ungated!(start, Normal, template!(Word), WarnFollowing), ungated!(no_start, CrateLevel, template!(Word), WarnFollowing), ungated!(no_main, CrateLevel, template!(Word), WarnFollowing), @@ -222,11 +228,15 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ ungated!(inline, Normal, template!(Word, List: "always|never"), FutureWarnFollowing, @only_local: true), ungated!(cold, Normal, template!(Word), WarnFollowing, @only_local: true), ungated!(no_builtins, CrateLevel, template!(Word), WarnFollowing), - ungated!(target_feature, Normal, template!(List: r#"enable = "name""#), DuplicatesOk), + ungated!( + target_feature, Normal, template!(List: r#"enable = "name""#), + DuplicatesOk, @only_local: true, + ), ungated!(track_caller, Normal, template!(Word), WarnFollowing), + ungated!(instruction_set, Normal, template!(List: "set"), ErrorPreceding), gated!( no_sanitize, Normal, - template!(List: "address, memory, thread"), DuplicatesOk, + template!(List: "address, kcfi, memory, thread"), DuplicatesOk, experimental!(no_sanitize) ), gated!(no_coverage, Normal, template!(Word), WarnFollowing, experimental!(no_coverage)), @@ -235,25 +245,23 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ doc, Normal, template!(List: "hidden|inline|...", NameValueStr: "string"), DuplicatesOk ), + // Debugging + ungated!( + debugger_visualizer, Normal, + template!(List: r#"natvis_file = "...", gdb_script_file = "...""#), DuplicatesOk + ), + // ========================================================================== // Unstable attributes: // ========================================================================== - // RFC #3191: #[debugger_visualizer] support - gated!( - debugger_visualizer, Normal, template!(List: r#"natvis_file = "...", gdb_script_file = "...""#), - DuplicatesOk, experimental!(debugger_visualizer) - ), - // Linking: - gated!(naked, Normal, template!(Word), WarnFollowing, @only_local: true, naked_functions, experimental!(naked)), gated!( - link_ordinal, Normal, template!(List: "ordinal"), ErrorPreceding, raw_dylib, - experimental!(link_ordinal) + naked, Normal, template!(Word), WarnFollowing, @only_local: true, + naked_functions, experimental!(naked) ), // Plugins: - // XXX Modified for use in rust-analyzer // BuiltinAttribute { // name: sym::plugin, // only_local: false, @@ -270,10 +278,6 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ // cfg_fn!(plugin) // ), // }, - BuiltinAttribute { - name: "plugin", - template: template!(List: "name"), - }, // Testing: gated!( @@ -282,7 +286,8 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ ), // RFC #1268 gated!( - marker, Normal, template!(Word), WarnFollowing, marker_trait_attr, experimental!(marker) + marker, Normal, template!(Word), WarnFollowing, @only_local: true, + marker_trait_attr, experimental!(marker) ), gated!( thread_local, Normal, template!(Word), WarnFollowing, @@ -294,21 +299,12 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ optimize, Normal, template!(List: "size|speed"), ErrorPreceding, optimize_attribute, experimental!(optimize), ), - // RFC 2867 - gated!( - instruction_set, Normal, template!(List: "set"), ErrorPreceding, - isa_attribute, experimental!(instruction_set) - ), gated!( ffi_returns_twice, Normal, template!(Word), WarnFollowing, experimental!(ffi_returns_twice) ), gated!(ffi_pure, Normal, template!(Word), WarnFollowing, experimental!(ffi_pure)), gated!(ffi_const, Normal, template!(Word), WarnFollowing, experimental!(ffi_const)), - gated!( - register_attr, CrateLevel, template!(List: "attr1, attr2, ..."), DuplicatesOk, - experimental!(register_attr), - ), gated!( register_tool, CrateLevel, template!(List: "tool1, tool2, ..."), DuplicatesOk, experimental!(register_tool), @@ -321,7 +317,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ // RFC 2632 gated!( const_trait, Normal, template!(Word), WarnFollowing, const_trait_impl, - "`const` is a temporary placeholder for marking a trait that is suitable for `const` \ + "`const_trait` is a temporary placeholder for marking a trait that is suitable for `const` \ `impls` and all default bodies as `const`, which may be removed or renamed in the \ future." ), @@ -331,22 +327,47 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ experimental!(deprecated_safe), ), + // `#[collapse_debuginfo]` + gated!( + collapse_debuginfo, Normal, template!(Word), WarnFollowing, + experimental!(collapse_debuginfo) + ), + + // RFC 2397 + gated!(do_not_recommend, Normal, template!(Word), WarnFollowing, experimental!(do_not_recommend)), + + // `#[cfi_encoding = ""]` + gated!( + cfi_encoding, Normal, template!(NameValueStr: "encoding"), ErrorPreceding, + experimental!(cfi_encoding) + ), + // ========================================================================== // Internal attributes: Stability, deprecation, and unsafe: // ========================================================================== - ungated!(feature, CrateLevel, template!(List: "name1, name2, ..."), DuplicatesOk), + ungated!( + feature, CrateLevel, + template!(List: "name1, name2, ..."), DuplicatesOk, @only_local: true, + ), // DuplicatesOk since it has its own validation ungated!( - stable, Normal, template!(List: r#"feature = "name", since = "version""#), DuplicatesOk, + stable, Normal, + template!(List: r#"feature = "name", since = "version""#), DuplicatesOk, @only_local: true, ), ungated!( unstable, Normal, template!(List: r#"feature = "name", reason = "...", issue = "N""#), DuplicatesOk, ), ungated!(rustc_const_unstable, Normal, template!(List: r#"feature = "name""#), DuplicatesOk), - ungated!(rustc_const_stable, Normal, template!(List: r#"feature = "name""#), DuplicatesOk), - ungated!(rustc_safe_intrinsic, Normal, template!(List: r#"feature = "name""#), DuplicatesOk), + ungated!( + rustc_const_stable, Normal, + template!(List: r#"feature = "name""#), DuplicatesOk, @only_local: true, + ), + ungated!( + rustc_default_body_unstable, Normal, + template!(List: r#"feature = "name", reason = "...", issue = "N""#), DuplicatesOk + ), gated!( allow_internal_unstable, Normal, template!(Word, List: "feat1, feat2, ..."), DuplicatesOk, "allow_internal_unstable side-steps feature gating and stability checks", @@ -360,6 +381,10 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ allow_internal_unsafe, Normal, template!(Word), WarnFollowing, "allow_internal_unsafe side-steps the unsafe_code lint", ), + ungated!(rustc_safe_intrinsic, Normal, template!(Word), DuplicatesOk), + rustc_attr!(rustc_allowed_through_unstable_modules, Normal, template!(Word), WarnFollowing, + "rustc_allowed_through_unstable_modules special cases accidental stabilizations of stable items \ + through unstable paths"), // ========================================================================== // Internal attributes: Type system related: @@ -377,10 +402,9 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_attr!(rustc_allocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL), rustc_attr!(rustc_nounwind, Normal, template!(Word), WarnFollowing, IMPL_DETAIL), - gated!( - alloc_error_handler, Normal, template!(Word), WarnFollowing, - experimental!(alloc_error_handler) - ), + rustc_attr!(rustc_reallocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL), + rustc_attr!(rustc_deallocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL), + rustc_attr!(rustc_allocator_zeroed, Normal, template!(Word), WarnFollowing, IMPL_DETAIL), gated!( default_lib_allocator, Normal, template!(Word), WarnFollowing, allocator_internals, experimental!(default_lib_allocator), @@ -461,6 +485,12 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ // Used by the `rustc::untranslatable_diagnostic` and `rustc::diagnostic_outside_of_impl` lints // to assist in changes to diagnostic APIs. rustc_attr!(rustc_lint_diagnostics, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE), + // Used by the `rustc::bad_opt_access` lint to identify `DebuggingOptions` and `CodegenOptions` + // types (as well as any others in future). + rustc_attr!(rustc_lint_opt_ty, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE), + // Used by the `rustc::bad_opt_access` lint on fields + // types (as well as any others in future). + rustc_attr!(rustc_lint_opt_deny_field_access, Normal, template!(List: "message"), WarnFollowing, INTERNAL_UNSTABLE), // ========================================================================== // Internal attributes, Const related: @@ -504,18 +534,25 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ "language items are subject to change", ), rustc_attr!( - rustc_pass_by_value, Normal, - template!(Word), ErrorFollowing, + rustc_pass_by_value, Normal, template!(Word), ErrorFollowing, "#[rustc_pass_by_value] is used to mark types that must be passed by value instead of reference." ), rustc_attr!( rustc_coherence_is_core, AttributeType::CrateLevel, template!(Word), ErrorFollowing, @only_local: true, "#![rustc_coherence_is_core] allows inherent methods on builtin types, only intended to be used in `core`." ), + rustc_attr!( + rustc_coinductive, AttributeType::Normal, template!(Word), WarnFollowing, @only_local: true, + "#![rustc_coinductive] changes a trait to be coinductive, allowing cycles in the trait solver." + ), rustc_attr!( rustc_allow_incoherent_impl, AttributeType::Normal, template!(Word), ErrorFollowing, @only_local: true, "#[rustc_allow_incoherent_impl] has to be added to all impl items of an incoherent inherent impl." ), + rustc_attr!( + rustc_deny_explicit_impl, AttributeType::Normal, template!(Word), ErrorFollowing, @only_local: false, + "#[rustc_deny_explicit_impl] enforces that a trait can have no user-provided impls" + ), rustc_attr!( rustc_has_incoherent_inherent_impls, AttributeType::Normal, template!(Word), ErrorFollowing, "#[rustc_has_incoherent_inherent_impls] allows the addition of incoherent inherent impls for \ @@ -527,24 +564,20 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ and it is only intended to be used in `alloc`." ), - // modified for r-a - // BuiltinAttribute { - // name: sym::rustc_diagnostic_item, - // // FIXME: This can be `true` once we always use `tcx.is_diagnostic_item`. - // only_local: false, - // type_: Normal, - // template: template!(NameValueStr: "name"), - // duplicates: ErrorFollowing, - // gate: Gated( - // Stability::Unstable, - // sym::rustc_attrs, - // "diagnostic items compiler internal support for linting", - // cfg_fn!(rustc_attrs), - // ), - // }, BuiltinAttribute { + // name: sym::rustc_diagnostic_item, name: "rustc_diagnostic_item", + // FIXME: This can be `true` once we always use `tcx.is_diagnostic_item`. + // only_local: false, + // type_: Normal, template: template!(NameValueStr: "name"), + // duplicates: ErrorFollowing, + // gate: Gated( + // Stability::Unstable, + // sym::rustc_attrs, + // "diagnostic items compiler internal support for linting", + // cfg_fn!(rustc_attrs), + // ), }, gated!( // Used in resolve: @@ -568,7 +601,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ for reserving for `for From for T` impl" ), rustc_attr!( - rustc_test_marker, Normal, template!(Word), WarnFollowing, + rustc_test_marker, Normal, template!(NameValueStr: "name"), WarnFollowing, "the `#[rustc_test_marker]` attribute is used internally to track tests", ), rustc_attr!( @@ -594,11 +627,16 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ definition of a trait, it's currently in experimental form and should be changed before \ being exposed outside of the std" ), + rustc_attr!( + rustc_doc_primitive, Normal, template!(NameValueStr: "primitive name"), ErrorFollowing, + r#"`rustc_doc_primitive` is a rustc internal attribute"#, + ), // ========================================================================== // Internal attributes, Testing: // ========================================================================== + rustc_attr!(TEST, rustc_effective_visibility, Normal, template!(Word), WarnFollowing), rustc_attr!(TEST, rustc_outlives, Normal, template!(Word), WarnFollowing), rustc_attr!(TEST, rustc_capture_analysis, Normal, template!(Word), WarnFollowing), rustc_attr!(TEST, rustc_insignificant_dtor, Normal, template!(Word), WarnFollowing), @@ -639,6 +677,10 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_attr!(TEST, rustc_polymorphize_error, Normal, template!(Word), WarnFollowing), rustc_attr!(TEST, rustc_def_path, Normal, template!(Word), WarnFollowing), rustc_attr!(TEST, rustc_mir, Normal, template!(List: "arg1, arg2, ..."), DuplicatesOk), + gated!( + custom_mir, Normal, template!(List: r#"dialect = "...", phase = "...""#), + ErrorFollowing, "the `#[custom_mir]` attribute is just used for the Rust test suite", + ), rustc_attr!(TEST, rustc_dump_program_clauses, Normal, template!(Word), WarnFollowing), rustc_attr!(TEST, rustc_dump_env_program_clauses, Normal, template!(Word), WarnFollowing), rustc_attr!(TEST, rustc_object_lifetime_default, Normal, template!(Word), WarnFollowing), diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attr/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr/tests.rs new file mode 100644 index 0000000000000..e4c8d446af7bd --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-def/src/attr/tests.rs @@ -0,0 +1,40 @@ +//! This module contains tests for doc-expression parsing. +//! Currently, it tests `#[doc(hidden)]` and `#[doc(alias)]`. + +use mbe::syntax_node_to_token_tree; +use syntax::{ast, AstNode}; + +use crate::attr::{DocAtom, DocExpr}; + +fn assert_parse_result(input: &str, expected: DocExpr) { + let (tt, _) = { + let source_file = ast::SourceFile::parse(input).ok().unwrap(); + let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); + syntax_node_to_token_tree(tt.syntax()) + }; + let cfg = DocExpr::parse(&tt); + assert_eq!(cfg, expected); +} + +#[test] +fn test_doc_expr_parser() { + assert_parse_result("#![doc(hidden)]", DocAtom::Flag("hidden".into()).into()); + + assert_parse_result( + r#"#![doc(alias = "foo")]"#, + DocAtom::KeyValue { key: "alias".into(), value: "foo".into() }.into(), + ); + + assert_parse_result(r#"#![doc(alias("foo"))]"#, DocExpr::Alias(["foo".into()].into())); + assert_parse_result( + r#"#![doc(alias("foo", "bar", "baz"))]"#, + DocExpr::Alias(["foo".into(), "bar".into(), "baz".into()].into()), + ); + + assert_parse_result( + r#" + #[doc(alias("Bar", "Qux"))] + struct Foo;"#, + DocExpr::Alias(["Bar".into(), "Qux".into()].into()), + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/body.rs index b70e658efd79c..36626ed1a9b1f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body.rs @@ -6,267 +6,30 @@ mod tests; pub mod scope; mod pretty; -use std::{ops::Index, sync::Arc}; +use std::ops::Index; use base_db::CrateId; use cfg::{CfgExpr, CfgOptions}; -use drop_bomb::DropBomb; use either::Either; -use hir_expand::{ - attrs::RawAttrs, hygiene::Hygiene, ExpandError, ExpandResult, HirFileId, InFile, MacroCallId, -}; +use hir_expand::{name::Name, HirFileId, InFile}; use la_arena::{Arena, ArenaMap}; -use limit::Limit; use profile::Count; use rustc_hash::FxHashMap; -use syntax::{ast, AstPtr, SyntaxNode, SyntaxNodePtr}; +use syntax::{ast, AstPtr, SyntaxNodePtr}; +use triomphe::Arc; use crate::{ - attr::Attrs, db::DefDatabase, - expr::{ + expander::Expander, + hir::{ dummy_expr_id, Binding, BindingId, Expr, ExprId, Label, LabelId, Pat, PatId, RecordFieldPat, }, - item_scope::BuiltinShadowMode, - macro_id_to_def_id, nameres::DefMap, path::{ModPath, Path}, src::{HasChildSource, HasSource}, - AsMacroCall, BlockId, DefWithBodyId, HasModule, LocalModuleId, Lookup, MacroId, ModuleId, - UnresolvedMacro, + BlockId, DefWithBodyId, HasModule, Lookup, }; -pub use lower::LowerCtx; - -/// A subset of Expander that only deals with cfg attributes. We only need it to -/// avoid cyclic queries in crate def map during enum processing. -#[derive(Debug)] -pub(crate) struct CfgExpander { - cfg_options: CfgOptions, - hygiene: Hygiene, - krate: CrateId, -} - -#[derive(Debug)] -pub struct Expander { - cfg_expander: CfgExpander, - def_map: Arc, - current_file_id: HirFileId, - module: LocalModuleId, - /// `recursion_depth == usize::MAX` indicates that the recursion limit has been reached. - recursion_depth: usize, -} - -impl CfgExpander { - pub(crate) fn new( - db: &dyn DefDatabase, - current_file_id: HirFileId, - krate: CrateId, - ) -> CfgExpander { - let hygiene = Hygiene::new(db.upcast(), current_file_id); - let cfg_options = db.crate_graph()[krate].cfg_options.clone(); - CfgExpander { cfg_options, hygiene, krate } - } - - pub(crate) fn parse_attrs(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> Attrs { - Attrs::filter(db, self.krate, RawAttrs::new(db.upcast(), owner, &self.hygiene)) - } - - pub(crate) fn is_cfg_enabled(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> bool { - let attrs = self.parse_attrs(db, owner); - attrs.is_cfg_enabled(&self.cfg_options) - } -} - -impl Expander { - pub fn new(db: &dyn DefDatabase, current_file_id: HirFileId, module: ModuleId) -> Expander { - let cfg_expander = CfgExpander::new(db, current_file_id, module.krate); - let def_map = module.def_map(db); - Expander { - cfg_expander, - def_map, - current_file_id, - module: module.local_id, - recursion_depth: 0, - } - } - - pub fn enter_expand( - &mut self, - db: &dyn DefDatabase, - macro_call: ast::MacroCall, - ) -> Result>, UnresolvedMacro> { - let mut unresolved_macro_err = None; - - let result = self.within_limit(db, |this| { - let macro_call = InFile::new(this.current_file_id, ¯o_call); - - let resolver = - |path| this.resolve_path_as_macro(db, &path).map(|it| macro_id_to_def_id(db, it)); - - let mut err = None; - let call_id = match macro_call.as_call_id_with_errors( - db, - this.def_map.krate(), - resolver, - &mut |e| { - err.get_or_insert(e); - }, - ) { - Ok(call_id) => call_id, - Err(resolve_err) => { - unresolved_macro_err = Some(resolve_err); - return ExpandResult { value: None, err: None }; - } - }; - ExpandResult { value: call_id.ok(), err } - }); - - if let Some(err) = unresolved_macro_err { - Err(err) - } else { - Ok(result) - } - } - - pub fn enter_expand_id( - &mut self, - db: &dyn DefDatabase, - call_id: MacroCallId, - ) -> ExpandResult> { - self.within_limit(db, |_this| ExpandResult::ok(Some(call_id))) - } - - fn enter_expand_inner( - db: &dyn DefDatabase, - call_id: MacroCallId, - mut err: Option, - ) -> ExpandResult> { - if err.is_none() { - err = db.macro_expand_error(call_id); - } - - let file_id = call_id.as_file(); - - let raw_node = match db.parse_or_expand(file_id) { - Some(it) => it, - None => { - // Only `None` if the macro expansion produced no usable AST. - if err.is_none() { - tracing::warn!("no error despite `parse_or_expand` failing"); - } - - return ExpandResult::only_err(err.unwrap_or_else(|| { - ExpandError::Other("failed to parse macro invocation".into()) - })); - } - }; - - ExpandResult { value: Some((file_id, raw_node)), err } - } - - pub fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) { - self.cfg_expander.hygiene = Hygiene::new(db.upcast(), mark.file_id); - self.current_file_id = mark.file_id; - if self.recursion_depth == usize::MAX { - // Recursion limit has been reached somewhere in the macro expansion tree. Reset the - // depth only when we get out of the tree. - if !self.current_file_id.is_macro() { - self.recursion_depth = 0; - } - } else { - self.recursion_depth -= 1; - } - mark.bomb.defuse(); - } - - pub(crate) fn to_source(&self, value: T) -> InFile { - InFile { file_id: self.current_file_id, value } - } - - pub(crate) fn parse_attrs(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> Attrs { - self.cfg_expander.parse_attrs(db, owner) - } - - pub(crate) fn cfg_options(&self) -> &CfgOptions { - &self.cfg_expander.cfg_options - } - - pub fn current_file_id(&self) -> HirFileId { - self.current_file_id - } - - fn parse_path(&mut self, db: &dyn DefDatabase, path: ast::Path) -> Option { - let ctx = LowerCtx::with_hygiene(db, &self.cfg_expander.hygiene); - Path::from_src(path, &ctx) - } - - fn resolve_path_as_macro(&self, db: &dyn DefDatabase, path: &ModPath) -> Option { - self.def_map.resolve_path(db, self.module, path, BuiltinShadowMode::Other).0.take_macros() - } - - fn recursion_limit(&self, db: &dyn DefDatabase) -> Limit { - let limit = db.crate_limits(self.cfg_expander.krate).recursion_limit as _; - - #[cfg(not(test))] - return Limit::new(limit); - - // Without this, `body::tests::your_stack_belongs_to_me` stack-overflows in debug - #[cfg(test)] - return Limit::new(std::cmp::min(32, limit)); - } - - fn within_limit( - &mut self, - db: &dyn DefDatabase, - op: F, - ) -> ExpandResult> - where - F: FnOnce(&mut Self) -> ExpandResult>, - { - if self.recursion_depth == usize::MAX { - // Recursion limit has been reached somewhere in the macro expansion tree. We should - // stop expanding other macro calls in this tree, or else this may result in - // exponential number of macro expansions, leading to a hang. - // - // The overflow error should have been reported when it occurred (see the next branch), - // so don't return overflow error here to avoid diagnostics duplication. - cov_mark::hit!(overflow_but_not_me); - return ExpandResult::only_err(ExpandError::RecursionOverflowPosioned); - } else if self.recursion_limit(db).check(self.recursion_depth + 1).is_err() { - self.recursion_depth = usize::MAX; - cov_mark::hit!(your_stack_belongs_to_me); - return ExpandResult::only_err(ExpandError::Other( - "reached recursion limit during macro expansion".into(), - )); - } - - let ExpandResult { value, err } = op(self); - let Some(call_id) = value else { - return ExpandResult { value: None, err }; - }; - - Self::enter_expand_inner(db, call_id, err).map(|value| { - value.and_then(|(new_file_id, node)| { - let node = T::cast(node)?; - - self.recursion_depth += 1; - self.cfg_expander.hygiene = Hygiene::new(db.upcast(), new_file_id); - let old_file_id = std::mem::replace(&mut self.current_file_id, new_file_id); - let mark = - Mark { file_id: old_file_id, bomb: DropBomb::new("expansion mark dropped") }; - Some((mark, node)) - }) - }) - } -} - -#[derive(Debug)] -pub struct Mark { - file_id: HirFileId, - bomb: DropBomb, -} - /// The body of an item (function, const etc.). #[derive(Debug, Eq, PartialEq)] pub struct Body { @@ -343,6 +106,8 @@ pub enum BodyDiagnostic { MacroError { node: InFile>, message: String }, UnresolvedProcMacro { node: InFile>, krate: CrateId }, UnresolvedMacroCall { node: InFile>, path: ModPath }, + UnreachableLabel { node: InFile>, name: Name }, + UndeclaredLabel { node: InFile>, name: Name }, } impl Body { @@ -353,45 +118,54 @@ impl Body { let _p = profile::span("body_with_source_map_query"); let mut params = None; - let (file_id, module, body) = match def { - DefWithBodyId::FunctionId(f) => { - let f = f.lookup(db); - let src = f.source(db); - params = src.value.param_list().map(|param_list| { - let item_tree = f.id.item_tree(db); - let func = &item_tree[f.id.value]; - let krate = f.container.module(db).krate; - let crate_graph = db.crate_graph(); + let (file_id, module, body, is_async_fn) = { + match def { + DefWithBodyId::FunctionId(f) => { + let data = db.function_data(f); + let f = f.lookup(db); + let src = f.source(db); + params = src.value.param_list().map(|param_list| { + let item_tree = f.id.item_tree(db); + let func = &item_tree[f.id.value]; + let krate = f.container.module(db).krate; + let crate_graph = db.crate_graph(); + ( + param_list, + func.params.clone().map(move |param| { + item_tree + .attrs(db, krate, param.into()) + .is_cfg_enabled(&crate_graph[krate].cfg_options) + }), + ) + }); ( - param_list, - func.params.clone().map(move |param| { - item_tree - .attrs(db, krate, param.into()) - .is_cfg_enabled(&crate_graph[krate].cfg_options) - }), + src.file_id, + f.module(db), + src.value.body().map(ast::Expr::from), + data.has_async_kw(), ) - }); - (src.file_id, f.module(db), src.value.body().map(ast::Expr::from)) - } - DefWithBodyId::ConstId(c) => { - let c = c.lookup(db); - let src = c.source(db); - (src.file_id, c.module(db), src.value.body()) - } - DefWithBodyId::StaticId(s) => { - let s = s.lookup(db); - let src = s.source(db); - (src.file_id, s.module(db), src.value.body()) - } - DefWithBodyId::VariantId(v) => { - let e = v.parent.lookup(db); - let src = v.parent.child_source(db); - let variant = &src.value[v.local_id]; - (src.file_id, e.container, variant.expr()) + } + DefWithBodyId::ConstId(c) => { + let c = c.lookup(db); + let src = c.source(db); + (src.file_id, c.module(db), src.value.body(), false) + } + DefWithBodyId::StaticId(s) => { + let s = s.lookup(db); + let src = s.source(db); + (src.file_id, s.module(db), src.value.body(), false) + } + DefWithBodyId::VariantId(v) => { + let e = v.parent.lookup(db); + let src = v.parent.child_source(db); + let variant = &src.value[v.local_id]; + (src.file_id, e.container, variant.expr(), false) + } } }; let expander = Expander::new(db, file_id, module); - let (mut body, source_map) = Body::new(db, expander, params, body); + let (mut body, source_map) = + Body::new(db, def, expander, params, body, module.krate, is_async_fn); body.shrink_to_fit(); (Arc::new(body), Arc::new(source_map)) @@ -406,22 +180,32 @@ impl Body { &'a self, db: &'a dyn DefDatabase, ) -> impl Iterator)> + '_ { - self.block_scopes - .iter() - .map(move |&block| (block, db.block_def_map(block).expect("block ID without DefMap"))) + self.block_scopes.iter().map(move |&block| (block, db.block_def_map(block))) } pub fn pretty_print(&self, db: &dyn DefDatabase, owner: DefWithBodyId) -> String { pretty::print_body_hir(db, self, owner) } + pub fn pretty_print_expr( + &self, + db: &dyn DefDatabase, + owner: DefWithBodyId, + expr: ExprId, + ) -> String { + pretty::print_expr_hir(db, self, owner, expr) + } + fn new( db: &dyn DefDatabase, + owner: DefWithBodyId, expander: Expander, params: Option<(ast::ParamList, impl Iterator)>, body: Option, + krate: CrateId, + is_async_fn: bool, ) -> (Body, BodySourceMap) { - lower::lower(db, expander, params, body) + lower::lower(db, owner, expander, params, body, krate, is_async_fn) } fn shrink_to_fit(&mut self) { @@ -437,15 +221,14 @@ impl Body { pub fn walk_bindings_in_pat(&self, pat_id: PatId, mut f: impl FnMut(BindingId)) { self.walk_pats(pat_id, &mut |pat| { - if let Pat::Bind { id, .. } = pat { + if let Pat::Bind { id, .. } = &self[pat] { f(*id); } }); } - pub fn walk_pats(&self, pat_id: PatId, f: &mut impl FnMut(&Pat)) { + pub fn walk_pats_shallow(&self, pat_id: PatId, mut f: impl FnMut(PatId)) { let pat = &self[pat_id]; - f(pat); match pat { Pat::Range { .. } | Pat::Lit(..) @@ -455,23 +238,28 @@ impl Body { | Pat::Missing => {} &Pat::Bind { subpat, .. } => { if let Some(subpat) = subpat { - self.walk_pats(subpat, f); + f(subpat); } } Pat::Or(args) | Pat::Tuple { args, .. } | Pat::TupleStruct { args, .. } => { - args.iter().copied().for_each(|p| self.walk_pats(p, f)); + args.iter().copied().for_each(|p| f(p)); } - Pat::Ref { pat, .. } => self.walk_pats(*pat, f), + Pat::Ref { pat, .. } => f(*pat), Pat::Slice { prefix, slice, suffix } => { let total_iter = prefix.iter().chain(slice.iter()).chain(suffix.iter()); - total_iter.copied().for_each(|p| self.walk_pats(p, f)); + total_iter.copied().for_each(|p| f(p)); } Pat::Record { args, .. } => { - args.iter().for_each(|RecordFieldPat { pat, .. }| self.walk_pats(*pat, f)); + args.iter().for_each(|RecordFieldPat { pat, .. }| f(*pat)); } - Pat::Box { inner } => self.walk_pats(*inner, f), + Pat::Box { inner } => f(*inner), } } + + pub fn walk_pats(&self, pat_id: PatId, f: &mut impl FnMut(PatId)) { + f(pat_id); + self.walk_pats_shallow(pat_id, |p| self.walk_pats(p, f)); + } } impl Default for Body { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs index fedaf39559858..7b88e525bf184 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs @@ -1,88 +1,62 @@ //! Transforms `ast::Expr` into an equivalent `hir_def::expr::Expr` //! representation. -use std::{mem, sync::Arc}; +use std::mem; +use base_db::CrateId; use either::Either; use hir_expand::{ ast_id_map::AstIdMap, - hygiene::Hygiene, name::{name, AsName, Name}, - AstId, ExpandError, HirFileId, InFile, + AstId, ExpandError, InFile, }; use intern::Interned; use la_arena::Arena; -use once_cell::unsync::OnceCell; use profile::Count; use rustc_hash::FxHashMap; use smallvec::SmallVec; use syntax::{ ast::{ - self, ArrayExprKind, AstChildren, HasArgList, HasLoopBody, HasName, LiteralKind, + self, ArrayExprKind, AstChildren, BlockExpr, HasArgList, HasAttrs, HasLoopBody, HasName, SlicePatComponents, }, AstNode, AstPtr, SyntaxNodePtr, }; +use triomphe::Arc; use crate::{ - adt::StructKind, - body::{Body, BodySourceMap, Expander, ExprPtr, LabelPtr, LabelSource, PatPtr}, - body::{BodyDiagnostic, ExprSource, PatSource}, - builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint}, + body::{Body, BodyDiagnostic, BodySourceMap, ExprPtr, LabelPtr, PatPtr}, + data::adt::StructKind, db::DefDatabase, - expr::{ - dummy_expr_id, Array, Binding, BindingAnnotation, BindingId, ClosureKind, Expr, ExprId, - FloatTypeWrapper, Label, LabelId, Literal, MatchArm, Movability, Pat, PatId, - RecordFieldPat, RecordLitField, Statement, + expander::Expander, + hir::{ + dummy_expr_id, Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, + ClosureKind, Expr, ExprId, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability, + Pat, PatId, RecordFieldPat, RecordLitField, Statement, }, item_scope::BuiltinShadowMode, + lang_item::LangItem, + lower::LowerCtx, + nameres::{DefMap, MacroSubNs}, path::{GenericArgs, Path}, type_ref::{Mutability, Rawness, TypeRef}, - AdtId, BlockId, BlockLoc, ModuleDefId, UnresolvedMacro, + AdtId, BlockId, BlockLoc, DefWithBodyId, ModuleDefId, UnresolvedMacro, }; -pub struct LowerCtx<'a> { - pub db: &'a dyn DefDatabase, - hygiene: Hygiene, - ast_id_map: Option<(HirFileId, OnceCell>)>, -} - -impl<'a> LowerCtx<'a> { - pub fn new(db: &'a dyn DefDatabase, file_id: HirFileId) -> Self { - LowerCtx { - db, - hygiene: Hygiene::new(db.upcast(), file_id), - ast_id_map: Some((file_id, OnceCell::new())), - } - } - - pub fn with_hygiene(db: &'a dyn DefDatabase, hygiene: &Hygiene) -> Self { - LowerCtx { db, hygiene: hygiene.clone(), ast_id_map: None } - } - - pub(crate) fn hygiene(&self) -> &Hygiene { - &self.hygiene - } - - pub(crate) fn lower_path(&self, ast: ast::Path) -> Option { - Path::from_src(ast, self) - } - - pub(crate) fn ast_id(&self, item: &N) -> Option> { - let &(file_id, ref ast_id_map) = self.ast_id_map.as_ref()?; - let ast_id_map = ast_id_map.get_or_init(|| self.db.ast_id_map(file_id)); - Some(InFile::new(file_id, ast_id_map.ast_id(item))) - } -} - pub(super) fn lower( db: &dyn DefDatabase, + owner: DefWithBodyId, expander: Expander, params: Option<(ast::ParamList, impl Iterator)>, body: Option, + krate: CrateId, + is_async_fn: bool, ) -> (Body, BodySourceMap) { ExprCollector { db, + owner, + krate, + def_map: expander.module.def_map(db), source_map: BodySourceMap::default(), ast_id_map: db.ast_id_map(expander.current_file_id), body: Body { @@ -96,25 +70,79 @@ pub(super) fn lower( _c: Count::new(), }, expander, + current_try_block_label: None, is_lowering_assignee_expr: false, is_lowering_generator: false, + label_ribs: Vec::new(), + current_binding_owner: None, } - .collect(params, body) + .collect(params, body, is_async_fn) } struct ExprCollector<'a> { db: &'a dyn DefDatabase, expander: Expander, + owner: DefWithBodyId, + def_map: Arc, ast_id_map: Arc, + krate: CrateId, body: Body, source_map: BodySourceMap, + is_lowering_assignee_expr: bool, is_lowering_generator: bool, + + current_try_block_label: Option, + // points to the expression that a try expression will target (replaces current_try_block_label) + // catch_scope: Option, + // points to the expression that an unlabeled control flow will target + // loop_scope: Option, + // needed to diagnose non label control flow in while conditions + // is_in_loop_condition: bool, + + // resolution + label_ribs: Vec, + current_binding_owner: Option, +} + +#[derive(Clone, Debug)] +struct LabelRib { + kind: RibKind, + // Once we handle macro hygiene this will need to be a map + label: Option<(Name, LabelId)>, +} + +impl LabelRib { + fn new(kind: RibKind) -> Self { + LabelRib { kind, label: None } + } + fn new_normal(label: (Name, LabelId)) -> Self { + LabelRib { kind: RibKind::Normal, label: Some(label) } + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +enum RibKind { + Normal, + Closure, + Constant, +} + +impl RibKind { + /// This rib forbids referring to labels defined in upwards ribs. + fn is_label_barrier(self) -> bool { + match self { + RibKind::Normal => false, + RibKind::Closure | RibKind::Constant => true, + } + } } #[derive(Debug, Default)] struct BindingList { map: FxHashMap, + is_used: FxHashMap, + reject_new: bool, } impl BindingList { @@ -124,7 +152,27 @@ impl BindingList { name: Name, mode: BindingAnnotation, ) -> BindingId { - *self.map.entry(name).or_insert_with_key(|n| ec.alloc_binding(n.clone(), mode)) + let id = *self.map.entry(name).or_insert_with_key(|n| ec.alloc_binding(n.clone(), mode)); + if ec.body.bindings[id].mode != mode { + ec.body.bindings[id].problems = Some(BindingProblems::BoundInconsistently); + } + self.check_is_used(ec, id); + id + } + + fn check_is_used(&mut self, ec: &mut ExprCollector<'_>, id: BindingId) { + match self.is_used.get(&id) { + None => { + if self.reject_new { + ec.body.bindings[id].problems = Some(BindingProblems::NotBoundAcrossAll); + } + } + Some(true) => { + ec.body.bindings[id].problems = Some(BindingProblems::BoundMoreThanOnce); + } + Some(false) => {} + } + self.is_used.insert(id, true); } } @@ -133,6 +181,7 @@ impl ExprCollector<'_> { mut self, param_list: Option<(ast::ParamList, impl Iterator)>, body: Option, + is_async_fn: bool, ) -> (Body, BodySourceMap) { if let Some((param_list, mut attr_enabled)) = param_list { if let Some(self_param) = @@ -152,72 +201,35 @@ impl ExprCollector<'_> { self.body.params.push(param_pat); } - for pat in param_list - .params() - .zip(attr_enabled) - .filter_map(|(param, enabled)| param.pat().filter(|_| enabled)) + for (param, _) in param_list.params().zip(attr_enabled).filter(|(_, enabled)| *enabled) { - let param_pat = self.collect_pat(pat); + let param_pat = self.collect_pat_top(param.pat()); self.body.params.push(param_pat); } }; + self.body.body_expr = self.with_label_rib(RibKind::Closure, |this| { + if is_async_fn { + match body { + Some(e) => { + let expr = this.collect_expr(e); + this.alloc_expr_desugared(Expr::Async { + id: None, + statements: Box::new([]), + tail: Some(expr), + }) + } + None => this.missing_expr(), + } + } else { + this.collect_expr_opt(body) + } + }); - self.body.body_expr = self.collect_expr_opt(body); (self.body, self.source_map) } fn ctx(&self) -> LowerCtx<'_> { - LowerCtx::new(self.db, self.expander.current_file_id) - } - - fn alloc_expr(&mut self, expr: Expr, ptr: ExprPtr) -> ExprId { - let src = self.expander.to_source(ptr); - let id = self.make_expr(expr, src.clone()); - self.source_map.expr_map.insert(src, id); - id - } - // desugared exprs don't have ptr, that's wrong and should be fixed - // somehow. - fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId { - self.body.exprs.alloc(expr) - } - fn missing_expr(&mut self) -> ExprId { - self.alloc_expr_desugared(Expr::Missing) - } - fn make_expr(&mut self, expr: Expr, src: ExprSource) -> ExprId { - let id = self.body.exprs.alloc(expr); - self.source_map.expr_map_back.insert(id, src); - id - } - - fn alloc_binding(&mut self, name: Name, mode: BindingAnnotation) -> BindingId { - self.body.bindings.alloc(Binding { name, mode, definitions: SmallVec::new() }) - } - fn alloc_pat(&mut self, pat: Pat, ptr: PatPtr) -> PatId { - let src = self.expander.to_source(ptr); - let id = self.make_pat(pat, src.clone()); - self.source_map.pat_map.insert(src, id); - id - } - fn missing_pat(&mut self) -> PatId { - self.body.pats.alloc(Pat::Missing) - } - fn make_pat(&mut self, pat: Pat, src: PatSource) -> PatId { - let id = self.body.pats.alloc(pat); - self.source_map.pat_map_back.insert(id, src); - id - } - - fn alloc_label(&mut self, label: Label, ptr: LabelPtr) -> LabelId { - let src = self.expander.to_source(ptr); - let id = self.make_label(label, src.clone()); - self.source_map.label_map.insert(src, id); - id - } - fn make_label(&mut self, label: Label, src: LabelSource) -> LabelId { - let id = self.body.labels.alloc(label); - self.source_map.label_map_back.insert(id, src); - id + self.expander.ctx(self.db) } fn collect_expr(&mut self, expr: ast::Expr) -> ExprId { @@ -229,6 +241,7 @@ impl ExprCollector<'_> { let syntax_ptr = AstPtr::new(&expr); self.check_cfg(&expr)?; + // FIXME: Move some of these arms out into separate methods for clarity Some(match expr { ast::Expr::IfExpr(e) => { let then_branch = self.collect_block_opt(e.then_branch()); @@ -246,18 +259,12 @@ impl ExprCollector<'_> { self.alloc_expr(Expr::If { condition, then_branch, else_branch }, syntax_ptr) } ast::Expr::LetExpr(e) => { - let pat = self.collect_pat_opt(e.pat()); + let pat = self.collect_pat_top(e.pat()); let expr = self.collect_expr_opt(e.expr()); self.alloc_expr(Expr::Let { pat, expr }, syntax_ptr) } ast::Expr::BlockExpr(e) => match e.modifier() { - Some(ast::BlockModifier::Try(_)) => { - self.collect_block_(e, |id, statements, tail| Expr::TryBlock { - id, - statements, - tail, - }) - } + Some(ast::BlockModifier::Try(_)) => self.desugar_try_block(e), Some(ast::BlockModifier::Unsafe(_)) => { self.collect_block_(e, |id, statements, tail| Expr::Unsafe { id, @@ -267,50 +274,74 @@ impl ExprCollector<'_> { } Some(ast::BlockModifier::Label(label)) => { let label = self.collect_label(label); - self.collect_block_(e, |id, statements, tail| Expr::Block { - id, - statements, - tail, - label: Some(label), + self.with_labeled_rib(label, |this| { + this.collect_block_(e, |id, statements, tail| Expr::Block { + id, + statements, + tail, + label: Some(label), + }) + }) + } + Some(ast::BlockModifier::Async(_)) => { + self.with_label_rib(RibKind::Closure, |this| { + this.collect_block_(e, |id, statements, tail| Expr::Async { + id, + statements, + tail, + }) + }) + } + Some(ast::BlockModifier::Const(_)) => { + self.with_label_rib(RibKind::Constant, |this| { + let (result_expr_id, prev_binding_owner) = + this.initialize_binding_owner(syntax_ptr); + let inner_expr = this.collect_block(e); + let x = this.db.intern_anonymous_const((this.owner, inner_expr)); + this.body.exprs[result_expr_id] = Expr::Const(x); + this.current_binding_owner = prev_binding_owner; + result_expr_id }) } - Some(ast::BlockModifier::Async(_)) => self - .collect_block_(e, |id, statements, tail| Expr::Async { id, statements, tail }), - Some(ast::BlockModifier::Const(_)) => self - .collect_block_(e, |id, statements, tail| Expr::Const { id, statements, tail }), None => self.collect_block(e), }, ast::Expr::LoopExpr(e) => { let label = e.label().map(|label| self.collect_label(label)); - let body = self.collect_block_opt(e.loop_body()); + let body = self.collect_labelled_block_opt(label, e.loop_body()); self.alloc_expr(Expr::Loop { body, label }, syntax_ptr) } ast::Expr::WhileExpr(e) => { let label = e.label().map(|label| self.collect_label(label)); - let body = self.collect_block_opt(e.loop_body()); - + let body = self.collect_labelled_block_opt(label, e.loop_body()); let condition = self.collect_expr_opt(e.condition()); self.alloc_expr(Expr::While { condition, body, label }, syntax_ptr) } - ast::Expr::ForExpr(e) => { - let label = e.label().map(|label| self.collect_label(label)); - let iterable = self.collect_expr_opt(e.iterable()); - let pat = self.collect_pat_opt(e.pat()); - let body = self.collect_block_opt(e.loop_body()); - self.alloc_expr(Expr::For { iterable, pat, body, label }, syntax_ptr) - } + ast::Expr::ForExpr(e) => self.collect_for_loop(syntax_ptr, e), ast::Expr::CallExpr(e) => { - let callee = self.collect_expr_opt(e.expr()); - let args = if let Some(arg_list) = e.arg_list() { - arg_list.args().filter_map(|e| self.maybe_collect_expr(e)).collect() - } else { - Box::default() + let is_rustc_box = { + let attrs = e.attrs(); + attrs.filter_map(|x| x.as_simple_atom()).any(|x| x == "rustc_box") }; - self.alloc_expr( - Expr::Call { callee, args, is_assignee_expr: self.is_lowering_assignee_expr }, - syntax_ptr, - ) + if is_rustc_box { + let expr = self.collect_expr_opt(e.arg_list().and_then(|x| x.args().next())); + self.alloc_expr(Expr::Box { expr }, syntax_ptr) + } else { + let callee = self.collect_expr_opt(e.expr()); + let args = if let Some(arg_list) = e.arg_list() { + arg_list.args().filter_map(|e| self.maybe_collect_expr(e)).collect() + } else { + Box::default() + }; + 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()); @@ -336,7 +367,7 @@ impl ExprCollector<'_> { .arms() .filter_map(|arm| { self.check_cfg(&arm).map(|()| MatchArm { - pat: self.collect_pat_opt(arm.pat()), + pat: self.collect_pat_top(arm.pat()), expr: self.collect_expr_opt(arm.expr()), guard: arm .guard() @@ -357,16 +388,20 @@ impl ExprCollector<'_> { .unwrap_or(Expr::Missing); self.alloc_expr(path, syntax_ptr) } - ast::Expr::ContinueExpr(e) => self.alloc_expr( - Expr::Continue { label: e.lifetime().map(|l| Name::new_lifetime(&l)) }, - syntax_ptr, - ), + ast::Expr::ContinueExpr(e) => { + let label = self.resolve_label(e.lifetime()).unwrap_or_else(|e| { + self.source_map.diagnostics.push(e); + None + }); + self.alloc_expr(Expr::Continue { label }, syntax_ptr) + } ast::Expr::BreakExpr(e) => { + let label = self.resolve_label(e.lifetime()).unwrap_or_else(|e| { + self.source_map.diagnostics.push(e); + None + }); let expr = e.expr().map(|e| self.collect_expr(e)); - self.alloc_expr( - Expr::Break { expr, label: e.lifetime().map(|l| Name::new_lifetime(&l)) }, - syntax_ptr, - ) + self.alloc_expr(Expr::Break { expr, label }, syntax_ptr) } ast::Expr::ParenExpr(e) => { let inner = self.collect_expr_opt(e.expr()); @@ -437,10 +472,7 @@ impl ExprCollector<'_> { let expr = self.collect_expr_opt(e.expr()); self.alloc_expr(Expr::Await { expr }, syntax_ptr) } - ast::Expr::TryExpr(e) => { - let expr = self.collect_expr_opt(e.expr()); - self.alloc_expr(Expr::Try { expr }, syntax_ptr) - } + ast::Expr::TryExpr(e) => self.collect_try_operator(syntax_ptr, e), ast::Expr::CastExpr(e) => { let expr = self.collect_expr_opt(e.expr()); let type_ref = Interned::new(TypeRef::from_ast_opt(&self.ctx(), e.ty())); @@ -470,14 +502,16 @@ impl ExprCollector<'_> { None => self.alloc_expr(Expr::Missing, syntax_ptr), } } - ast::Expr::ClosureExpr(e) => { + ast::Expr::ClosureExpr(e) => self.with_label_rib(RibKind::Closure, |this| { + let (result_expr_id, prev_binding_owner) = + this.initialize_binding_owner(syntax_ptr); let mut args = Vec::new(); let mut arg_types = Vec::new(); if let Some(pl) = e.param_list() { for param in pl.params() { - let pat = self.collect_pat_opt(param.pat()); + let pat = this.collect_pat_top(param.pat()); let type_ref = - param.ty().map(|it| Interned::new(TypeRef::from_ast(&self.ctx(), it))); + param.ty().map(|it| Interned::new(TypeRef::from_ast(&this.ctx(), it))); args.push(pat); arg_types.push(type_ref); } @@ -485,14 +519,14 @@ impl ExprCollector<'_> { let ret_type = e .ret_type() .and_then(|r| r.ty()) - .map(|it| Interned::new(TypeRef::from_ast(&self.ctx(), it))); + .map(|it| Interned::new(TypeRef::from_ast(&this.ctx(), it))); - let prev_is_lowering_generator = self.is_lowering_generator; - self.is_lowering_generator = false; + let prev_is_lowering_generator = mem::take(&mut this.is_lowering_generator); + let prev_try_block_label = this.current_try_block_label.take(); - let body = self.collect_expr_opt(e.body()); + let body = this.collect_expr_opt(e.body()); - let closure_kind = if self.is_lowering_generator { + let closure_kind = if this.is_lowering_generator { let movability = if e.static_token().is_some() { Movability::Static } else { @@ -504,19 +538,21 @@ impl ExprCollector<'_> { } else { ClosureKind::Closure }; - self.is_lowering_generator = prev_is_lowering_generator; - - self.alloc_expr( - Expr::Closure { - args: args.into(), - arg_types: arg_types.into(), - ret_type, - body, - closure_kind, - }, - syntax_ptr, - ) - } + let capture_by = + if e.move_token().is_some() { CaptureBy::Value } else { CaptureBy::Ref }; + this.is_lowering_generator = prev_is_lowering_generator; + this.current_binding_owner = prev_binding_owner; + this.current_try_block_label = prev_try_block_label; + this.body.exprs[result_expr_id] = Expr::Closure { + args: args.into(), + arg_types: arg_types.into(), + ret_type, + body, + closure_kind, + capture_by, + }; + result_expr_id + }), ast::Expr::BinExpr(e) => { let op = e.op_kind(); if let Some(ast::BinaryOp::Assignment { op: None }) = op { @@ -528,9 +564,18 @@ impl ExprCollector<'_> { 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(); + let mut exprs: Vec<_> = e.fields().map(|expr| self.collect_expr(expr)).collect(); + // if there is a leading comma, the user is most likely to type out a leading expression + // so we insert a missing expression at the beginning for IDE features + if comma_follows_token(e.l_paren_token()) { + exprs.insert(0, self.missing_expr()); + } + self.alloc_expr( - Expr::Tuple { exprs, is_assignee_expr: self.is_lowering_assignee_expr }, + Expr::Tuple { + exprs: exprs.into_boxed_slice(), + is_assignee_expr: self.is_lowering_assignee_expr, + }, syntax_ptr, ) } @@ -555,7 +600,17 @@ impl ExprCollector<'_> { } ArrayExprKind::Repeat { initializer, repeat } => { let initializer = self.collect_expr_opt(initializer); - let repeat = self.collect_expr_opt(repeat); + let repeat = self.with_label_rib(RibKind::Constant, |this| { + if let Some(repeat) = repeat { + let syntax_ptr = AstPtr::new(&repeat); + this.collect_as_a_binding_owner_bad( + |this| this.collect_expr(repeat), + syntax_ptr, + ) + } else { + this.missing_expr() + } + }); self.alloc_expr( Expr::Array(Array::Repeat { initializer, repeat }), syntax_ptr, @@ -601,6 +656,244 @@ impl ExprCollector<'_> { }) } + fn initialize_binding_owner( + &mut self, + syntax_ptr: AstPtr, + ) -> (ExprId, Option) { + let result_expr_id = self.alloc_expr(Expr::Missing, syntax_ptr); + let prev_binding_owner = self.current_binding_owner.take(); + self.current_binding_owner = Some(result_expr_id); + (result_expr_id, prev_binding_owner) + } + + /// FIXME: This function is bad. It will produce a dangling `Missing` expr which wastes memory. Currently + /// it is used only for const blocks and repeat expressions, which are also hacky and ideally should have + /// their own body. Don't add more usage for this function so that we can remove this function after + /// separating those bodies. + fn collect_as_a_binding_owner_bad( + &mut self, + job: impl FnOnce(&mut ExprCollector<'_>) -> ExprId, + syntax_ptr: AstPtr, + ) -> ExprId { + let (id, prev_owner) = self.initialize_binding_owner(syntax_ptr); + let tmp = job(self); + self.body.exprs[id] = mem::replace(&mut self.body.exprs[tmp], Expr::Missing); + self.current_binding_owner = prev_owner; + id + } + + /// Desugar `try { ; }` into `': { ; ::std::ops::Try::from_output() }`, + /// `try { ; }` into `': { ; ::std::ops::Try::from_output(()) }` + /// and save the `` to use it as a break target for desugaring of the `?` operator. + fn desugar_try_block(&mut self, e: BlockExpr) -> ExprId { + let Some(try_from_output) = LangItem::TryTraitFromOutput.path(self.db, self.krate) else { + return self.collect_block(e); + }; + let label = self.alloc_label_desugared(Label { name: Name::generate_new_name() }); + let old_label = self.current_try_block_label.replace(label); + + let (btail, expr_id) = self.with_labeled_rib(label, |this| { + let mut btail = None; + let block = this.collect_block_(e, |id, statements, tail| { + btail = tail; + Expr::Block { id, statements, tail, label: Some(label) } + }); + (btail, block) + }); + + let callee = self.alloc_expr_desugared(Expr::Path(try_from_output)); + let next_tail = match btail { + Some(tail) => self.alloc_expr_desugared(Expr::Call { + callee, + args: Box::new([tail]), + is_assignee_expr: false, + }), + None => { + let unit = self.alloc_expr_desugared(Expr::Tuple { + exprs: Box::new([]), + is_assignee_expr: false, + }); + self.alloc_expr_desugared(Expr::Call { + callee, + args: Box::new([unit]), + is_assignee_expr: false, + }) + } + }; + let Expr::Block { tail, .. } = &mut self.body.exprs[expr_id] else { + unreachable!("block was lowered to non-block"); + }; + *tail = Some(next_tail); + self.current_try_block_label = old_label; + expr_id + } + + /// Desugar `ast::ForExpr` from: `[opt_ident]: for in ` into: + /// ```ignore (pseudo-rust) + /// match IntoIterator::into_iter() { + /// mut iter => { + /// [opt_ident]: loop { + /// match Iterator::next(&mut iter) { + /// None => break, + /// Some() => , + /// }; + /// } + /// } + /// } + /// ``` + fn collect_for_loop(&mut self, syntax_ptr: AstPtr, e: ast::ForExpr) -> ExprId { + let (into_iter_fn, iter_next_fn, option_some, option_none) = 'if_chain: { + if let Some(into_iter_fn) = LangItem::IntoIterIntoIter.path(self.db, self.krate) { + if let Some(iter_next_fn) = LangItem::IteratorNext.path(self.db, self.krate) { + if let Some(option_some) = LangItem::OptionSome.path(self.db, self.krate) { + if let Some(option_none) = LangItem::OptionNone.path(self.db, self.krate) { + break 'if_chain (into_iter_fn, iter_next_fn, option_some, option_none); + } + } + } + } + // Some of the needed lang items are missing, so we can't desugar + return self.alloc_expr(Expr::Missing, syntax_ptr); + }; + let head = self.collect_expr_opt(e.iterable()); + let into_iter_fn_expr = self.alloc_expr(Expr::Path(into_iter_fn), syntax_ptr.clone()); + let iterator = self.alloc_expr( + Expr::Call { + callee: into_iter_fn_expr, + args: Box::new([head]), + is_assignee_expr: false, + }, + syntax_ptr.clone(), + ); + let none_arm = MatchArm { + pat: self.alloc_pat_desugared(Pat::Path(Box::new(option_none))), + guard: None, + expr: self.alloc_expr(Expr::Break { expr: None, label: None }, syntax_ptr.clone()), + }; + let some_pat = Pat::TupleStruct { + path: Some(Box::new(option_some)), + args: Box::new([self.collect_pat_top(e.pat())]), + ellipsis: None, + }; + let label = e.label().map(|label| self.collect_label(label)); + let some_arm = MatchArm { + pat: self.alloc_pat_desugared(some_pat), + guard: None, + expr: self.with_opt_labeled_rib(label, |this| { + this.collect_expr_opt(e.loop_body().map(|x| x.into())) + }), + }; + let iter_name = Name::generate_new_name(); + let iter_binding = self.alloc_binding(iter_name.clone(), BindingAnnotation::Mutable); + let iter_expr = self.alloc_expr(Expr::Path(Path::from(iter_name)), syntax_ptr.clone()); + let iter_expr_mut = self.alloc_expr( + Expr::Ref { expr: iter_expr, rawness: Rawness::Ref, mutability: Mutability::Mut }, + syntax_ptr.clone(), + ); + let iter_next_fn_expr = self.alloc_expr(Expr::Path(iter_next_fn), syntax_ptr.clone()); + let iter_next_expr = self.alloc_expr( + Expr::Call { + callee: iter_next_fn_expr, + args: Box::new([iter_expr_mut]), + is_assignee_expr: false, + }, + syntax_ptr.clone(), + ); + let loop_inner = self.alloc_expr( + Expr::Match { expr: iter_next_expr, arms: Box::new([none_arm, some_arm]) }, + syntax_ptr.clone(), + ); + let loop_outer = + self.alloc_expr(Expr::Loop { body: loop_inner, label }, syntax_ptr.clone()); + let iter_pat = self.alloc_pat_desugared(Pat::Bind { id: iter_binding, subpat: None }); + self.alloc_expr( + Expr::Match { + expr: iterator, + arms: Box::new([MatchArm { pat: iter_pat, guard: None, expr: loop_outer }]), + }, + syntax_ptr.clone(), + ) + } + + /// Desugar `ast::TryExpr` from: `?` into: + /// ```ignore (pseudo-rust) + /// match Try::branch() { + /// ControlFlow::Continue(val) => val, + /// ControlFlow::Break(residual) => + /// // If there is an enclosing `try {...}`: + /// break 'catch_target Try::from_residual(residual), + /// // Otherwise: + /// return Try::from_residual(residual), + /// } + /// ``` + fn collect_try_operator(&mut self, syntax_ptr: AstPtr, e: ast::TryExpr) -> ExprId { + let (try_branch, cf_continue, cf_break, try_from_residual) = 'if_chain: { + if let Some(try_branch) = LangItem::TryTraitBranch.path(self.db, self.krate) { + if let Some(cf_continue) = LangItem::ControlFlowContinue.path(self.db, self.krate) { + if let Some(cf_break) = LangItem::ControlFlowBreak.path(self.db, self.krate) { + if let Some(try_from_residual) = + LangItem::TryTraitFromResidual.path(self.db, self.krate) + { + break 'if_chain (try_branch, cf_continue, cf_break, try_from_residual); + } + } + } + } + // Some of the needed lang items are missing, so we can't desugar + return self.alloc_expr(Expr::Missing, syntax_ptr); + }; + let operand = self.collect_expr_opt(e.expr()); + let try_branch = self.alloc_expr(Expr::Path(try_branch), syntax_ptr.clone()); + let expr = self.alloc_expr( + Expr::Call { callee: try_branch, args: Box::new([operand]), is_assignee_expr: false }, + syntax_ptr.clone(), + ); + let continue_name = Name::generate_new_name(); + let continue_binding = + self.alloc_binding(continue_name.clone(), BindingAnnotation::Unannotated); + let continue_bpat = + self.alloc_pat_desugared(Pat::Bind { id: continue_binding, subpat: None }); + self.add_definition_to_binding(continue_binding, continue_bpat); + let continue_arm = MatchArm { + pat: self.alloc_pat_desugared(Pat::TupleStruct { + path: Some(Box::new(cf_continue)), + args: Box::new([continue_bpat]), + ellipsis: None, + }), + guard: None, + expr: self.alloc_expr(Expr::Path(Path::from(continue_name)), syntax_ptr.clone()), + }; + let break_name = Name::generate_new_name(); + let break_binding = self.alloc_binding(break_name.clone(), BindingAnnotation::Unannotated); + let break_bpat = self.alloc_pat_desugared(Pat::Bind { id: break_binding, subpat: None }); + self.add_definition_to_binding(break_binding, break_bpat); + let break_arm = MatchArm { + pat: self.alloc_pat_desugared(Pat::TupleStruct { + path: Some(Box::new(cf_break)), + args: Box::new([break_bpat]), + ellipsis: None, + }), + guard: None, + expr: { + let x = self.alloc_expr(Expr::Path(Path::from(break_name)), syntax_ptr.clone()); + let callee = self.alloc_expr(Expr::Path(try_from_residual), syntax_ptr.clone()); + let result = self.alloc_expr( + Expr::Call { callee, args: Box::new([x]), is_assignee_expr: false }, + syntax_ptr.clone(), + ); + self.alloc_expr( + match self.current_try_block_label { + Some(label) => Expr::Break { expr: Some(result), label: Some(label) }, + None => Expr::Return { expr: Some(result) }, + }, + syntax_ptr.clone(), + ) + }, + }; + let arms = Box::new([continue_arm, break_arm]); + self.alloc_expr(Expr::Match { expr, arms }, syntax_ptr) + } + fn collect_macro_call( &mut self, mcall: ast::MacroCall, @@ -616,7 +909,19 @@ impl ExprCollector<'_> { let outer_file = self.expander.current_file_id; let macro_call_ptr = self.expander.to_source(AstPtr::new(&mcall)); - let res = self.expander.enter_expand(self.db, mcall); + let module = self.expander.module.local_id; + let res = self.expander.enter_expand(self.db, mcall, |path| { + self.def_map + .resolve_path( + self.db, + module, + &path, + crate::item_scope::BuiltinShadowMode::Other, + Some(MacroSubNs::Bang), + ) + .0 + .take_macros() + }); let res = match res { Ok(res) => res, @@ -639,7 +944,7 @@ impl ExprCollector<'_> { krate: *krate, }); } - Some(ExpandError::RecursionOverflowPosioned) => { + Some(ExpandError::RecursionOverflowPoisoned) => { // Recursion limit has been reached in the macro expansion tree, but not in // this very macro call. Don't add diagnostics to avoid duplication. } @@ -663,7 +968,11 @@ impl ExprCollector<'_> { self.db.ast_id_map(self.expander.current_file_id), ); - let id = collector(self, Some(expansion)); + if record_diagnostics { + // FIXME: Report parse errors here + } + + let id = collector(self, Some(expansion.tree())); self.ast_id_map = prev_ast_id_map; self.expander.exit(self.db, mark); id @@ -720,7 +1029,7 @@ impl ExprCollector<'_> { if self.check_cfg(&stmt).is_none() { return; } - let pat = self.collect_pat_opt(stmt.pat()); + let pat = self.collect_pat_top(stmt.pat()); let type_ref = stmt.ty().map(|it| Interned::new(TypeRef::from_ast(&self.ctx(), it))); let initializer = stmt.initializer().map(|e| self.collect_expr(e)); @@ -763,22 +1072,36 @@ impl ExprCollector<'_> { fn collect_block_( &mut self, block: ast::BlockExpr, - mk_block: impl FnOnce(BlockId, Box<[Statement]>, Option) -> Expr, + mk_block: impl FnOnce(Option, Box<[Statement]>, Option) -> Expr, ) -> ExprId { - let file_local_id = self.ast_id_map.ast_id(&block); - let ast_id = AstId::new(self.expander.current_file_id, file_local_id); - let block_loc = - BlockLoc { ast_id, module: self.expander.def_map.module_id(self.expander.module) }; - let block_id = self.db.intern_block(block_loc); - - let (module, def_map) = match self.db.block_def_map(block_id) { - Some(def_map) => { - self.body.block_scopes.push(block_id); - (def_map.root(), def_map) - } - None => (self.expander.module, self.expander.def_map.clone()), + let block_has_items = { + let statement_has_item = block.statements().any(|stmt| match stmt { + ast::Stmt::Item(_) => true, + // Macro calls can be both items and expressions. The syntax library always treats + // them as expressions here, so we undo that. + ast::Stmt::ExprStmt(es) => matches!(es.expr(), Some(ast::Expr::MacroExpr(_))), + _ => false, + }); + statement_has_item || matches!(block.tail_expr(), Some(ast::Expr::MacroExpr(_))) + }; + + let block_id = if block_has_items { + let file_local_id = self.ast_id_map.ast_id(&block); + let ast_id = AstId::new(self.expander.current_file_id, file_local_id); + Some(self.db.intern_block(BlockLoc { ast_id, module: self.expander.module })) + } else { + None }; - let prev_def_map = mem::replace(&mut self.expander.def_map, def_map); + + let (module, def_map) = + match block_id.map(|block_id| (self.db.block_def_map(block_id), block_id)) { + Some((def_map, block_id)) => { + self.body.block_scopes.push(block_id); + (def_map.module_id(DefMap::ROOT), def_map) + } + None => (self.expander.module, self.def_map.clone()), + }; + let prev_def_map = mem::replace(&mut self.def_map, def_map); let prev_local_module = mem::replace(&mut self.expander.module, module); let mut statements = Vec::new(); @@ -800,7 +1123,7 @@ impl ExprCollector<'_> { let expr_id = self .alloc_expr(mk_block(block_id, statements.into_boxed_slice(), tail), syntax_node_ptr); - self.expander.def_map = prev_def_map; + self.def_map = prev_def_map; self.expander.module = prev_local_module; expr_id } @@ -812,43 +1135,46 @@ impl ExprCollector<'_> { } } - fn collect_label(&mut self, ast_label: ast::Label) -> LabelId { - let label = Label { - name: ast_label.lifetime().as_ref().map_or_else(Name::missing, Name::new_lifetime), - }; - self.alloc_label(label, AstPtr::new(&ast_label)) + fn collect_labelled_block_opt( + &mut self, + label: Option, + expr: Option, + ) -> ExprId { + match label { + Some(label) => self.with_labeled_rib(label, |this| this.collect_block_opt(expr)), + None => self.collect_block_opt(expr), + } } - fn collect_pat(&mut self, pat: ast::Pat) -> PatId { - self.collect_pat_(pat, &mut BindingList::default()) - } + // region: patterns - fn collect_pat_opt(&mut self, pat: Option) -> PatId { + fn collect_pat_top(&mut self, pat: Option) -> PatId { match pat { - Some(pat) => self.collect_pat(pat), + Some(pat) => self.collect_pat(pat, &mut BindingList::default()), None => self.missing_pat(), } } - fn collect_pat_(&mut self, pat: ast::Pat, binding_list: &mut BindingList) -> PatId { + fn collect_pat(&mut self, pat: ast::Pat, binding_list: &mut BindingList) -> PatId { let pattern = match &pat { ast::Pat::IdentPat(bp) => { let name = bp.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing); let annotation = BindingAnnotation::new(bp.mut_token().is_some(), bp.ref_token().is_some()); - let subpat = bp.pat().map(|subpat| self.collect_pat_(subpat, binding_list)); + let subpat = bp.pat().map(|subpat| self.collect_pat(subpat, binding_list)); let is_simple_ident_pat = annotation == BindingAnnotation::Unannotated && subpat.is_none(); let (binding, pattern) = if is_simple_ident_pat { // This could also be a single-segment path pattern. To // decide that, we need to try resolving the name. - let (resolved, _) = self.expander.def_map.resolve_path( + let (resolved, _) = self.def_map.resolve_path( self.db, - self.expander.module, + self.expander.module.local_id, &name.clone().into(), BuiltinShadowMode::Other, + None, ); match resolved.take_values() { Some(ModuleDefId::ConstId(_)) => (None, Pat::Path(name.into())), @@ -887,11 +1213,15 @@ impl ExprCollector<'_> { ast::Pat::TupleStructPat(p) => { let path = p.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new); - let (args, ellipsis) = self.collect_tuple_pat(p.fields(), binding_list); + let (args, ellipsis) = self.collect_tuple_pat( + p.fields(), + comma_follows_token(p.l_paren_token()), + binding_list, + ); Pat::TupleStruct { path, args, ellipsis } } ast::Pat::RefPat(p) => { - let pat = self.collect_pat_opt_(p.pat(), binding_list); + let pat = self.collect_pat_opt(p.pat(), binding_list); let mutability = Mutability::from_mutable(p.mut_token().is_some()); Pat::Ref { pat, mutability } } @@ -900,13 +1230,42 @@ impl ExprCollector<'_> { p.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new); path.map(Pat::Path).unwrap_or(Pat::Missing) } - ast::Pat::OrPat(p) => { - let pats = p.pats().map(|p| self.collect_pat_(p, binding_list)).collect(); - Pat::Or(pats) + ast::Pat::OrPat(p) => 'b: { + let prev_is_used = mem::take(&mut binding_list.is_used); + let prev_reject_new = mem::take(&mut binding_list.reject_new); + let mut pats = Vec::with_capacity(p.pats().count()); + let mut it = p.pats(); + let Some(first) = it.next() else { + break 'b Pat::Or(Box::new([])); + }; + pats.push(self.collect_pat(first, binding_list)); + binding_list.reject_new = true; + for rest in it { + for (_, x) in binding_list.is_used.iter_mut() { + *x = false; + } + pats.push(self.collect_pat(rest, binding_list)); + for (&id, &x) in binding_list.is_used.iter() { + if !x { + self.body.bindings[id].problems = + Some(BindingProblems::NotBoundAcrossAll); + } + } + } + binding_list.reject_new = prev_reject_new; + let current_is_used = mem::replace(&mut binding_list.is_used, prev_is_used); + for (id, _) in current_is_used.into_iter() { + binding_list.check_is_used(self, id); + } + Pat::Or(pats.into()) } - ast::Pat::ParenPat(p) => return self.collect_pat_opt_(p.pat(), binding_list), + ast::Pat::ParenPat(p) => return self.collect_pat_opt(p.pat(), binding_list), ast::Pat::TuplePat(p) => { - let (args, ellipsis) = self.collect_tuple_pat(p.fields(), binding_list); + let (args, ellipsis) = self.collect_tuple_pat( + p.fields(), + comma_follows_token(p.l_paren_token()), + binding_list, + ); Pat::Tuple { args, ellipsis } } ast::Pat::WildcardPat(_) => Pat::Wild, @@ -919,7 +1278,7 @@ impl ExprCollector<'_> { .fields() .filter_map(|f| { let ast_pat = f.pat()?; - let pat = self.collect_pat_(ast_pat, binding_list); + let pat = self.collect_pat(ast_pat, binding_list); let name = f.field_name()?.as_name(); Some(RecordFieldPat { name, pat }) }) @@ -938,26 +1297,18 @@ impl ExprCollector<'_> { // FIXME properly handle `RestPat` Pat::Slice { - prefix: prefix - .into_iter() - .map(|p| self.collect_pat_(p, binding_list)) - .collect(), - slice: slice.map(|p| self.collect_pat_(p, binding_list)), - suffix: suffix - .into_iter() - .map(|p| self.collect_pat_(p, binding_list)) - .collect(), + prefix: prefix.into_iter().map(|p| self.collect_pat(p, binding_list)).collect(), + slice: slice.map(|p| self.collect_pat(p, binding_list)), + suffix: suffix.into_iter().map(|p| self.collect_pat(p, binding_list)).collect(), } } - ast::Pat::LiteralPat(lit) => { - if let Some(ast_lit) = lit.literal() { - let expr = Expr::Literal(ast_lit.kind().into()); - let expr_ptr = AstPtr::new(&ast::Expr::Literal(ast_lit)); - let expr_id = self.alloc_expr(expr, expr_ptr); - Pat::Lit(expr_id) - } else { - Pat::Missing - } + #[rustfmt::skip] // https://github.com/rust-lang/rustfmt/issues/5676 + ast::Pat::LiteralPat(lit) => 'b: { + let Some((hir_lit, ast_lit)) = pat_literal_to_hir(lit) else { break 'b Pat::Missing }; + let expr = Expr::Literal(hir_lit); + let expr_ptr = AstPtr::new(&ast::Expr::Literal(ast_lit)); + let expr_id = self.alloc_expr(expr, expr_ptr); + Pat::Lit(expr_id) } ast::Pat::RestPat(_) => { // `RestPat` requires special handling and should not be mapped @@ -969,12 +1320,18 @@ impl ExprCollector<'_> { Pat::Missing } ast::Pat::BoxPat(boxpat) => { - let inner = self.collect_pat_opt_(boxpat.pat(), binding_list); + let inner = self.collect_pat_opt(boxpat.pat(), binding_list); Pat::Box { inner } } ast::Pat::ConstBlockPat(const_block_pat) => { - if let Some(expr) = const_block_pat.block_expr() { - let expr_id = self.collect_block(expr); + if let Some(block) = const_block_pat.block_expr() { + let expr_id = self.with_label_rib(RibKind::Constant, |this| { + let syntax_ptr = AstPtr::new(&block.clone().into()); + this.collect_as_a_binding_owner_bad( + |this| this.collect_block(block), + syntax_ptr, + ) + }); Pat::ConstBlock(expr_id) } else { Pat::Missing @@ -986,23 +1343,45 @@ impl ExprCollector<'_> { let src = self.expander.to_source(Either::Left(AstPtr::new(&pat))); let pat = self.collect_macro_call(call, macro_ptr, true, |this, expanded_pat| { - this.collect_pat_opt_(expanded_pat, binding_list) + this.collect_pat_opt(expanded_pat, binding_list) }); self.source_map.pat_map.insert(src, pat); return pat; } None => Pat::Missing, }, - // FIXME: implement - ast::Pat::RangePat(_) => Pat::Missing, + // FIXME: implement in a way that also builds source map and calculates assoc resolutions in type inference. + ast::Pat::RangePat(p) => { + let mut range_part_lower = |p: Option| { + p.and_then(|x| match &x { + ast::Pat::LiteralPat(x) => { + Some(Box::new(LiteralOrConst::Literal(pat_literal_to_hir(x)?.0))) + } + ast::Pat::IdentPat(p) => { + let name = + p.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing); + Some(Box::new(LiteralOrConst::Const(name.into()))) + } + ast::Pat::PathPat(p) => p + .path() + .and_then(|path| self.expander.parse_path(self.db, path)) + .map(LiteralOrConst::Const) + .map(Box::new), + _ => None, + }) + }; + let start = range_part_lower(p.start()); + let end = range_part_lower(p.end()); + Pat::Range { start, end } + } }; let ptr = AstPtr::new(&pat); self.alloc_pat(pattern, Either::Left(ptr)) } - fn collect_pat_opt_(&mut self, pat: Option, binding_list: &mut BindingList) -> PatId { + fn collect_pat_opt(&mut self, pat: Option, binding_list: &mut BindingList) -> PatId { match pat { - Some(pat) => self.collect_pat_(pat, binding_list), + Some(pat) => self.collect_pat(pat, binding_list), None => self.missing_pat(), } } @@ -1010,20 +1389,28 @@ impl ExprCollector<'_> { fn collect_tuple_pat( &mut self, args: AstChildren, + has_leading_comma: bool, binding_list: &mut BindingList, ) -> (Box<[PatId]>, Option) { // Find the location of the `..`, if there is one. Note that we do not // consider the possibility of there being multiple `..` here. let ellipsis = args.clone().position(|p| matches!(p, ast::Pat::RestPat(_))); // We want to skip the `..` pattern here, since we account for it above. - let args = args + let mut args: Vec<_> = args .filter(|p| !matches!(p, ast::Pat::RestPat(_))) - .map(|p| self.collect_pat_(p, binding_list)) + .map(|p| self.collect_pat(p, binding_list)) .collect(); + // if there is a leading comma, the user is most likely to type out a leading pattern + // so we insert a missing pattern at the beginning for IDE features + if has_leading_comma { + args.insert(0, self.missing_pat()); + } - (args, ellipsis) + (args.into_boxed_slice(), ellipsis) } + // endregion: patterns + /// Returns `None` (and emits diagnostics) when `owner` if `#[cfg]`d out, and `Some(())` when /// not. fn check_cfg(&mut self, owner: &dyn ast::HasAttrs) -> Option<()> { @@ -1051,42 +1438,147 @@ impl ExprCollector<'_> { fn add_definition_to_binding(&mut self, binding_id: BindingId, pat_id: PatId) { self.body.bindings[binding_id].definitions.push(pat_id); } -} -impl From for Literal { - fn from(ast_lit_kind: ast::LiteralKind) -> Self { - match ast_lit_kind { - // FIXME: these should have actual values filled in, but unsure on perf impact - LiteralKind::IntNumber(lit) => { - if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) { - Literal::Float( - FloatTypeWrapper::new(lit.float_value().unwrap_or(Default::default())), - builtin, - ) - } else if let builtin @ Some(_) = lit.suffix().and_then(BuiltinInt::from_suffix) { - Literal::Int(lit.value().unwrap_or(0) as i128, builtin) - } else { - let builtin = lit.suffix().and_then(BuiltinUint::from_suffix); - Literal::Uint(lit.value().unwrap_or(0), builtin) + // region: labels + + fn collect_label(&mut self, ast_label: ast::Label) -> LabelId { + let label = Label { + name: ast_label.lifetime().as_ref().map_or_else(Name::missing, Name::new_lifetime), + }; + self.alloc_label(label, AstPtr::new(&ast_label)) + } + + fn resolve_label( + &self, + lifetime: Option, + ) -> Result, BodyDiagnostic> { + let Some(lifetime) = lifetime else { + return Ok(None) + }; + let name = Name::new_lifetime(&lifetime); + + for (rib_idx, rib) in self.label_ribs.iter().enumerate().rev() { + if let Some((label_name, id)) = &rib.label { + if *label_name == name { + return if self.is_label_valid_from_rib(rib_idx) { + Ok(Some(*id)) + } else { + Err(BodyDiagnostic::UnreachableLabel { + name, + node: InFile::new( + self.expander.current_file_id, + AstPtr::new(&lifetime), + ), + }) + }; } } - LiteralKind::FloatNumber(lit) => { - let ty = lit.suffix().and_then(BuiltinFloat::from_suffix); - Literal::Float(FloatTypeWrapper::new(lit.value().unwrap_or(Default::default())), ty) - } - LiteralKind::ByteString(bs) => { - let text = bs.value().map(Box::from).unwrap_or_else(Default::default); - Literal::ByteString(text) - } - LiteralKind::String(s) => { - let text = s.value().map(Box::from).unwrap_or_else(Default::default); - Literal::String(text) - } - LiteralKind::Byte(b) => { - Literal::Uint(b.value().unwrap_or_default() as u128, Some(BuiltinUint::U8)) - } - LiteralKind::Char(c) => Literal::Char(c.value().unwrap_or_default()), - LiteralKind::Bool(val) => Literal::Bool(val), } + + Err(BodyDiagnostic::UndeclaredLabel { + name, + node: InFile::new(self.expander.current_file_id, AstPtr::new(&lifetime)), + }) } + + fn is_label_valid_from_rib(&self, rib_index: usize) -> bool { + !self.label_ribs[rib_index + 1..].iter().any(|rib| rib.kind.is_label_barrier()) + } + + fn with_label_rib(&mut self, kind: RibKind, f: impl FnOnce(&mut Self) -> T) -> T { + self.label_ribs.push(LabelRib::new(kind)); + let res = f(self); + self.label_ribs.pop(); + res + } + + fn with_labeled_rib(&mut self, label: LabelId, f: impl FnOnce(&mut Self) -> T) -> T { + self.label_ribs.push(LabelRib::new_normal((self.body[label].name.clone(), label))); + let res = f(self); + self.label_ribs.pop(); + res + } + + fn with_opt_labeled_rib( + &mut self, + label: Option, + f: impl FnOnce(&mut Self) -> T, + ) -> T { + match label { + None => f(self), + Some(label) => self.with_labeled_rib(label, f), + } + } + // endregion: labels +} + +fn pat_literal_to_hir(lit: &ast::LiteralPat) -> Option<(Literal, ast::Literal)> { + let ast_lit = lit.literal()?; + let mut hir_lit: Literal = ast_lit.kind().into(); + if lit.minus_token().is_some() { + let Some(h) = hir_lit.negate() else { + return None; + }; + hir_lit = h; + } + Some((hir_lit, ast_lit)) +} + +impl ExprCollector<'_> { + fn alloc_expr(&mut self, expr: Expr, ptr: ExprPtr) -> ExprId { + let src = self.expander.to_source(ptr); + let id = self.body.exprs.alloc(expr); + self.source_map.expr_map_back.insert(id, src.clone()); + self.source_map.expr_map.insert(src, id); + id + } + // FIXME: desugared exprs don't have ptr, that's wrong and should be fixed somehow. + fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId { + self.body.exprs.alloc(expr) + } + fn missing_expr(&mut self) -> ExprId { + self.alloc_expr_desugared(Expr::Missing) + } + + fn alloc_binding(&mut self, name: Name, mode: BindingAnnotation) -> BindingId { + self.body.bindings.alloc(Binding { + name, + mode, + definitions: SmallVec::new(), + owner: self.current_binding_owner, + problems: None, + }) + } + + fn alloc_pat(&mut self, pat: Pat, ptr: PatPtr) -> PatId { + let src = self.expander.to_source(ptr); + let id = self.body.pats.alloc(pat); + self.source_map.pat_map_back.insert(id, src.clone()); + self.source_map.pat_map.insert(src, id); + id + } + // FIXME: desugared pats don't have ptr, that's wrong and should be fixed somehow. + fn alloc_pat_desugared(&mut self, pat: Pat) -> PatId { + self.body.pats.alloc(pat) + } + fn missing_pat(&mut self) -> PatId { + self.body.pats.alloc(Pat::Missing) + } + + fn alloc_label(&mut self, label: Label, ptr: LabelPtr) -> LabelId { + let src = self.expander.to_source(ptr); + let id = self.body.labels.alloc(label); + self.source_map.label_map_back.insert(id, src.clone()); + self.source_map.label_map.insert(src, id); + id + } + // FIXME: desugared labels don't have ptr, that's wrong and should be fixed somehow. + fn alloc_label_desugared(&mut self, label: Label) -> LabelId { + self.body.labels.alloc(label) + } +} + +fn comma_follows_token(t: Option) -> bool { + (|| syntax::algo::skip_trivia_token(t?.next_token()?, syntax::Direction::Next))() + .map_or(false, |it| it.kind() == syntax::T![,]) } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs index 5a9b825a2530b..88380aa355d16 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs @@ -2,10 +2,14 @@ use std::fmt::{self, Write}; +use hir_expand::db::ExpandDatabase; use syntax::ast::HasName; use crate::{ - expr::{Array, BindingAnnotation, BindingId, ClosureKind, Literal, Movability, Statement}, + hir::{ + Array, BindingAnnotation, BindingId, CaptureBy, ClosureKind, Literal, LiteralOrConst, + Movability, Statement, + }, pretty::{print_generic_args, print_path, print_type_ref}, type_ref::TypeRef, }; @@ -13,47 +17,70 @@ use crate::{ 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) + format!( + "fn {}", + item_tree_id.item_tree(db)[item_tree_id.value].name.display(db.upcast()) + ) } 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) + format!( + "static {} = ", + item_tree_id.item_tree(db)[item_tree_id.value].name.display(db.upcast()) + ) } 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(), + Some(name) => name.display(db.upcast()).to_string(), None => "_".to_string(), }; format!("const {name} = ") } DefWithBodyId::VariantId(it) => { - needs_semi = false; let src = it.parent.child_source(db); let variant = &src.value[it.local_id]; - let name = match &variant.name() { + match &variant.name() { Some(name) => name.to_string(), None => "_".to_string(), - }; - format!("{name}") + } } }; - let mut p = Printer { body, buf: header, indent_level: 0, needs_indent: false }; + let mut p = + Printer { db: db.upcast(), body, buf: header, indent_level: 0, needs_indent: false }; + if let DefWithBodyId::FunctionId(it) = owner { + p.buf.push('('); + body.params.iter().zip(&db.function_data(it).params).for_each(|(¶m, ty)| { + p.print_pat(param); + p.buf.push(':'); + p.print_type_ref(ty); + }); + p.buf.push(')'); + p.buf.push(' '); + } p.print_expr(body.body_expr); - if needs_semi { + if matches!(owner, DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_)) { p.buf.push(';'); } p.buf } +pub(super) fn print_expr_hir( + db: &dyn DefDatabase, + body: &Body, + _owner: DefWithBodyId, + expr: ExprId, +) -> String { + let mut p = + Printer { db: db.upcast(), body, buf: String::new(), indent_level: 0, needs_indent: false }; + p.print_expr(expr); + p.buf +} + macro_rules! w { ($dst:expr, $($arg:tt)*) => { { let _ = write!($dst, $($arg)*); } @@ -70,6 +97,7 @@ macro_rules! wln { } struct Printer<'a> { + db: &'a dyn ExpandDatabase, body: &'a Body, buf: String, indent_level: usize, @@ -144,29 +172,19 @@ impl<'a> Printer<'a> { } Expr::Loop { body, label } => { if let Some(lbl) = label { - w!(self, "{}: ", self.body[*lbl].name); + w!(self, "{}: ", self.body[*lbl].name.display(self.db)); } 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, "{}: ", self.body[*lbl].name.display(self.db)); } 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, "("); @@ -182,10 +200,10 @@ impl<'a> Printer<'a> { } Expr::MethodCall { receiver, method_name, args, generic_args } => { self.print_expr(*receiver); - w!(self, ".{}", method_name); + w!(self, ".{}", method_name.display(self.db)); if let Some(args) = generic_args { w!(self, "::<"); - print_generic_args(args, self).unwrap(); + print_generic_args(self.db, args, self).unwrap(); w!(self, ">"); } w!(self, "("); @@ -219,14 +237,14 @@ impl<'a> Printer<'a> { } Expr::Continue { label } => { w!(self, "continue"); - if let Some(label) = label { - w!(self, " {}", label); + if let Some(lbl) = label { + w!(self, " {}", self.body[*lbl].name.display(self.db)); } } Expr::Break { expr, label } => { w!(self, "break"); - if let Some(label) = label { - w!(self, " {}", label); + if let Some(lbl) = label { + w!(self, " {}", self.body[*lbl].name.display(self.db)); } if let Some(expr) = expr { self.whitespace(); @@ -265,7 +283,7 @@ impl<'a> Printer<'a> { w!(self, "{{"); self.indented(|p| { for field in &**fields { - w!(p, "{}: ", field.name); + w!(p, "{}: ", field.name.display(self.db)); p.print_expr(field.expr); wln!(p, ","); } @@ -282,16 +300,12 @@ impl<'a> Printer<'a> { } Expr::Field { expr, name } => { self.print_expr(*expr); - w!(self, ".{}", name); + w!(self, ".{}", name.display(self.db)); } Expr::Await { expr } => { self.print_expr(*expr); w!(self, ".await"); } - Expr::Try { expr } => { - self.print_expr(*expr); - w!(self, "?"); - } Expr::Cast { expr, type_ref } => { self.print_expr(*expr); w!(self, " as "); @@ -359,7 +373,7 @@ impl<'a> Printer<'a> { self.print_expr(*index); w!(self, "]"); } - Expr::Closure { args, arg_types, ret_type, body, closure_kind } => { + Expr::Closure { args, arg_types, ret_type, body, closure_kind, capture_by } => { match closure_kind { ClosureKind::Generator(Movability::Static) => { w!(self, "static "); @@ -369,6 +383,12 @@ impl<'a> Printer<'a> { } _ => (), } + match capture_by { + CaptureBy::Value => { + w!(self, "move "); + } + CaptureBy::Ref => (), + } w!(self, "|"); for (i, (pat, ty)) in args.iter().zip(arg_types.iter()).enumerate() { if i != 0 { @@ -418,20 +438,17 @@ impl<'a> Printer<'a> { } Expr::Literal(lit) => self.print_literal(lit), Expr::Block { id: _, statements, tail, label } => { - let label = label.map(|lbl| format!("{}: ", self.body[lbl].name)); + let label = label.map(|lbl| format!("{}: ", self.body[lbl].name.display(self.db))); self.print_block(label.as_deref(), statements, tail); } Expr::Unsafe { id: _, statements, tail } => { self.print_block(Some("unsafe "), statements, tail); } - Expr::TryBlock { id: _, statements, tail } => { - self.print_block(Some("try "), statements, tail); - } Expr::Async { id: _, statements, tail } => { self.print_block(Some("async "), statements, tail); } - Expr::Const { id: _, statements, tail } => { - self.print_block(Some("const "), statements, tail); + Expr::Const(id) => { + w!(self, "const {{ /* {id:?} */ }}"); } } } @@ -439,7 +456,7 @@ impl<'a> Printer<'a> { fn print_block( &mut self, label: Option<&str>, - statements: &Box<[Statement]>, + statements: &[Statement], tail: &Option>, ) { self.whitespace(); @@ -449,7 +466,7 @@ impl<'a> Printer<'a> { w!(self, "{{"); if !statements.is_empty() || tail.is_some() { self.indented(|p| { - for stmt in &**statements { + for stmt in statements { p.print_stmt(stmt); } if let Some(tail) = tail { @@ -497,7 +514,7 @@ impl<'a> Printer<'a> { w!(self, " {{"); self.indented(|p| { for arg in args.iter() { - w!(p, "{}: ", arg.name); + w!(p, "{}: ", arg.name.display(self.db)); p.print_pat(arg.pat); wln!(p, ","); } @@ -508,9 +525,13 @@ impl<'a> Printer<'a> { w!(self, "}}"); } Pat::Range { start, end } => { - self.print_expr(*start); - w!(self, "..."); - self.print_expr(*end); + if let Some(start) = start { + self.print_literal_or_const(start); + } + w!(self, "..="); + if let Some(end) = end { + self.print_literal_or_const(end); + } } Pat::Slice { prefix, slice, suffix } => { w!(self, "["); @@ -601,10 +622,18 @@ impl<'a> Printer<'a> { } } + fn print_literal_or_const(&mut self, literal_or_const: &LiteralOrConst) { + match literal_or_const { + LiteralOrConst::Literal(l) => self.print_literal(l), + LiteralOrConst::Const(c) => self.print_path(c), + } + } + fn print_literal(&mut self, literal: &Literal) { match literal { Literal::String(it) => w!(self, "{:?}", it), Literal::ByteString(it) => w!(self, "\"{}\"", it.escape_ascii()), + Literal::CString(it) => w!(self, "\"{}\\0\"", it), Literal::Char(it) => w!(self, "'{}'", it.escape_debug()), Literal::Bool(it) => w!(self, "{}", it), Literal::Int(i, suffix) => { @@ -629,11 +658,11 @@ impl<'a> Printer<'a> { } fn print_type_ref(&mut self, ty: &TypeRef) { - print_type_ref(ty, self).unwrap(); + print_type_ref(self.db, ty, self).unwrap(); } fn print_path(&mut self, path: &Path) { - print_path(path, self).unwrap(); + print_path(self.db, path, self).unwrap(); } fn print_binding(&mut self, id: BindingId) { @@ -644,6 +673,6 @@ impl<'a> Printer<'a> { BindingAnnotation::Ref => "ref ", BindingAnnotation::RefMut => "ref mut ", }; - w!(self, "{}{}", mode, name); + w!(self, "{}{}", mode, name.display(self.db)); } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs index 12fc1f116d7da..69741c445fbae 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs @@ -1,14 +1,13 @@ //! Name resolution for expressions. -use std::sync::Arc; - use hir_expand::name::Name; -use la_arena::{Arena, Idx}; +use la_arena::{Arena, Idx, IdxRange, RawIdx}; use rustc_hash::FxHashMap; +use triomphe::Arc; use crate::{ body::Body, db::DefDatabase, - expr::{Binding, BindingId, Expr, ExprId, LabelId, Pat, PatId, Statement}, + hir::{Binding, BindingId, Expr, ExprId, LabelId, Pat, PatId, Statement}, BlockId, DefWithBodyId, }; @@ -17,6 +16,7 @@ pub type ScopeId = Idx; #[derive(Debug, PartialEq, Eq)] pub struct ExprScopes { scopes: Arena, + scope_entries: Arena, scope_by_expr: FxHashMap, } @@ -41,7 +41,7 @@ pub struct ScopeData { parent: Option, block: Option, label: Option<(LabelId, Name)>, - entries: Vec, + entries: IdxRange, } impl ExprScopes { @@ -53,7 +53,7 @@ impl ExprScopes { } pub fn entries(&self, scope: ScopeId) -> &[ScopeEntry] { - &self.scopes[scope].entries + &self.scope_entries[self.scopes[scope].entries.clone()] } /// If `scope` refers to a block expression scope, returns the corresponding `BlockId`. @@ -85,10 +85,17 @@ impl ExprScopes { } } +fn empty_entries(idx: usize) -> IdxRange { + IdxRange::new(Idx::from_raw(RawIdx::from(idx as u32))..Idx::from_raw(RawIdx::from(idx as u32))) +} + impl ExprScopes { fn new(body: &Body) -> ExprScopes { - let mut scopes = - ExprScopes { scopes: Arena::default(), scope_by_expr: FxHashMap::default() }; + let mut scopes = ExprScopes { + scopes: Arena::default(), + scope_entries: Arena::default(), + scope_by_expr: FxHashMap::default(), + }; let mut root = scopes.root_scope(); scopes.add_params_bindings(body, root, &body.params); compute_expr_scopes(body.body_expr, body, &mut scopes, &mut root); @@ -96,7 +103,12 @@ impl ExprScopes { } fn root_scope(&mut self) -> ScopeId { - self.scopes.alloc(ScopeData { parent: None, block: None, label: None, entries: vec![] }) + self.scopes.alloc(ScopeData { + parent: None, + block: None, + label: None, + entries: empty_entries(self.scope_entries.len()), + }) } fn new_scope(&mut self, parent: ScopeId) -> ScopeId { @@ -104,32 +116,38 @@ impl ExprScopes { parent: Some(parent), block: None, label: None, - entries: vec![], + entries: empty_entries(self.scope_entries.len()), }) } fn new_labeled_scope(&mut self, parent: ScopeId, label: Option<(LabelId, Name)>) -> ScopeId { - self.scopes.alloc(ScopeData { parent: Some(parent), block: None, label, entries: vec![] }) + self.scopes.alloc(ScopeData { + parent: Some(parent), + block: None, + label, + entries: empty_entries(self.scope_entries.len()), + }) } fn new_block_scope( &mut self, parent: ScopeId, - block: BlockId, + block: Option, label: Option<(LabelId, Name)>, ) -> ScopeId { self.scopes.alloc(ScopeData { parent: Some(parent), - block: Some(block), + block, label, - entries: vec![], + entries: empty_entries(self.scope_entries.len()), }) } fn add_bindings(&mut self, body: &Body, scope: ScopeId, binding: BindingId) { let Binding { name, .. } = &body.bindings[binding]; - let entry = ScopeEntry { name: name.clone(), binding }; - self.scopes[scope].entries.push(entry); + let entry = self.scope_entries.alloc(ScopeEntry { name: name.clone(), binding }); + self.scopes[scope].entries = + IdxRange::new_inclusive(self.scopes[scope].entries.start()..=entry); } fn add_pat_bindings(&mut self, body: &Body, scope: ScopeId, pat: PatId) { @@ -150,9 +168,9 @@ impl ExprScopes { } fn shrink_to_fit(&mut self) { - let ExprScopes { scopes, scope_by_expr } = self; + let ExprScopes { scopes, scope_entries, scope_by_expr } = self; scopes.shrink_to_fit(); - scopes.values_mut().for_each(|it| it.entries.shrink_to_fit()); + scope_entries.shrink_to_fit(); scope_by_expr.shrink_to_fit(); } } @@ -200,22 +218,16 @@ fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope scopes.set_scope(expr, scope); compute_block_scopes(statements, *tail, body, scopes, &mut scope); } - Expr::Unsafe { id, statements, tail } - | Expr::Async { id, statements, tail } - | Expr::Const { id, statements, tail } - | Expr::TryBlock { id, statements, tail } => { + Expr::Const(_) => { + // FIXME: This is broken. + } + Expr::Unsafe { id, statements, tail } | Expr::Async { id, statements, tail } => { let mut scope = scopes.new_block_scope(*scope, *id, None); // Overwrite the old scope for the block expr, so that every block scope can be found // via the block itself (important for blocks that only contain items, no expressions). scopes.set_scope(expr, scope); compute_block_scopes(statements, *tail, body, scopes, &mut scope); } - Expr::For { iterable, pat, body: body_expr, label } => { - compute_expr_scopes(*iterable, body, scopes, scope); - let mut scope = scopes.new_labeled_scope(*scope, make_label(label)); - scopes.add_pat_bindings(body, scope, *pat); - compute_expr_scopes(*body_expr, body, scopes, &mut scope); - } Expr::While { condition, body: body_expr, label } => { let mut scope = scopes.new_labeled_scope(*scope, make_label(label)); compute_expr_scopes(*condition, body, scopes, &mut scope); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs index 77ac221e590c5..6e77744f21539 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs @@ -148,8 +148,8 @@ fn f() { } "#, expect![[r#" - BlockId(1) in ModuleId { krate: CrateId(0), block: Some(BlockId(0)), local_id: Idx::(1) } - BlockId(0) in ModuleId { krate: CrateId(0), block: None, local_id: Idx::(0) } + BlockId(1) in BlockRelativeModuleId { block: Some(BlockId(0)), local_id: Idx::(1) } + BlockId(0) in BlockRelativeModuleId { block: None, local_id: Idx::(0) } crate scope "#]], ); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/builtin_type.rs b/src/tools/rust-analyzer/crates/hir-def/src/builtin_type.rs index dd69c3ab47315..61b2481978e27 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/builtin_type.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/builtin_type.rs @@ -106,8 +106,14 @@ impl AsName for BuiltinType { impl fmt::Display for BuiltinType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let type_name = self.as_name(); - type_name.fmt(f) + match self { + BuiltinType::Char => f.write_str("char"), + BuiltinType::Bool => f.write_str("bool"), + BuiltinType::Str => f.write_str("str"), + BuiltinType::Int(it) => it.fmt(f), + BuiltinType::Uint(it) => it.fmt(f), + BuiltinType::Float(it) => it.fmt(f), + } } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs b/src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs index 68b57acca2adc..bb79e28f2673a 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs @@ -10,9 +10,9 @@ use syntax::ast::HasDocComments; use crate::{ db::DefDatabase, - dyn_map::DynMap, + dyn_map::{keys, DynMap}, item_scope::ItemScope, - keys, + nameres::DefMap, src::{HasChildSource, HasSource}, AdtId, AssocItemId, DefWithBodyId, EnumId, EnumVariantId, FieldId, ImplId, Lookup, MacroId, ModuleDefId, ModuleId, TraitId, VariantId, @@ -206,7 +206,7 @@ impl ChildBySource for DefWithBodyId { for (_, def_map) in body.blocks(db) { // All block expressions are merged into the same map, because they logically all add // inner items to the containing `DefWithBodyId`. - def_map[def_map.root()].scope.child_by_source_to(db, res, file_id); + def_map[DefMap::ROOT].scope.child_by_source_to(db, res, file_id); } } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data.rs b/src/tools/rust-analyzer/crates/hir-def/src/data.rs index 1633a33bedde8..40e6a4308784c 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/data.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/data.rs @@ -1,22 +1,28 @@ //! Contains basic data about various HIR declarations. -use std::sync::Arc; +pub mod adt; -use hir_expand::{name::Name, AstId, ExpandResult, HirFileId, InFile, MacroCallId, MacroDefKind}; +use hir_expand::{ + name::Name, AstId, ExpandResult, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefKind, +}; use intern::Interned; use smallvec::SmallVec; -use syntax::ast; +use syntax::{ast, Parse}; +use triomphe::Arc; use crate::{ attr::Attrs, - body::{Expander, Mark}, db::DefDatabase, - item_tree::{self, AssocItem, FnFlags, ItemTree, ItemTreeId, ModItem, Param, TreeId}, + expander::{Expander, Mark}, + item_tree::{ + self, AssocItem, FnFlags, ItemTree, ItemTreeId, MacroCall, ModItem, Param, TreeId, + }, + macro_call_as_call_id, macro_id_to_def_id, nameres::{ attr_resolution::ResolvedAttr, diagnostics::DefDiagnostic, proc_macro::{parse_macro_name_and_helper_attrs, ProcMacroKind}, - DefMap, + DefMap, MacroSubNs, }, type_ref::{TraitRef, TypeBound, TypeRef}, visibility::RawVisibility, @@ -28,9 +34,8 @@ use crate::{ #[derive(Debug, Clone, PartialEq, Eq)] pub struct FunctionData { pub name: Name, - pub params: Vec<(Option, Interned)>, + pub params: Vec>, pub ret_type: Interned, - pub async_ret_type: Option>, pub attrs: Attrs, pub visibility: RawVisibility, pub abi: Option>, @@ -43,16 +48,16 @@ impl FunctionData { pub(crate) fn fn_data_query(db: &dyn DefDatabase, func: FunctionId) -> Arc { let loc = func.lookup(db); let krate = loc.container.module(db).krate; - let crate_graph = db.crate_graph(); - let cfg_options = &crate_graph[krate].cfg_options; let item_tree = loc.id.item_tree(db); let func = &item_tree[loc.id.value]; let visibility = if let ItemContainerId::TraitId(trait_id) = loc.container { - db.trait_data(trait_id).visibility.clone() + trait_vis(db, trait_id) } else { item_tree[func.visibility].clone() }; + let crate_graph = db.crate_graph(); + let cfg_options = &crate_graph[krate].cfg_options; let enabled_params = func .params .clone() @@ -99,12 +104,11 @@ impl FunctionData { params: enabled_params .clone() .filter_map(|id| match &item_tree[id] { - Param::Normal(name, ty) => Some((name.clone(), ty.clone())), + Param::Normal(ty) => Some(ty.clone()), Param::Varargs => None, }) .collect(), ret_type: func.ret_type.clone(), - async_ret_type: func.async_ret_type.clone(), attrs: item_tree.attrs(db, krate, ModItem::from(loc.id.value).into()), visibility, abi: func.abi.clone(), @@ -188,7 +192,7 @@ impl TypeAliasData { let item_tree = loc.id.item_tree(db); let typ = &item_tree[loc.id.value]; let visibility = if let ItemContainerId::TraitId(trait_id) = loc.container { - db.trait_data(trait_id).visibility.clone() + trait_vis(db, trait_id) } else { item_tree[typ.visibility].clone() }; @@ -471,7 +475,7 @@ impl ConstData { let item_tree = loc.id.item_tree(db); let konst = &item_tree[loc.id.value]; let visibility = if let ItemContainerId::TraitId(trait_id) = loc.container { - db.trait_data(trait_id).visibility.clone() + trait_vis(db, trait_id) } else { item_tree[konst.visibility].clone() }; @@ -519,7 +523,7 @@ struct AssocItemCollector<'a> { db: &'a dyn DefDatabase, module_id: ModuleId, def_map: Arc, - inactive_diagnostics: Vec, + diagnostics: Vec, container: ItemContainerId, expander: Expander, @@ -542,7 +546,7 @@ impl<'a> AssocItemCollector<'a> { expander: Expander::new(db, file_id, module_id), items: Vec::new(), attr_calls: Vec::new(), - inactive_diagnostics: Vec::new(), + diagnostics: Vec::new(), } } @@ -556,11 +560,10 @@ impl<'a> AssocItemCollector<'a> { ( self.items, if self.attr_calls.is_empty() { None } else { Some(Box::new(self.attr_calls)) }, - self.inactive_diagnostics, + self.diagnostics, ) } - // FIXME: proc-macro diagnostics fn collect(&mut self, item_tree: &ItemTree, tree_id: TreeId, assoc_items: &[AssocItem]) { let container = self.container; self.items.reserve(assoc_items.len()); @@ -568,7 +571,7 @@ impl<'a> AssocItemCollector<'a> { 'items: for &item in assoc_items { let attrs = item_tree.attrs(self.db, self.module_id.krate, ModItem::from(item).into()); if !attrs.is_cfg_enabled(self.expander.cfg_options()) { - self.inactive_diagnostics.push(DefDiagnostic::unconfigured_code( + self.diagnostics.push(DefDiagnostic::unconfigured_code( self.module_id.local_id, InFile::new(self.expander.current_file_id(), item.ast_id(item_tree).upcast()), attrs.cfg().unwrap(), @@ -582,84 +585,164 @@ impl<'a> AssocItemCollector<'a> { AstId::new(self.expander.current_file_id(), item.ast_id(item_tree).upcast()); let ast_id_with_path = AstIdWithPath { path: (*attr.path).clone(), ast_id }; - if let Ok(ResolvedAttr::Macro(call_id)) = self.def_map.resolve_attr_macro( + match self.def_map.resolve_attr_macro( self.db, self.module_id.local_id, ast_id_with_path, attr, ) { - self.attr_calls.push((ast_id, call_id)); - // If proc attribute macro expansion is disabled, skip expanding it here - if !self.db.enable_proc_attr_macros() { - continue 'attrs; - } - let loc = self.db.lookup_intern_macro_call(call_id); - if let MacroDefKind::ProcMacro(exp, ..) = loc.def.kind { - // If there's no expander for the proc macro (e.g. the - // proc macro is ignored, or building the proc macro - // crate failed), skip expansion like we would if it was - // disabled. This is analogous to the handling in - // `DefCollector::collect_macros`. - if exp.is_dummy() { + Ok(ResolvedAttr::Macro(call_id)) => { + self.attr_calls.push((ast_id, call_id)); + // If proc attribute macro expansion is disabled, skip expanding it here + if !self.db.expand_proc_attr_macros() { continue 'attrs; } - } - match self.expander.enter_expand_id::(self.db, call_id) { - ExpandResult { value: Some((mark, _)), .. } => { - self.collect_macro_items(mark); - continue 'items; + let loc = self.db.lookup_intern_macro_call(call_id); + if let MacroDefKind::ProcMacro(exp, ..) = loc.def.kind { + // If there's no expander for the proc macro (e.g. the + // proc macro is ignored, or building the proc macro + // crate failed), skip expansion like we would if it was + // disabled. This is analogous to the handling in + // `DefCollector::collect_macros`. + if exp.is_dummy() { + continue 'attrs; + } } - ExpandResult { .. } => {} + + let res = + self.expander.enter_expand_id::(self.db, call_id); + self.collect_macro_items(res, &|| loc.kind.clone()); + continue 'items; + } + Ok(_) => (), + Err(_) => { + self.diagnostics.push(DefDiagnostic::unresolved_macro_call( + self.module_id.local_id, + MacroCallKind::Attr { + ast_id, + attr_args: Arc::new((tt::Subtree::empty(), Default::default())), + invoc_attr_index: attr.id, + }, + attr.path().clone(), + )); } } } - match item { - AssocItem::Function(id) => { - let item = &item_tree[id]; + self.collect_item(item_tree, tree_id, container, item); + } + } - let def = - FunctionLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db); - self.items.push((item.name.clone(), def.into())); - } - AssocItem::Const(id) => { - let item = &item_tree[id]; - - let name = match item.name.clone() { - Some(name) => name, - None => continue, - }; - let def = - ConstLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db); - self.items.push((name, def.into())); - } - AssocItem::TypeAlias(id) => { - let item = &item_tree[id]; + fn collect_item( + &mut self, + item_tree: &ItemTree, + tree_id: TreeId, + container: ItemContainerId, + item: AssocItem, + ) { + match item { + AssocItem::Function(id) => { + let item = &item_tree[id]; - let def = TypeAliasLoc { container, id: ItemTreeId::new(tree_id, id) } - .intern(self.db); - self.items.push((item.name.clone(), def.into())); - } - AssocItem::MacroCall(call) => { - if let Some(root) = self.db.parse_or_expand(self.expander.current_file_id()) { - let call = &item_tree[call]; - - let ast_id_map = self.db.ast_id_map(self.expander.current_file_id()); - let call = ast_id_map.get(call.ast_id).to_node(&root); - let _cx = - stdx::panic_context::enter(format!("collect_items MacroCall: {call}")); - let res = self.expander.enter_expand::(self.db, call); - - if let Ok(ExpandResult { value: Some((mark, _)), .. }) = res { - self.collect_macro_items(mark); - } + let def = + FunctionLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db); + self.items.push((item.name.clone(), def.into())); + } + AssocItem::Const(id) => { + let item = &item_tree[id]; + let Some(name) = item.name.clone() else { return }; + let def = ConstLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db); + self.items.push((name, def.into())); + } + AssocItem::TypeAlias(id) => { + let item = &item_tree[id]; + + let def = + TypeAliasLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db); + self.items.push((item.name.clone(), def.into())); + } + AssocItem::MacroCall(call) => { + let file_id = self.expander.current_file_id(); + let MacroCall { ast_id, expand_to, ref path } = item_tree[call]; + let module = self.expander.module.local_id; + + let resolver = |path| { + self.def_map + .resolve_path( + self.db, + module, + &path, + crate::item_scope::BuiltinShadowMode::Other, + Some(MacroSubNs::Bang), + ) + .0 + .take_macros() + .map(|it| macro_id_to_def_id(self.db, it)) + }; + match macro_call_as_call_id( + self.db.upcast(), + &AstIdWithPath::new(file_id, ast_id, Clone::clone(path)), + expand_to, + self.expander.module.krate(), + resolver, + ) { + Ok(Some(call_id)) => { + let res = + self.expander.enter_expand_id::(self.db, call_id); + self.collect_macro_items(res, &|| hir_expand::MacroCallKind::FnLike { + ast_id: InFile::new(file_id, ast_id), + expand_to: hir_expand::ExpandTo::Items, + }); + } + Ok(None) => (), + Err(_) => { + self.diagnostics.push(DefDiagnostic::unresolved_macro_call( + self.module_id.local_id, + MacroCallKind::FnLike { + ast_id: InFile::new(file_id, ast_id), + expand_to, + }, + Clone::clone(path), + )); } } } } } - fn collect_macro_items(&mut self, mark: Mark) { + fn collect_macro_items( + &mut self, + ExpandResult { value, err }: ExpandResult)>>, + error_call_kind: &dyn Fn() -> hir_expand::MacroCallKind, + ) { + let Some((mark, parse)) = value else { return }; + + if let Some(err) = err { + let diag = match err { + // why is this reported here? + hir_expand::ExpandError::UnresolvedProcMacro(krate) => { + DefDiagnostic::unresolved_proc_macro( + self.module_id.local_id, + error_call_kind(), + krate, + ) + } + _ => DefDiagnostic::macro_error( + self.module_id.local_id, + error_call_kind(), + err.to_string(), + ), + }; + self.diagnostics.push(diag); + } + if let errors @ [_, ..] = parse.errors() { + self.diagnostics.push(DefDiagnostic::macro_expansion_parse_error( + self.module_id.local_id, + error_call_kind(), + errors.into(), + )); + } + let tree_id = item_tree::TreeId::new(self.expander.current_file_id(), None); let item_tree = tree_id.item_tree(self.db); let iter: SmallVec<[_; 2]> = @@ -670,3 +753,10 @@ impl<'a> AssocItemCollector<'a> { self.expander.exit(self.db, mark); } } + +fn trait_vis(db: &dyn DefDatabase, trait_id: TraitId) -> RawVisibility { + let ItemLoc { id: tree_id, .. } = trait_id.lookup(db); + let item_tree = tree_id.item_tree(db); + let tr_def = &item_tree[tree_id.value]; + item_tree[tr_def.visibility].clone() +} diff --git a/src/tools/rust-analyzer/crates/hir-def/src/adt.rs b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs similarity index 89% rename from src/tools/rust-analyzer/crates/hir-def/src/adt.rs rename to src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs index b336f59ffee31..6db5abccc9210 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/adt.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs @@ -1,8 +1,7 @@ //! Defines hir-level representation of structs, enums and unions -use std::sync::Arc; - use base_db::CrateId; +use bitflags::bitflags; use cfg::CfgOptions; use either::Either; @@ -12,15 +11,17 @@ use hir_expand::{ }; use intern::Interned; use la_arena::{Arena, ArenaMap}; -use rustc_abi::{Integer, IntegerType}; +use rustc_abi::{Align, Integer, IntegerType, ReprFlags, ReprOptions}; use syntax::ast::{self, HasName, HasVisibility}; +use triomphe::Arc; use crate::{ - body::{CfgExpander, LowerCtx}, builtin_type::{BuiltinInt, BuiltinUint}, db::DefDatabase, + expander::CfgExpander, item_tree::{AttrOwner, Field, FieldAstId, Fields, ItemTree, ModItem, RawVisibilityId}, - layout::{Align, ReprFlags, ReprOptions}, + lang_item::LangItem, + lower::LowerCtx, nameres::diagnostics::DefDiagnostic, src::HasChildSource, src::HasSource, @@ -39,8 +40,27 @@ pub struct StructData { pub variant_data: Arc, pub repr: Option, pub visibility: RawVisibility, - pub rustc_has_incoherent_inherent_impls: bool, - pub fundamental: bool, + pub flags: StructFlags, +} + +bitflags! { +#[derive(Debug, Clone, PartialEq, Eq)] + pub struct StructFlags: u8 { + const NO_FLAGS = 0; + /// Indicates whether the struct is `PhantomData`. + const IS_PHANTOM_DATA = 1 << 2; + /// Indicates whether the struct has a `#[fundamental]` attribute. + const IS_FUNDAMENTAL = 1 << 3; + // FIXME: should this be a flag? + /// Indicates whether the struct has a `#[rustc_has_incoherent_inherent_impls]` attribute. + const IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL = 1 << 4; + /// Indicates whether this struct is `Box`. + const IS_BOX = 1 << 5; + /// Indicates whether this struct is `ManuallyDrop`. + const IS_MANUALLY_DROP = 1 << 6; + /// Indicates whether this struct is `UnsafeCell`. + const IS_UNSAFE_CELL = 1 << 7; + } } #[derive(Debug, Clone, PartialEq, Eq)] @@ -174,10 +194,25 @@ impl StructData { let item_tree = loc.id.item_tree(db); let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into()); let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone(); + let attrs = item_tree.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into()); - let rustc_has_incoherent_inherent_impls = - attrs.by_key("rustc_has_incoherent_inherent_impls").exists(); - let fundamental = attrs.by_key("fundamental").exists(); + + let mut flags = StructFlags::NO_FLAGS; + if attrs.by_key("rustc_has_incoherent_inherent_impls").exists() { + flags |= StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL; + } + if attrs.by_key("fundamental").exists() { + flags |= StructFlags::IS_FUNDAMENTAL; + } + if let Some(lang) = attrs.lang_item() { + match lang { + LangItem::PhantomData => flags |= StructFlags::IS_PHANTOM_DATA, + LangItem::OwnedBox => flags |= StructFlags::IS_BOX, + LangItem::ManuallyDrop => flags |= StructFlags::IS_MANUALLY_DROP, + LangItem::UnsafeCell => flags |= StructFlags::IS_UNSAFE_CELL, + _ => (), + } + } let strukt = &item_tree[loc.id.value]; let (variant_data, diagnostics) = lower_fields( @@ -196,8 +231,7 @@ impl StructData { variant_data: Arc::new(variant_data), repr, visibility: item_tree[strukt.visibility].clone(), - rustc_has_incoherent_inherent_impls, - fundamental, + flags, }), diagnostics.into(), ) @@ -218,9 +252,13 @@ impl StructData { let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone(); let attrs = item_tree.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into()); - let rustc_has_incoherent_inherent_impls = - attrs.by_key("rustc_has_incoherent_inherent_impls").exists(); - let fundamental = attrs.by_key("fundamental").exists(); + let mut flags = StructFlags::NO_FLAGS; + if attrs.by_key("rustc_has_incoherent_inherent_impls").exists() { + flags |= StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL; + } + if attrs.by_key("fundamental").exists() { + flags |= StructFlags::IS_FUNDAMENTAL; + } let union = &item_tree[loc.id.value]; let (variant_data, diagnostics) = lower_fields( @@ -239,8 +277,7 @@ impl StructData { variant_data: Arc::new(variant_data), repr, visibility: item_tree[union.visibility].clone(), - rustc_has_incoherent_inherent_impls, - fundamental, + flags, }), diagnostics.into(), ) @@ -436,7 +473,7 @@ fn lower_struct( trace: &mut Trace>, ast: &InFile, ) -> StructKind { - let ctx = LowerCtx::new(db, ast.file_id); + let ctx = LowerCtx::new(db, &expander.hygiene(), ast.file_id); match &ast.value { ast::StructKind::Tuple(fl) => { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/db.rs b/src/tools/rust-analyzer/crates/hir-def/src/db.rs index 9371fc14dd8a0..6d18e3f56cabc 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/db.rs @@ -1,32 +1,32 @@ //! Defines database & queries for name resolution. -use std::sync::Arc; - use base_db::{salsa, CrateId, SourceDatabase, Upcast}; use either::Either; use hir_expand::{db::ExpandDatabase, HirFileId}; use intern::Interned; use la_arena::ArenaMap; use syntax::{ast, AstPtr}; +use triomphe::Arc; use crate::{ - adt::{EnumData, StructData}, attr::{Attrs, AttrsWithOwner}, body::{scope::ExprScopes, Body, BodySourceMap}, data::{ + adt::{EnumData, StructData}, ConstData, FunctionData, ImplData, Macro2Data, MacroRulesData, ProcMacroData, StaticData, TraitAliasData, TraitData, TypeAliasData, }, generics::GenericParams, + hir::ExprId, import_map::ImportMap, item_tree::{AttrOwner, ItemTree}, lang_item::{LangItem, LangItemTarget, LangItems}, nameres::{diagnostics::DefDiagnostic, DefMap}, visibility::{self, Visibility}, - AttrDefId, BlockId, BlockLoc, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, ExternBlockId, - ExternBlockLoc, FunctionId, FunctionLoc, GenericDefId, ImplId, ImplLoc, LocalEnumVariantId, - LocalFieldId, Macro2Id, Macro2Loc, MacroRulesId, MacroRulesLoc, ProcMacroId, ProcMacroLoc, - StaticId, StaticLoc, StructId, StructLoc, TraitAliasId, TraitAliasLoc, TraitId, TraitLoc, - TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, VariantId, + AnonymousConstId, AttrDefId, BlockId, BlockLoc, ConstId, ConstLoc, DefWithBodyId, EnumId, + EnumLoc, ExternBlockId, ExternBlockLoc, FunctionId, FunctionLoc, GenericDefId, ImplId, ImplLoc, + LocalEnumVariantId, LocalFieldId, Macro2Id, Macro2Loc, MacroRulesId, MacroRulesLoc, + ProcMacroId, ProcMacroLoc, StaticId, StaticLoc, StructId, StructLoc, TraitAliasId, + TraitAliasLoc, TraitId, TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, VariantId, }; #[salsa::query_group(InternDatabaseStorage)] @@ -61,12 +61,14 @@ pub trait InternDatabase: SourceDatabase { fn intern_proc_macro(&self, loc: ProcMacroLoc) -> ProcMacroId; #[salsa::interned] fn intern_macro_rules(&self, loc: MacroRulesLoc) -> MacroRulesId; + #[salsa::interned] + fn intern_anonymous_const(&self, id: (DefWithBodyId, ExprId)) -> AnonymousConstId; } #[salsa::query_group(DefDatabaseStorage)] pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast { #[salsa::input] - fn enable_proc_attr_macros(&self) -> bool; + fn expand_proc_attr_macros(&self) -> bool; #[salsa::invoke(ItemTree::file_item_tree_query)] fn file_item_tree(&self, file_id: HirFileId) -> Arc; @@ -94,7 +96,9 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast Option>; + fn block_def_map(&self, block: BlockId) -> Arc; + + // region:data #[salsa::invoke(StructData::struct_data_query)] fn struct_data(&self, id: StructId) -> Arc; @@ -151,6 +155,8 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast Arc; + // endregion:data + #[salsa::invoke(Body::body_with_source_map_query)] fn body_with_source_map(&self, def: DefWithBodyId) -> (Arc, Arc); @@ -163,6 +169,8 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast Interned; + // region:attrs + #[salsa::invoke(Attrs::variants_attrs_query)] fn variants_attrs(&self, def: EnumId) -> Arc>; @@ -182,10 +190,13 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast Arc, AstPtr>>>; #[salsa::invoke(AttrsWithOwner::attrs_query)] - fn attrs(&self, def: AttrDefId) -> AttrsWithOwner; + fn attrs(&self, def: AttrDefId) -> Attrs; - #[salsa::invoke(LangItems::crate_lang_items_query)] - fn crate_lang_items(&self, krate: CrateId) -> Arc; + #[salsa::transparent] + #[salsa::invoke(AttrsWithOwner::attrs_with_owner)] + fn attrs_with_owner(&self, def: AttrDefId) -> AttrsWithOwner; + + // endregion:attrs #[salsa::invoke(LangItems::lang_item_query)] fn lang_item(&self, start_crate: CrateId, item: LangItem) -> Option; @@ -193,6 +204,8 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast Arc; + // region:visibilities + #[salsa::invoke(visibility::field_visibilities_query)] fn field_visibilities(&self, var: VariantId) -> Arc>; @@ -203,9 +216,17 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast Visibility; + // endregion:visibilities + + #[salsa::invoke(LangItems::crate_lang_items_query)] + fn crate_lang_items(&self, krate: CrateId) -> Arc; + #[salsa::transparent] fn crate_limits(&self, crate_id: CrateId) -> CrateLimits; + #[salsa::transparent] + fn recursion_limit(&self, crate_id: CrateId) -> u32; + fn crate_supports_no_std(&self, crate_id: CrateId) -> bool; } @@ -228,6 +249,10 @@ fn crate_limits(db: &dyn DefDatabase, crate_id: CrateId) -> CrateLimits { } } +fn recursion_limit(db: &dyn DefDatabase, crate_id: CrateId) -> u32 { + db.crate_limits(crate_id).recursion_limit +} + fn crate_supports_no_std(db: &dyn DefDatabase, crate_id: CrateId) -> bool { let file = db.crate_graph()[crate_id].root_file_id; let item_tree = db.file_item_tree(file.into()); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs index 166aa04da044f..63138aa6ad78f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs @@ -21,6 +21,8 @@ //! //! This is a work of fiction. Any similarities to Kotlin's `BindingContext` are //! a coincidence. +pub mod keys; + use std::{ hash::Hash, marker::PhantomData, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/keys.rs b/src/tools/rust-analyzer/crates/hir-def/src/dyn_map/keys.rs similarity index 100% rename from src/tools/rust-analyzer/crates/hir-def/src/keys.rs rename to src/tools/rust-analyzer/crates/hir-def/src/dyn_map/keys.rs diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expander.rs b/src/tools/rust-analyzer/crates/hir-def/src/expander.rs new file mode 100644 index 0000000000000..34ed1e72f2001 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-def/src/expander.rs @@ -0,0 +1,211 @@ +//! Macro expansion utilities. + +use base_db::CrateId; +use cfg::CfgOptions; +use drop_bomb::DropBomb; +use hir_expand::{ + attrs::RawAttrs, hygiene::Hygiene, mod_path::ModPath, ExpandError, ExpandResult, HirFileId, + InFile, MacroCallId, UnresolvedMacro, +}; +use limit::Limit; +use syntax::{ast, Parse, SyntaxNode}; + +use crate::{ + attr::Attrs, db::DefDatabase, lower::LowerCtx, macro_id_to_def_id, path::Path, AsMacroCall, + MacroId, ModuleId, +}; + +/// A subset of Expander that only deals with cfg attributes. We only need it to +/// avoid cyclic queries in crate def map during enum processing. +#[derive(Debug)] +pub(crate) struct CfgExpander { + cfg_options: CfgOptions, + hygiene: Hygiene, + krate: CrateId, +} + +#[derive(Debug)] +pub struct Expander { + cfg_expander: CfgExpander, + pub(crate) current_file_id: HirFileId, + pub(crate) module: ModuleId, + /// `recursion_depth == usize::MAX` indicates that the recursion limit has been reached. + recursion_depth: u32, + recursion_limit: Limit, +} + +impl CfgExpander { + pub(crate) fn new( + db: &dyn DefDatabase, + current_file_id: HirFileId, + krate: CrateId, + ) -> CfgExpander { + let hygiene = Hygiene::new(db.upcast(), current_file_id); + let cfg_options = db.crate_graph()[krate].cfg_options.clone(); + CfgExpander { cfg_options, hygiene, krate } + } + + pub(crate) fn parse_attrs(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> Attrs { + Attrs::filter(db, self.krate, RawAttrs::new(db.upcast(), owner, &self.hygiene)) + } + + pub(crate) fn is_cfg_enabled(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> bool { + let attrs = self.parse_attrs(db, owner); + attrs.is_cfg_enabled(&self.cfg_options) + } + + pub(crate) fn hygiene(&self) -> &Hygiene { + &self.hygiene + } +} + +impl Expander { + pub fn new(db: &dyn DefDatabase, current_file_id: HirFileId, module: ModuleId) -> Expander { + let cfg_expander = CfgExpander::new(db, current_file_id, module.krate); + let recursion_limit = db.recursion_limit(module.krate); + #[cfg(not(test))] + let recursion_limit = Limit::new(recursion_limit as usize); + // Without this, `body::tests::your_stack_belongs_to_me` stack-overflows in debug + #[cfg(test)] + let recursion_limit = Limit::new(std::cmp::min(32, recursion_limit as usize)); + Expander { cfg_expander, current_file_id, module, recursion_depth: 0, recursion_limit } + } + + pub fn enter_expand( + &mut self, + db: &dyn DefDatabase, + macro_call: ast::MacroCall, + resolver: impl Fn(ModPath) -> Option, + ) -> Result)>>, UnresolvedMacro> { + // FIXME: within_limit should support this, instead of us having to extract the error + let mut unresolved_macro_err = None; + + let result = self.within_limit(db, |this| { + let macro_call = InFile::new(this.current_file_id, ¯o_call); + match macro_call.as_call_id_with_errors(db.upcast(), this.module.krate(), |path| { + resolver(path).map(|it| macro_id_to_def_id(db, it)) + }) { + Ok(call_id) => call_id, + Err(resolve_err) => { + unresolved_macro_err = Some(resolve_err); + ExpandResult { value: None, err: None } + } + } + }); + + if let Some(err) = unresolved_macro_err { + Err(err) + } else { + Ok(result) + } + } + + pub fn enter_expand_id( + &mut self, + db: &dyn DefDatabase, + call_id: MacroCallId, + ) -> ExpandResult)>> { + self.within_limit(db, |_this| ExpandResult::ok(Some(call_id))) + } + + fn enter_expand_inner( + db: &dyn DefDatabase, + call_id: MacroCallId, + error: Option, + ) -> ExpandResult>>> { + let file_id = call_id.as_file(); + let ExpandResult { value, err } = db.parse_or_expand_with_err(file_id); + + ExpandResult { value: Some(InFile::new(file_id, value)), err: error.or(err) } + } + + pub fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) { + self.cfg_expander.hygiene = Hygiene::new(db.upcast(), mark.file_id); + self.current_file_id = mark.file_id; + if self.recursion_depth == u32::MAX { + // Recursion limit has been reached somewhere in the macro expansion tree. Reset the + // depth only when we get out of the tree. + if !self.current_file_id.is_macro() { + self.recursion_depth = 0; + } + } else { + self.recursion_depth -= 1; + } + mark.bomb.defuse(); + } + + pub fn ctx<'a>(&self, db: &'a dyn DefDatabase) -> LowerCtx<'a> { + LowerCtx::new(db, &self.cfg_expander.hygiene, self.current_file_id) + } + + pub(crate) fn to_source(&self, value: T) -> InFile { + InFile { file_id: self.current_file_id, value } + } + + pub(crate) fn parse_attrs(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> Attrs { + self.cfg_expander.parse_attrs(db, owner) + } + + pub(crate) fn cfg_options(&self) -> &CfgOptions { + &self.cfg_expander.cfg_options + } + + pub fn current_file_id(&self) -> HirFileId { + self.current_file_id + } + + pub(crate) fn parse_path(&mut self, db: &dyn DefDatabase, path: ast::Path) -> Option { + let ctx = LowerCtx::with_hygiene(db, &self.cfg_expander.hygiene); + Path::from_src(path, &ctx) + } + + fn within_limit( + &mut self, + db: &dyn DefDatabase, + op: F, + ) -> ExpandResult)>> + where + F: FnOnce(&mut Self) -> ExpandResult>, + { + if self.recursion_depth == u32::MAX { + // Recursion limit has been reached somewhere in the macro expansion tree. We should + // stop expanding other macro calls in this tree, or else this may result in + // exponential number of macro expansions, leading to a hang. + // + // The overflow error should have been reported when it occurred (see the next branch), + // so don't return overflow error here to avoid diagnostics duplication. + cov_mark::hit!(overflow_but_not_me); + return ExpandResult::only_err(ExpandError::RecursionOverflowPoisoned); + } else if self.recursion_limit.check(self.recursion_depth as usize + 1).is_err() { + self.recursion_depth = u32::MAX; + cov_mark::hit!(your_stack_belongs_to_me); + return ExpandResult::only_err(ExpandError::Other( + "reached recursion limit during macro expansion".into(), + )); + } + + let ExpandResult { value, err } = op(self); + let Some(call_id) = value else { + return ExpandResult { value: None, err }; + }; + + Self::enter_expand_inner(db, call_id, err).map(|value| { + value.and_then(|InFile { file_id, value }| { + let parse = value.cast::()?; + + self.recursion_depth += 1; + self.cfg_expander.hygiene = Hygiene::new(db.upcast(), file_id); + let old_file_id = std::mem::replace(&mut self.current_file_id, file_id); + let mark = + Mark { file_id: old_file_id, bomb: DropBomb::new("expansion mark dropped") }; + Some((mark, parse)) + }) + }) + } +} + +#[derive(Debug)] +pub struct Mark { + file_id: HirFileId, + bomb: DropBomb, +} diff --git a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs index 3f43923208371..e8cc2eab4617e 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs @@ -42,7 +42,7 @@ const MAX_PATH_LEN: usize = 15; #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum PrefixKind { /// Causes paths to always start with either `self`, `super`, `crate` or a crate-name. - /// This is the same as plain, just that paths will start with `self` iprepended f the path + /// This is the same as plain, just that paths will start with `self` prepended if the path /// starts with an identifier that is not a crate. BySelf, /// Causes paths to ignore imports in the local module. @@ -81,7 +81,7 @@ fn find_path_inner( } let def_map = from.def_map(db); - let crate_root = def_map.crate_root(db); + let crate_root = def_map.crate_root(); // - if the item is a module, jump straight to module search if let ItemInNs::Types(ModuleDefId::ModuleId(module_id)) = item { let mut visited_modules = FxHashSet::default(); @@ -183,7 +183,7 @@ fn find_path_for_module( // - if the item is the crate root of a dependency crate, return the name from the extern prelude let root_def_map = crate_root.def_map(db); - for (name, &def_id) in root_def_map.extern_prelude() { + for (name, def_id) in root_def_map.extern_prelude() { if module_id == def_id { let name = scope_name.unwrap_or_else(|| name.clone()); @@ -454,7 +454,7 @@ fn find_local_import_locations( worklist.push(ancestor); } - let def_map = def_map.crate_root(db).def_map(db); + let def_map = def_map.crate_root().def_map(db); let mut seen: FxHashSet<_> = FxHashSet::default(); @@ -543,6 +543,7 @@ mod tests { module.local_id, &mod_path, crate::item_scope::BuiltinShadowMode::Module, + None, ) .0 .take_types() diff --git a/src/tools/rust-analyzer/crates/hir-def/src/generics.rs b/src/tools/rust-analyzer/crates/hir-def/src/generics.rs index e4912fa8a64a4..f19c3f028f42f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/generics.rs @@ -12,16 +12,17 @@ use hir_expand::{ use intern::Interned; use la_arena::{Arena, ArenaMap, Idx}; use once_cell::unsync::Lazy; -use std::ops::DerefMut; use stdx::impl_from; use syntax::ast::{self, HasGenericParams, HasName, HasTypeBounds}; +use triomphe::Arc; use crate::{ - body::{Expander, LowerCtx}, child_by_source::ChildBySource, db::DefDatabase, - dyn_map::DynMap, - keys, + dyn_map::{keys, DynMap}, + expander::Expander, + lower::LowerCtx, + nameres::{DefMap, MacroSubNs}, src::{HasChildSource, HasSource}, type_ref::{LifetimeRef, TypeBound, TypeRef}, AdtId, ConstParamId, GenericDefId, HasModule, LifetimeParamId, LocalLifetimeParamId, @@ -153,7 +154,6 @@ impl GenericParams { def: GenericDefId, ) -> Interned { let _p = profile::span("generic_params_query"); - macro_rules! id_to_generics { ($id:ident) => {{ let id = $id.lookup(db).id; @@ -176,8 +176,10 @@ impl GenericParams { // Don't create an `Expander` nor call `loc.source(db)` if not needed since this // causes a reparse after the `ItemTree` has been created. - let mut expander = Lazy::new(|| Expander::new(db, loc.source(db).file_id, module)); - for (_, param) in &func_data.params { + let mut expander = Lazy::new(|| { + (module.def_map(db), Expander::new(db, loc.source(db).file_id, module)) + }); + for param in &func_data.params { generic_params.fill_implicit_impl_trait_args(db, &mut expander, param); } @@ -329,7 +331,7 @@ impl GenericParams { pub(crate) fn fill_implicit_impl_trait_args( &mut self, db: &dyn DefDatabase, - expander: &mut impl DerefMut, + exp: &mut Lazy<(Arc, Expander), impl FnOnce() -> (Arc, Expander)>, type_ref: &TypeRef, ) { type_ref.walk(&mut |type_ref| { @@ -349,14 +351,28 @@ impl GenericParams { } if let TypeRef::Macro(mc) = type_ref { let macro_call = mc.to_node(db.upcast()); - match expander.enter_expand::(db, macro_call) { - Ok(ExpandResult { value: Some((mark, expanded)), .. }) => { - let ctx = LowerCtx::new(db, expander.current_file_id()); - let type_ref = TypeRef::from_ast(&ctx, expanded); - self.fill_implicit_impl_trait_args(db, expander, &type_ref); - expander.exit(db, mark); - } - _ => {} + let (def_map, expander) = &mut **exp; + + let module = expander.module.local_id; + let resolver = |path| { + def_map + .resolve_path( + db, + module, + &path, + crate::item_scope::BuiltinShadowMode::Other, + Some(MacroSubNs::Bang), + ) + .0 + .take_macros() + }; + if let Ok(ExpandResult { value: Some((mark, expanded)), .. }) = + expander.enter_expand(db, macro_call, resolver) + { + let ctx = expander.ctx(db); + let type_ref = TypeRef::from_ast(&ctx, expanded.tree()); + self.fill_implicit_impl_trait_args(db, &mut *exp, &type_ref); + exp.1.exit(db, mark); } } }); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs similarity index 76% rename from src/tools/rust-analyzer/crates/hir-def/src/expr.rs rename to src/tools/rust-analyzer/crates/hir-def/src/hir.rs index 19fa6b25419e1..4ad8a7aa8eb12 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs @@ -12,26 +12,29 @@ //! //! See also a neighboring `body` module. +pub mod type_ref; + use std::fmt; use hir_expand::name::Name; use intern::Interned; use la_arena::{Idx, RawIdx}; use smallvec::SmallVec; +use syntax::ast; use crate::{ builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint}, path::{GenericArgs, Path}, type_ref::{Mutability, Rawness, TypeRef}, - BlockId, + AnonymousConstId, BlockId, }; pub use syntax::ast::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp}; -pub type ExprId = Idx; - pub type BindingId = Idx; +pub type ExprId = Idx; + /// FIXME: this is a hacky function which should be removed pub(crate) fn dummy_expr_id() -> ExprId { ExprId::from_raw(RawIdx::from(u32::MAX)) @@ -82,6 +85,7 @@ impl fmt::Display for FloatTypeWrapper { pub enum Literal { String(Box), ByteString(Box<[u8]>), + CString(Box), Char(char), Bool(bool), Int(i128, Option), @@ -92,6 +96,66 @@ pub enum Literal { Float(FloatTypeWrapper, Option), } +#[derive(Debug, Clone, Eq, PartialEq)] +/// Used in range patterns. +pub enum LiteralOrConst { + Literal(Literal), + Const(Path), +} + +impl Literal { + pub fn negate(self) -> Option { + if let Literal::Int(i, k) = self { + Some(Literal::Int(-i, k)) + } else { + None + } + } +} + +impl From for Literal { + fn from(ast_lit_kind: ast::LiteralKind) -> Self { + use ast::LiteralKind; + match ast_lit_kind { + // FIXME: these should have actual values filled in, but unsure on perf impact + LiteralKind::IntNumber(lit) => { + if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) { + Literal::Float( + FloatTypeWrapper::new(lit.float_value().unwrap_or(Default::default())), + builtin, + ) + } else if let builtin @ Some(_) = lit.suffix().and_then(BuiltinUint::from_suffix) { + Literal::Uint(lit.value().unwrap_or(0), builtin) + } else { + let builtin = lit.suffix().and_then(BuiltinInt::from_suffix); + Literal::Int(lit.value().unwrap_or(0) as i128, builtin) + } + } + LiteralKind::FloatNumber(lit) => { + let ty = lit.suffix().and_then(BuiltinFloat::from_suffix); + Literal::Float(FloatTypeWrapper::new(lit.value().unwrap_or(Default::default())), ty) + } + LiteralKind::ByteString(bs) => { + let text = bs.value().map(Box::from).unwrap_or_else(Default::default); + Literal::ByteString(text) + } + LiteralKind::String(s) => { + let text = s.value().map(Box::from).unwrap_or_else(Default::default); + Literal::String(text) + } + LiteralKind::CString(s) => { + let text = s.value().map(Box::from).unwrap_or_else(Default::default); + Literal::CString(text) + } + LiteralKind::Byte(b) => { + Literal::Uint(b.value().unwrap_or_default() as u128, Some(BuiltinUint::U8)) + } + LiteralKind::Char(c) => Literal::Char(c.value().unwrap_or_default()), + LiteralKind::Bool(val) => Literal::Bool(val), + } + } +} + #[derive(Debug, Clone, Eq, PartialEq)] pub enum Expr { /// This is produced if the syntax tree does not have a required expression piece. @@ -107,28 +171,19 @@ pub enum Expr { expr: ExprId, }, Block { - id: BlockId, + id: Option, statements: Box<[Statement]>, tail: Option, label: Option, }, - TryBlock { - id: BlockId, - statements: Box<[Statement]>, - tail: Option, - }, Async { - id: BlockId, - statements: Box<[Statement]>, - tail: Option, - }, - Const { - id: BlockId, + id: Option, statements: Box<[Statement]>, tail: Option, }, + Const(AnonymousConstId), Unsafe { - id: BlockId, + id: Option, statements: Box<[Statement]>, tail: Option, }, @@ -141,12 +196,6 @@ pub enum Expr { body: ExprId, label: Option, }, - For { - iterable: ExprId, - pat: PatId, - body: ExprId, - label: Option, - }, Call { callee: ExprId, args: Box<[ExprId]>, @@ -163,11 +212,11 @@ pub enum Expr { arms: Box<[MatchArm]>, }, Continue { - label: Option, + label: Option, }, Break { expr: Option, - label: Option, + label: Option, }, Return { expr: Option, @@ -192,9 +241,6 @@ pub enum Expr { Await { expr: ExprId, }, - Try { - expr: ExprId, - }, Cast { expr: ExprId, type_ref: Interned, @@ -231,6 +277,7 @@ pub enum Expr { ret_type: Option>, body: ExprId, closure_kind: ClosureKind, + capture_by: CaptureBy, }, Tuple { exprs: Box<[ExprId]>, @@ -248,6 +295,14 @@ pub enum ClosureKind { Async, } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum CaptureBy { + /// `move |x| y + x`. + Value, + /// `move` keyword was not specified. + Ref, +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Movability { Static, @@ -302,11 +357,10 @@ impl Expr { Expr::Let { expr, .. } => { f(*expr); } + Expr::Const(_) => (), Expr::Block { statements, tail, .. } - | Expr::TryBlock { statements, tail, .. } | Expr::Unsafe { statements, tail, .. } - | Expr::Async { statements, tail, .. } - | Expr::Const { statements, tail, .. } => { + | Expr::Async { statements, tail, .. } => { for stmt in statements.iter() { match stmt { Statement::Let { initializer, else_branch, .. } => { @@ -329,10 +383,6 @@ impl Expr { f(*condition); f(*body); } - Expr::For { iterable, body, .. } => { - f(*iterable); - f(*body); - } Expr::Call { callee, args, .. } => { f(*callee); args.iter().copied().for_each(f); @@ -383,7 +433,6 @@ impl Expr { } Expr::Field { expr, .. } | Expr::Await { expr } - | Expr::Try { expr } | Expr::Cast { expr, .. } | Expr::Ref { expr, .. } | Expr::UnaryOp { expr, .. } @@ -437,11 +486,38 @@ impl BindingAnnotation { } } +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum BindingProblems { + /// https://doc.rust-lang.org/stable/error_codes/E0416.html + BoundMoreThanOnce, + /// https://doc.rust-lang.org/stable/error_codes/E0409.html + BoundInconsistently, + /// https://doc.rust-lang.org/stable/error_codes/E0408.html + NotBoundAcrossAll, +} + #[derive(Debug, Clone, Eq, PartialEq)] pub struct Binding { pub name: Name, pub mode: BindingAnnotation, pub definitions: SmallVec<[PatId; 1]>, + /// Id of the closure/generator that owns this binding. If it is owned by the + /// top level expression, this field would be `None`. + pub owner: Option, + pub problems: Option, +} + +impl Binding { + pub fn is_upvar(&self, relative_to: ExprId) -> bool { + match self.owner { + Some(x) => { + // We assign expression ids in a way that outer closures will receive + // a lower id + x.into_raw() < relative_to.into_raw() + } + None => true, + } + } } #[derive(Debug, Clone, Eq, PartialEq)] @@ -458,7 +534,7 @@ pub enum Pat { Tuple { args: Box<[PatId]>, ellipsis: Option }, Or(Box<[PatId]>), Record { path: Option>, args: Box<[RecordFieldPat]>, ellipsis: bool }, - Range { start: ExprId, end: ExprId }, + Range { start: Option>, end: Option> }, Slice { prefix: Box<[PatId]>, slice: Option, suffix: Box<[PatId]> }, Path(Box), Lit(ExprId), diff --git a/src/tools/rust-analyzer/crates/hir-def/src/type_ref.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs similarity index 96% rename from src/tools/rust-analyzer/crates/hir-def/src/type_ref.rs rename to src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs index 8e30f429a9f80..0573c9a6f8af4 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/type_ref.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs @@ -1,9 +1,11 @@ //! HIR for references to types. Paths in these are not yet resolved. They can //! be directly created from an ast::TypeRef, without further queries. +use core::fmt; use std::fmt::Write; use hir_expand::{ + db::ExpandDatabase, name::{AsName, Name}, AstId, }; @@ -11,9 +13,9 @@ use intern::Interned; use syntax::ast::{self, HasName}; use crate::{ - body::LowerCtx, builtin_type::{BuiltinInt, BuiltinType, BuiltinUint}, - expr::Literal, + hir::Literal, + lower::LowerCtx, path::Path, }; @@ -383,15 +385,6 @@ pub enum ConstRefOrPath { Path(Name), } -impl std::fmt::Display for ConstRefOrPath { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - ConstRefOrPath::Scalar(s) => s.fmt(f), - ConstRefOrPath::Path(n) => n.fmt(f), - } - } -} - impl ConstRefOrPath { pub(crate) fn from_expr_opt(expr: Option) -> Self { match expr { @@ -400,6 +393,19 @@ impl ConstRefOrPath { } } + pub fn display<'a>(&'a self, db: &'a dyn ExpandDatabase) -> impl fmt::Display + 'a { + struct Display<'a>(&'a dyn ExpandDatabase, &'a ConstRefOrPath); + impl fmt::Display for Display<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.1 { + ConstRefOrPath::Scalar(s) => s.fmt(f), + ConstRefOrPath::Path(n) => n.display(self.0).fmt(f), + } + } + } + Display(db, self) + } + // FIXME: as per the comments on `TypeRef::Array`, this evaluation should not happen at this // parse stage. fn from_expr(expr: ast::Expr) -> Self { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs index 4f1f6000db0e3..48532655e04b1 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs @@ -1,6 +1,6 @@ //! A map of all publicly exported items in a crate. -use std::{fmt, hash::BuildHasherDefault, sync::Arc}; +use std::{fmt, hash::BuildHasherDefault}; use base_db::CrateId; use fst::{self, Streamer}; @@ -8,10 +8,11 @@ use hir_expand::name::Name; use indexmap::{map::Entry, IndexMap}; use itertools::Itertools; use rustc_hash::{FxHashSet, FxHasher}; +use triomphe::Arc; use crate::{ - db::DefDatabase, item_scope::ItemInNs, visibility::Visibility, AssocItemId, ModuleDefId, - ModuleId, TraitId, + db::DefDatabase, item_scope::ItemInNs, nameres::DefMap, visibility::Visibility, AssocItemId, + ModuleDefId, ModuleId, TraitId, }; type FxIndexMap = IndexMap>; @@ -32,13 +33,23 @@ pub struct ImportPath { pub segments: Vec, } -impl fmt::Display for ImportPath { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&self.segments.iter().format("::"), f) +impl ImportPath { + pub fn display<'a>(&'a self, db: &'a dyn DefDatabase) -> impl fmt::Display + 'a { + struct Display<'a> { + db: &'a dyn DefDatabase, + path: &'a ImportPath, + } + impl fmt::Display for Display<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt( + &self.path.segments.iter().map(|it| it.display(self.db.upcast())).format("::"), + f, + ) + } + } + Display { db, path: self } } -} -impl ImportPath { fn len(&self) -> usize { self.segments.len() } @@ -75,7 +86,7 @@ impl ImportMap { let mut importables = import_map .map .iter() - .map(|(item, info)| (item, fst_path(&info.path))) + .map(|(item, info)| (item, fst_path(db, &info.path))) .collect::>(); importables.sort_by(|(_, fst_path), (_, fst_path2)| fst_path.cmp(fst_path2)); @@ -112,6 +123,25 @@ impl ImportMap { self.map.get(&item) } + #[cfg(test)] + fn fmt_for_test(&self, db: &dyn DefDatabase) -> String { + let mut importable_paths: Vec<_> = self + .map + .iter() + .map(|(item, info)| { + let ns = match item { + ItemInNs::Types(_) => "t", + ItemInNs::Values(_) => "v", + ItemInNs::Macros(_) => "m", + }; + format!("- {} ({ns})", info.path.display(db)) + }) + .collect(); + + importable_paths.sort(); + importable_paths.join("\n") + } + fn collect_trait_assoc_items( &mut self, db: &dyn DefDatabase, @@ -153,7 +183,7 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> ImportMap { // We look only into modules that are public(ly reexported), starting with the crate root. let empty = ImportPath { segments: vec![] }; - let root = def_map.module_id(def_map.root()); + let root = def_map.module_id(DefMap::ROOT); let mut worklist = vec![(root, empty)]; while let Some((module, mod_path)) = worklist.pop() { let ext_def_map; @@ -233,13 +263,10 @@ impl fmt::Debug for ImportMap { let mut importable_paths: Vec<_> = self .map .iter() - .map(|(item, info)| { - let ns = match item { - ItemInNs::Types(_) => "t", - ItemInNs::Values(_) => "v", - ItemInNs::Macros(_) => "m", - }; - format!("- {} ({ns})", info.path) + .map(|(item, _)| match item { + ItemInNs::Types(it) => format!("- {it:?} (t)",), + ItemInNs::Values(it) => format!("- {it:?} (v)",), + ItemInNs::Macros(it) => format!("- {it:?} (m)",), }) .collect(); @@ -248,9 +275,9 @@ impl fmt::Debug for ImportMap { } } -fn fst_path(path: &ImportPath) -> String { +fn fst_path(db: &dyn DefDatabase, path: &ImportPath) -> String { let _p = profile::span("fst_path"); - let mut s = path.to_string(); + let mut s = path.display(db).to_string(); s.make_ascii_lowercase(); s } @@ -343,7 +370,12 @@ impl Query { self } - fn import_matches(&self, import: &ImportInfo, enforce_lowercase: bool) -> bool { + fn import_matches( + &self, + db: &dyn DefDatabase, + import: &ImportInfo, + enforce_lowercase: bool, + ) -> bool { let _p = profile::span("import_map::Query::import_matches"); if import.is_trait_assoc_item { if self.exclude_import_kinds.contains(&ImportKind::AssociatedItem) { @@ -354,9 +386,9 @@ impl Query { } let mut input = if import.is_trait_assoc_item || self.name_only { - import.path.segments.last().unwrap().to_string() + import.path.segments.last().unwrap().display(db.upcast()).to_string() } else { - import.path.to_string() + import.path.display(db).to_string() }; if enforce_lowercase || !self.case_sensitive { input.make_ascii_lowercase(); @@ -421,25 +453,27 @@ pub fn search_dependencies( let importables = &import_map.importables[indexed_value.value as usize..]; let common_importable_data = &import_map.map[&importables[0]]; - if !query.import_matches(common_importable_data, true) { + if !query.import_matches(db, common_importable_data, true) { continue; } // Path shared by the importable items in this group. - let common_importables_path_fst = fst_path(&common_importable_data.path); + let common_importables_path_fst = fst_path(db, &common_importable_data.path); // Add the items from this `ModPath` group. Those are all subsequent items in // `importables` whose paths match `path`. let iter = importables .iter() .copied() - .take_while(|item| common_importables_path_fst == fst_path(&import_map.map[item].path)) + .take_while(|item| { + common_importables_path_fst == fst_path(db, &import_map.map[item].path) + }) .filter(|&item| match item_import_kind(item) { Some(import_kind) => !query.exclude_import_kinds.contains(&import_kind), None => true, }) .filter(|item| { !query.case_sensitive // we've already checked the common importables path case-insensitively - || query.import_matches(&import_map.map[item], false) + || query.import_matches(db, &import_map.map[item], false) }); res.extend(iter); @@ -472,7 +506,7 @@ mod tests { use base_db::{fixture::WithFixture, SourceDatabase, Upcast}; use expect_test::{expect, Expect}; - use crate::{test_db::TestDB, ItemContainerId, Lookup}; + use crate::{db::DefDatabase, test_db::TestDB, ItemContainerId, Lookup}; use super::*; @@ -496,7 +530,7 @@ mod tests { let (path, mark) = match assoc_item_path(&db, &dependency_imports, dependency) { Some(assoc_item_path) => (assoc_item_path, "a"), None => ( - dependency_imports.path_of(dependency)?.to_string(), + dependency_imports.path_of(dependency)?.display(&db).to_string(), match dependency { ItemInNs::Types(ModuleDefId::FunctionId(_)) | ItemInNs::Values(ModuleDefId::FunctionId(_)) => "f", @@ -547,7 +581,11 @@ mod tests { None } })?; - return Some(format!("{}::{assoc_item_name}", dependency_imports.path_of(trait_)?)); + return Some(format!( + "{}::{}", + dependency_imports.path_of(trait_)?.display(db), + assoc_item_name.display(db.upcast()) + )); } None } @@ -587,7 +625,7 @@ mod tests { let map = db.import_map(krate); - Some(format!("{name}:\n{map:?}\n")) + Some(format!("{name}:\n{}\n", map.fmt_for_test(db.upcast()))) }) .sorted() .collect::(); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs index 991e447033fe7..3ed321d189d36 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs @@ -4,7 +4,7 @@ use std::collections::hash_map::Entry; use base_db::CrateId; -use hir_expand::{attrs::AttrId, name::Name, AstId, MacroCallId}; +use hir_expand::{attrs::AttrId, db::ExpandDatabase, name::Name, AstId, MacroCallId}; use itertools::Itertools; use once_cell::sync::Lazy; use profile::Count; @@ -358,12 +358,16 @@ impl ItemScope { } } - pub(crate) fn dump(&self, buf: &mut String) { + pub(crate) fn dump(&self, db: &dyn ExpandDatabase, buf: &mut String) { let mut entries: Vec<_> = self.resolutions().collect(); entries.sort_by_key(|(name, _)| name.clone()); for (name, def) in entries { - format_to!(buf, "{}:", name.map_or("_".to_string(), |name| name.to_string())); + format_to!( + buf, + "{}:", + name.map_or("_".to_string(), |name| name.display(db).to_string()) + ); if def.types.is_some() { buf.push_str(" t"); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs index 9da5b2d47c877..590ed64af5d80 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs @@ -40,7 +40,6 @@ use std::{ hash::{Hash, Hasher}, marker::PhantomData, ops::Index, - sync::Arc, }; use ast::{AstNode, HasName, StructKind}; @@ -60,6 +59,7 @@ use rustc_hash::FxHashMap; use smallvec::SmallVec; use stdx::never; use syntax::{ast, match_ast, SyntaxKind}; +use triomphe::Arc; use crate::{ attr::Attrs, @@ -101,16 +101,14 @@ pub struct ItemTree { top_level: SmallVec<[ModItem; 1]>, attrs: FxHashMap, + // FIXME: Remove this indirection, an item tree is almost always non-empty? data: Option>, } impl ItemTree { pub(crate) fn file_item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc { let _p = profile::span("file_item_tree_query").detail(|| format!("{file_id:?}")); - let syntax = match db.parse_or_expand(file_id) { - Some(node) => node, - None => return Default::default(), - }; + let syntax = db.parse_or_expand(file_id); if never!(syntax.kind() == SyntaxKind::ERROR, "{:?} from {:?} {}", file_id, syntax, syntax) { // FIXME: not 100% sure why these crop up, but return an empty tree to avoid a panic @@ -169,8 +167,8 @@ impl ItemTree { Attrs::filter(db, krate, self.raw_attrs(of).clone()) } - pub fn pretty_print(&self) -> String { - pretty::print_item_tree(self) + pub fn pretty_print(&self, db: &dyn DefDatabase) -> String { + pretty::print_item_tree(db.upcast(), self) } fn data(&self) -> &ItemTreeData { @@ -600,19 +598,18 @@ pub struct Function { pub abi: Option>, pub params: IdxRange, pub ret_type: Interned, - pub async_ret_type: Option>, pub ast_id: FileAstId, pub(crate) flags: FnFlags, } #[derive(Debug, Clone, Eq, PartialEq)] pub enum Param { - Normal(Option, Interned), + Normal(Interned), Varargs, } bitflags::bitflags! { - #[derive(Default)] + #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] pub(crate) struct FnFlags: u8 { const HAS_SELF_PARAM = 1 << 0; const HAS_BODY = 1 << 1; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs index 77b186f8e3fe6..46633667ed3e2 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs @@ -1,6 +1,6 @@ //! AST -> `ItemTree` lowering code. -use std::{collections::hash_map::Entry, sync::Arc}; +use std::collections::hash_map::Entry; use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, HirFileId}; use syntax::ast::{self, HasModuleItem, HasTypeBounds}; @@ -20,7 +20,7 @@ pub(super) struct Ctx<'a> { db: &'a dyn DefDatabase, tree: ItemTree, source_ast_id_map: Arc, - body_ctx: crate::body::LowerCtx<'a>, + body_ctx: crate::lower::LowerCtx<'a>, } impl<'a> Ctx<'a> { @@ -29,7 +29,7 @@ impl<'a> Ctx<'a> { db, tree: ItemTree::default(), source_ast_id_map: db.ast_id_map(file), - body_ctx: crate::body::LowerCtx::new(db, file), + body_ctx: crate::lower::LowerCtx::with_file_id(db, file), } } @@ -293,7 +293,7 @@ impl<'a> Ctx<'a> { } }; let ty = Interned::new(self_type); - let idx = self.data().params.alloc(Param::Normal(None, ty)); + let idx = self.data().params.alloc(Param::Normal(ty)); self.add_attrs( idx.into(), RawAttrs::new(self.db.upcast(), &self_param, self.hygiene()), @@ -306,19 +306,7 @@ impl<'a> Ctx<'a> { None => { let type_ref = TypeRef::from_ast_opt(&self.body_ctx, param.ty()); let ty = Interned::new(type_ref); - let mut pat = param.pat(); - // FIXME: This really shouldn't be here, in fact FunctionData/ItemTree's function shouldn't know about - // pattern names at all - let name = 'name: loop { - match pat { - Some(ast::Pat::RefPat(ref_pat)) => pat = ref_pat.pat(), - Some(ast::Pat::IdentPat(ident)) => { - break 'name ident.name().map(|it| it.as_name()) - } - _ => break 'name None, - } - }; - self.data().params.alloc(Param::Normal(name, ty)) + self.data().params.alloc(Param::Normal(ty)) } }; self.add_attrs(idx.into(), RawAttrs::new(self.db.upcast(), ¶m, self.hygiene())); @@ -336,13 +324,12 @@ impl<'a> Ctx<'a> { None => TypeRef::unit(), }; - let (ret_type, async_ret_type) = if func.async_token().is_some() { - let async_ret_type = ret_type.clone(); + let ret_type = if func.async_token().is_some() { let future_impl = desugar_future_path(ret_type); let ty_bound = Interned::new(TypeBound::Path(future_impl, TraitBoundModifier::None)); - (TypeRef::ImplTrait(vec![ty_bound]), Some(async_ret_type)) + TypeRef::ImplTrait(vec![ty_bound]) } else { - (ret_type, None) + ret_type }; let abi = func.abi().map(lower_abi); @@ -376,7 +363,6 @@ impl<'a> Ctx<'a> { abi, params, ret_type: Interned::new(ret_type), - async_ret_type: async_ret_type.map(Interned::new), ast_id, flags, }; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs index 5f29997964b9c..e873316a578c0 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs @@ -2,6 +2,8 @@ use std::fmt::{self, Write}; +use hir_expand::db::ExpandDatabase; + use crate::{ generics::{TypeOrConstParamData, WherePredicate, WherePredicateTypeTarget}, pretty::{print_path, print_type_bounds, print_type_ref}, @@ -10,8 +12,8 @@ use crate::{ use super::*; -pub(super) fn print_item_tree(tree: &ItemTree) -> String { - let mut p = Printer { tree, buf: String::new(), indent_level: 0, needs_indent: true }; +pub(super) fn print_item_tree(db: &dyn ExpandDatabase, tree: &ItemTree) -> String { + let mut p = Printer { db, tree, buf: String::new(), indent_level: 0, needs_indent: true }; if let Some(attrs) = tree.attrs.get(&AttrOwner::TopLevel) { p.print_attrs(attrs, true); @@ -43,6 +45,7 @@ macro_rules! wln { } struct Printer<'a> { + db: &'a dyn ExpandDatabase, tree: &'a ItemTree, buf: String, indent_level: usize, @@ -88,7 +91,7 @@ impl<'a> Printer<'a> { self, "#{}[{}{}]", inner, - attr.path, + attr.path.display(self.db), attr.input.as_ref().map(|it| it.to_string()).unwrap_or_default(), ); } @@ -102,7 +105,7 @@ impl<'a> Printer<'a> { fn print_visibility(&mut self, vis: RawVisibilityId) { match &self.tree[vis] { - RawVisibility::Module(path) => w!(self, "pub({}) ", path), + RawVisibility::Module(path) => w!(self, "pub({}) ", path.display(self.db)), RawVisibility::Public => w!(self, "pub "), }; } @@ -117,7 +120,7 @@ impl<'a> Printer<'a> { let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field]; this.print_attrs_of(field); this.print_visibility(*visibility); - w!(this, "{}: ", name); + w!(this, "{}: ", name.display(self.db)); this.print_type_ref(type_ref); wln!(this, ","); } @@ -131,7 +134,7 @@ impl<'a> Printer<'a> { let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field]; this.print_attrs_of(field); this.print_visibility(*visibility); - w!(this, "{}: ", name); + w!(this, "{}: ", name.display(self.db)); this.print_type_ref(type_ref); wln!(this, ","); } @@ -164,20 +167,20 @@ impl<'a> Printer<'a> { fn print_use_tree(&mut self, use_tree: &UseTree) { match &use_tree.kind { UseTreeKind::Single { path, alias } => { - w!(self, "{}", path); + w!(self, "{}", path.display(self.db)); if let Some(alias) = alias { w!(self, " as {}", alias); } } UseTreeKind::Glob { path } => { if let Some(path) = path { - w!(self, "{}::", path); + w!(self, "{}::", path.display(self.db)); } w!(self, "*"); } UseTreeKind::Prefixed { prefix, list } => { if let Some(prefix) = prefix { - w!(self, "{}::", prefix); + w!(self, "{}::", prefix.display(self.db)); } w!(self, "{{"); for (i, tree) in list.iter().enumerate() { @@ -205,7 +208,7 @@ impl<'a> Printer<'a> { ModItem::ExternCrate(it) => { let ExternCrate { name, alias, visibility, ast_id: _ } = &self.tree[it]; self.print_visibility(*visibility); - w!(self, "extern crate {}", name); + w!(self, "extern crate {}", name.display(self.db)); if let Some(alias) = alias { w!(self, " as {}", alias); } @@ -233,7 +236,6 @@ impl<'a> Printer<'a> { abi, params, ret_type, - async_ret_type: _, ast_id: _, flags, } = &self.tree[it]; @@ -253,26 +255,20 @@ impl<'a> Printer<'a> { if let Some(abi) = abi { w!(self, "extern \"{}\" ", abi); } - w!(self, "fn {}", name); + w!(self, "fn {}", name.display(self.db)); self.print_generic_params(explicit_generic_params); w!(self, "("); if !params.is_empty() { self.indented(|this| { - for (i, param) in params.clone().enumerate() { + for param in params.clone() { this.print_attrs_of(param); match &this.tree[param] { - Param::Normal(name, ty) => { - match name { - Some(name) => w!(this, "{}: ", name), - None => w!(this, "_: "), + Param::Normal(ty) => { + if flags.contains(FnFlags::HAS_SELF_PARAM) { + w!(this, "self: "); } this.print_type_ref(ty); - w!(this, ","); - if flags.contains(FnFlags::HAS_SELF_PARAM) && i == 0 { - wln!(this, " // self"); - } else { - wln!(this); - } + wln!(this, ","); } Param::Varargs => { wln!(this, "..."); @@ -293,7 +289,7 @@ impl<'a> Printer<'a> { ModItem::Struct(it) => { let Struct { visibility, name, fields, generic_params, ast_id: _ } = &self.tree[it]; self.print_visibility(*visibility); - w!(self, "struct {}", name); + w!(self, "struct {}", name.display(self.db)); self.print_generic_params(generic_params); self.print_fields_and_where_clause(fields, generic_params); if matches!(fields, Fields::Record(_)) { @@ -305,7 +301,7 @@ impl<'a> Printer<'a> { ModItem::Union(it) => { let Union { name, visibility, fields, generic_params, ast_id: _ } = &self.tree[it]; self.print_visibility(*visibility); - w!(self, "union {}", name); + w!(self, "union {}", name.display(self.db)); self.print_generic_params(generic_params); self.print_fields_and_where_clause(fields, generic_params); if matches!(fields, Fields::Record(_)) { @@ -317,14 +313,14 @@ impl<'a> Printer<'a> { ModItem::Enum(it) => { let Enum { name, visibility, variants, generic_params, ast_id: _ } = &self.tree[it]; self.print_visibility(*visibility); - w!(self, "enum {}", name); + w!(self, "enum {}", name.display(self.db)); self.print_generic_params(generic_params); self.print_where_clause_and_opening_brace(generic_params); self.indented(|this| { for variant in variants.clone() { let Variant { name, fields, ast_id: _ } = &this.tree[variant]; this.print_attrs_of(variant); - w!(this, "{}", name); + w!(this, "{}", name.display(self.db)); this.print_fields(fields); wln!(this, ","); } @@ -336,7 +332,7 @@ impl<'a> Printer<'a> { self.print_visibility(*visibility); w!(self, "const "); match name { - Some(name) => w!(self, "{}", name), + Some(name) => w!(self, "{}", name.display(self.db)), None => w!(self, "_"), } w!(self, ": "); @@ -350,7 +346,7 @@ impl<'a> Printer<'a> { if *mutable { w!(self, "mut "); } - w!(self, "{}: ", name); + w!(self, "{}: ", name.display(self.db)); self.print_type_ref(type_ref); w!(self, " = _;"); wln!(self); @@ -372,7 +368,7 @@ impl<'a> Printer<'a> { if *is_auto { w!(self, "auto "); } - w!(self, "trait {}", name); + w!(self, "trait {}", name.display(self.db)); self.print_generic_params(generic_params); self.print_where_clause_and_opening_brace(generic_params); self.indented(|this| { @@ -385,7 +381,7 @@ impl<'a> Printer<'a> { ModItem::TraitAlias(it) => { let TraitAlias { name, visibility, generic_params, ast_id: _ } = &self.tree[it]; self.print_visibility(*visibility); - w!(self, "trait {}", name); + w!(self, "trait {}", name.display(self.db)); self.print_generic_params(generic_params); w!(self, " = "); self.print_where_clause(generic_params); @@ -418,7 +414,7 @@ impl<'a> Printer<'a> { let TypeAlias { name, visibility, bounds, type_ref, generic_params, ast_id: _ } = &self.tree[it]; self.print_visibility(*visibility); - w!(self, "type {}", name); + w!(self, "type {}", name.display(self.db)); self.print_generic_params(generic_params); if !bounds.is_empty() { w!(self, ": "); @@ -435,7 +431,7 @@ impl<'a> Printer<'a> { ModItem::Mod(it) => { let Mod { name, visibility, kind, ast_id: _ } = &self.tree[it]; self.print_visibility(*visibility); - w!(self, "mod {}", name); + w!(self, "mod {}", name.display(self.db)); match kind { ModKind::Inline { items } => { w!(self, " {{"); @@ -453,16 +449,16 @@ impl<'a> Printer<'a> { } ModItem::MacroCall(it) => { let MacroCall { path, ast_id: _, expand_to: _ } = &self.tree[it]; - wln!(self, "{}!(...);", path); + wln!(self, "{}!(...);", path.display(self.db)); } ModItem::MacroRules(it) => { let MacroRules { name, ast_id: _ } = &self.tree[it]; - wln!(self, "macro_rules! {} {{ ... }}", name); + wln!(self, "macro_rules! {} {{ ... }}", name.display(self.db)); } ModItem::MacroDef(it) => { let MacroDef { name, visibility, ast_id: _ } = &self.tree[it]; self.print_visibility(*visibility); - wln!(self, "macro {} {{ ... }}", name); + wln!(self, "macro {} {{ ... }}", name.display(self.db)); } } @@ -470,15 +466,15 @@ impl<'a> Printer<'a> { } fn print_type_ref(&mut self, type_ref: &TypeRef) { - print_type_ref(type_ref, self).unwrap(); + print_type_ref(self.db, type_ref, self).unwrap(); } fn print_type_bounds(&mut self, bounds: &[Interned]) { - print_type_bounds(bounds, self).unwrap(); + print_type_bounds(self.db, bounds, self).unwrap(); } fn print_path(&mut self, path: &Path) { - print_path(path, self).unwrap(); + print_path(self.db, path, self).unwrap(); } fn print_generic_params(&mut self, params: &GenericParams) { @@ -493,7 +489,7 @@ impl<'a> Printer<'a> { w!(self, ", "); } first = false; - w!(self, "{}", lt.name); + w!(self, "{}", lt.name.display(self.db)); } for (idx, x) in params.type_or_consts.iter() { if !first { @@ -502,11 +498,11 @@ impl<'a> Printer<'a> { first = false; match x { TypeOrConstParamData::TypeParamData(ty) => match &ty.name { - Some(name) => w!(self, "{}", name), + Some(name) => w!(self, "{}", name.display(self.db)), None => w!(self, "_anon_{}", idx.into_raw()), }, TypeOrConstParamData::ConstParamData(konst) => { - w!(self, "const {}: ", konst.name); + w!(self, "const {}: ", konst.name.display(self.db)); self.print_type_ref(&konst.ty); } } @@ -538,7 +534,12 @@ impl<'a> Printer<'a> { let (target, bound) = match pred { WherePredicate::TypeBound { target, bound } => (target, bound), WherePredicate::Lifetime { target, bound } => { - wln!(this, "{}: {},", target.name, bound.name); + wln!( + this, + "{}: {},", + target.name.display(self.db), + bound.name.display(self.db) + ); continue; } WherePredicate::ForLifetime { lifetimes, target, bound } => { @@ -547,7 +548,7 @@ impl<'a> Printer<'a> { if i != 0 { w!(this, ", "); } - w!(this, "{}", lt); + w!(this, "{}", lt.display(self.db)); } w!(this, "> "); (target, bound) @@ -558,7 +559,7 @@ impl<'a> Printer<'a> { WherePredicateTypeTarget::TypeRef(ty) => this.print_type_ref(ty), WherePredicateTypeTarget::TypeOrConstParam(id) => { match ¶ms.type_or_consts[*id].name() { - Some(name) => w!(this, "{}", name), + Some(name) => w!(this, "{}", name.display(self.db)), None => w!(this, "_anon_{}", id.into_raw()), } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs index e30d9652bb5dd..5ded4b6b273ae 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs @@ -6,7 +6,7 @@ use crate::{db::DefDatabase, test_db::TestDB}; fn check(ra_fixture: &str, expect: Expect) { let (db, file_id) = TestDB::with_single_file(ra_fixture); let item_tree = db.file_item_tree(file_id.into()); - let pretty = item_tree.pretty_print(); + let pretty = item_tree.pretty_print(&db); expect.assert_eq(&pretty); } @@ -165,7 +165,7 @@ trait Tr: SuperTrait + 'lifetime { fn method(&self); } "#, - expect![[r##" + expect![[r#" pub static mut ST: () = _; pub(self) const _: Anon = _; @@ -174,8 +174,8 @@ trait Tr: SuperTrait + 'lifetime { #[inner_attr_in_fn] pub(self) fn f( #[attr] - arg: u8, - _: (), + u8, + (), ) -> () { ... } pub(self) trait Tr @@ -186,10 +186,10 @@ trait Tr: SuperTrait + 'lifetime { pub(self) type Assoc: AssocBound = Default; pub(self) fn method( - _: &Self, // self + self: &Self, ) -> (); } - "##]], + "#]], ); } @@ -336,7 +336,7 @@ trait Tr<'a, T: 'a>: Super where Self: for<'a> Tr<'a, T> {} T: 'b { pub(self) fn f( - arg: impl Copy, + impl Copy, ) -> impl Copy where G: 'a { ... } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs index d338bb412054e..0e9ac58fbaaad 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs @@ -2,14 +2,13 @@ //! //! This attribute to tell the compiler about semi built-in std library //! features, such as Fn family of traits. -use std::sync::Arc; - use rustc_hash::FxHashMap; use syntax::SmolStr; +use triomphe::Arc; use crate::{ - db::DefDatabase, AdtId, AssocItemId, AttrDefId, CrateId, EnumId, EnumVariantId, FunctionId, - ImplId, ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, UnionId, + db::DefDatabase, path::Path, AdtId, AssocItemId, AttrDefId, CrateId, EnumId, EnumVariantId, + FunctionId, ImplId, ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, UnionId, }; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -200,7 +199,7 @@ pub enum GenericRequirement { macro_rules! language_item_table { ( - $( $(#[$attr:meta])* $variant:ident, $name:ident, $method:ident, $target:expr, $generics:expr; )* + $( $(#[$attr:meta])* $variant:ident, $module:ident :: $name:ident, $method:ident, $target:expr, $generics:expr; )* ) => { /// A representation of all the valid language items in Rust. @@ -220,11 +219,6 @@ macro_rules! language_item_table { } } - /// Opposite of [`LangItem::name`] - pub fn from_name(name: &hir_expand::name::Name) -> Option { - Self::from_str(name.as_str()?) - } - /// Opposite of [`LangItem::name`] pub fn from_str(name: &str) -> Option { match name { @@ -236,84 +230,100 @@ macro_rules! language_item_table { } } +impl LangItem { + /// Opposite of [`LangItem::name`] + pub fn from_name(name: &hir_expand::name::Name) -> Option { + Self::from_str(name.as_str()?) + } + + pub fn path(&self, db: &dyn DefDatabase, start_crate: CrateId) -> Option { + let t = db.lang_item(start_crate, *self)?; + Some(Path::LangItem(t)) + } +} + language_item_table! { // Variant name, Name, Getter method name, Target Generic requirements; - Sized, sized, sized_trait, Target::Trait, GenericRequirement::Exact(0); - Unsize, unsize, unsize_trait, Target::Trait, GenericRequirement::Minimum(1); + Sized, sym::sized, sized_trait, Target::Trait, GenericRequirement::Exact(0); + Unsize, sym::unsize, unsize_trait, Target::Trait, GenericRequirement::Minimum(1); /// Trait injected by `#[derive(PartialEq)]`, (i.e. "Partial EQ"). - StructuralPeq, structural_peq, structural_peq_trait, Target::Trait, GenericRequirement::None; + StructuralPeq, sym::structural_peq, structural_peq_trait, Target::Trait, GenericRequirement::None; /// Trait injected by `#[derive(Eq)]`, (i.e. "Total EQ"; no, I will not apologize). - StructuralTeq, structural_teq, structural_teq_trait, Target::Trait, GenericRequirement::None; - Copy, copy, copy_trait, Target::Trait, GenericRequirement::Exact(0); - Clone, clone, clone_trait, Target::Trait, GenericRequirement::None; - Sync, sync, sync_trait, Target::Trait, GenericRequirement::Exact(0); - DiscriminantKind, discriminant_kind, discriminant_kind_trait, Target::Trait, GenericRequirement::None; + StructuralTeq, sym::structural_teq, structural_teq_trait, Target::Trait, GenericRequirement::None; + Copy, sym::copy, copy_trait, Target::Trait, GenericRequirement::Exact(0); + Clone, sym::clone, clone_trait, Target::Trait, GenericRequirement::None; + Sync, sym::sync, sync_trait, Target::Trait, GenericRequirement::Exact(0); + DiscriminantKind, sym::discriminant_kind, discriminant_kind_trait, Target::Trait, GenericRequirement::None; /// The associated item of the [`DiscriminantKind`] trait. - Discriminant, discriminant_type, discriminant_type, Target::AssocTy, GenericRequirement::None; + Discriminant, sym::discriminant_type, discriminant_type, Target::AssocTy, GenericRequirement::None; + + PointeeTrait, sym::pointee_trait, pointee_trait, Target::Trait, GenericRequirement::None; + Metadata, sym::metadata_type, metadata_type, Target::AssocTy, GenericRequirement::None; + DynMetadata, sym::dyn_metadata, dyn_metadata, Target::Struct, GenericRequirement::None; - PointeeTrait, pointee_trait, pointee_trait, Target::Trait, GenericRequirement::None; - Metadata, metadata_type, metadata_type, Target::AssocTy, GenericRequirement::None; - DynMetadata, dyn_metadata, dyn_metadata, Target::Struct, GenericRequirement::None; + Freeze, sym::freeze, freeze_trait, Target::Trait, GenericRequirement::Exact(0); - Freeze, freeze, freeze_trait, Target::Trait, GenericRequirement::Exact(0); + FnPtrTrait, sym::fn_ptr_trait, fn_ptr_trait, Target::Trait, GenericRequirement::Exact(0); + FnPtrAddr, sym::fn_ptr_addr, fn_ptr_addr, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; - Drop, drop, drop_trait, Target::Trait, GenericRequirement::None; - Destruct, destruct, destruct_trait, Target::Trait, GenericRequirement::None; + Drop, sym::drop, drop_trait, Target::Trait, GenericRequirement::None; + Destruct, sym::destruct, destruct_trait, Target::Trait, GenericRequirement::None; - CoerceUnsized, coerce_unsized, coerce_unsized_trait, Target::Trait, GenericRequirement::Minimum(1); - DispatchFromDyn, dispatch_from_dyn, dispatch_from_dyn_trait, Target::Trait, GenericRequirement::Minimum(1); + CoerceUnsized, sym::coerce_unsized, coerce_unsized_trait, Target::Trait, GenericRequirement::Minimum(1); + DispatchFromDyn, sym::dispatch_from_dyn, dispatch_from_dyn_trait, Target::Trait, GenericRequirement::Minimum(1); // language items relating to transmutability - TransmuteOpts, transmute_opts, transmute_opts, Target::Struct, GenericRequirement::Exact(0); - TransmuteTrait, transmute_trait, transmute_trait, Target::Trait, GenericRequirement::Exact(3); - - Add, add, add_trait, Target::Trait, GenericRequirement::Exact(1); - Sub, sub, sub_trait, Target::Trait, GenericRequirement::Exact(1); - Mul, mul, mul_trait, Target::Trait, GenericRequirement::Exact(1); - Div, div, div_trait, Target::Trait, GenericRequirement::Exact(1); - Rem, rem, rem_trait, Target::Trait, GenericRequirement::Exact(1); - Neg, neg, neg_trait, Target::Trait, GenericRequirement::Exact(0); - Not, not, not_trait, Target::Trait, GenericRequirement::Exact(0); - BitXor, bitxor, bitxor_trait, Target::Trait, GenericRequirement::Exact(1); - BitAnd, bitand, bitand_trait, Target::Trait, GenericRequirement::Exact(1); - BitOr, bitor, bitor_trait, Target::Trait, GenericRequirement::Exact(1); - Shl, shl, shl_trait, Target::Trait, GenericRequirement::Exact(1); - Shr, shr, shr_trait, Target::Trait, GenericRequirement::Exact(1); - AddAssign, add_assign, add_assign_trait, Target::Trait, GenericRequirement::Exact(1); - SubAssign, sub_assign, sub_assign_trait, Target::Trait, GenericRequirement::Exact(1); - MulAssign, mul_assign, mul_assign_trait, Target::Trait, GenericRequirement::Exact(1); - DivAssign, div_assign, div_assign_trait, Target::Trait, GenericRequirement::Exact(1); - RemAssign, rem_assign, rem_assign_trait, Target::Trait, GenericRequirement::Exact(1); - BitXorAssign, bitxor_assign, bitxor_assign_trait, Target::Trait, GenericRequirement::Exact(1); - BitAndAssign, bitand_assign, bitand_assign_trait, Target::Trait, GenericRequirement::Exact(1); - BitOrAssign, bitor_assign, bitor_assign_trait, Target::Trait, GenericRequirement::Exact(1); - ShlAssign, shl_assign, shl_assign_trait, Target::Trait, GenericRequirement::Exact(1); - ShrAssign, shr_assign, shr_assign_trait, Target::Trait, GenericRequirement::Exact(1); - Index, index, index_trait, Target::Trait, GenericRequirement::Exact(1); - IndexMut, index_mut, index_mut_trait, Target::Trait, GenericRequirement::Exact(1); - - UnsafeCell, unsafe_cell, unsafe_cell_type, Target::Struct, GenericRequirement::None; - VaList, va_list, va_list, Target::Struct, GenericRequirement::None; - - Deref, deref, deref_trait, Target::Trait, GenericRequirement::Exact(0); - DerefMut, deref_mut, deref_mut_trait, Target::Trait, GenericRequirement::Exact(0); - DerefTarget, deref_target, deref_target, Target::AssocTy, GenericRequirement::None; - Receiver, receiver, receiver_trait, Target::Trait, GenericRequirement::None; - - Fn, fn, fn_trait, Target::Trait, GenericRequirement::Exact(1); - FnMut, fn_mut, fn_mut_trait, Target::Trait, GenericRequirement::Exact(1); - FnOnce, fn_once, fn_once_trait, Target::Trait, GenericRequirement::Exact(1); - - FnOnceOutput, fn_once_output, fn_once_output, Target::AssocTy, GenericRequirement::None; - - Future, future_trait, future_trait, Target::Trait, GenericRequirement::Exact(0); - GeneratorState, generator_state, gen_state, Target::Enum, GenericRequirement::None; - Generator, generator, gen_trait, Target::Trait, GenericRequirement::Minimum(1); - Unpin, unpin, unpin_trait, Target::Trait, GenericRequirement::None; - Pin, pin, pin_type, Target::Struct, GenericRequirement::None; - - PartialEq, eq, eq_trait, Target::Trait, GenericRequirement::Exact(1); - PartialOrd, partial_ord, partial_ord_trait, Target::Trait, GenericRequirement::Exact(1); + TransmuteOpts, sym::transmute_opts, transmute_opts, Target::Struct, GenericRequirement::Exact(0); + TransmuteTrait, sym::transmute_trait, transmute_trait, Target::Trait, GenericRequirement::Exact(3); + + Add, sym::add, add_trait, Target::Trait, GenericRequirement::Exact(1); + Sub, sym::sub, sub_trait, Target::Trait, GenericRequirement::Exact(1); + Mul, sym::mul, mul_trait, Target::Trait, GenericRequirement::Exact(1); + Div, sym::div, div_trait, Target::Trait, GenericRequirement::Exact(1); + Rem, sym::rem, rem_trait, Target::Trait, GenericRequirement::Exact(1); + Neg, sym::neg, neg_trait, Target::Trait, GenericRequirement::Exact(0); + Not, sym::not, not_trait, Target::Trait, GenericRequirement::Exact(0); + BitXor, sym::bitxor, bitxor_trait, Target::Trait, GenericRequirement::Exact(1); + BitAnd, sym::bitand, bitand_trait, Target::Trait, GenericRequirement::Exact(1); + BitOr, sym::bitor, bitor_trait, Target::Trait, GenericRequirement::Exact(1); + Shl, sym::shl, shl_trait, Target::Trait, GenericRequirement::Exact(1); + Shr, sym::shr, shr_trait, Target::Trait, GenericRequirement::Exact(1); + AddAssign, sym::add_assign, add_assign_trait, Target::Trait, GenericRequirement::Exact(1); + SubAssign, sym::sub_assign, sub_assign_trait, Target::Trait, GenericRequirement::Exact(1); + MulAssign, sym::mul_assign, mul_assign_trait, Target::Trait, GenericRequirement::Exact(1); + DivAssign, sym::div_assign, div_assign_trait, Target::Trait, GenericRequirement::Exact(1); + RemAssign, sym::rem_assign, rem_assign_trait, Target::Trait, GenericRequirement::Exact(1); + BitXorAssign, sym::bitxor_assign, bitxor_assign_trait, Target::Trait, GenericRequirement::Exact(1); + BitAndAssign, sym::bitand_assign, bitand_assign_trait, Target::Trait, GenericRequirement::Exact(1); + BitOrAssign, sym::bitor_assign, bitor_assign_trait, Target::Trait, GenericRequirement::Exact(1); + ShlAssign, sym::shl_assign, shl_assign_trait, Target::Trait, GenericRequirement::Exact(1); + ShrAssign, sym::shr_assign, shr_assign_trait, Target::Trait, GenericRequirement::Exact(1); + Index, sym::index, index_trait, Target::Trait, GenericRequirement::Exact(1); + IndexMut, sym::index_mut, index_mut_trait, Target::Trait, GenericRequirement::Exact(1); + + UnsafeCell, sym::unsafe_cell, unsafe_cell_type, Target::Struct, GenericRequirement::None; + VaList, sym::va_list, va_list, Target::Struct, GenericRequirement::None; + + Deref, sym::deref, deref_trait, Target::Trait, GenericRequirement::Exact(0); + DerefMut, sym::deref_mut, deref_mut_trait, Target::Trait, GenericRequirement::Exact(0); + DerefTarget, sym::deref_target, deref_target, Target::AssocTy, GenericRequirement::None; + Receiver, sym::receiver, receiver_trait, Target::Trait, GenericRequirement::None; + + Fn, kw::fn, fn_trait, Target::Trait, GenericRequirement::Exact(1); + FnMut, sym::fn_mut, fn_mut_trait, Target::Trait, GenericRequirement::Exact(1); + FnOnce, sym::fn_once, fn_once_trait, Target::Trait, GenericRequirement::Exact(1); + + FnOnceOutput, sym::fn_once_output, fn_once_output, Target::AssocTy, GenericRequirement::None; + + Future, sym::future_trait, future_trait, Target::Trait, GenericRequirement::Exact(0); + GeneratorState, sym::generator_state, gen_state, Target::Enum, GenericRequirement::None; + Generator, sym::generator, gen_trait, Target::Trait, GenericRequirement::Minimum(1); + Unpin, sym::unpin, unpin_trait, Target::Trait, GenericRequirement::None; + Pin, sym::pin, pin_type, Target::Struct, GenericRequirement::None; + + PartialEq, sym::eq, eq_trait, Target::Trait, GenericRequirement::Exact(1); + PartialOrd, sym::partial_ord, partial_ord_trait, Target::Trait, GenericRequirement::Exact(1); + CVoid, sym::c_void, c_void, Target::Enum, GenericRequirement::None; // A number of panic-related lang items. The `panic` item corresponds to divide-by-zero and // various panic cases with `match`. The `panic_bounds_check` item is for indexing arrays. @@ -322,92 +332,103 @@ language_item_table! { // in the sense that a crate is not required to have it defined to use it, but a final product // is required to define it somewhere. Additionally, there are restrictions on crates that use // a weak lang item, but do not have it defined. - Panic, panic, panic_fn, Target::Fn, GenericRequirement::Exact(0); - PanicNounwind, panic_nounwind, panic_nounwind, Target::Fn, GenericRequirement::Exact(0); - PanicFmt, panic_fmt, panic_fmt, Target::Fn, GenericRequirement::None; - PanicDisplay, panic_display, panic_display, Target::Fn, GenericRequirement::None; - ConstPanicFmt, const_panic_fmt, const_panic_fmt, Target::Fn, GenericRequirement::None; - PanicBoundsCheck, panic_bounds_check, panic_bounds_check_fn, Target::Fn, GenericRequirement::Exact(0); - PanicInfo, panic_info, panic_info, Target::Struct, GenericRequirement::None; - PanicLocation, panic_location, panic_location, Target::Struct, GenericRequirement::None; - PanicImpl, panic_impl, panic_impl, Target::Fn, GenericRequirement::None; - PanicCannotUnwind, panic_cannot_unwind, panic_cannot_unwind, Target::Fn, GenericRequirement::Exact(0); + Panic, sym::panic, panic_fn, Target::Fn, GenericRequirement::Exact(0); + PanicNounwind, sym::panic_nounwind, panic_nounwind, Target::Fn, GenericRequirement::Exact(0); + PanicFmt, sym::panic_fmt, panic_fmt, Target::Fn, GenericRequirement::None; + PanicDisplay, sym::panic_display, panic_display, Target::Fn, GenericRequirement::None; + ConstPanicFmt, sym::const_panic_fmt, const_panic_fmt, Target::Fn, GenericRequirement::None; + PanicBoundsCheck, sym::panic_bounds_check, panic_bounds_check_fn, Target::Fn, GenericRequirement::Exact(0); + PanicMisalignedPointerDereference, sym::panic_misaligned_pointer_dereference, panic_misaligned_pointer_dereference_fn, Target::Fn, GenericRequirement::Exact(0); + PanicInfo, sym::panic_info, panic_info, Target::Struct, GenericRequirement::None; + PanicLocation, sym::panic_location, panic_location, Target::Struct, GenericRequirement::None; + PanicImpl, sym::panic_impl, panic_impl, Target::Fn, GenericRequirement::None; + PanicCannotUnwind, sym::panic_cannot_unwind, panic_cannot_unwind, Target::Fn, GenericRequirement::Exact(0); /// libstd panic entry point. Necessary for const eval to be able to catch it - BeginPanic, begin_panic, begin_panic_fn, Target::Fn, GenericRequirement::None; + BeginPanic, sym::begin_panic, begin_panic_fn, Target::Fn, GenericRequirement::None; - ExchangeMalloc, exchange_malloc, exchange_malloc_fn, Target::Fn, GenericRequirement::None; - BoxFree, box_free, box_free_fn, Target::Fn, GenericRequirement::Minimum(1); - DropInPlace, drop_in_place, drop_in_place_fn, Target::Fn, GenericRequirement::Minimum(1); - AllocLayout, alloc_layout, alloc_layout, Target::Struct, GenericRequirement::None; + // Lang items needed for `format_args!()`. + FormatAlignment, sym::format_alignment, format_alignment, Target::Enum, GenericRequirement::None; + FormatArgument, sym::format_argument, format_argument, Target::Struct, GenericRequirement::None; + FormatArguments, sym::format_arguments, format_arguments, Target::Struct, GenericRequirement::None; + FormatCount, sym::format_count, format_count, Target::Enum, GenericRequirement::None; + FormatPlaceholder, sym::format_placeholder, format_placeholder, Target::Struct, GenericRequirement::None; + FormatUnsafeArg, sym::format_unsafe_arg, format_unsafe_arg, Target::Struct, GenericRequirement::None; - Start, start, start_fn, Target::Fn, GenericRequirement::Exact(1); + ExchangeMalloc, sym::exchange_malloc, exchange_malloc_fn, Target::Fn, GenericRequirement::None; + BoxFree, sym::box_free, box_free_fn, Target::Fn, GenericRequirement::Minimum(1); + DropInPlace, sym::drop_in_place, drop_in_place_fn, Target::Fn, GenericRequirement::Minimum(1); + AllocLayout, sym::alloc_layout, alloc_layout, Target::Struct, GenericRequirement::None; - EhPersonality, eh_personality, eh_personality, Target::Fn, GenericRequirement::None; - EhCatchTypeinfo, eh_catch_typeinfo, eh_catch_typeinfo, Target::Static, GenericRequirement::None; + Start, sym::start, start_fn, Target::Fn, GenericRequirement::Exact(1); - OwnedBox, owned_box, owned_box, Target::Struct, GenericRequirement::Minimum(1); + EhPersonality, sym::eh_personality, eh_personality, Target::Fn, GenericRequirement::None; + EhCatchTypeinfo, sym::eh_catch_typeinfo, eh_catch_typeinfo, Target::Static, GenericRequirement::None; - PhantomData, phantom_data, phantom_data, Target::Struct, GenericRequirement::Exact(1); + OwnedBox, sym::owned_box, owned_box, Target::Struct, GenericRequirement::Minimum(1); - ManuallyDrop, manually_drop, manually_drop, Target::Struct, GenericRequirement::None; + PhantomData, sym::phantom_data, phantom_data, Target::Struct, GenericRequirement::Exact(1); - MaybeUninit, maybe_uninit, maybe_uninit, Target::Union, GenericRequirement::None; + ManuallyDrop, sym::manually_drop, manually_drop, Target::Struct, GenericRequirement::None; + + MaybeUninit, sym::maybe_uninit, maybe_uninit, Target::Union, GenericRequirement::None; /// Align offset for stride != 1; must not panic. - AlignOffset, align_offset, align_offset_fn, Target::Fn, GenericRequirement::None; + AlignOffset, sym::align_offset, align_offset_fn, Target::Fn, GenericRequirement::None; - Termination, termination, termination, Target::Trait, GenericRequirement::None; + Termination, sym::termination, termination, Target::Trait, GenericRequirement::None; - Try, Try, try_trait, Target::Trait, GenericRequirement::None; + Try, sym::Try, try_trait, Target::Trait, GenericRequirement::None; - Tuple, tuple_trait, tuple_trait, Target::Trait, GenericRequirement::Exact(0); + Tuple, sym::tuple_trait, tuple_trait, Target::Trait, GenericRequirement::Exact(0); - SliceLen, slice_len_fn, slice_len_fn, Target::Method(MethodKind::Inherent), GenericRequirement::None; + SliceLen, sym::slice_len_fn, slice_len_fn, Target::Method(MethodKind::Inherent), GenericRequirement::None; // Language items from AST lowering - TryTraitFromResidual, from_residual, from_residual_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; - TryTraitFromOutput, from_output, from_output_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; - TryTraitBranch, branch, branch_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; - TryTraitFromYeet, from_yeet, from_yeet_fn, Target::Fn, GenericRequirement::None; + TryTraitFromResidual, sym::from_residual, from_residual_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; + TryTraitFromOutput, sym::from_output, from_output_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; + TryTraitBranch, sym::branch, branch_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; + TryTraitFromYeet, sym::from_yeet, from_yeet_fn, Target::Fn, GenericRequirement::None; + + PointerLike, sym::pointer_like, pointer_like, Target::Trait, GenericRequirement::Exact(0); - PointerSized, pointer_sized, pointer_sized, Target::Trait, GenericRequirement::Exact(0); + ConstParamTy, sym::const_param_ty, const_param_ty_trait, Target::Trait, GenericRequirement::Exact(0); - Poll, Poll, poll, Target::Enum, GenericRequirement::None; - PollReady, Ready, poll_ready_variant, Target::Variant, GenericRequirement::None; - PollPending, Pending, poll_pending_variant, Target::Variant, GenericRequirement::None; + Poll, sym::Poll, poll, Target::Enum, GenericRequirement::None; + PollReady, sym::Ready, poll_ready_variant, Target::Variant, GenericRequirement::None; + PollPending, sym::Pending, poll_pending_variant, Target::Variant, GenericRequirement::None; // FIXME(swatinem): the following lang items are used for async lowering and // should become obsolete eventually. - ResumeTy, ResumeTy, resume_ty, Target::Struct, GenericRequirement::None; - GetContext, get_context, get_context_fn, Target::Fn, GenericRequirement::None; - - Context, Context, context, Target::Struct, GenericRequirement::None; - FuturePoll, poll, future_poll_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; + ResumeTy, sym::ResumeTy, resume_ty, Target::Struct, GenericRequirement::None; + GetContext, sym::get_context, get_context_fn, Target::Fn, GenericRequirement::None; - FromFrom, from, from_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; + Context, sym::Context, context, Target::Struct, GenericRequirement::None; + FuturePoll, sym::poll, future_poll_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; - OptionSome, Some, option_some_variant, Target::Variant, GenericRequirement::None; - OptionNone, None, option_none_variant, Target::Variant, GenericRequirement::None; + Option, sym::Option, option_type, Target::Enum, GenericRequirement::None; + OptionSome, sym::Some, option_some_variant, Target::Variant, GenericRequirement::None; + OptionNone, sym::None, option_none_variant, Target::Variant, GenericRequirement::None; - ResultOk, Ok, result_ok_variant, Target::Variant, GenericRequirement::None; - ResultErr, Err, result_err_variant, Target::Variant, GenericRequirement::None; + ResultOk, sym::Ok, result_ok_variant, Target::Variant, GenericRequirement::None; + ResultErr, sym::Err, result_err_variant, Target::Variant, GenericRequirement::None; - ControlFlowContinue, Continue, cf_continue_variant, Target::Variant, GenericRequirement::None; - ControlFlowBreak, Break, cf_break_variant, Target::Variant, GenericRequirement::None; + ControlFlowContinue, sym::Continue, cf_continue_variant, Target::Variant, GenericRequirement::None; + ControlFlowBreak, sym::Break, cf_break_variant, Target::Variant, GenericRequirement::None; - IntoFutureIntoFuture, into_future, into_future_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; - IntoIterIntoIter, into_iter, into_iter_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; - IteratorNext, next, next_fn, Target::Method(MethodKind::Trait { body: false}), GenericRequirement::None; + IntoFutureIntoFuture, sym::into_future, into_future_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; + IntoIterIntoIter, sym::into_iter, into_iter_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; + IteratorNext, sym::next, next_fn, Target::Method(MethodKind::Trait { body: false}), GenericRequirement::None; - PinNewUnchecked, new_unchecked, new_unchecked_fn, Target::Method(MethodKind::Inherent), GenericRequirement::None; + PinNewUnchecked, sym::new_unchecked, new_unchecked_fn, Target::Method(MethodKind::Inherent), GenericRequirement::None; - RangeFrom, RangeFrom, range_from_struct, Target::Struct, GenericRequirement::None; - RangeFull, RangeFull, range_full_struct, Target::Struct, GenericRequirement::None; - RangeInclusiveStruct, RangeInclusive, range_inclusive_struct, Target::Struct, GenericRequirement::None; - RangeInclusiveNew, range_inclusive_new, range_inclusive_new_method, Target::Method(MethodKind::Inherent), GenericRequirement::None; - Range, Range, range_struct, Target::Struct, GenericRequirement::None; - RangeToInclusive, RangeToInclusive, range_to_inclusive_struct, Target::Struct, GenericRequirement::None; - RangeTo, RangeTo, range_to_struct, Target::Struct, GenericRequirement::None; + RangeFrom, sym::RangeFrom, range_from_struct, Target::Struct, GenericRequirement::None; + RangeFull, sym::RangeFull, range_full_struct, Target::Struct, GenericRequirement::None; + RangeInclusiveStruct, sym::RangeInclusive, range_inclusive_struct, Target::Struct, GenericRequirement::None; + RangeInclusiveNew, sym::range_inclusive_new, range_inclusive_new_method, Target::Method(MethodKind::Inherent), GenericRequirement::None; + Range, sym::Range, range_struct, Target::Struct, GenericRequirement::None; + RangeToInclusive, sym::RangeToInclusive, range_to_inclusive_struct, Target::Struct, GenericRequirement::None; + RangeTo, sym::RangeTo, range_to_struct, Target::Struct, GenericRequirement::None; - String, String, string, Target::Struct, GenericRequirement::None; + String, sym::String, string, Target::Struct, GenericRequirement::None; + CStr, sym::CStr, c_str, Target::Struct, GenericRequirement::None; } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/layout.rs b/src/tools/rust-analyzer/crates/hir-def/src/layout.rs deleted file mode 100644 index 49b1190ad46a3..0000000000000 --- a/src/tools/rust-analyzer/crates/hir-def/src/layout.rs +++ /dev/null @@ -1,97 +0,0 @@ -//! Definitions needed for computing data layout of types. - -use std::cmp; - -use la_arena::{Idx, RawIdx}; -pub use rustc_abi::{ - Abi, AbiAndPrefAlign, AddressSpace, Align, Endian, FieldsShape, Integer, IntegerType, - LayoutCalculator, Niche, Primitive, ReprFlags, ReprOptions, Scalar, Size, StructKind, - TargetDataLayout, TargetDataLayoutErrors, WrappingRange, -}; - -use crate::LocalEnumVariantId; - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct RustcEnumVariantIdx(pub LocalEnumVariantId); - -impl rustc_index::vec::Idx for RustcEnumVariantIdx { - fn new(idx: usize) -> Self { - RustcEnumVariantIdx(Idx::from_raw(RawIdx::from(idx as u32))) - } - - fn index(self) -> usize { - u32::from(self.0.into_raw()) as usize - } -} - -pub type Layout = rustc_abi::LayoutS; -pub type TagEncoding = rustc_abi::TagEncoding; -pub type Variants = rustc_abi::Variants; - -pub trait IntegerExt { - fn repr_discr( - dl: &TargetDataLayout, - repr: &ReprOptions, - min: i128, - max: i128, - ) -> Result<(Integer, bool), LayoutError>; -} - -impl IntegerExt for Integer { - /// Finds the appropriate Integer type and signedness for the given - /// signed discriminant range and `#[repr]` attribute. - /// N.B.: `u128` values above `i128::MAX` will be treated as signed, but - /// that shouldn't affect anything, other than maybe debuginfo. - fn repr_discr( - dl: &TargetDataLayout, - repr: &ReprOptions, - min: i128, - max: i128, - ) -> Result<(Integer, bool), LayoutError> { - // Theoretically, negative values could be larger in unsigned representation - // than the unsigned representation of the signed minimum. However, if there - // are any negative values, the only valid unsigned representation is u128 - // which can fit all i128 values, so the result remains unaffected. - let unsigned_fit = Integer::fit_unsigned(cmp::max(min as u128, max as u128)); - let signed_fit = cmp::max(Integer::fit_signed(min), Integer::fit_signed(max)); - - if let Some(ity) = repr.int { - let discr = Integer::from_attr(dl, ity); - let fit = if ity.is_signed() { signed_fit } else { unsigned_fit }; - if discr < fit { - return Err(LayoutError::UserError( - "Integer::repr_discr: `#[repr]` hint too small for \ - discriminant range of enum " - .to_string(), - )); - } - return Ok((discr, ity.is_signed())); - } - - let at_least = if repr.c() { - // This is usually I32, however it can be different on some platforms, - // notably hexagon and arm-none/thumb-none - dl.c_enum_min_size - } else { - // repr(Rust) enums try to be as small as possible - Integer::I8 - }; - - // If there are no negative values, we can use the unsigned fit. - Ok(if min >= 0 { - (cmp::max(unsigned_fit, at_least), false) - } else { - (cmp::max(signed_fit, at_least), true) - }) - } -} - -#[derive(Debug, PartialEq, Eq, Clone)] -pub enum LayoutError { - UserError(String), - SizeOverflow, - TargetLayoutNotAvailable, - HasPlaceholder, - NotImplemented, - Unknown, -} diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs index 8c2e93f090597..9cd3dfd6f7c96 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -18,24 +18,23 @@ pub mod db; pub mod attr; pub mod path; -pub mod type_ref; pub mod builtin_type; -pub mod builtin_attr; pub mod per_ns; pub mod item_scope; +pub mod lower; +pub mod expander; + pub mod dyn_map; -pub mod keys; pub mod item_tree; -pub mod adt; pub mod data; pub mod generics; pub mod lang_item; -pub mod layout; -pub mod expr; +pub mod hir; +pub use self::hir::type_ref; pub mod body; pub mod resolver; @@ -49,29 +48,34 @@ pub mod visibility; pub mod find_path; pub mod import_map; +pub use rustc_abi as layout; +use triomphe::Arc; + #[cfg(test)] mod test_db; #[cfg(test)] mod macro_expansion_tests; mod pretty; -use std::{ - hash::{Hash, Hasher}, - sync::Arc, -}; +use std::hash::{Hash, Hasher}; -use base_db::{impl_intern_key, salsa, CrateId, ProcMacroKind}; +use base_db::{ + impl_intern_key, + salsa::{self, InternId}, + CrateId, ProcMacroKind, +}; use hir_expand::{ ast_id_map::FileAstId, attrs::{Attr, AttrId, AttrInput}, builtin_attr_macro::BuiltinAttrExpander, builtin_derive_macro::BuiltinDeriveExpander, builtin_fn_macro::{BuiltinFnLikeExpander, EagerExpander}, - eager::{expand_eager_macro, ErrorEmitted, ErrorSink}, + db::ExpandDatabase, + eager::expand_eager_macro, hygiene::Hygiene, proc_macro::ProcMacroExpander, - AstId, ExpandError, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefId, - MacroDefKind, UnresolvedMacro, + AstId, ExpandError, ExpandResult, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, + MacroDefId, MacroDefKind, UnresolvedMacro, }; use item_tree::ExternBlock; use la_arena::Idx; @@ -82,8 +86,8 @@ use syntax::ast; use ::tt::token_id as tt; use crate::{ - adt::VariantData, builtin_type::BuiltinType, + data::adt::VariantData, item_tree::{ Const, Enum, Function, Impl, ItemTreeId, ItemTreeNode, MacroDef, MacroRules, ModItem, Static, Struct, Trait, TraitAlias, TypeAlias, Union, @@ -104,13 +108,7 @@ pub struct ModuleId { impl ModuleId { pub fn def_map(&self, db: &dyn db::DefDatabase) -> Arc { match self.block { - Some(block) => { - db.block_def_map(block).unwrap_or_else(|| { - // NOTE: This should be unreachable - all `ModuleId`s come from their `DefMap`s, - // so the `DefMap` here must exist. - unreachable!("no `block_def_map` for `ModuleId` {:?}", self); - }) - } + Some(block) => db.block_def_map(block), None => db.crate_def_map(self.krate), } } @@ -236,7 +234,7 @@ pub struct EnumVariantId { pub local_id: LocalEnumVariantId, } -pub type LocalEnumVariantId = Idx; +pub type LocalEnumVariantId = Idx; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct FieldId { @@ -244,7 +242,7 @@ pub struct FieldId { pub local_id: LocalFieldId, } -pub type LocalFieldId = Idx; +pub type LocalFieldId = Idx; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct ConstId(salsa::InternId); @@ -478,6 +476,46 @@ impl_from!( for ModuleDefId ); +// FIXME: make this a DefWithBodyId +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] +pub struct AnonymousConstId(InternId); +impl_intern_key!(AnonymousConstId); + +/// A constant, which might appears as a const item, an annonymous const block in expressions +/// or patterns, or as a constant in types with const generics. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum GeneralConstId { + ConstId(ConstId), + AnonymousConstId(AnonymousConstId), +} + +impl_from!(ConstId, AnonymousConstId for GeneralConstId); + +impl GeneralConstId { + pub fn generic_def(self, db: &dyn db::DefDatabase) -> Option { + match self { + GeneralConstId::ConstId(x) => Some(x.into()), + GeneralConstId::AnonymousConstId(x) => { + let (parent, _) = db.lookup_intern_anonymous_const(x); + parent.as_generic_def_id() + } + } + } + + pub fn name(self, db: &dyn db::DefDatabase) -> String { + match self { + GeneralConstId::ConstId(const_id) => db + .const_data(const_id) + .name + .as_ref() + .and_then(|x| x.as_str()) + .unwrap_or("_") + .to_owned(), + GeneralConstId::AnonymousConstId(id) => format!("{{anonymous const {id:?}}}"), + } + } +} + /// The defs which have a body. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum DefWithBodyId { @@ -799,52 +837,43 @@ impl AttrDefId { pub trait AsMacroCall { fn as_call_id( &self, - db: &dyn db::DefDatabase, + db: &dyn ExpandDatabase, krate: CrateId, resolver: impl Fn(path::ModPath) -> Option, ) -> Option { - self.as_call_id_with_errors(db, krate, resolver, &mut |_| ()).ok()?.ok() + self.as_call_id_with_errors(db, krate, resolver).ok()?.value } fn as_call_id_with_errors( &self, - db: &dyn db::DefDatabase, + db: &dyn ExpandDatabase, krate: CrateId, resolver: impl Fn(path::ModPath) -> Option, - error_sink: &mut dyn FnMut(ExpandError), - ) -> Result, UnresolvedMacro>; + ) -> Result>, UnresolvedMacro>; } impl AsMacroCall for InFile<&ast::MacroCall> { fn as_call_id_with_errors( &self, - db: &dyn db::DefDatabase, + db: &dyn ExpandDatabase, krate: CrateId, resolver: impl Fn(path::ModPath) -> Option, - mut error_sink: &mut dyn FnMut(ExpandError), - ) -> Result, UnresolvedMacro> { + ) -> Result>, UnresolvedMacro> { let expands_to = hir_expand::ExpandTo::from_call_site(self.value); let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value)); - let h = Hygiene::new(db.upcast(), self.file_id); - let path = - self.value.path().and_then(|path| path::ModPath::from_src(db.upcast(), path, &h)); - - let path = match error_sink - .option(path, || ExpandError::Other("malformed macro invocation".into())) - { - Ok(path) => path, - Err(error) => { - return Ok(Err(error)); - } + let h = Hygiene::new(db, self.file_id); + let path = self.value.path().and_then(|path| path::ModPath::from_src(db, path, &h)); + + let Some(path) = path else { + return Ok(ExpandResult::only_err(ExpandError::Other("malformed macro invocation".into()))); }; - macro_call_as_call_id( + macro_call_as_call_id_( db, &AstIdWithPath::new(ast_id.file_id, ast_id.value, path), expands_to, krate, resolver, - error_sink, ) } } @@ -863,26 +892,37 @@ impl AstIdWithPath { } fn macro_call_as_call_id( - db: &dyn db::DefDatabase, + db: &dyn ExpandDatabase, + call: &AstIdWithPath, + expand_to: ExpandTo, + krate: CrateId, + resolver: impl Fn(path::ModPath) -> Option, +) -> Result, UnresolvedMacro> { + macro_call_as_call_id_(db, call, expand_to, krate, resolver).map(|res| res.value) +} + +fn macro_call_as_call_id_( + db: &dyn ExpandDatabase, call: &AstIdWithPath, expand_to: ExpandTo, krate: CrateId, resolver: impl Fn(path::ModPath) -> Option, - error_sink: &mut dyn FnMut(ExpandError), -) -> Result, UnresolvedMacro> { +) -> Result>, UnresolvedMacro> { let def = resolver(call.path.clone()).ok_or_else(|| UnresolvedMacro { path: call.path.clone() })?; let res = if let MacroDefKind::BuiltInEager(..) = def.kind { - let macro_call = InFile::new(call.ast_id.file_id, call.ast_id.to_node(db.upcast())); - - expand_eager_macro(db.upcast(), krate, macro_call, def, &resolver, error_sink)? + let macro_call = InFile::new(call.ast_id.file_id, call.ast_id.to_node(db)); + expand_eager_macro(db, krate, macro_call, def, &resolver)? } else { - Ok(def.as_lazy_macro( - db.upcast(), - krate, - MacroCallKind::FnLike { ast_id: call.ast_id, expand_to }, - )) + ExpandResult { + value: Some(def.as_lazy_macro( + db, + krate, + MacroCallKind::FnLike { ast_id: call.ast_id, expand_to }, + )), + err: None, + } }; Ok(res) } @@ -986,7 +1026,6 @@ fn attr_macro_as_call_id( macro_attr: &Attr, krate: CrateId, def: MacroDefId, - is_derive: bool, ) -> MacroCallId { let arg = match macro_attr.input.as_deref() { Some(AttrInput::TokenTree(tt, map)) => ( @@ -1007,7 +1046,6 @@ fn attr_macro_as_call_id( ast_id: item_attr.ast_id, attr_args: Arc::new(arg), invoc_attr_index: macro_attr.id, - is_derive, }, ) } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/lower.rs new file mode 100644 index 0000000000000..af623fd0e5d4d --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-def/src/lower.rs @@ -0,0 +1,45 @@ +//! Context for lowering paths. +use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, AstId, HirFileId, InFile}; +use once_cell::unsync::OnceCell; +use syntax::ast; +use triomphe::Arc; + +use crate::{db::DefDatabase, path::Path}; + +pub struct LowerCtx<'a> { + pub db: &'a dyn DefDatabase, + hygiene: Hygiene, + ast_id_map: Option<(HirFileId, OnceCell>)>, +} + +impl<'a> LowerCtx<'a> { + pub fn new(db: &'a dyn DefDatabase, hygiene: &Hygiene, file_id: HirFileId) -> Self { + LowerCtx { db, hygiene: hygiene.clone(), ast_id_map: Some((file_id, OnceCell::new())) } + } + + pub fn with_file_id(db: &'a dyn DefDatabase, file_id: HirFileId) -> Self { + LowerCtx { + db, + hygiene: Hygiene::new(db.upcast(), file_id), + ast_id_map: Some((file_id, OnceCell::new())), + } + } + + pub fn with_hygiene(db: &'a dyn DefDatabase, hygiene: &Hygiene) -> Self { + LowerCtx { db, hygiene: hygiene.clone(), ast_id_map: None } + } + + pub(crate) fn hygiene(&self) -> &Hygiene { + &self.hygiene + } + + pub(crate) fn lower_path(&self, ast: ast::Path) -> Option { + Path::from_src(ast, self) + } + + pub(crate) fn ast_id(&self, item: &N) -> Option> { + let &(file_id, ref ast_id_map) = self.ast_id_map.as_ref()?; + let ast_id_map = ast_id_map.get_or_init(|| self.db.ast_id_map(file_id)); + Some(InFile::new(file_id, ast_id_map.ast_id(item))) + } +} diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs index fafcde25ae708..80474bc154d08 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs @@ -16,7 +16,7 @@ struct Foo; #[derive(Copy)] struct Foo; -impl < > core::marker::Copy for Foo< > {}"#]], +impl < > core::marker::Copy for Foo< > where {}"#]], ); } @@ -41,7 +41,7 @@ macro Copy {} #[derive(Copy)] struct Foo; -impl < > crate ::marker::Copy for Foo< > {}"#]], +impl < > crate ::marker::Copy for Foo< > where {}"#]], ); } @@ -57,7 +57,7 @@ struct Foo; #[derive(Copy)] struct Foo; -impl core::marker::Copy for Foo {}"#]], +impl core::marker::Copy for Foo where {}"#]], ); } @@ -74,7 +74,7 @@ struct Foo; #[derive(Copy)] struct Foo; -impl core::marker::Copy for Foo {}"#]], +impl core::marker::Copy for Foo where {}"#]], ); } @@ -84,13 +84,33 @@ fn test_clone_expand() { r#" //- minicore: derive, clone #[derive(Clone)] -struct Foo; +enum Command { + Move { x: A, y: B }, + Do(&'static str), + Jump, +} "#, expect![[r#" #[derive(Clone)] -struct Foo; +enum Command { + Move { x: A, y: B }, + Do(&'static str), + Jump, +} -impl core::clone::Clone for Foo {}"#]], +impl core::clone::Clone for Command where { + fn clone(&self ) -> Self { + match self { + Command::Move { + x: x, y: y, + } + =>Command::Move { + x: x.clone(), y: y.clone(), + } + , Command::Do(f0, )=>Command::Do(f0.clone(), ), Command::Jump=>Command::Jump, + } + } +}"#]], ); } @@ -106,6 +126,270 @@ struct Foo(u32); #[derive(Clone)] struct Foo(u32); -impl core::clone::Clone for Foo {}"#]], +impl core::clone::Clone for Foo where { + fn clone(&self ) -> Self { + match self { + Foo(f0, )=>Foo(f0.clone(), ), + } + } +}"#]], + ); +} + +#[test] +fn test_default_expand() { + check( + r#" +//- minicore: derive, default +#[derive(Default)] +struct Foo { + field1: i32, + field2: (), +} +#[derive(Default)] +enum Bar { + Foo(u8), + #[default] + Bar, +} +"#, + expect![[r#" +#[derive(Default)] +struct Foo { + field1: i32, + field2: (), +} +#[derive(Default)] +enum Bar { + Foo(u8), + #[default] + Bar, +} + +impl < > core::default::Default for Foo< > where { + fn default() -> Self { + Foo { + field1: core::default::Default::default(), field2: core::default::Default::default(), + } + } +} +impl < > core::default::Default for Bar< > where { + fn default() -> Self { + Bar::Bar + } +}"#]], + ); +} + +#[test] +fn test_partial_eq_expand() { + check( + r#" +//- minicore: derive, eq +#[derive(PartialEq, Eq)] +enum Command { + Move { x: i32, y: i32 }, + Do(&'static str), + Jump, +} +"#, + expect![[r#" +#[derive(PartialEq, Eq)] +enum Command { + Move { x: i32, y: i32 }, + Do(&'static str), + Jump, +} + +impl < > core::cmp::PartialEq for Command< > where { + fn eq(&self , other: &Self ) -> bool { + match (self , other) { + (Command::Move { + x: x_self, y: y_self, + } + , Command::Move { + x: x_other, y: y_other, + } + )=>x_self.eq(x_other) && y_self.eq(y_other), (Command::Do(f0_self, ), Command::Do(f0_other, ))=>f0_self.eq(f0_other), (Command::Jump, Command::Jump)=>true , _unused=>false + } + } +} +impl < > core::cmp::Eq for Command< > where {}"#]], + ); +} + +#[test] +fn test_partial_ord_expand() { + check( + r#" +//- minicore: derive, ord +#[derive(PartialOrd, Ord)] +enum Command { + Move { x: i32, y: i32 }, + Do(&'static str), + Jump, +} +"#, + expect![[r#" +#[derive(PartialOrd, Ord)] +enum Command { + Move { x: i32, y: i32 }, + Do(&'static str), + Jump, +} + +impl < > core::cmp::PartialOrd for Command< > where { + fn partial_cmp(&self , other: &Self ) -> core::option::Option::Option { + match core::intrinsics::discriminant_value(self ).partial_cmp(&core::intrinsics::discriminant_value(other)) { + core::option::Option::Some(core::cmp::Ordering::Equal)=> { + match (self , other) { + (Command::Move { + x: x_self, y: y_self, + } + , Command::Move { + x: x_other, y: y_other, + } + )=>match x_self.partial_cmp(&x_other) { + core::option::Option::Some(core::cmp::Ordering::Equal)=> { + match y_self.partial_cmp(&y_other) { + core::option::Option::Some(core::cmp::Ordering::Equal)=> { + core::option::Option::Some(core::cmp::Ordering::Equal) + } + c=>return c, + } + } + c=>return c, + } + , (Command::Do(f0_self, ), Command::Do(f0_other, ))=>match f0_self.partial_cmp(&f0_other) { + core::option::Option::Some(core::cmp::Ordering::Equal)=> { + core::option::Option::Some(core::cmp::Ordering::Equal) + } + c=>return c, + } + , (Command::Jump, Command::Jump)=>core::option::Option::Some(core::cmp::Ordering::Equal), _unused=>core::option::Option::Some(core::cmp::Ordering::Equal) + } + } + c=>return c, + } + } +} +impl < > core::cmp::Ord for Command< > where { + fn cmp(&self , other: &Self ) -> core::cmp::Ordering { + match core::intrinsics::discriminant_value(self ).cmp(&core::intrinsics::discriminant_value(other)) { + core::cmp::Ordering::Equal=> { + match (self , other) { + (Command::Move { + x: x_self, y: y_self, + } + , Command::Move { + x: x_other, y: y_other, + } + )=>match x_self.cmp(&x_other) { + core::cmp::Ordering::Equal=> { + match y_self.cmp(&y_other) { + core::cmp::Ordering::Equal=> { + core::cmp::Ordering::Equal + } + c=>return c, + } + } + c=>return c, + } + , (Command::Do(f0_self, ), Command::Do(f0_other, ))=>match f0_self.cmp(&f0_other) { + core::cmp::Ordering::Equal=> { + core::cmp::Ordering::Equal + } + c=>return c, + } + , (Command::Jump, Command::Jump)=>core::cmp::Ordering::Equal, _unused=>core::cmp::Ordering::Equal + } + } + c=>return c, + } + } +}"#]], + ); +} + +#[test] +fn test_hash_expand() { + check( + r#" +//- minicore: derive, hash +use core::hash::Hash; + +#[derive(Hash)] +enum Command { + Move { x: i32, y: i32 }, + Do(&'static str), + Jump, +} +"#, + expect![[r#" +use core::hash::Hash; + +#[derive(Hash)] +enum Command { + Move { x: i32, y: i32 }, + Do(&'static str), + Jump, +} + +impl < > core::hash::Hash for Command< > where { + fn hash(&self , state: &mut H) { + core::mem::discriminant(self ).hash(state); + match self { + Command::Move { + x: x, y: y, + } + => { + x.hash(state); + y.hash(state); + } + , Command::Do(f0, )=> { + f0.hash(state); + } + , Command::Jump=> {} + , + } + } +}"#]], + ); +} + +#[test] +fn test_debug_expand() { + check( + r#" +//- minicore: derive, fmt +use core::fmt::Debug; + +#[derive(Debug)] +enum Command { + Move { x: i32, y: i32 }, + Do(&'static str), + Jump, +} +"#, + expect![[r#" +use core::fmt::Debug; + +#[derive(Debug)] +enum Command { + Move { x: i32, y: i32 }, + Do(&'static str), + Jump, +} + +impl < > core::fmt::Debug for Command< > where { + fn fmt(&self , f: &mut core::fmt::Formatter) -> core::fmt::Result { + match self { + Command::Move { + x: x, y: y, + } + =>f.debug_struct("Move").field("x", &x).field("y", &y).finish(), Command::Do(f0, )=>f.debug_tuple("Do").field(&f0).finish(), Command::Jump=>f.write_str("Jump"), + } + } +}"#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs index 5fbd1789b3a9a..977f300636f5e 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs @@ -13,12 +13,12 @@ macro_rules! column {() => {}} fn main() { column!(); } "#, - expect![[r##" + expect![[r#" #[rustc_builtin_macro] macro_rules! column {() => {}} -fn main() { 0; } -"##]], +fn main() { 0 as u32; } +"#]], ); } @@ -31,12 +31,12 @@ macro_rules! line {() => {}} fn main() { line!() } "#, - expect![[r##" + expect![[r#" #[rustc_builtin_macro] macro_rules! line {() => {}} -fn main() { 0 } -"##]], +fn main() { 0 as u32 } +"#]], ); } @@ -97,7 +97,7 @@ fn main() { option_env!("TEST_ENV_VAR"); } #[rustc_builtin_macro] macro_rules! option_env {() => {}} -fn main() { $crate::option::Option::None:: < &str>; } +fn main() { ::core::option::Option::None:: < &str>; } "#]], ); } @@ -193,7 +193,7 @@ fn main() { format_args!("{} {:?}", arg1(a, b, c), arg2); } "#, - expect![[r#" + expect![[r##" #[rustc_builtin_macro] macro_rules! format_args { ($fmt:expr) => ({ /* compiler built-in */ }); @@ -201,9 +201,9 @@ macro_rules! format_args { } fn main() { - $crate::fmt::Arguments::new_v1(&[], &[$crate::fmt::Argument::new(&(arg1(a, b, c)), $crate::fmt::Display::fmt), $crate::fmt::Argument::new(&(arg2), $crate::fmt::Display::fmt), ]); + ::core::fmt::Arguments::new_v1(&["", " ", ], &[::core::fmt::Argument::new(&(arg1(a, b, c)), ::core::fmt::Display::fmt), ::core::fmt::Argument::new(&(arg2), ::core::fmt::Debug::fmt), ]); } -"#]], +"##]], ); } @@ -221,7 +221,7 @@ fn main() { format_args!("{} {:?}", a::(), b); } "#, - expect![[r#" + expect![[r##" #[rustc_builtin_macro] macro_rules! format_args { ($fmt:expr) => ({ /* compiler built-in */ }); @@ -229,9 +229,76 @@ macro_rules! format_args { } fn main() { - $crate::fmt::Arguments::new_v1(&[], &[$crate::fmt::Argument::new(&(a::()), $crate::fmt::Display::fmt), $crate::fmt::Argument::new(&(b), $crate::fmt::Display::fmt), ]); + ::core::fmt::Arguments::new_v1(&["", " ", ], &[::core::fmt::Argument::new(&(a::()), ::core::fmt::Display::fmt), ::core::fmt::Argument::new(&(b), ::core::fmt::Debug::fmt), ]); } -"#]], +"##]], + ); +} + +#[test] +fn test_format_args_expand_with_raw_strings() { + check( + r##" +#[rustc_builtin_macro] +macro_rules! format_args { + ($fmt:expr) => ({ /* compiler built-in */ }); + ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ }) +} + +fn main() { + format_args!( + r#"{},mismatch,"{}","{}""#, + location_csv_pat(db, &analysis, vfs, &sm, pat_id), + mismatch.expected.display(db), + mismatch.actual.display(db) + ); +} +"##, + expect![[r##" +#[rustc_builtin_macro] +macro_rules! format_args { + ($fmt:expr) => ({ /* compiler built-in */ }); + ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ }) +} + +fn main() { + ::core::fmt::Arguments::new_v1(&[r#""#, r#",mismatch,""#, r#"",""#, r#"""#, ], &[::core::fmt::Argument::new(&(location_csv_pat(db, &analysis, vfs, &sm, pat_id)), ::core::fmt::Display::fmt), ::core::fmt::Argument::new(&(mismatch.expected.display(db)), ::core::fmt::Display::fmt), ::core::fmt::Argument::new(&(mismatch.actual.display(db)), ::core::fmt::Display::fmt), ]); +} +"##]], + ); +} + +#[test] +fn test_format_args_expand_eager() { + check( + r#" +#[rustc_builtin_macro] +macro_rules! concat {} + +#[rustc_builtin_macro] +macro_rules! format_args { + ($fmt:expr) => ({ /* compiler built-in */ }); + ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ }) +} + +fn main() { + format_args!(concat!("xxx{}y", "{:?}zzz"), 2, b); +} +"#, + expect![[r##" +#[rustc_builtin_macro] +macro_rules! concat {} + +#[rustc_builtin_macro] +macro_rules! format_args { + ($fmt:expr) => ({ /* compiler built-in */ }); + ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ }) +} + +fn main() { + ::core::fmt::Arguments::new_v1(&["xxx", "y", "zzz", ], &[::core::fmt::Argument::new(&(2), ::core::fmt::Display::fmt), ::core::fmt::Argument::new(&(b), ::core::fmt::Debug::fmt), ]); +} +"##]], ); } @@ -250,7 +317,7 @@ fn main() { format_args!/*+errors*/("{} {:?}", a.); } "#, - expect![[r#" + expect![[r##" #[rustc_builtin_macro] macro_rules! format_args { ($fmt:expr) => ({ /* compiler built-in */ }); @@ -259,10 +326,10 @@ macro_rules! format_args { fn main() { let _ = - /* parse error: expected field name or number */ -$crate::fmt::Arguments::new_v1(&[], &[$crate::fmt::Argument::new(&(a.), $crate::fmt::Display::fmt), ]); + /* error: no rule matches input tokens *//* parse error: expected field name or number */ +::core::fmt::Arguments::new_v1(&["", " ", ], &[::core::fmt::Argument::new(&(a.), ::core::fmt::Display::fmt), ::core::fmt::Argument::new(&(), ::core::fmt::Debug::fmt), ]); } -"#]], +"##]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs index 7a3e8c3b05c91..553ffe3d0b88b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs @@ -4,6 +4,7 @@ mod tt_conversion; mod matching; mod meta_syntax; +mod metavar_expr; mod regression; use expect_test::expect; @@ -98,7 +99,7 @@ fn#19 main#20(#21)#21 {#22 ); } #[test] -fn float_field_acces_macro_input() { +fn float_field_access_macro_input() { check( r#" macro_rules! foo { @@ -922,7 +923,7 @@ macro_rules! m { fn bar() -> &'a Baz {} -fn bar() -> extern "Rust"fn() -> Ret {} +fn bar() -> extern "Rust" fn() -> Ret {} "#]], ); } @@ -1293,19 +1294,53 @@ ok!(); } #[test] -fn test_vertical_bar_with_pat() { +fn test_vertical_bar_with_pat_param() { check( r#" -macro_rules! m { (|$pat:pat| ) => { ok!(); } } +macro_rules! m { (|$pat:pat_param| ) => { ok!(); } } m! { |x| } "#, expect![[r#" -macro_rules! m { (|$pat:pat| ) => { ok!(); } } +macro_rules! m { (|$pat:pat_param| ) => { ok!(); } } ok!(); "#]], ); } +#[test] +fn test_new_std_matches() { + check( + r#" +macro_rules! matches { + ($expression:expr, $pattern:pat $(if $guard:expr)? $(,)?) => { + match $expression { + $pattern $(if $guard)? => true, + _ => false + } + }; +} +fn main() { + matches!(0, 0 | 1 if true); +} + "#, + expect![[r#" +macro_rules! matches { + ($expression:expr, $pattern:pat $(if $guard:expr)? $(,)?) => { + match $expression { + $pattern $(if $guard)? => true, + _ => false + } + }; +} +fn main() { + match 0 { + 0|1 if true =>true , _=>false + }; +} + "#]], + ); +} + #[test] fn test_dollar_crate_lhs_is_not_meta() { check( @@ -1580,92 +1615,6 @@ struct Foo; ) } -#[test] -fn test_dollar_dollar() { - check( - r#" -macro_rules! register_struct { ($Struct:ident) => { - macro_rules! register_methods { ($$($method:ident),*) => { - macro_rules! implement_methods { ($$$$($$val:expr),*) => { - struct $Struct; - impl $Struct { $$(fn $method() -> &'static [u32] { &[$$$$($$$$val),*] })*} - }} - }} -}} - -register_struct!(Foo); -register_methods!(alpha, beta); -implement_methods!(1, 2, 3); -"#, - expect![[r#" -macro_rules! register_struct { ($Struct:ident) => { - macro_rules! register_methods { ($$($method:ident),*) => { - macro_rules! implement_methods { ($$$$($$val:expr),*) => { - struct $Struct; - impl $Struct { $$(fn $method() -> &'static [u32] { &[$$$$($$$$val),*] })*} - }} - }} -}} - -macro_rules !register_methods { - ($($method: ident), *) = > { - macro_rules!implement_methods { - ($$($val: expr), *) = > { - struct Foo; - impl Foo { - $(fn $method()-> &'static[u32] { - &[$$($$val), *] - } - )* - } - } - } - } -} -macro_rules !implement_methods { - ($($val: expr), *) = > { - struct Foo; - impl Foo { - fn alpha()-> &'static[u32] { - &[$($val), *] - } - fn beta()-> &'static[u32] { - &[$($val), *] - } - } - } -} -struct Foo; -impl Foo { - fn alpha() -> &'static[u32] { - &[1, 2, 3] - } - fn beta() -> &'static[u32] { - &[1, 2, 3] - } -} -"#]], - ) -} - -#[test] -fn test_metavar_exprs() { - check( - r#" -macro_rules! m { - ( $( $t:tt )* ) => ( $( ${ignore(t)} -${index()} )-* ); -} -const _: i32 = m!(a b c); - "#, - expect![[r#" -macro_rules! m { - ( $( $t:tt )* ) => ( $( ${ignore(t)} -${index()} )-* ); -} -const _: i32 = -0--1--2; - "#]], - ); -} - #[test] fn test_punct_without_space() { // Puncts are "glued" greedily. diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs index 26f16542cbb26..0909d8c835443 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs @@ -33,7 +33,7 @@ m!(&k"); "#, expect![[r#" macro_rules! m { ($i:literal) => {}; } -/* error: Failed to lower macro args to token tree */"#]], +/* error: invalid token tree */"#]], ); } @@ -73,7 +73,7 @@ fn main() { macro_rules! asi { ($($stmt:stmt)*) => ($($stmt)*); } fn main() { - let a = 2let b = 5drop(b-a)println!("{}", a+b) + let a = 2 let b = 5 drop(b-a)println!("{}", a+b) } "#]], ) @@ -106,7 +106,6 @@ stringify!(; #[test] fn range_patterns() { - // FIXME: rustc thinks there are three patterns here, not one. check( r#" macro_rules! m { @@ -118,7 +117,7 @@ m!(.. .. ..); macro_rules! m { ($($p:pat)*) => (stringify!($($p |)*);) } -stringify!(.. .. .. |); +stringify!(.. | .. | .. |); "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs new file mode 100644 index 0000000000000..967b5ad36babf --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs @@ -0,0 +1,311 @@ +//! Tests for RFC 3086 metavariable expressions. + +use expect_test::expect; + +use crate::macro_expansion_tests::check; + +#[test] +fn test_dollar_dollar() { + check( + r#" +macro_rules! register_struct { ($Struct:ident) => { + macro_rules! register_methods { ($$($method:ident),*) => { + macro_rules! implement_methods { ($$$$($$val:expr),*) => { + struct $Struct; + impl $Struct { $$(fn $method() -> &'static [u32] { &[$$$$($$$$val),*] })*} + }} + }} +}} + +register_struct!(Foo); +register_methods!(alpha, beta); +implement_methods!(1, 2, 3); +"#, + expect![[r#" +macro_rules! register_struct { ($Struct:ident) => { + macro_rules! register_methods { ($$($method:ident),*) => { + macro_rules! implement_methods { ($$$$($$val:expr),*) => { + struct $Struct; + impl $Struct { $$(fn $method() -> &'static [u32] { &[$$$$($$$$val),*] })*} + }} + }} +}} + +macro_rules !register_methods { + ($($method: ident), *) = > { + macro_rules!implement_methods { + ($$($val: expr), *) = > { + struct Foo; + impl Foo { + $(fn $method()-> &'static[u32] { + &[$$($$val), *] + } + )* + } + } + } + } +} +macro_rules !implement_methods { + ($($val: expr), *) = > { + struct Foo; + impl Foo { + fn alpha()-> &'static[u32] { + &[$($val), *] + } + fn beta()-> &'static[u32] { + &[$($val), *] + } + } + } +} +struct Foo; +impl Foo { + fn alpha() -> &'static[u32] { + &[1, 2, 3] + } + fn beta() -> &'static[u32] { + &[1, 2, 3] + } +} +"#]], + ) +} + +#[test] +fn test_metavar_exprs() { + check( + r#" +macro_rules! m { + ( $( $t:tt )* ) => ( $( ${ignore(t)} -${index()} )-* ); +} +const _: i32 = m!(a b c); + "#, + expect![[r#" +macro_rules! m { + ( $( $t:tt )* ) => ( $( ${ignore(t)} -${index()} )-* ); +} +const _: i32 = -0--1--2; + "#]], + ); +} + +#[test] +fn count_basic() { + check( + r#" +macro_rules! m { + ($($t:ident),*) => { + ${count(t)} + } +} + +fn test() { + m!(); + m!(a); + m!(a, a); +} +"#, + expect![[r#" +macro_rules! m { + ($($t:ident),*) => { + ${count(t)} + } +} + +fn test() { + 0; + 1; + 2; +} +"#]], + ); +} + +#[test] +fn count_with_depth() { + check( + r#" +macro_rules! foo { + ($( $( $($t:ident)* ),* );*) => { + $( + { + let depth_none = ${count(t)}; + let depth_zero = ${count(t, 0)}; + let depth_one = ${count(t, 1)}; + } + )* + } +} + +fn bar() { + foo!( + a a a, a, a a; + a a a + ) +} +"#, + expect![[r#" +macro_rules! foo { + ($( $( $($t:ident)* ),* );*) => { + $( + { + let depth_none = ${count(t)}; + let depth_zero = ${count(t, 0)}; + let depth_one = ${count(t, 1)}; + } + )* + } +} + +fn bar() { + { + let depth_none = 6; + let depth_zero = 3; + let depth_one = 6; + } { + let depth_none = 3; + let depth_zero = 1; + let depth_one = 3; + } +} +"#]], + ); +} + +#[test] +fn count_depth_out_of_bounds() { + check( + r#" +macro_rules! foo { + ($($t:ident)*) => { ${count(t, 1)} }; + ($( $( $l:literal )* );*) => { $(${count(l, 1)};)* } +} +macro_rules! bar { + ($($t:ident)*) => { ${count(t, 1024)} }; + ($( $( $l:literal )* );*) => { $(${count(l, 8192)};)* } +} + +fn test() { + foo!(a b); + foo!(1 2; 3); + bar!(a b); + bar!(1 2; 3); +} +"#, + expect![[r#" +macro_rules! foo { + ($($t:ident)*) => { ${count(t, 1)} }; + ($( $( $l:literal )* );*) => { $(${count(l, 1)};)* } +} +macro_rules! bar { + ($($t:ident)*) => { ${count(t, 1024)} }; + ($( $( $l:literal )* );*) => { $(${count(l, 8192)};)* } +} + +fn test() { + /* error: ${count} out of bounds */; + /* error: ${count} out of bounds */; + /* error: ${count} out of bounds */; + /* error: ${count} out of bounds */; +} +"#]], + ); +} + +#[test] +fn misplaced_count() { + check( + r#" +macro_rules! foo { + ($($t:ident)*) => { $(${count(t)})* }; + ($l:literal) => { ${count(l)} } +} + +fn test() { + foo!(a b c); + foo!(1); +} +"#, + expect![[r#" +macro_rules! foo { + ($($t:ident)*) => { $(${count(t)})* }; + ($l:literal) => { ${count(l)} } +} + +fn test() { + /* error: ${count} misplaced */; + /* error: ${count} misplaced */; +} +"#]], + ); +} + +#[test] +fn malformed_count() { + check( + r#" +macro_rules! too_many_args { + ($($t:ident)*) => { ${count(t, 1, leftover)} } +} +macro_rules! depth_suffixed { + ($($t:ident)*) => { ${count(t, 0usize)} } +} +macro_rules! depth_too_large { + ($($t:ident)*) => { ${count(t, 18446744073709551616)} } +} + +fn test() { + too_many_args!(); + depth_suffixed!(); + depth_too_large!(); +} +"#, + expect![[r#" +macro_rules! too_many_args { + ($($t:ident)*) => { ${count(t, 1, leftover)} } +} +macro_rules! depth_suffixed { + ($($t:ident)*) => { ${count(t, 0usize)} } +} +macro_rules! depth_too_large { + ($($t:ident)*) => { ${count(t, 18446744073709551616)} } +} + +fn test() { + /* error: invalid macro definition: invalid metavariable expression */; + /* error: invalid macro definition: invalid metavariable expression */; + /* error: invalid macro definition: invalid metavariable expression */; +} +"#]], + ); +} + +#[test] +fn count_interaction_with_empty_binding() { + // FIXME: Should this error? rustc currently accepts it. + check( + r#" +macro_rules! m { + ($($t:ident),*) => { + ${count(t, 100)} + } +} + +fn test() { + m!(); +} +"#, + expect![[r#" +macro_rules! m { + ($($t:ident),*) => { + ${count(t, 100)} + } +} + +fn test() { + 0; +} +"#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs index b663a2917897e..d8e4a4dcc7c2b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs @@ -297,55 +297,55 @@ macro_rules! impl_fn_for_zst { #[derive(Clone)] struct CharEscapeDebugContinue; impl Fn<(char, )> for CharEscapeDebugContinue { - #[inline] extern "rust-call"fn call(&self , (c, ): (char, )) -> char::EscapeDebug { { + #[inline] extern "rust-call" fn call(&self , (c, ): (char, )) -> char::EscapeDebug { { c.escape_debug_ext(false ) } } } impl FnMut<(char, )> for CharEscapeDebugContinue { - #[inline] extern "rust-call"fn call_mut(&mut self , (c, ): (char, )) -> char::EscapeDebug { + #[inline] extern "rust-call" fn call_mut(&mut self , (c, ): (char, )) -> char::EscapeDebug { Fn::call(&*self , (c, )) } } impl FnOnce<(char, )> for CharEscapeDebugContinue { type Output = char::EscapeDebug; - #[inline] extern "rust-call"fn call_once(self , (c, ): (char, )) -> char::EscapeDebug { + #[inline] extern "rust-call" fn call_once(self , (c, ): (char, )) -> char::EscapeDebug { Fn::call(&self , (c, )) } } #[derive(Clone)] struct CharEscapeUnicode; impl Fn<(char, )> for CharEscapeUnicode { - #[inline] extern "rust-call"fn call(&self , (c, ): (char, )) -> char::EscapeUnicode { { + #[inline] extern "rust-call" fn call(&self , (c, ): (char, )) -> char::EscapeUnicode { { c.escape_unicode() } } } impl FnMut<(char, )> for CharEscapeUnicode { - #[inline] extern "rust-call"fn call_mut(&mut self , (c, ): (char, )) -> char::EscapeUnicode { + #[inline] extern "rust-call" fn call_mut(&mut self , (c, ): (char, )) -> char::EscapeUnicode { Fn::call(&*self , (c, )) } } impl FnOnce<(char, )> for CharEscapeUnicode { type Output = char::EscapeUnicode; - #[inline] extern "rust-call"fn call_once(self , (c, ): (char, )) -> char::EscapeUnicode { + #[inline] extern "rust-call" fn call_once(self , (c, ): (char, )) -> char::EscapeUnicode { Fn::call(&self , (c, )) } } #[derive(Clone)] struct CharEscapeDefault; impl Fn<(char, )> for CharEscapeDefault { - #[inline] extern "rust-call"fn call(&self , (c, ): (char, )) -> char::EscapeDefault { { + #[inline] extern "rust-call" fn call(&self , (c, ): (char, )) -> char::EscapeDefault { { c.escape_default() } } } impl FnMut<(char, )> for CharEscapeDefault { - #[inline] extern "rust-call"fn call_mut(&mut self , (c, ): (char, )) -> char::EscapeDefault { + #[inline] extern "rust-call" fn call_mut(&mut self , (c, ): (char, )) -> char::EscapeDefault { Fn::call(&*self , (c, )) } } impl FnOnce<(char, )> for CharEscapeDefault { type Output = char::EscapeDefault; - #[inline] extern "rust-call"fn call_once(self , (c, ): (char, )) -> char::EscapeDefault { + #[inline] extern "rust-call" fn call_once(self , (c, ): (char, )) -> char::EscapeDefault { Fn::call(&self , (c, )) } } @@ -833,7 +833,7 @@ macro_rules! rgb_color { /* parse error: expected SEMICOLON */ /* parse error: expected expression, item or let statement */ pub fn new() { - let _ = 0as u32<<(8+8); + let _ = 0 as u32<<(8+8); } // MACRO_ITEMS@0..31 // FN@0..31 diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs index b8d2ca687c9e0..ae56934f632f1 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs @@ -98,7 +98,7 @@ macro_rules! m1 { ($x:ident) => { ($x } } macro_rules! m2 { ($x:ident) => {} } /* error: invalid macro definition: expected subtree */ -/* error: Failed to lower macro args to token tree */ +/* error: invalid token tree */ "#]], ) } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs similarity index 86% rename from src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests.rs rename to src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs index 314bf22b95ee7..4a62696df0810 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs @@ -14,7 +14,7 @@ mod builtin_fn_macro; mod builtin_derive_macro; mod proc_macros; -use std::{iter, ops::Range, sync::Arc}; +use std::{iter, ops::Range, sync}; use ::mbe::TokenMap; use base_db::{fixture::WithFixture, ProcMacro, SourceDatabase}; @@ -33,8 +33,13 @@ use syntax::{ use tt::token_id::{Subtree, TokenId}; use crate::{ - db::DefDatabase, macro_id_to_def_id, nameres::ModuleSource, resolver::HasResolver, - src::HasSource, test_db::TestDB, AdtId, AsMacroCall, Lookup, ModuleDefId, + db::DefDatabase, + macro_id_to_def_id, + nameres::{DefMap, MacroSubNs, ModuleSource}, + resolver::HasResolver, + src::HasSource, + test_db::TestDB, + AdtId, AsMacroCall, Lookup, ModuleDefId, }; #[track_caller] @@ -50,13 +55,13 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream ProcMacro { name: "identity_when_valid".into(), kind: base_db::ProcMacroKind::Attr, - expander: Arc::new(IdentityWhenValidProcMacroExpander), + expander: sync::Arc::new(IdentityWhenValidProcMacroExpander), }, )]; let db = TestDB::with_files_extra_proc_macros(ra_fixture, extra_proc_macros); let krate = db.crate_graph().iter().next().unwrap(); let def_map = db.crate_def_map(krate); - let local_id = def_map.root(); + let local_id = DefMap::ROOT; let module = def_map.module_id(local_id); let resolver = module.resolver(&db); let source = def_map[local_id].definition_source(&db); @@ -125,21 +130,17 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream for macro_call in source_file.syntax().descendants().filter_map(ast::MacroCall::cast) { let macro_call = InFile::new(source.file_id, ¯o_call); - let mut error = None; - let macro_call_id = macro_call - .as_call_id_with_errors( - &db, - krate, - |path| { - resolver.resolve_path_as_macro(&db, &path).map(|it| macro_id_to_def_id(&db, it)) - }, - &mut |err| error = Some(err), - ) - .unwrap() + let res = macro_call + .as_call_id_with_errors(&db, krate, |path| { + resolver + .resolve_path_as_macro(&db, &path, Some(MacroSubNs::Bang)) + .map(|it| macro_id_to_def_id(&db, it)) + }) .unwrap(); + let macro_call_id = res.value.unwrap(); let macro_file = MacroFile { macro_call_id }; let mut expansion_result = db.parse_macro_expansion(macro_file); - expansion_result.err = expansion_result.err.or(error); + expansion_result.err = expansion_result.err.or(res.err); expansions.push((macro_call.value.clone(), expansion_result, db.macro_arg(macro_call_id))); } @@ -157,34 +158,33 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream if let Some(err) = exp.err { format_to!(expn_text, "/* error: {} */", err); } - if let Some((parse, token_map)) = exp.value { - if expect_errors { - assert!(!parse.errors().is_empty(), "no parse errors in expansion"); - for e in parse.errors() { - format_to!(expn_text, "/* parse error: {} */\n", e); - } - } else { - assert!( - parse.errors().is_empty(), - "parse errors in expansion: \n{:#?}", - parse.errors() - ); + let (parse, token_map) = exp.value; + if expect_errors { + assert!(!parse.errors().is_empty(), "no parse errors in expansion"); + for e in parse.errors() { + format_to!(expn_text, "/* parse error: {} */\n", e); } - let pp = pretty_print_macro_expansion( - parse.syntax_node(), - show_token_ids.then_some(&*token_map), + } else { + assert!( + parse.errors().is_empty(), + "parse errors in expansion: \n{:#?}", + parse.errors() ); - let indent = IndentLevel::from_node(call.syntax()); - let pp = reindent(indent, pp); - format_to!(expn_text, "{}", pp); + } + let pp = pretty_print_macro_expansion( + parse.syntax_node(), + show_token_ids.then_some(&*token_map), + ); + let indent = IndentLevel::from_node(call.syntax()); + let pp = reindent(indent, pp); + format_to!(expn_text, "{}", pp); - if tree { - let tree = format!("{:#?}", parse.syntax_node()) - .split_inclusive('\n') - .map(|line| format!("// {line}")) - .collect::(); - format_to!(expn_text, "\n{}", tree) - } + if tree { + let tree = format!("{:#?}", parse.syntax_node()) + .split_inclusive('\n') + .map(|line| format!("// {line}")) + .collect::(); + format_to!(expn_text, "\n{}", tree) } let range = call.syntax().text_range(); let range: Range = range.into(); @@ -287,6 +287,7 @@ fn pretty_print_macro_expansion(expn: SyntaxNode, map: Option<&TokenMap>) -> Str let curr_kind = token.kind(); let space = match (prev_kind, curr_kind) { _ if prev_kind.is_trivia() || curr_kind.is_trivia() => "", + _ if prev_kind.is_literal() && !curr_kind.is_punct() => " ", (T!['{'], T!['}']) => "", (T![=], _) | (_, T![=]) => " ", (_, T!['{']) => " ", diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs index 4efe8c58a69e3..9b520bc3030f0 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs @@ -57,9 +57,9 @@ mod path_resolution; #[cfg(test)] mod tests; -use std::{cmp::Ord, ops::Deref, sync::Arc}; +use std::{cmp::Ord, ops::Deref}; -use base_db::{CrateId, Edition, FileId}; +use base_db::{CrateId, Edition, FileId, ProcMacroKind}; use hir_expand::{name::Name, InFile, MacroCallId, MacroDefId}; use itertools::Itertools; use la_arena::Arena; @@ -67,6 +67,7 @@ use profile::Count; use rustc_hash::{FxHashMap, FxHashSet}; use stdx::format_to; use syntax::{ast, SmolStr}; +use triomphe::Arc; use crate::{ db::DefDatabase, @@ -76,7 +77,8 @@ use crate::{ path::ModPath, per_ns::PerNs, visibility::Visibility, - AstId, BlockId, BlockLoc, FunctionId, LocalModuleId, MacroId, ModuleId, ProcMacroId, + AstId, BlockId, BlockLoc, FunctionId, LocalModuleId, Lookup, MacroExpander, MacroId, ModuleId, + ProcMacroId, }; /// Contains the results of (early) name resolution. @@ -92,7 +94,6 @@ use crate::{ pub struct DefMap { _c: Count, block: Option, - root: LocalModuleId, modules: Arena, krate: CrateId, /// The prelude module for this crate. This either comes from an import @@ -102,7 +103,22 @@ pub struct DefMap { /// but that attribute is nightly and when used in a block, it affects resolution globally /// so we aren't handling this correctly anyways). prelude: Option, - /// The extern prelude is only populated for non-block DefMaps + /// `macro_use` prelude that contains macros from `#[macro_use]`'d external crates. Note that + /// this contains all kinds of macro, not just `macro_rules!` macro. + macro_use_prelude: FxHashMap, + + /// 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)>>, + + diagnostics: Vec, + + data: Arc, +} + +/// Data that belongs to a crate which is shared between a crate's def map and all its block def maps. +#[derive(Clone, Debug, PartialEq, Eq)] +struct DefMapCrateData { extern_prelude: FxHashMap, /// Side table for resolving derive helpers. @@ -110,9 +126,6 @@ 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, @@ -122,10 +135,36 @@ pub struct DefMap { unstable_features: FxHashSet, /// #[rustc_coherence_is_core] rustc_coherence_is_core: bool, + no_core: bool, + no_std: bool, edition: Edition, recursion_limit: Option, - diagnostics: Vec, +} + +impl DefMapCrateData { + fn shrink_to_fit(&mut self) { + let Self { + extern_prelude, + exported_derives, + fn_proc_macro_mapping, + registered_attrs, + registered_tools, + unstable_features, + proc_macro_loading_error: _, + rustc_coherence_is_core: _, + no_core: _, + no_std: _, + edition: _, + recursion_limit: _, + } = self; + extern_prelude.shrink_to_fit(); + exported_derives.shrink_to_fit(); + fn_proc_macro_mapping.shrink_to_fit(); + registered_attrs.shrink_to_fit(); + registered_tools.shrink_to_fit(); + unstable_features.shrink_to_fit(); + } } /// For `DefMap`s computed for a block expression, this stores its location in the parent map. @@ -134,7 +173,23 @@ struct BlockInfo { /// The `BlockId` this `DefMap` was created from. block: BlockId, /// The containing module. - parent: ModuleId, + parent: BlockRelativeModuleId, +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +struct BlockRelativeModuleId { + block: Option, + local_id: LocalModuleId, +} + +impl BlockRelativeModuleId { + fn def_map(self, db: &dyn DefDatabase, krate: CrateId) -> Arc { + self.into_module(krate).def_map(db) + } + + fn into_module(self, krate: CrateId) -> ModuleId { + ModuleId { krate, block: self.block, local_id: self.local_id } + } } impl std::ops::Index for DefMap { @@ -224,6 +279,8 @@ pub struct ModuleData { } impl DefMap { + pub const ROOT: LocalModuleId = LocalModuleId::from_raw(la_arena::RawIdx::from_u32(0)); + pub(crate) fn crate_def_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc { let _p = profile::span("crate_def_map_query").detail(|| { db.crate_graph()[krate].display_name.as_deref().unwrap_or_default().to_string() @@ -243,17 +300,10 @@ impl DefMap { Arc::new(def_map) } - pub(crate) fn block_def_map_query( - db: &dyn DefDatabase, - block_id: BlockId, - ) -> Option> { + pub(crate) fn block_def_map_query(db: &dyn DefDatabase, block_id: BlockId) -> Arc { let block: BlockLoc = db.lookup_intern_block(block_id); let tree_id = TreeId::new(block.ast_id.file_id, Some(block_id)); - let item_tree = tree_id.item_tree(db); - if item_tree.top_level_items().is_empty() { - return None; - } let parent_map = block.module.def_map(db); let krate = block.module.krate; @@ -265,36 +315,48 @@ impl DefMap { let module_data = ModuleData::new(ModuleOrigin::BlockExpr { block: block.ast_id }, visibility); - let mut def_map = DefMap::empty(krate, parent_map.edition, module_data); - def_map.block = Some(BlockInfo { block: block_id, parent: block.module }); + let mut def_map = DefMap::empty(krate, parent_map.data.edition, module_data); + def_map.data = parent_map.data.clone(); + def_map.block = Some(BlockInfo { + block: block_id, + parent: BlockRelativeModuleId { + block: block.module.block, + local_id: block.module.local_id, + }, + }); let def_map = collector::collect_defs(db, def_map, tree_id); - Some(Arc::new(def_map)) + Arc::new(def_map) } fn empty(krate: CrateId, edition: Edition, module_data: ModuleData) -> DefMap { let mut modules: Arena = Arena::default(); let root = modules.alloc(module_data); + assert_eq!(root, Self::ROOT); DefMap { _c: Count::new(), block: None, + modules, krate, - edition, - recursion_limit: None, - extern_prelude: FxHashMap::default(), - 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, - registered_attrs: Vec::new(), - registered_tools: Vec::new(), - unstable_features: FxHashSet::default(), + macro_use_prelude: FxHashMap::default(), + derive_helpers_in_scope: FxHashMap::default(), diagnostics: Vec::new(), - rustc_coherence_is_core: false, + data: Arc::new(DefMapCrateData { + extern_prelude: FxHashMap::default(), + exported_derives: FxHashMap::default(), + fn_proc_macro_mapping: FxHashMap::default(), + proc_macro_loading_error: None, + registered_attrs: Vec::new(), + registered_tools: Vec::new(), + unstable_features: FxHashSet::default(), + rustc_coherence_is_core: false, + no_core: false, + no_std: false, + edition, + recursion_limit: None, + }), } } @@ -317,31 +379,31 @@ impl DefMap { } pub fn registered_tools(&self) -> &[SmolStr] { - &self.registered_tools + &self.data.registered_tools } pub fn registered_attrs(&self) -> &[SmolStr] { - &self.registered_attrs + &self.data.registered_attrs } pub fn is_unstable_feature_enabled(&self, feature: &str) -> bool { - self.unstable_features.contains(feature) + self.data.unstable_features.contains(feature) } pub fn is_rustc_coherence_is_core(&self) -> bool { - self.rustc_coherence_is_core + self.data.rustc_coherence_is_core } - pub fn root(&self) -> LocalModuleId { - self.root + pub fn is_no_std(&self) -> bool { + self.data.no_std || self.data.no_core } pub fn fn_as_proc_macro(&self, id: FunctionId) -> Option { - self.fn_proc_macro_mapping.get(&id).copied() + self.data.fn_proc_macro_mapping.get(&id).copied() } pub fn proc_macro_loading_error(&self) -> Option<&str> { - self.proc_macro_loading_error.as_deref() + self.data.proc_macro_loading_error.as_deref() } pub fn krate(&self) -> CrateId { @@ -356,8 +418,12 @@ impl DefMap { self.prelude } - pub(crate) fn extern_prelude(&self) -> impl Iterator + '_ { - self.extern_prelude.iter() + pub(crate) fn extern_prelude(&self) -> impl Iterator + '_ { + self.data.extern_prelude.iter().map(|(name, def)| (name, *def)) + } + + pub(crate) fn macro_use_prelude(&self) -> impl Iterator + '_ { + self.macro_use_prelude.iter().map(|(name, def)| (name, *def)) } pub fn module_id(&self, local_id: LocalModuleId) -> ModuleId { @@ -365,11 +431,8 @@ impl DefMap { ModuleId { krate: self.krate, local_id, block } } - pub(crate) fn crate_root(&self, db: &dyn DefDatabase) -> ModuleId { - self.with_ancestor_maps(db, self.root, &mut |def_map, _module| { - if def_map.block.is_none() { Some(def_map.module_id(def_map.root)) } else { None } - }) - .expect("DefMap chain without root") + pub(crate) fn crate_root(&self) -> ModuleId { + ModuleId { krate: self.krate, block: None, local_id: DefMap::ROOT } } pub(crate) fn resolve_path( @@ -378,9 +441,16 @@ impl DefMap { original_module: LocalModuleId, path: &ModPath, shadow: BuiltinShadowMode, + expected_macro_subns: Option, ) -> (PerNs, Option) { - let res = - self.resolve_path_fp_with_macro(db, ResolveMode::Other, original_module, path, shadow); + let res = self.resolve_path_fp_with_macro( + db, + ResolveMode::Other, + original_module, + path, + shadow, + expected_macro_subns, + ); (res.resolved_def, res.segment_index) } @@ -397,6 +467,7 @@ impl DefMap { original_module, path, shadow, + None, // Currently this function isn't used for macro resolution. ); (res.resolved_def, res.segment_index) } @@ -416,7 +487,7 @@ impl DefMap { } let mut block = self.block; while let Some(block_info) = block { - let parent = block_info.parent.def_map(db); + let parent = block_info.parent.def_map(db, self.krate); if let Some(it) = f(&parent, block_info.parent.local_id) { return Some(it); } @@ -429,7 +500,8 @@ impl DefMap { /// If this `DefMap` is for a block expression, returns the module containing the block (which /// might again be a block, or a module inside a block). pub fn parent(&self) -> Option { - Some(self.block?.parent) + let BlockRelativeModuleId { block, local_id } = self.block?.parent; + Some(ModuleId { krate: self.krate, block, local_id }) } /// Returns the module containing `local_mod`, either the parent `mod`, or the module (or block) containing @@ -437,7 +509,13 @@ impl DefMap { pub fn containing_module(&self, local_mod: LocalModuleId) -> Option { match self[local_mod].parent { Some(parent) => Some(self.module_id(parent)), - None => self.block.map(|block| block.parent), + None => { + self.block.map( + |BlockInfo { parent: BlockRelativeModuleId { block, local_id }, .. }| { + ModuleId { krate: self.krate, block, local_id } + }, + ) + } } } @@ -448,25 +526,31 @@ impl DefMap { let mut arc; let mut current_map = self; while let Some(block) = current_map.block { - go(&mut buf, current_map, "block scope", current_map.root); + go(&mut buf, db, current_map, "block scope", Self::ROOT); buf.push('\n'); - arc = block.parent.def_map(db); + arc = block.parent.def_map(db, self.krate); current_map = &arc; } - go(&mut buf, current_map, "crate", current_map.root); + go(&mut buf, db, current_map, "crate", Self::ROOT); return buf; - fn go(buf: &mut String, map: &DefMap, path: &str, module: LocalModuleId) { + fn go( + buf: &mut String, + db: &dyn DefDatabase, + map: &DefMap, + path: &str, + module: LocalModuleId, + ) { format_to!(buf, "{}\n", path); - map.modules[module].scope.dump(buf); + map.modules[module].scope.dump(db.upcast(), buf); for (name, child) in map.modules[module].children.iter().sorted_by(|a, b| Ord::cmp(&a.0, &b.0)) { - let path = format!("{path}::{name}"); + let path = format!("{path}::{}", name.display(db.upcast())); buf.push('\n'); - go(buf, map, &path, *child); + go(buf, db, map, &path, *child); } } } @@ -477,7 +561,7 @@ impl DefMap { let mut current_map = self; while let Some(block) = current_map.block { format_to!(buf, "{:?} in {:?}\n", block.block, block.parent); - arc = block.parent.def_map(db); + arc = block.parent.def_map(db, self.krate); current_map = &arc; } @@ -489,34 +573,20 @@ impl DefMap { // Exhaustive match to require handling new fields. let Self { _c: _, - exported_derives, - extern_prelude, + macro_use_prelude, diagnostics, modules, - registered_attrs, - registered_tools, - fn_proc_macro_mapping, derive_helpers_in_scope, - unstable_features, - proc_macro_loading_error: _, block: _, - edition: _, - recursion_limit: _, krate: _, prelude: _, - root: _, - rustc_coherence_is_core: _, + data: _, } = self; - extern_prelude.shrink_to_fit(); - exported_derives.shrink_to_fit(); + macro_use_prelude.shrink_to_fit(); diagnostics.shrink_to_fit(); modules.shrink_to_fit(); - registered_attrs.shrink_to_fit(); - registered_tools.shrink_to_fit(); - fn_proc_macro_mapping.shrink_to_fit(); derive_helpers_in_scope.shrink_to_fit(); - unstable_features.shrink_to_fit(); for (_, module) in modules.iter_mut() { module.children.shrink_to_fit(); module.scope.shrink_to_fit(); @@ -529,7 +599,7 @@ impl DefMap { } pub fn recursion_limit(&self) -> Option { - self.recursion_limit + self.data.recursion_limit } } @@ -564,3 +634,48 @@ pub enum ModuleSource { Module(ast::Module), BlockExpr(ast::BlockExpr), } + +/// See `sub_namespace_match()`. +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum MacroSubNs { + /// Function-like macros, suffixed with `!`. + Bang, + /// Macros inside attributes, i.e. attribute macros and derive macros. + Attr, +} + +impl MacroSubNs { + fn from_id(db: &dyn DefDatabase, macro_id: MacroId) -> Self { + let expander = match macro_id { + MacroId::Macro2Id(it) => it.lookup(db).expander, + MacroId::MacroRulesId(it) => it.lookup(db).expander, + MacroId::ProcMacroId(it) => { + return match it.lookup(db).kind { + ProcMacroKind::CustomDerive | ProcMacroKind::Attr => Self::Attr, + ProcMacroKind::FuncLike => Self::Bang, + }; + } + }; + + // Eager macros aren't *guaranteed* to be bang macros, but they *are* all bang macros currently. + match expander { + MacroExpander::Declarative + | MacroExpander::BuiltIn(_) + | MacroExpander::BuiltInEager(_) => Self::Bang, + MacroExpander::BuiltInAttr(_) | MacroExpander::BuiltInDerive(_) => Self::Attr, + } + } +} + +/// Quoted from [rustc]: +/// Macro namespace is separated into two sub-namespaces, one for bang macros and +/// one for attribute-like macros (attributes, derives). +/// We ignore resolutions from one sub-namespace when searching names in scope for another. +/// +/// [rustc]: https://github.com/rust-lang/rust/blob/1.69.0/compiler/rustc_resolve/src/macros.rs#L75 +fn sub_namespace_match(candidate: Option, expected: Option) -> bool { + match (candidate, expected) { + (Some(candidate), Some(expected)) => candidate == expected, + _ => true, + } +} diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs index 79cabeb0fb7a4..a7abf445918aa 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs @@ -4,7 +4,8 @@ use hir_expand::{attrs::Attr, MacroCallId}; use syntax::{ast, SmolStr}; use crate::{ - attr_macro_as_call_id, builtin_attr, + attr::builtin::{find_builtin_attr_idx, TOOL_MODULES}, + attr_macro_as_call_id, db::DefDatabase, item_scope::BuiltinShadowMode, macro_id_to_def_id, @@ -13,7 +14,7 @@ use crate::{ AstIdWithPath, LocalModuleId, UnresolvedMacro, }; -use super::DefMap; +use super::{DefMap, MacroSubNs}; pub enum ResolvedAttr { /// Attribute resolved to an attribute macro. @@ -42,9 +43,12 @@ impl DefMap { original_module, &ast_id.path, BuiltinShadowMode::Module, + Some(MacroSubNs::Attr), ); let def = match resolved_res.resolved_def.take_macros() { Some(def) => { + // `MacroSubNs` is just a hint, so the path may still resolve to a custom derive + // macro, or even function-like macro when the path is qualified. if def.is_attribute(db) { def } else { @@ -60,7 +64,6 @@ impl DefMap { attr, self.krate, macro_id_to_def_id(db, def), - false, ))) } @@ -75,20 +78,16 @@ impl DefMap { let name = name.to_smol_str(); let pred = |n: &_| *n == name; - let registered = self.registered_tools.iter().map(SmolStr::as_str); - let is_tool = builtin_attr::TOOL_MODULES.iter().copied().chain(registered).any(pred); + let registered = self.data.registered_tools.iter().map(SmolStr::as_str); + let is_tool = TOOL_MODULES.iter().copied().chain(registered).any(pred); // FIXME: tool modules can be shadowed by actual modules if is_tool { return true; } if segments.len() == 1 { - let registered = self.registered_attrs.iter().map(SmolStr::as_str); - let is_inert = builtin_attr::INERT_ATTRIBUTES - .iter() - .map(|it| it.name) - .chain(registered) - .any(pred); + let mut registered = self.data.registered_attrs.iter().map(SmolStr::as_str); + let is_inert = find_builtin_attr_idx(&name).is_some() || registered.any(pred); return is_inert; } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs index ddcee77ec4ccf..06542b4b1e999 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs @@ -5,7 +5,7 @@ use std::{iter, mem}; -use base_db::{CrateId, Edition, FileId}; +use base_db::{CrateId, Dependency, Edition, FileId}; use cfg::{CfgExpr, CfgOptions}; use either::Either; use hir_expand::{ @@ -14,10 +14,11 @@ use hir_expand::{ builtin_attr_macro::find_builtin_attr, builtin_derive_macro::find_builtin_derive, builtin_fn_macro::find_builtin_macro, + hygiene::Hygiene, name::{name, AsName, Name}, proc_macro::ProcMacroExpander, - ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId, - MacroDefKind, + ExpandResult, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroCallLoc, + MacroDefId, MacroDefKind, }; use itertools::{izip, Itertools}; use la_arena::Idx; @@ -25,6 +26,7 @@ use limit::Limit; use rustc_hash::{FxHashMap, FxHashSet}; use stdx::always; use syntax::{ast, SmolStr}; +use triomphe::Arc; use crate::{ attr::Attrs, @@ -33,8 +35,8 @@ use crate::{ derive_macro_as_call_id, item_scope::{ImportType, PerNsGlobImports}, item_tree::{ - self, Fields, FileItemTreeId, ImportKind, ItemTree, ItemTreeId, ItemTreeNode, MacroCall, - MacroDef, MacroRules, Mod, ModItem, ModKind, TreeId, + self, ExternCrate, Fields, FileItemTreeId, ImportKind, ItemTree, ItemTreeId, ItemTreeNode, + MacroCall, MacroDef, MacroRules, Mod, ModItem, ModKind, TreeId, }, macro_call_as_call_id, macro_id_to_def_id, nameres::{ @@ -42,7 +44,8 @@ use crate::{ mod_resolution::ModDir, path_resolution::ReachedFixedPoint, proc_macro::{parse_macro_name_and_helper_attrs, ProcMacroDef, ProcMacroKind}, - BuiltinShadowMode, DefMap, ModuleData, ModuleOrigin, ResolveMode, + sub_namespace_match, BuiltinShadowMode, DefMap, MacroSubNs, ModuleData, ModuleOrigin, + ResolveMode, }, path::{ImportAlias, ModPath, PathKind}, per_ns::PerNs, @@ -59,7 +62,7 @@ static GLOB_RECURSION_LIMIT: Limit = Limit::new(100); static EXPANSION_DEPTH_LIMIT: Limit = Limit::new(128); static FIXED_POINT_LIMIT: Limit = Limit::new(8192); -pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: DefMap, tree_id: TreeId) -> DefMap { +pub(super) fn collect_defs(db: &dyn DefDatabase, def_map: DefMap, tree_id: TreeId) -> DefMap { let crate_graph = db.crate_graph(); let mut deps = FxHashMap::default(); @@ -67,36 +70,33 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: DefMap, tree_id: T let krate = &crate_graph[def_map.krate]; for dep in &krate.dependencies { tracing::debug!("crate dep {:?} -> {:?}", dep.name, dep.crate_id); - let dep_def_map = db.crate_def_map(dep.crate_id); - let dep_root = dep_def_map.module_id(dep_def_map.root); - deps.insert(dep.as_name(), dep_root); - - if dep.is_prelude() && !tree_id.is_block() { - def_map.extern_prelude.insert(dep.as_name(), dep_root); - } + deps.insert(dep.as_name(), dep.clone()); } let cfg_options = &krate.cfg_options; - let proc_macros = match &krate.proc_macro { - Ok(proc_macros) => { - proc_macros - .iter() - .enumerate() - .map(|(idx, it)| { - // FIXME: a hacky way to create a Name from string. - let name = - tt::Ident { text: it.name.clone(), span: tt::TokenId::unspecified() }; - (name.as_name(), ProcMacroExpander::new(base_db::ProcMacroId(idx as u32))) - }) - .collect() - } - Err(e) => { - def_map.proc_macro_loading_error = Some(e.clone().into_boxed_str()); - Vec::new() + + let is_proc_macro = krate.is_proc_macro; + let proc_macros = if is_proc_macro { + match db.proc_macros().get(&def_map.krate) { + Some(Ok(proc_macros)) => { + Ok(proc_macros + .iter() + .enumerate() + .map(|(idx, it)| { + // FIXME: a hacky way to create a Name from string. + let name = + tt::Ident { text: it.name.clone(), span: tt::TokenId::unspecified() }; + (name.as_name(), ProcMacroExpander::new(base_db::ProcMacroId(idx as u32))) + }) + .collect()) + } + Some(Err(e)) => Err(e.clone().into_boxed_str()), + None => Err("No proc-macros present for crate".to_owned().into_boxed_str()), } + } else { + Ok(vec![]) }; - let is_proc_macro = krate.is_proc_macro; let mut collector = DefCollector { db, @@ -112,6 +112,7 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: DefMap, tree_id: T from_glob_import: Default::default(), skip_attrs: Default::default(), is_proc_macro, + hygienes: FxHashMap::default(), }; if tree_id.is_block() { collector.seed_with_inner(tree_id); @@ -238,7 +239,7 @@ enum MacroDirectiveKind { struct DefCollector<'a> { db: &'a dyn DefDatabase, def_map: DefMap, - deps: FxHashMap, + deps: FxHashMap, glob_imports: FxHashMap>, unresolved_imports: Vec, indeterminate_imports: Vec, @@ -249,7 +250,7 @@ struct DefCollector<'a> { /// built by the build system, and is the list of proc. macros we can actually expand. It is /// empty when proc. macro support is disabled (in which case we still do name resolution for /// them). - proc_macros: Vec<(Name, ProcMacroExpander)>, + proc_macros: Result, Box>, is_proc_macro: bool, from_glob_import: PerNsGlobImports, /// If we fail to resolve an attribute on a `ModItem`, we fall back to ignoring the attribute. @@ -259,6 +260,12 @@ 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>, + /// `Hygiene` cache, because `Hygiene` construction is expensive. + /// + /// Almost all paths should have been lowered to `ModPath` during `ItemTree` construction. + /// However, `DefCollector` still needs to lower paths in attributes, in particular those in + /// derive meta item list. + hygienes: FxHashMap, } impl DefCollector<'_> { @@ -267,86 +274,117 @@ impl DefCollector<'_> { let file_id = self.db.crate_graph()[self.def_map.krate].root_file_id; let item_tree = self.db.file_item_tree(file_id.into()); - let module_id = self.def_map.root; + let module_id = DefMap::ROOT; let attrs = item_tree.top_level_attrs(self.db, self.def_map.krate); - if attrs.cfg().map_or(true, |cfg| self.cfg_options.check(&cfg) != Some(false)) { - self.inject_prelude(&attrs); - - // Process other crate-level attributes. - for attr in &*attrs { - let attr_name = match attr.path.as_ident() { - Some(name) => name, - None => continue, - }; + let crate_data = Arc::get_mut(&mut self.def_map.data).unwrap(); - if *attr_name == hir_expand::name![recursion_limit] { - if let Some(limit) = attr.string_value() { - if let Ok(limit) = limit.parse() { - self.def_map.recursion_limit = Some(limit); - } - } - continue; + if let Err(e) = &self.proc_macros { + crate_data.proc_macro_loading_error = Some(e.clone()); + } + + for (name, dep) in &self.deps { + if dep.is_prelude() { + crate_data.extern_prelude.insert( + name.clone(), + ModuleId { krate: dep.crate_id, block: None, local_id: DefMap::ROOT }, + ); + } + } + + // Process other crate-level attributes. + for attr in &*attrs { + if let Some(cfg) = attr.cfg() { + if self.cfg_options.check(&cfg) == Some(false) { + return; } + } + let attr_name = match attr.path.as_ident() { + Some(name) => name, + None => continue, + }; - if *attr_name == hir_expand::name![crate_type] { - if let Some("proc-macro") = attr.string_value().map(SmolStr::as_str) { - self.is_proc_macro = true; + if *attr_name == hir_expand::name![recursion_limit] { + if let Some(limit) = attr.string_value() { + if let Ok(limit) = limit.parse() { + crate_data.recursion_limit = Some(limit); } - continue; } + continue; + } - if attr_name.as_text().as_deref() == Some("rustc_coherence_is_core") { - self.def_map.rustc_coherence_is_core = true; - continue; + if *attr_name == hir_expand::name![crate_type] { + if let Some("proc-macro") = attr.string_value().map(SmolStr::as_str) { + self.is_proc_macro = true; } + continue; + } - if *attr_name == hir_expand::name![feature] { - let features = - attr.parse_path_comma_token_tree().into_iter().flatten().filter_map( - |feat| match feat.segments() { - [name] => Some(name.to_smol_str()), - _ => None, - }, - ); - self.def_map.unstable_features.extend(features); - } + if *attr_name == hir_expand::name![no_core] { + crate_data.no_core = true; + continue; + } - let attr_is_register_like = *attr_name == hir_expand::name![register_attr] - || *attr_name == hir_expand::name![register_tool]; - if !attr_is_register_like { - continue; - } + if *attr_name == hir_expand::name![no_std] { + crate_data.no_std = true; + continue; + } - let registered_name = match attr.single_ident_value() { - Some(ident) => ident.as_name(), - _ => continue, - }; + if attr_name.as_text().as_deref() == Some("rustc_coherence_is_core") { + crate_data.rustc_coherence_is_core = true; + continue; + } - if *attr_name == hir_expand::name![register_attr] { - self.def_map.registered_attrs.push(registered_name.to_smol_str()); - cov_mark::hit!(register_attr); - } else { - self.def_map.registered_tools.push(registered_name.to_smol_str()); - cov_mark::hit!(register_tool); - } + if *attr_name == hir_expand::name![feature] { + let hygiene = &Hygiene::new_unhygienic(); + let features = attr + .parse_path_comma_token_tree(self.db.upcast(), hygiene) + .into_iter() + .flatten() + .filter_map(|feat| match feat.segments() { + [name] => Some(name.to_smol_str()), + _ => None, + }); + crate_data.unstable_features.extend(features); } - ModCollector { - def_collector: self, - macro_depth: 0, - module_id, - tree_id: TreeId::new(file_id.into(), None), - item_tree: &item_tree, - mod_dir: ModDir::root(), + let attr_is_register_like = *attr_name == hir_expand::name![register_attr] + || *attr_name == hir_expand::name![register_tool]; + if !attr_is_register_like { + continue; + } + + let registered_name = match attr.single_ident_value() { + Some(ident) => ident.as_name(), + _ => continue, + }; + + if *attr_name == hir_expand::name![register_attr] { + crate_data.registered_attrs.push(registered_name.to_smol_str()); + cov_mark::hit!(register_attr); + } else { + crate_data.registered_tools.push(registered_name.to_smol_str()); + cov_mark::hit!(register_tool); } - .collect_in_top_module(item_tree.top_level_items()); } + + crate_data.shrink_to_fit(); + self.inject_prelude(); + + ModCollector { + def_collector: self, + macro_depth: 0, + module_id, + tree_id: TreeId::new(file_id.into(), None), + item_tree: &item_tree, + mod_dir: ModDir::root(), + } + .collect_in_top_module(item_tree.top_level_items()); } fn seed_with_inner(&mut self, tree_id: TreeId) { let item_tree = tree_id.item_tree(self.db); - let module_id = self.def_map.root; + let module_id = DefMap::ROOT; let is_cfg_enabled = item_tree .top_level_attrs(self.db, self.def_map.krate) @@ -428,7 +466,7 @@ impl DefCollector<'_> { // Additionally, while the proc macro entry points must be `pub`, they are not publicly // exported in type/value namespace. This function reduces the visibility of all items // in the crate root that aren't proc macros. - let root = self.def_map.root; + let root = DefMap::ROOT; let module_id = self.def_map.module_id(root); let root = &mut self.def_map.modules[root]; root.scope.censor_non_proc_macros(module_id); @@ -456,12 +494,8 @@ impl DefCollector<'_> { directive.module_id, MacroCallKind::Attr { ast_id: ast_id.ast_id, - attr_args: std::sync::Arc::new(( - tt::Subtree::empty(), - Default::default(), - )), + attr_args: Arc::new((tt::Subtree::empty(), Default::default())), invoc_attr_index: attr.id, - is_derive: false, }, attr.path().clone(), )); @@ -495,15 +529,15 @@ impl DefCollector<'_> { } } - fn inject_prelude(&mut self, crate_attrs: &Attrs) { + fn inject_prelude(&mut self) { // See compiler/rustc_builtin_macros/src/standard_library_imports.rs - if crate_attrs.by_key("no_core").exists() { + if self.def_map.data.no_core { // libcore does not get a prelude. return; } - let krate = if crate_attrs.by_key("no_std").exists() { + let krate = if self.def_map.data.no_std { name![core] } else { let std = name![std]; @@ -516,43 +550,31 @@ impl DefCollector<'_> { } }; - let edition = match self.def_map.edition { + let edition = match self.def_map.data.edition { Edition::Edition2015 => name![rust_2015], Edition::Edition2018 => name![rust_2018], Edition::Edition2021 => name![rust_2021], }; - let path_kind = match self.def_map.edition { + let path_kind = match self.def_map.data.edition { Edition::Edition2015 => PathKind::Plain, _ => PathKind::Abs, }; - let path = - ModPath::from_segments(path_kind, [krate.clone(), name![prelude], edition].into_iter()); - // Fall back to the older `std::prelude::v1` for compatibility with Rust <1.52.0 - // FIXME remove this fallback - let fallback_path = - ModPath::from_segments(path_kind, [krate, name![prelude], name![v1]].into_iter()); - - for path in &[path, fallback_path] { - let (per_ns, _) = self.def_map.resolve_path( - self.db, - self.def_map.root, - path, - BuiltinShadowMode::Other, - ); + let path = ModPath::from_segments(path_kind, [krate, name![prelude], edition]); - match per_ns.types { - Some((ModuleDefId::ModuleId(m), _)) => { - self.def_map.prelude = Some(m); - break; - } - types => { - tracing::debug!( - "could not resolve prelude path `{}` to module (resolved to {:?})", - path, - types - ); - } + let (per_ns, _) = + self.def_map.resolve_path(self.db, DefMap::ROOT, &path, BuiltinShadowMode::Other, None); + + match per_ns.types { + Some((ModuleDefId::ModuleId(m), _)) => { + self.def_map.prelude = Some(m); + } + types => { + tracing::debug!( + "could not resolve prelude path `{}` to module (resolved to {:?})", + path.display(self.db.upcast()), + types + ); } } } @@ -578,23 +600,29 @@ impl DefCollector<'_> { def: ProcMacroDef, id: ItemTreeId, fn_id: FunctionId, - module_id: ModuleId, ) { + if self.def_map.block.is_some() { + return; + } + let crate_root = self.def_map.module_id(DefMap::ROOT); + let kind = def.kind.to_basedb_kind(); - let (expander, kind) = match self.proc_macros.iter().find(|(n, _)| n == &def.name) { - Some(&(_, expander)) => (expander, kind), - None => (ProcMacroExpander::dummy(), kind), - }; + let (expander, kind) = + match self.proc_macros.as_ref().map(|it| it.iter().find(|(n, _)| n == &def.name)) { + Ok(Some(&(_, expander))) => (expander, kind), + _ => (ProcMacroExpander::dummy(), kind), + }; let proc_macro_id = - ProcMacroLoc { container: module_id, id, expander, kind }.intern(self.db); + ProcMacroLoc { container: crate_root, id, expander, kind }.intern(self.db); self.define_proc_macro(def.name.clone(), proc_macro_id); + let crate_data = Arc::get_mut(&mut self.def_map.data).unwrap(); if let ProcMacroKind::CustomDerive { helpers } = def.kind { - self.def_map + crate_data .exported_derives .insert(macro_id_to_def_id(self.db, proc_macro_id.into()), helpers); } - self.def_map.fn_proc_macro_mapping.insert(fn_id, proc_macro_id); + crate_data.fn_proc_macro_mapping.insert(fn_id, proc_macro_id); } /// Define a macro with `macro_rules`. @@ -636,7 +664,7 @@ impl DefCollector<'_> { // In Rust, `#[macro_export]` macros are unconditionally visible at the // crate root, even if the parent modules is **not** visible. if export { - let module_id = self.def_map.root; + let module_id = DefMap::ROOT; self.def_map.modules[module_id].scope.declare(macro_.into()); self.update( module_id, @@ -687,7 +715,7 @@ impl DefCollector<'_> { /// A proc macro is similar to normal macro scope, but it would not visible in legacy textual scoped. /// And unconditionally exported. fn define_proc_macro(&mut self, name: Name, macro_: ProcMacroId) { - let module_id = self.def_map.root; + let module_id = DefMap::ROOT; self.def_map.modules[module_id].scope.declare(macro_.into()); self.update( module_id, @@ -697,39 +725,28 @@ impl DefCollector<'_> { ); } - /// Import macros from `#[macro_use] extern crate`. - fn import_macros_from_extern_crate( - &mut self, - current_module_id: LocalModuleId, - extern_crate: &item_tree::ExternCrate, - ) { - tracing::debug!( - "importing macros from extern crate: {:?} ({:?})", - extern_crate, - self.def_map.edition, - ); - - if let Some(m) = self.resolve_extern_crate(&extern_crate.name) { - if m == self.def_map.module_id(current_module_id) { - cov_mark::hit!(ignore_macro_use_extern_crate_self); - return; - } - - cov_mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use); - self.import_all_macros_exported(current_module_id, m.krate); - } - } - - /// Import all exported macros from another crate + /// Import exported macros from another crate. `names`, if `Some(_)`, specifies the name of + /// macros to be imported. Otherwise this method imports all exported macros. /// /// Exported macros are just all macros in the root module scope. /// Note that it contains not only all `#[macro_export]` macros, but also all aliases /// created by `use` in the root module, ignoring the visibility of `use`. - fn import_all_macros_exported(&mut self, current_module_id: LocalModuleId, krate: CrateId) { + fn import_macros_from_extern_crate(&mut self, krate: CrateId, names: Option>) { let def_map = self.db.crate_def_map(krate); - for (name, def) in def_map[def_map.root].scope.macros() { - // `#[macro_use]` brings macros into legacy scope. Yes, even non-`macro_rules!` macros. - self.define_legacy_macro(current_module_id, name.clone(), def); + // `#[macro_use]` brings macros into macro_use prelude. Yes, even non-`macro_rules!` + // macros. + let root_scope = &def_map[DefMap::ROOT].scope; + if let Some(names) = names { + for name in names { + // FIXME: Report diagnostic on 404. + if let Some(def) = root_scope.get(&name).take_macros() { + self.def_map.macro_use_prelude.insert(name, def); + } + } + } else { + for (name, def) in root_scope.macros() { + self.def_map.macro_use_prelude.insert(name.clone(), def); + } } } @@ -762,8 +779,9 @@ impl DefCollector<'_> { } fn resolve_import(&self, module_id: LocalModuleId, import: &Import) -> PartialResolvedImport { - let _p = profile::span("resolve_import").detail(|| format!("{}", import.path)); - tracing::debug!("resolving import: {:?} ({:?})", import, self.def_map.edition); + let _p = profile::span("resolve_import") + .detail(|| format!("{}", import.path.display(self.db.upcast()))); + tracing::debug!("resolving import: {:?} ({:?})", import, self.def_map.data.edition); if import.is_extern_crate { let name = import .path @@ -785,6 +803,7 @@ impl DefCollector<'_> { module_id, &import.path, BuiltinShadowMode::Module, + None, // An import may resolve to any kind of macro. ); let def = res.resolved_def; @@ -815,16 +834,13 @@ impl DefCollector<'_> { fn resolve_extern_crate(&self, name: &Name) -> Option { if *name == name!(self) { cov_mark::hit!(extern_crate_self_as); - let root = match self.def_map.block { - Some(_) => { - let def_map = self.def_map.crate_root(self.db).def_map(self.db); - def_map.module_id(def_map.root()) - } - None => self.def_map.module_id(self.def_map.root()), - }; - Some(root) + Some(self.def_map.crate_root()) } else { - self.deps.get(name).copied() + self.deps.get(name).map(|dep| ModuleId { + krate: dep.crate_id, + block: None, + local_id: DefMap::ROOT, + }) } } @@ -863,11 +879,14 @@ impl DefCollector<'_> { // extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658 if import.is_extern_crate && self.def_map.block.is_none() - && module_id == self.def_map.root + && module_id == DefMap::ROOT { if let (Some(ModuleDefId::ModuleId(def)), Some(name)) = (def.take_types(), name) { - self.def_map.extern_prelude.insert(name.clone(), def); + Arc::get_mut(&mut self.def_map.data) + .unwrap() + .extern_prelude + .insert(name.clone(), def); } } @@ -1082,7 +1101,14 @@ impl DefCollector<'_> { resolved.push((directive.module_id, directive.depth, directive.container, call_id)); }; let mut res = ReachedFixedPoint::Yes; + // Retain unresolved macros after this round of resolution. macros.retain(|directive| { + let subns = match &directive.kind { + MacroDirectiveKind::FnLike { .. } => MacroSubNs::Bang, + MacroDirectiveKind::Attr { .. } | MacroDirectiveKind::Derive { .. } => { + MacroSubNs::Attr + } + }; let resolver = |path| { let resolved_res = self.def_map.resolve_path_fp_with_macro( self.db, @@ -1090,6 +1116,7 @@ impl DefCollector<'_> { directive.module_id, &path, BuiltinShadowMode::Module, + Some(subns), ); resolved_res .resolved_def @@ -1101,15 +1128,15 @@ impl DefCollector<'_> { match &directive.kind { MacroDirectiveKind::FnLike { ast_id, expand_to } => { let call_id = macro_call_as_call_id( - self.db, + self.db.upcast(), ast_id, *expand_to, self.def_map.krate, resolver_def_id, - &mut |_err| (), ); - if let Ok(Ok(call_id)) = call_id { + if let Ok(Some(call_id)) = call_id { push_resolved(directive, call_id); + res = ReachedFixedPoint::No; return false; } @@ -1134,7 +1161,7 @@ impl DefCollector<'_> { // 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) { + if let Some(helpers) = def_map.data.exported_derives.get(&def_id) { self.def_map .derive_helpers_in_scope .entry(ast_id.ast_id.map(|it| it.upcast())) @@ -1214,7 +1241,19 @@ impl DefCollector<'_> { }; let ast_id = ast_id.with_value(ast_adt_id); - match attr.parse_path_comma_token_tree() { + let extend_unhygenic; + let hygiene = if file_id.is_macro() { + self.hygienes + .entry(file_id) + .or_insert_with(|| Hygiene::new(self.db.upcast(), file_id)) + } else { + // Avoid heap allocation (`Hygiene` embraces `Arc`) and hash map entry + // when we're in an oridinary (non-macro) file. + extend_unhygenic = Hygiene::new_unhygienic(); + &extend_unhygenic + }; + + match attr.parse_path_comma_token_tree(self.db.upcast(), hygiene) { Some(derive_macros) => { let mut len = 0; for (idx, path) in derive_macros.enumerate() { @@ -1241,7 +1280,6 @@ impl DefCollector<'_> { attr, self.def_map.krate, def, - true, ); self.def_map.modules[directive.module_id] .scope @@ -1261,18 +1299,12 @@ impl DefCollector<'_> { } // Not resolved to a derive helper or the derive attribute, so try to treat as a normal attribute. - let call_id = attr_macro_as_call_id( - self.db, - file_ast_id, - attr, - self.def_map.krate, - def, - false, - ); + let call_id = + attr_macro_as_call_id(self.db, file_ast_id, attr, self.def_map.krate, def); let loc: MacroCallLoc = self.db.lookup_intern_macro_call(call_id); // If proc attribute macro expansion is disabled, skip expanding it here - if !self.db.enable_proc_attr_macros() { + if !self.db.expand_proc_attr_macros() { self.def_map.diagnostics.push(DefDiagnostic::unresolved_proc_macro( directive.module_id, loc.kind, @@ -1345,25 +1377,31 @@ impl DefCollector<'_> { let file_id = macro_call_id.as_file(); // First, fetch the raw expansion result for purposes of error reporting. This goes through - // `macro_expand_error` to avoid depending on the full expansion result (to improve + // `parse_macro_expansion_error` to avoid depending on the full expansion result (to improve // incrementality). - let loc: MacroCallLoc = self.db.lookup_intern_macro_call(macro_call_id); - let err = self.db.macro_expand_error(macro_call_id); + let ExpandResult { value, err } = self.db.parse_macro_expansion_error(macro_call_id); if let Some(err) = err { + let loc: MacroCallLoc = self.db.lookup_intern_macro_call(macro_call_id); let diag = match err { + // why is this reported here? hir_expand::ExpandError::UnresolvedProcMacro(krate) => { always!(krate == loc.def.krate); - // Missing proc macros are non-fatal, so they are handled specially. DefDiagnostic::unresolved_proc_macro(module_id, loc.kind.clone(), loc.def.krate) } - _ => DefDiagnostic::macro_error(module_id, loc.kind, err.to_string()), + _ => DefDiagnostic::macro_error(module_id, loc.kind.clone(), err.to_string()), }; self.def_map.diagnostics.push(diag); } + if let errors @ [_, ..] = &*value { + let loc: MacroCallLoc = self.db.lookup_intern_macro_call(macro_call_id); + let diag = DefDiagnostic::macro_expansion_parse_error(module_id, loc.kind, &errors); + self.def_map.diagnostics.push(diag); + } // 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(); ModCollector { def_collector: &mut *self, @@ -1384,8 +1422,9 @@ impl DefCollector<'_> { for directive in &self.unresolved_macros { match &directive.kind { MacroDirectiveKind::FnLike { ast_id, expand_to } => { + // FIXME: we shouldn't need to re-resolve the macro here just to get the unresolved error! let macro_call_as_call_id = macro_call_as_call_id( - self.db, + self.db.upcast(), ast_id, *expand_to, self.def_map.krate, @@ -1396,13 +1435,13 @@ impl DefCollector<'_> { directive.module_id, &path, BuiltinShadowMode::Module, + Some(MacroSubNs::Bang), ); resolved_res .resolved_def .take_macros() .map(|it| macro_id_to_def_id(self.db, it)) }, - &mut |_| (), ); if let Err(UnresolvedMacro { path }) = macro_call_as_call_id { self.def_map.diagnostics.push(DefDiagnostic::unresolved_macro_call( @@ -1489,6 +1528,7 @@ impl ModCollector<'_, '_> { fn collect(&mut self, items: &[ModItem], container: ItemContainerId) { let krate = self.def_collector.def_map.krate; + let is_crate_root = self.module_id == DefMap::ROOT; // Note: don't assert that inserted value is fresh: it's simply not true // for macros. @@ -1496,28 +1536,22 @@ impl ModCollector<'_, '_> { // Prelude module is always considered to be `#[macro_use]`. if let Some(prelude_module) = self.def_collector.def_map.prelude { - if prelude_module.krate != krate { + if prelude_module.krate != krate && is_crate_root { cov_mark::hit!(prelude_is_macro_use); - self.def_collector.import_all_macros_exported(self.module_id, prelude_module.krate); + self.def_collector.import_macros_from_extern_crate(prelude_module.krate, None); } } // This should be processed eagerly instead of deferred to resolving. // `#[macro_use] extern crate` is hoisted to imports macros before collecting // any other items. - for &item in items { - let attrs = self.item_tree.attrs(self.def_collector.db, krate, item.into()); - if attrs.cfg().map_or(true, |cfg| self.is_cfg_enabled(&cfg)) { + // + // If we're not at the crate root, `macro_use`d extern crates are an error so let's just + // ignore them. + if is_crate_root { + for &item in items { if let ModItem::ExternCrate(id) = item { - let import = &self.item_tree[id]; - let attrs = self.item_tree.attrs( - self.def_collector.db, - krate, - ModItem::from(id).into(), - ); - if attrs.by_key("macro_use").exists() { - self.def_collector.import_macros_from_extern_crate(self.module_id, import); - } + self.process_macro_use_extern_crate(id); } } } @@ -1610,14 +1644,12 @@ impl ModCollector<'_, '_> { FunctionLoc { container, id: ItemTreeId::new(self.tree_id, id) }.intern(db); let vis = resolve_vis(def_map, &self.item_tree[it.visibility]); - if self.def_collector.is_proc_macro && self.module_id == def_map.root { + if self.def_collector.is_proc_macro && self.module_id == DefMap::ROOT { if let Some(proc_macro) = attrs.parse_proc_macro_decl(&it.name) { - let crate_root = def_map.module_id(def_map.root); self.def_collector.export_proc_macro( proc_macro, ItemTreeId::new(self.tree_id, id), fn_id, - crate_root, ); } } @@ -1744,6 +1776,52 @@ impl ModCollector<'_, '_> { } } + fn process_macro_use_extern_crate(&mut self, extern_crate: FileItemTreeId) { + let db = self.def_collector.db; + let attrs = self.item_tree.attrs( + db, + self.def_collector.def_map.krate, + ModItem::from(extern_crate).into(), + ); + if let Some(cfg) = attrs.cfg() { + if !self.is_cfg_enabled(&cfg) { + return; + } + } + + let target_crate = + match self.def_collector.resolve_extern_crate(&self.item_tree[extern_crate].name) { + Some(m) => { + if m == self.def_collector.def_map.module_id(self.module_id) { + cov_mark::hit!(ignore_macro_use_extern_crate_self); + return; + } + m.krate + } + None => return, + }; + + cov_mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use); + + let mut single_imports = Vec::new(); + let hygiene = Hygiene::new_unhygienic(); + for attr in attrs.by_key("macro_use").attrs() { + let Some(paths) = attr.parse_path_comma_token_tree(db.upcast(), &hygiene) else { + // `#[macro_use]` (without any paths) found, forget collected names and just import + // all visible macros. + self.def_collector.import_macros_from_extern_crate(target_crate, None); + return; + }; + for path in paths { + if let Some(name) = path.as_ident() { + single_imports.push(name.clone()); + } + } + } + + self.def_collector.import_macros_from_extern_crate(target_crate, Some(single_imports)); + } + fn collect_module(&mut self, module_id: FileItemTreeId, attrs: &Attrs) { let path_attr = attrs.by_key("path").string_value(); let is_macro_use = attrs.by_key("macro_use").exists(); @@ -1844,7 +1922,6 @@ impl ModCollector<'_, '_> { let vis = def_map .resolve_visibility(self.def_collector.db, self.module_id, visibility, false) .unwrap_or(Visibility::Public); - let modules = &mut def_map.modules; let origin = match definition { None => ModuleOrigin::Inline { definition: declaration, @@ -1858,6 +1935,7 @@ impl ModCollector<'_, '_> { }, }; + let modules = &mut def_map.modules; let res = modules.alloc(ModuleData::new(origin, vis)); modules[res].parent = Some(self.module_id); for (name, mac) in modules[self.module_id].scope.collect_legacy_macros() { @@ -1919,7 +1997,10 @@ impl ModCollector<'_, '_> { if self.def_collector.def_map.is_builtin_or_registered_attr(&attr.path) { continue; } - tracing::debug!("non-builtin attribute {}", attr.path); + tracing::debug!( + "non-builtin attribute {}", + attr.path.display(self.def_collector.db.upcast()) + ); let ast_id = AstIdWithPath::new( self.file_id(), @@ -2053,8 +2134,8 @@ impl ModCollector<'_, '_> { stdx::always!( name == mac.name, "built-in macro {} has #[rustc_builtin_macro] which declares different name {}", - mac.name, - name + mac.name.display(self.def_collector.db.upcast()), + name.display(self.def_collector.db.upcast()) ); helpers_opt = Some(helpers); } @@ -2089,73 +2170,60 @@ impl ModCollector<'_, '_> { &self.item_tree[mac.visibility], ); if let Some(helpers) = helpers_opt { - self.def_collector - .def_map - .exported_derives - .insert(macro_id_to_def_id(self.def_collector.db, macro_id.into()), helpers); + if self.def_collector.def_map.block.is_none() { + Arc::get_mut(&mut self.def_collector.def_map.data) + .unwrap() + .exported_derives + .insert(macro_id_to_def_id(self.def_collector.db, macro_id.into()), helpers); + } } } fn collect_macro_call(&mut self, mac: &MacroCall, container: ItemContainerId) { let ast_id = AstIdWithPath::new(self.file_id(), mac.ast_id, ModPath::clone(&mac.path)); + let db = self.def_collector.db; + + // FIXME: Immediately expanding in "Case 1" is insufficient since "Case 2" may also define + // new legacy macros that create textual scopes. We need a way to resolve names in textual + // scopes without eager expansion. - // Case 1: try to resolve in legacy scope and expand macro_rules - let mut error = None; - match macro_call_as_call_id( - self.def_collector.db, + // Case 1: try to resolve macro calls with single-segment name and expand macro_rules + if let Ok(res) = macro_call_as_call_id( + db.upcast(), &ast_id, mac.expand_to, self.def_collector.def_map.krate, |path| { path.as_ident().and_then(|name| { - self.def_collector.def_map.with_ancestor_maps( - self.def_collector.db, - self.module_id, - &mut |map, module| { - map[module] - .scope - .get_legacy_macro(name) - .and_then(|it| it.last()) - .map(|&it| macro_id_to_def_id(self.def_collector.db, it)) - }, - ) + let def_map = &self.def_collector.def_map; + def_map + .with_ancestor_maps(db, self.module_id, &mut |map, module| { + map[module].scope.get_legacy_macro(name)?.last().copied() + }) + .or_else(|| def_map[self.module_id].scope.get(name).take_macros()) + .or_else(|| def_map.macro_use_prelude.get(name).copied()) + .filter(|&id| { + sub_namespace_match( + Some(MacroSubNs::from_id(db, id)), + Some(MacroSubNs::Bang), + ) + }) + .map(|it| macro_id_to_def_id(self.def_collector.db, it)) }) }, - &mut |err| { - error.get_or_insert(err); - }, ) { - Ok(Ok(macro_call_id)) => { - // Legacy macros need to be expanded immediately, so that any macros they produce - // are in scope. + // Legacy macros need to be expanded immediately, so that any macros they produce + // are in scope. + if let Some(val) = res { self.def_collector.collect_macro_expansion( self.module_id, - macro_call_id, + val, self.macro_depth + 1, container, ); - - if let Some(err) = error { - self.def_collector.def_map.diagnostics.push(DefDiagnostic::macro_error( - self.module_id, - MacroCallKind::FnLike { ast_id: ast_id.ast_id, expand_to: mac.expand_to }, - err.to_string(), - )); - } - - return; } - Ok(Err(_)) => { - // Built-in macro failed eager expansion. - self.def_collector.def_map.diagnostics.push(DefDiagnostic::macro_error( - self.module_id, - MacroCallKind::FnLike { ast_id: ast_id.ast_id, expand_to: mac.expand_to }, - error.unwrap().to_string(), - )); - return; - } - Err(UnresolvedMacro { .. }) => (), + return; } // Case 2: resolve in module scope, expand during name resolution. @@ -2215,10 +2283,11 @@ mod tests { unresolved_macros: Vec::new(), mod_dirs: FxHashMap::default(), cfg_options: &CfgOptions::default(), - proc_macros: Default::default(), + proc_macros: Ok(vec![]), from_glob_import: Default::default(), skip_attrs: Default::default(), is_proc_macro: false, + hygienes: FxHashMap::default(), }; collector.seed_with_top_level(); collector.collect(); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs index b024d7c6777c0..18b424255cd6b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs @@ -4,7 +4,10 @@ use base_db::CrateId; use cfg::{CfgExpr, CfgOptions}; use hir_expand::{attrs::AttrId, MacroCallKind}; use la_arena::Idx; -use syntax::ast::{self, AnyHasAttrs}; +use syntax::{ + ast::{self, AnyHasAttrs}, + SyntaxError, +}; use crate::{ item_tree::{self, ItemTreeId}, @@ -29,11 +32,15 @@ pub enum DefDiagnosticKind { MacroError { ast: MacroCallKind, message: String }, + MacroExpansionParseError { ast: MacroCallKind, errors: Box<[SyntaxError]> }, + UnimplementedBuiltinMacro { ast: AstId }, InvalidDeriveTarget { ast: AstId, id: usize }, MalformedDerive { ast: AstId, id: usize }, + + MacroDefError { ast: AstId, message: String }, } #[derive(Debug, PartialEq, Eq)] @@ -81,7 +88,8 @@ impl DefDiagnostic { Self { in_module: container, kind: DefDiagnosticKind::UnconfiguredCode { ast, cfg, opts } } } - pub(super) fn unresolved_proc_macro( + // FIXME: Whats the difference between this and unresolved_macro_call + pub(crate) fn unresolved_proc_macro( container: LocalModuleId, ast: MacroCallKind, krate: CrateId, @@ -89,7 +97,7 @@ impl DefDiagnostic { Self { in_module: container, kind: DefDiagnosticKind::UnresolvedProcMacro { ast, krate } } } - pub(super) fn macro_error( + pub(crate) fn macro_error( container: LocalModuleId, ast: MacroCallKind, message: String, @@ -97,7 +105,22 @@ impl DefDiagnostic { Self { in_module: container, kind: DefDiagnosticKind::MacroError { ast, message } } } - pub(super) fn unresolved_macro_call( + pub(crate) fn macro_expansion_parse_error( + container: LocalModuleId, + ast: MacroCallKind, + errors: &[SyntaxError], + ) -> Self { + Self { + in_module: container, + kind: DefDiagnosticKind::MacroExpansionParseError { + ast, + errors: errors.to_vec().into_boxed_slice(), + }, + } + } + + // FIXME: Whats the difference between this and unresolved_proc_macro + pub(crate) fn unresolved_macro_call( container: LocalModuleId, ast: MacroCallKind, path: ModPath, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs index 51c565fe12339..2dcc2c30fe169 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs @@ -74,12 +74,20 @@ 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()) => { - candidate_files.push(format!("{name}.rs")); - candidate_files.push(format!("{name}/mod.rs")); + candidate_files.push(format!("{}.rs", name.display(db.upcast()))); + candidate_files.push(format!("{}/mod.rs", name.display(db.upcast()))); } None => { - candidate_files.push(format!("{}{name}.rs", self.dir_path.0)); - candidate_files.push(format!("{}{name}/mod.rs", self.dir_path.0)); + candidate_files.push(format!( + "{}{}.rs", + self.dir_path.0, + name.display(db.upcast()) + )); + candidate_files.push(format!( + "{}{}/mod.rs", + self.dir_path.0, + name.display(db.upcast()) + )); } }; @@ -91,7 +99,7 @@ impl ModDir { let (dir_path, root_non_dir_owner) = if is_mod_rs || attr_path.is_some() { (DirPath::empty(), false) } else { - (DirPath::new(format!("{name}/")), true) + (DirPath::new(format!("{}/", name.display(db.upcast()))), true) }; if let Some(mod_dir) = self.child(dir_path, root_non_dir_owner) { return Ok((file_id, is_mod_rs, mod_dir)); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs index 25478481dd0b3..5f6163175a726 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs @@ -16,11 +16,11 @@ use hir_expand::name::Name; use crate::{ db::DefDatabase, item_scope::BUILTIN_SCOPE, - nameres::{BuiltinShadowMode, DefMap}, + nameres::{sub_namespace_match, BuiltinShadowMode, DefMap, MacroSubNs}, path::{ModPath, PathKind}, per_ns::PerNs, visibility::{RawVisibility, Visibility}, - AdtId, CrateId, EnumVariantId, LocalModuleId, ModuleDefId, ModuleId, + AdtId, CrateId, EnumVariantId, LocalModuleId, ModuleDefId, }; #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -58,18 +58,22 @@ impl ResolvePathResult { } } -impl DefMap { - pub(super) fn resolve_name_in_extern_prelude( - &self, +impl PerNs { + pub(super) fn filter_macro( + mut self, db: &dyn DefDatabase, - name: &Name, - ) -> Option { - match self.block { - Some(_) => self.crate_root(db).def_map(db).extern_prelude.get(name).copied(), - None => self.extern_prelude.get(name).copied(), - } + expected: Option, + ) -> Self { + self.macros = self.macros.filter(|&(id, _)| { + let this = MacroSubNs::from_id(db, id); + sub_namespace_match(Some(this), expected) + }); + + self } +} +impl DefMap { pub(crate) fn resolve_visibility( &self, db: &dyn DefDatabase, @@ -83,7 +87,7 @@ impl DefMap { let mut vis = match visibility { RawVisibility::Module(path) => { let (result, remaining) = - self.resolve_path(db, original_module, path, BuiltinShadowMode::Module); + self.resolve_path(db, original_module, path, BuiltinShadowMode::Module, None); if remaining.is_some() { return None; } @@ -106,7 +110,7 @@ impl DefMap { // ...unless we're resolving visibility for an associated item in an impl. if self.block_id() != m.block && !within_impl { cov_mark::hit!(adjust_vis_in_block_def_map); - vis = Visibility::Module(self.module_id(self.root())); + vis = Visibility::Module(self.module_id(Self::ROOT)); tracing::debug!("visibility {:?} points outside DefMap, adjusting to {:?}", m, vis); } } @@ -124,6 +128,9 @@ impl DefMap { mut original_module: LocalModuleId, path: &ModPath, shadow: BuiltinShadowMode, + // Pass `MacroSubNs` if we know we're resolving macro names and which kind of macro we're + // resolving them to. Pass `None` otherwise, e.g. when we're resolving import paths. + expected_macro_subns: Option, ) -> ResolvePathResult { let mut result = ResolvePathResult::empty(ReachedFixedPoint::No); @@ -136,6 +143,7 @@ impl DefMap { original_module, path, shadow, + expected_macro_subns, ); // Merge `new` into `result`. @@ -154,7 +162,7 @@ impl DefMap { match ¤t_map.block { Some(block) => { original_module = block.parent.local_id; - arc = block.parent.def_map(db); + arc = block.parent.def_map(db, current_map.krate); current_map = &*arc; } None => return result, @@ -169,11 +177,15 @@ impl DefMap { original_module: LocalModuleId, path: &ModPath, shadow: BuiltinShadowMode, + expected_macro_subns: Option, ) -> ResolvePathResult { let graph = db.crate_graph(); let _cx = stdx::panic_context::enter(format!( - "DefMap {:?} crate_name={:?} block={:?} path={path}", - self.krate, graph[self.krate].display_name, self.block + "DefMap {:?} crate_name={:?} block={:?} path={}", + self.krate, + graph[self.krate].display_name, + self.block, + path.display(db.upcast()) )); let mut segments = path.segments().iter().enumerate(); @@ -181,21 +193,21 @@ impl DefMap { PathKind::DollarCrate(krate) => { if krate == self.krate { cov_mark::hit!(macro_dollar_crate_self); - PerNs::types(self.crate_root(db).into(), Visibility::Public) + PerNs::types(self.crate_root().into(), Visibility::Public) } else { let def_map = db.crate_def_map(krate); - let module = def_map.module_id(def_map.root); + let module = def_map.module_id(Self::ROOT); cov_mark::hit!(macro_dollar_crate_other); PerNs::types(module.into(), Visibility::Public) } } - PathKind::Crate => PerNs::types(self.crate_root(db).into(), Visibility::Public), + PathKind::Crate => PerNs::types(self.crate_root().into(), Visibility::Public), // plain import or absolute path in 2015: crate-relative with // fallback to extern prelude (with the simplification in // rust-lang/rust#57745) // FIXME there must be a nicer way to write this condition PathKind::Plain | PathKind::Abs - if self.edition == Edition::Edition2015 + if self.data.edition == Edition::Edition2015 && (path.kind == PathKind::Abs || mode == ResolveMode::Import) => { let (_, segment) = match segments.next() { @@ -220,7 +232,13 @@ impl DefMap { if path.segments().len() == 1 { shadow } else { BuiltinShadowMode::Module }; tracing::debug!("resolving {:?} in module", segment); - self.resolve_name_in_module(db, original_module, segment, prefer_module) + self.resolve_name_in_module( + db, + original_module, + segment, + prefer_module, + expected_macro_subns, + ) } PathKind::Super(lvl) => { let mut module = original_module; @@ -236,16 +254,20 @@ impl DefMap { ); tracing::debug!( "`super` path: {} -> {} in parent map", - path, - new_path - ); - return block.parent.def_map(db).resolve_path_fp_with_macro( - db, - mode, - block.parent.local_id, - &new_path, - shadow, + path.display(db.upcast()), + new_path.display(db.upcast()) ); + return block + .parent + .def_map(db, self.krate) + .resolve_path_fp_with_macro( + db, + mode, + block.parent.local_id, + &new_path, + shadow, + expected_macro_subns, + ); } None => { tracing::debug!("super path in root module"); @@ -271,7 +293,7 @@ impl DefMap { Some((_, segment)) => segment, None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), }; - if let Some(&def) = self.extern_prelude.get(segment) { + if let Some(&def) = self.data.extern_prelude.get(segment) { tracing::debug!("absolute path {:?} resolved to crate {:?}", path, def); PerNs::types(def.into(), Visibility::Public) } else { @@ -303,7 +325,12 @@ impl DefMap { ); tracing::debug!("resolving {:?} in other crate", path); let defp_map = module.def_map(db); - let (def, s) = defp_map.resolve_path(db, module.local_id, &path, shadow); + // Macro sub-namespaces only matter when resolving single-segment paths + // because `macro_use` and other preludes should be taken into account. At + // this point, we know we're resolving a multi-segment path so macro kind + // expectation is discarded. + let (def, s) = + defp_map.resolve_path(db, module.local_id, &path, shadow, None); return ResolvePathResult::with( def, ReachedFixedPoint::Yes, @@ -331,11 +358,11 @@ impl DefMap { Some(local_id) => { let variant = EnumVariantId { parent: e, local_id }; match &*enum_data.variants[local_id].variant_data { - crate::adt::VariantData::Record(_) => { + crate::data::adt::VariantData::Record(_) => { PerNs::types(variant.into(), Visibility::Public) } - crate::adt::VariantData::Tuple(_) - | crate::adt::VariantData::Unit => { + crate::data::adt::VariantData::Tuple(_) + | crate::data::adt::VariantData::Unit => { PerNs::both(variant.into(), variant.into(), Visibility::Public) } } @@ -381,19 +408,24 @@ impl DefMap { module: LocalModuleId, name: &Name, shadow: BuiltinShadowMode, + expected_macro_subns: Option, ) -> PerNs { // Resolve in: // - legacy scope of macro // - current module / scope - // - extern prelude + // - extern prelude / macro_use prelude // - std prelude let from_legacy_macro = self[module] .scope .get_legacy_macro(name) // FIXME: shadowing .and_then(|it| it.last()) - .map_or_else(PerNs::none, |&m| PerNs::macros(m, Visibility::Public)); - let from_scope = self[module].scope.get(name); + .copied() + .filter(|&id| { + sub_namespace_match(Some(MacroSubNs::from_id(db, id)), expected_macro_subns) + }) + .map_or_else(PerNs::none, |m| PerNs::macros(m, Visibility::Public)); + let from_scope = self[module].scope.get(name).filter_macro(db, expected_macro_subns); let from_builtin = match self.block { Some(_) => { // Only resolve to builtins in the root `DefMap`. @@ -410,13 +442,27 @@ impl DefMap { }; let extern_prelude = || { - self.extern_prelude + if self.block.is_some() { + // Don't resolve extern prelude in block `DefMap`s. + return PerNs::none(); + } + self.data + .extern_prelude .get(name) .map_or(PerNs::none(), |&it| PerNs::types(it.into(), Visibility::Public)) }; + let macro_use_prelude = || { + self.macro_use_prelude + .get(name) + .map_or(PerNs::none(), |&it| PerNs::macros(it.into(), Visibility::Public)) + }; let prelude = || self.resolve_in_prelude(db, name); - from_legacy_macro.or(from_scope_or_builtin).or_else(extern_prelude).or_else(prelude) + from_legacy_macro + .or(from_scope_or_builtin) + .or_else(extern_prelude) + .or_else(macro_use_prelude) + .or_else(prelude) } fn resolve_name_in_crate_root_or_extern_prelude( @@ -426,13 +472,20 @@ impl DefMap { ) -> PerNs { let from_crate_root = match self.block { Some(_) => { - let def_map = self.crate_root(db).def_map(db); - def_map[def_map.root].scope.get(name) + let def_map = self.crate_root().def_map(db); + def_map[Self::ROOT].scope.get(name) } - None => self[self.root].scope.get(name), + None => self[Self::ROOT].scope.get(name), }; let from_extern_prelude = || { - self.resolve_name_in_extern_prelude(db, name) + if self.block.is_some() { + // Don't resolve extern prelude in block `DefMap`s. + return PerNs::none(); + } + self.data + .extern_prelude + .get(name) + .copied() .map_or(PerNs::none(), |it| PerNs::types(it.into(), Visibility::Public)) }; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/proc_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/proc_macro.rs index caad4a1f38172..751b7beaac153 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/proc_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/proc_macro.rs @@ -52,7 +52,7 @@ impl Attrs { } // This fn is intended for `#[proc_macro_derive(..)]` and `#[rustc_builtin_macro(..)]`, which have -// the same strucuture. +// the same structure. #[rustfmt::skip] pub(crate) fn parse_macro_name_and_helper_attrs(tt: &[TokenTree]) -> Option<(Name, Box<[Name]>)> { match tt { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs index 8a27c60df5c27..dd7c3c3630623 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs @@ -4,10 +4,9 @@ mod macros; mod mod_resolution; mod primitives; -use std::sync::Arc; - use base_db::{fixture::WithFixture, SourceDatabase}; use expect_test::{expect, Expect}; +use triomphe::Arc; use crate::{db::DefDatabase, test_db::TestDB}; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs index 13e6825f8210e..4931c36bbca92 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs @@ -1,8 +1,7 @@ -use std::sync::Arc; - use base_db::SourceDatabaseExt; +use triomphe::Arc; -use crate::{AdtId, ModuleDefId}; +use crate::{db::DefDatabase, AdtId, ModuleDefId}; use super::*; @@ -15,7 +14,7 @@ fn check_def_map_is_not_recomputed(ra_fixture_initial: &str, ra_fixture_change: }); assert!(format!("{events:?}").contains("crate_def_map"), "{events:#?}") } - db.set_file_text(pos.file_id, Arc::new(ra_fixture_change.to_string())); + db.set_file_text(pos.file_id, Arc::from(ra_fixture_change)); { let events = db.log_executed(|| { @@ -96,7 +95,7 @@ fn typing_inside_a_macro_should_not_invalidate_def_map() { }); assert!(format!("{events:?}").contains("crate_def_map"), "{events:#?}") } - db.set_file_text(pos.file_id, Arc::new("m!(Y);".to_string())); + db.set_file_text(pos.file_id, Arc::from("m!(Y);")); { let events = db.log_executed(|| { @@ -109,7 +108,7 @@ fn typing_inside_a_macro_should_not_invalidate_def_map() { } #[test] -fn typing_inside_a_function_should_not_invalidate_expansions() { +fn typing_inside_a_function_should_not_invalidate_item_expansions() { let (mut db, pos) = TestDB::with_position( r#" //- /lib.rs @@ -140,7 +139,7 @@ m!(Z); let n_recalculated_item_trees = events.iter().filter(|it| it.contains("item_tree")).count(); assert_eq!(n_recalculated_item_trees, 6); let n_reparsed_macros = - events.iter().filter(|it| it.contains("parse_macro_expansion")).count(); + events.iter().filter(|it| it.contains("parse_macro_expansion(")).count(); assert_eq!(n_reparsed_macros, 3); } @@ -150,7 +149,7 @@ fn quux() { 92 } m!(Y); m!(Z); "#; - db.set_file_text(pos.file_id, Arc::new(new_text.to_string())); + db.set_file_text(pos.file_id, Arc::from(new_text)); { let events = db.log_executed(|| { @@ -161,7 +160,7 @@ m!(Z); let n_recalculated_item_trees = events.iter().filter(|it| it.contains("item_tree")).count(); assert_eq!(n_recalculated_item_trees, 1); let n_reparsed_macros = - events.iter().filter(|it| it.contains("parse_macro_expansion")).count(); + events.iter().filter(|it| it.contains("parse_macro_expansion(")).count(); assert_eq!(n_reparsed_macros, 0); } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs index a4ccd14cbb463..f4cca8d68d0ac 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs @@ -259,6 +259,72 @@ mod priv_mod { ); } +#[test] +fn macro_use_filter() { + check( + r#" +//- /main.rs crate:main deps:empty,multiple,all +#[macro_use()] +extern crate empty; + +foo_not_imported!(); + +#[macro_use(bar1)] +#[macro_use()] +#[macro_use(bar2, bar3)] +extern crate multiple; + +bar1!(); +bar2!(); +bar3!(); +bar_not_imported!(); + +#[macro_use(baz1)] +#[macro_use] +#[macro_use(baz2)] +extern crate all; + +baz1!(); +baz2!(); +baz3!(); + +//- /empty.rs crate:empty +#[macro_export] +macro_rules! foo_not_imported { () => { struct NotOkFoo; } } + +//- /multiple.rs crate:multiple +#[macro_export] +macro_rules! bar1 { () => { struct OkBar1; } } +#[macro_export] +macro_rules! bar2 { () => { struct OkBar2; } } +#[macro_export] +macro_rules! bar3 { () => { struct OkBar3; } } +#[macro_export] +macro_rules! bar_not_imported { () => { struct NotOkBar; } } + +//- /all.rs crate:all +#[macro_export] +macro_rules! baz1 { () => { struct OkBaz1; } } +#[macro_export] +macro_rules! baz2 { () => { struct OkBaz2; } } +#[macro_export] +macro_rules! baz3 { () => { struct OkBaz3; } } +"#, + expect![[r#" + crate + OkBar1: t v + OkBar2: t v + OkBar3: t v + OkBaz1: t v + OkBaz2: t v + OkBaz3: t v + all: t + empty: t + multiple: t + "#]], + ); +} + #[test] fn prelude_is_macro_use() { cov_mark::check!(prelude_is_macro_use); @@ -664,6 +730,29 @@ pub struct bar; ); } +#[test] +fn macro_dollar_crate_is_correct_in_derive_meta() { + let map = compute_crate_def_map( + r#" +//- minicore: derive, clone +//- /main.rs crate:main deps:lib +lib::foo!(); + +//- /lib.rs crate:lib +#[macro_export] +macro_rules! foo { + () => { + #[derive($crate::Clone)] + struct S; + } +} + +pub use core::clone::Clone; +"#, + ); + assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 1); +} + #[test] fn expand_derive() { let map = compute_crate_def_map( @@ -683,7 +772,7 @@ pub macro Copy {} pub macro Clone {} "#, ); - assert_eq!(map.modules[map.root].scope.impls().len(), 2); + assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 2); } #[test] @@ -726,7 +815,7 @@ pub macro derive($item:item) {} pub macro Clone {} "#, ); - assert_eq!(map.modules[map.root].scope.impls().len(), 1); + assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 1); } #[test] @@ -991,7 +1080,7 @@ macro_rules! mbe { #[test] fn collects_derive_helpers() { - let def_map = compute_crate_def_map( + let db = TestDB::with_files( r#" #![crate_type="proc-macro"] struct TokenStream; @@ -1002,11 +1091,13 @@ pub fn derive_macro_2(_item: TokenStream) -> TokenStream { } "#, ); + let krate = db.crate_graph().iter().next().unwrap(); + let def_map = db.crate_def_map(krate); - assert_eq!(def_map.exported_derives.len(), 1); - match def_map.exported_derives.values().next() { + assert_eq!(def_map.data.exported_derives.len(), 1); + match def_map.data.exported_derives.values().next() { Some(helpers) => match &**helpers { - [attr] => assert_eq!(attr.to_string(), "helper_attr"), + [attr] => assert_eq!(attr.display(&db).to_string(), "helper_attr"), _ => unreachable!(), }, _ => unreachable!(), @@ -1169,7 +1260,7 @@ struct A; #[test] fn macro_use_imports_all_macro_types() { - let def_map = compute_crate_def_map( + let db = TestDB::with_files( r#" //- /main.rs crate:main deps:lib #[macro_use] @@ -1192,18 +1283,153 @@ struct TokenStream; fn proc_attr(a: TokenStream, b: TokenStream) -> TokenStream { a } "#, ); + let krate = db.crate_graph().iter().next().unwrap(); + let def_map = db.crate_def_map(krate); + + let root_module = &def_map[DefMap::ROOT].scope; + assert!( + root_module.legacy_macros().count() == 0, + "`#[macro_use]` shouldn't bring macros into textual macro scope", + ); - let root = &def_map[def_map.root()].scope; - let actual = root - .legacy_macros() - .sorted_by(|a, b| std::cmp::Ord::cmp(&a.0, &b.0)) - .map(|(name, _)| format!("{name}\n")) - .collect::(); + let actual = def_map + .macro_use_prelude + .iter() + .map(|(name, _)| name.display(&db).to_string()) + .sorted() + .join("\n"); expect![[r#" legacy macro20 - proc_attr - "#]] + proc_attr"#]] .assert_eq(&actual); } + +#[test] +fn non_prelude_macros_take_precedence_over_macro_use_prelude() { + check( + r#" +//- /lib.rs edition:2021 crate:lib deps:dep,core +#[macro_use] +extern crate dep; + +macro foo() { struct Ok; } +macro bar() { fn ok() {} } + +foo!(); +bar!(); + +//- /dep.rs crate:dep +#[macro_export] +macro_rules! foo { + () => { struct NotOk; } +} + +//- /core.rs crate:core +pub mod prelude { + pub mod rust_2021 { + #[macro_export] + macro_rules! bar { + () => { fn not_ok() {} } + } + } +} + "#, + expect![[r#" + crate + Ok: t v + bar: m + dep: t + foo: m + ok: v + "#]], + ); +} + +#[test] +fn macro_use_prelude_is_eagerly_expanded() { + // See FIXME in `ModCollector::collect_macro_call()`. + check( + r#" +//- /main.rs crate:main deps:lib +#[macro_use] +extern crate lib; +mk_foo!(); +mod a { + foo!(); +} +//- /lib.rs crate:lib +#[macro_export] +macro_rules! mk_foo { + () => { + macro_rules! foo { + () => { struct Ok; } + } + } +} + "#, + expect![[r#" + crate + a: t + lib: t + + crate::a + Ok: t v + "#]], + ); +} + +#[test] +fn macro_sub_namespace() { + let map = compute_crate_def_map( + r#" +//- minicore: derive, clone +macro_rules! Clone { () => {} } +macro_rules! derive { () => {} } + +#[derive(Clone)] +struct S; + "#, + ); + assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 1); +} + +#[test] +fn macro_sub_namespace2() { + check( + r#" +//- /main.rs edition:2021 crate:main deps:proc,core +use proc::{foo, bar}; + +foo!(); +bar!(); + +//- /proc.rs crate:proc +#![crate_type="proc-macro"] +#[proc_macro_derive(foo)] +pub fn foo() {} +#[proc_macro_attribute] +pub fn bar() {} + +//- /core.rs crate:core +pub mod prelude { + pub mod rust_2021 { + pub macro foo() { + struct Ok; + } + pub macro bar() { + fn ok() {} + } + } +} + "#, + expect![[r#" + crate + Ok: t v + bar: m + foo: m + ok: v + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs index a019312884787..81bc0ff91e3a7 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs @@ -887,7 +887,7 @@ mod module; //- /module.rs #![cfg(NEVER)] -struct AlsoShoulntAppear; +struct AlsoShouldNotAppear; "#, expect![[r#" crate diff --git a/src/tools/rust-analyzer/crates/hir-def/src/path.rs b/src/tools/rust-analyzer/crates/hir-def/src/path.rs index f3197d1800f22..b9b8082549718 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/path.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/path.rs @@ -7,15 +7,14 @@ use std::{ }; use crate::{ - body::LowerCtx, - type_ref::{ConstRefOrPath, LifetimeRef}, + lang_item::LangItemTarget, + lower::LowerCtx, + type_ref::{ConstRefOrPath, LifetimeRef, TypeBound, TypeRef}, }; use hir_expand::name::Name; use intern::Interned; use syntax::ast; -use crate::type_ref::{TypeBound, TypeRef}; - pub use hir_expand::mod_path::{path, ModPath, PathKind}; #[derive(Debug, Clone, PartialEq, Eq)] @@ -36,13 +35,19 @@ impl Display for ImportAlias { } #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct Path { - /// Type based path like `::foo`. - /// Note that paths like `::foo` are desugared to `Trait::::foo`. - type_anchor: Option>, - mod_path: Interned, - /// Invariant: the same len as `self.mod_path.segments` or `None` if all segments are `None`. - generic_args: Option>]>>, +pub enum Path { + /// A normal path + Normal { + /// Type based path like `::foo`. + /// Note that paths like `::foo` are desugared to `Trait::::foo`. + type_anchor: Option>, + mod_path: Interned, + /// Invariant: the same len as `self.mod_path.segments` or `None` if all segments are `None`. + generic_args: Option>]>>, + }, + /// A link to a lang item. It is used in desugaring of things like `x?`. We can show these + /// links via a normal path since they might be private and not accessible in the usage place. + LangItem(LangItemTarget), } /// Generic arguments to a path segment (e.g. the `i32` in `Option`). This @@ -102,51 +107,77 @@ impl Path { ) -> Path { let generic_args = generic_args.into(); assert_eq!(path.len(), generic_args.len()); - Path { type_anchor: None, mod_path: Interned::new(path), generic_args: Some(generic_args) } + Path::Normal { + type_anchor: None, + mod_path: Interned::new(path), + generic_args: Some(generic_args), + } + } + + /// Converts a known mod path to `Path`. + pub fn from_known_path_with_no_generic(path: ModPath) -> Path { + Path::Normal { type_anchor: None, mod_path: Interned::new(path), generic_args: None } } pub fn kind(&self) -> &PathKind { - &self.mod_path.kind + match self { + Path::Normal { mod_path, .. } => &mod_path.kind, + Path::LangItem(_) => &PathKind::Abs, + } } pub fn type_anchor(&self) -> Option<&TypeRef> { - self.type_anchor.as_deref() + match self { + Path::Normal { type_anchor, .. } => type_anchor.as_deref(), + Path::LangItem(_) => None, + } } pub fn segments(&self) -> PathSegments<'_> { - let s = PathSegments { - segments: self.mod_path.segments(), - generic_args: self.generic_args.as_deref(), + let Path::Normal { mod_path, generic_args, .. } = self else { + return PathSegments { + segments: &[], + generic_args: None, + }; }; + let s = + PathSegments { segments: mod_path.segments(), generic_args: generic_args.as_deref() }; if let Some(generic_args) = s.generic_args { assert_eq!(s.segments.len(), generic_args.len()); } s } - pub fn mod_path(&self) -> &ModPath { - &self.mod_path + pub fn mod_path(&self) -> Option<&ModPath> { + match self { + Path::Normal { mod_path, .. } => Some(&mod_path), + Path::LangItem(_) => None, + } } pub fn qualifier(&self) -> Option { - if self.mod_path.is_ident() { + let Path::Normal { mod_path, generic_args, type_anchor } = self else { + return None; + }; + if mod_path.is_ident() { return None; } - let res = Path { - type_anchor: self.type_anchor.clone(), + let res = Path::Normal { + type_anchor: type_anchor.clone(), mod_path: Interned::new(ModPath::from_segments( - self.mod_path.kind, - self.mod_path.segments()[..self.mod_path.segments().len() - 1].iter().cloned(), + mod_path.kind, + mod_path.segments()[..mod_path.segments().len() - 1].iter().cloned(), )), - generic_args: self.generic_args.as_ref().map(|it| it[..it.len() - 1].to_vec().into()), + generic_args: generic_args.as_ref().map(|it| it[..it.len() - 1].to_vec().into()), }; Some(res) } pub fn is_self_type(&self) -> bool { - self.type_anchor.is_none() - && self.generic_args.as_deref().is_none() - && self.mod_path.is_Self() + let Path::Normal { mod_path, generic_args, type_anchor } = self else { + return false; + }; + type_anchor.is_none() && generic_args.as_deref().is_none() && mod_path.is_Self() } } @@ -222,7 +253,7 @@ impl GenericArgs { impl From for Path { fn from(name: Name) -> Path { - Path { + Path::Normal { type_anchor: None, mod_path: Interned::new(ModPath::from_segments(PathKind::Plain, iter::once(name))), generic_args: None, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs index b7542bd777d04..26d2706175ca9 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs @@ -2,17 +2,15 @@ use std::iter; -use crate::type_ref::ConstRefOrPath; +use crate::{lower::LowerCtx, type_ref::ConstRefOrPath}; use either::Either; use hir_expand::name::{name, AsName}; use intern::Interned; use syntax::ast::{self, AstNode, HasTypeBounds}; -use super::AssociatedTypeBinding; use crate::{ - body::LowerCtx, - path::{GenericArg, GenericArgs, ModPath, Path, PathKind}, + path::{AssociatedTypeBinding, GenericArg, GenericArgs, ModPath, Path, PathKind}, type_ref::{LifetimeRef, TypeBound, TypeRef}, }; @@ -75,8 +73,11 @@ pub(super) fn lower_path(mut path: ast::Path, ctx: &LowerCtx<'_>) -> Option>::Foo desugars to Trait::Foo Some(trait_ref) => { - let Path { mod_path, generic_args: path_generic_args, .. } = - Path::from_src(trait_ref.path()?, ctx)?; + let Path::Normal { mod_path, generic_args: path_generic_args, .. } = + Path::from_src(trait_ref.path()?, ctx)? else + { + return None; + }; let num_segments = mod_path.segments().len(); kind = mod_path.kind; @@ -157,7 +158,7 @@ pub(super) fn lower_path(mut path: ast::Path, ctx: &LowerCtx<'_>) -> Option { + if assoc_type_arg.param_list().is_some() { + // We currently ignore associated return type bounds. + continue; + } if let Some(name_ref) = assoc_type_arg.name_ref() { let name = name_ref.as_name(); let args = assoc_type_arg diff --git a/src/tools/rust-analyzer/crates/hir-def/src/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/pretty.rs index 2d45c8c8da1a5..0aead6f37f73f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/pretty.rs @@ -2,7 +2,7 @@ use std::fmt::{self, Write}; -use hir_expand::mod_path::PathKind; +use hir_expand::{db::ExpandDatabase, mod_path::PathKind}; use intern::Interned; use itertools::Itertools; @@ -11,11 +11,14 @@ use crate::{ type_ref::{Mutability, TraitBoundModifier, TypeBound, TypeRef}, }; -pub(crate) fn print_path(path: &Path, buf: &mut dyn Write) -> fmt::Result { +pub(crate) fn print_path(db: &dyn ExpandDatabase, path: &Path, buf: &mut dyn Write) -> fmt::Result { + if let Path::LangItem(x) = path { + return write!(buf, "$lang_item::{x:?}"); + } match path.type_anchor() { Some(anchor) => { write!(buf, "<")?; - print_type_ref(anchor, buf)?; + print_type_ref(db, anchor, buf)?; write!(buf, ">::")?; } None => match path.kind() { @@ -41,10 +44,10 @@ pub(crate) fn print_path(path: &Path, buf: &mut dyn Write) -> fmt::Result { write!(buf, "::")?; } - write!(buf, "{}", segment.name)?; + write!(buf, "{}", segment.name.display(db))?; if let Some(generics) = segment.args_and_bindings { write!(buf, "::<")?; - print_generic_args(generics, buf)?; + print_generic_args(db, generics, buf)?; write!(buf, ">")?; } @@ -53,12 +56,16 @@ pub(crate) fn print_path(path: &Path, buf: &mut dyn Write) -> fmt::Result { Ok(()) } -pub(crate) fn print_generic_args(generics: &GenericArgs, buf: &mut dyn Write) -> fmt::Result { +pub(crate) fn print_generic_args( + db: &dyn ExpandDatabase, + 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)?; + print_generic_arg(db, self_ty, buf)?; first = false; args } else { @@ -69,35 +76,43 @@ pub(crate) fn print_generic_args(generics: &GenericArgs, buf: &mut dyn Write) -> write!(buf, ", ")?; } first = false; - print_generic_arg(arg, buf)?; + print_generic_arg(db, arg, buf)?; } for binding in generics.bindings.iter() { if !first { write!(buf, ", ")?; } first = false; - write!(buf, "{}", binding.name)?; + write!(buf, "{}", binding.name.display(db))?; if !binding.bounds.is_empty() { write!(buf, ": ")?; - print_type_bounds(&binding.bounds, buf)?; + print_type_bounds(db, &binding.bounds, buf)?; } if let Some(ty) = &binding.type_ref { write!(buf, " = ")?; - print_type_ref(ty, buf)?; + print_type_ref(db, ty, buf)?; } } Ok(()) } -pub(crate) fn print_generic_arg(arg: &GenericArg, buf: &mut dyn Write) -> fmt::Result { +pub(crate) fn print_generic_arg( + db: &dyn ExpandDatabase, + 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), + GenericArg::Type(ty) => print_type_ref(db, ty, buf), + GenericArg::Const(c) => write!(buf, "{}", c.display(db)), + GenericArg::Lifetime(lt) => write!(buf, "{}", lt.name.display(db)), } } -pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Result { +pub(crate) fn print_type_ref( + db: &dyn ExpandDatabase, + type_ref: &TypeRef, + buf: &mut dyn Write, +) -> fmt::Result { // FIXME: deduplicate with `HirDisplay` impl match type_ref { TypeRef::Never => write!(buf, "!")?, @@ -108,18 +123,18 @@ pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Re if i != 0 { write!(buf, ", ")?; } - print_type_ref(field, buf)?; + print_type_ref(db, field, buf)?; } write!(buf, ")")?; } - TypeRef::Path(path) => print_path(path, buf)?, + TypeRef::Path(path) => print_path(db, path, buf)?, TypeRef::RawPtr(pointee, mtbl) => { let mtbl = match mtbl { Mutability::Shared => "*const", Mutability::Mut => "*mut", }; write!(buf, "{mtbl} ")?; - print_type_ref(pointee, buf)?; + print_type_ref(db, pointee, buf)?; } TypeRef::Reference(pointee, lt, mtbl) => { let mtbl = match mtbl { @@ -128,19 +143,19 @@ pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Re }; write!(buf, "&")?; if let Some(lt) = lt { - write!(buf, "{} ", lt.name)?; + write!(buf, "{} ", lt.name.display(db))?; } write!(buf, "{mtbl}")?; - print_type_ref(pointee, buf)?; + print_type_ref(db, pointee, buf)?; } TypeRef::Array(elem, len) => { write!(buf, "[")?; - print_type_ref(elem, buf)?; - write!(buf, "; {len}]")?; + print_type_ref(db, elem, buf)?; + write!(buf, "; {}]", len.display(db))?; } TypeRef::Slice(elem) => { write!(buf, "[")?; - print_type_ref(elem, buf)?; + print_type_ref(db, elem, buf)?; write!(buf, "]")?; } TypeRef::Fn(args_and_ret, varargs, is_unsafe) => { @@ -154,7 +169,7 @@ pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Re if i != 0 { write!(buf, ", ")?; } - print_type_ref(typeref, buf)?; + print_type_ref(db, typeref, buf)?; } if *varargs { if !args.is_empty() { @@ -163,7 +178,7 @@ pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Re write!(buf, "...")?; } write!(buf, ") -> ")?; - print_type_ref(return_type, buf)?; + print_type_ref(db, return_type, buf)?; } TypeRef::Macro(_ast_id) => { write!(buf, "")?; @@ -171,11 +186,11 @@ pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Re TypeRef::Error => write!(buf, "{{unknown}}")?, TypeRef::ImplTrait(bounds) => { write!(buf, "impl ")?; - print_type_bounds(bounds, buf)?; + print_type_bounds(db, bounds, buf)?; } TypeRef::DynTrait(bounds) => { write!(buf, "dyn ")?; - print_type_bounds(bounds, buf)?; + print_type_bounds(db, bounds, buf)?; } } @@ -183,6 +198,7 @@ pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Re } pub(crate) fn print_type_bounds( + db: &dyn ExpandDatabase, bounds: &[Interned], buf: &mut dyn Write, ) -> fmt::Result { @@ -197,13 +213,13 @@ pub(crate) fn print_type_bounds( TraitBoundModifier::None => (), TraitBoundModifier::Maybe => write!(buf, "?")?, } - print_path(path, buf)?; + print_path(db, path, buf)?; } TypeBound::ForLifetime(lifetimes, path) => { - write!(buf, "for<{}> ", lifetimes.iter().format(", "))?; - print_path(path, buf)?; + write!(buf, "for<{}> ", lifetimes.iter().map(|it| it.display(db)).format(", "))?; + print_path(db, path, buf)?; } - TypeBound::Lifetime(lt) => write!(buf, "{}", lt.name)?, + TypeBound::Lifetime(lt) => write!(buf, "{}", lt.name.display(db))?, TypeBound::Error => write!(buf, "{{unknown}}")?, } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs index 61e64fc103631..06f5b2526a459 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs @@ -1,5 +1,5 @@ //! Name resolution façade. -use std::{fmt, hash::BuildHasherDefault, sync::Arc}; +use std::{fmt, hash::BuildHasherDefault}; use base_db::CrateId; use hir_expand::name::{name, Name}; @@ -7,16 +7,18 @@ use indexmap::IndexMap; use intern::Interned; use rustc_hash::FxHashSet; use smallvec::{smallvec, SmallVec}; +use triomphe::Arc; use crate::{ body::scope::{ExprScopes, ScopeId}, builtin_type::BuiltinType, db::DefDatabase, - expr::{BindingId, ExprId, LabelId}, generics::{GenericParams, TypeOrConstParamData}, + hir::{BindingId, ExprId, LabelId}, item_scope::{BuiltinShadowMode, BUILTIN_SCOPE}, - nameres::DefMap, - path::{ModPath, PathKind}, + lang_item::LangItemTarget, + nameres::{DefMap, MacroSubNs}, + path::{ModPath, Path, PathKind}, per_ns::PerNs, visibility::{RawVisibility, Visibility}, AdtId, AssocItemId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, ExternBlockId, @@ -78,7 +80,7 @@ enum Scope { ExprScope(ExprScope), } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum TypeNs { SelfType(ImplId), GenericParam(TypeParamId), @@ -153,7 +155,8 @@ impl Resolver { path: &ModPath, ) -> Option { let (item_map, module) = self.item_scope(); - let (module_res, idx) = item_map.resolve_path(db, module, path, BuiltinShadowMode::Module); + let (module_res, idx) = + item_map.resolve_path(db, module, path, BuiltinShadowMode::Module, None); match module_res.take_types()? { ModuleDefId::TraitId(it) => { let idx = idx?; @@ -176,8 +179,27 @@ impl Resolver { pub fn resolve_path_in_type_ns( &self, db: &dyn DefDatabase, - path: &ModPath, + path: &Path, ) -> Option<(TypeNs, Option)> { + let path = match path { + Path::Normal { mod_path, .. } => mod_path, + Path::LangItem(l) => { + return Some(( + match *l { + LangItemTarget::Union(x) => TypeNs::AdtId(x.into()), + LangItemTarget::TypeAlias(x) => TypeNs::TypeAliasId(x), + LangItemTarget::Struct(x) => TypeNs::AdtId(x.into()), + LangItemTarget::EnumVariant(x) => TypeNs::EnumVariantId(x), + LangItemTarget::EnumId(x) => TypeNs::AdtId(x.into()), + LangItemTarget::Trait(x) => TypeNs::TraitId(x), + LangItemTarget::Function(_) + | LangItemTarget::ImplDef(_) + | LangItemTarget::Static(_) => return None, + }, + None, + )) + } + }; let first_name = path.segments().first()?; let skip_to_mod = path.kind != PathKind::Plain; if skip_to_mod { @@ -217,7 +239,7 @@ impl Resolver { pub fn resolve_path_in_type_ns_fully( &self, db: &dyn DefDatabase, - path: &ModPath, + path: &Path, ) -> Option { let (res, unresolved) = self.resolve_path_in_type_ns(db, path)?; if unresolved.is_some() { @@ -245,8 +267,24 @@ impl Resolver { pub fn resolve_path_in_value_ns( &self, db: &dyn DefDatabase, - path: &ModPath, + path: &Path, ) -> Option { + let path = match path { + Path::Normal { mod_path, .. } => mod_path, + Path::LangItem(l) => { + return Some(ResolveValueResult::ValueNs(match *l { + LangItemTarget::Function(x) => ValueNs::FunctionId(x), + LangItemTarget::Static(x) => ValueNs::StaticId(x), + LangItemTarget::Struct(x) => ValueNs::StructId(x), + LangItemTarget::EnumVariant(x) => ValueNs::EnumVariantId(x), + LangItemTarget::Union(_) + | LangItemTarget::ImplDef(_) + | LangItemTarget::TypeAlias(_) + | LangItemTarget::Trait(_) + | LangItemTarget::EnumId(_) => return None, + })) + } + }; let n_segments = path.segments().len(); let tmp = name![self]; let first_name = if path.is_self() { &tmp } else { path.segments().first()? }; @@ -340,7 +378,7 @@ impl Resolver { pub fn resolve_path_in_value_ns_fully( &self, db: &dyn DefDatabase, - path: &ModPath, + path: &Path, ) -> Option { match self.resolve_path_in_value_ns(db, path)? { ResolveValueResult::ValueNs(it) => Some(it), @@ -348,9 +386,17 @@ impl Resolver { } } - pub fn resolve_path_as_macro(&self, db: &dyn DefDatabase, path: &ModPath) -> Option { + pub fn resolve_path_as_macro( + &self, + db: &dyn DefDatabase, + path: &ModPath, + expected_macro_kind: Option, + ) -> Option { let (item_map, module) = self.item_scope(); - item_map.resolve_path(db, module, path, BuiltinShadowMode::Other).0.take_macros() + item_map + .resolve_path(db, module, path, BuiltinShadowMode::Other, expected_macro_kind) + .0 + .take_macros() } /// Returns a set of names available in the current scope. @@ -415,7 +461,10 @@ impl Resolver { res.add(name, ScopeDef::ModuleDef(ModuleDefId::MacroId(mac))); }) }); - def_map.extern_prelude().for_each(|(name, &def)| { + def_map.macro_use_prelude().for_each(|(name, def)| { + res.add(name, ScopeDef::ModuleDef(def.into())); + }); + def_map.extern_prelude().for_each(|(name, def)| { res.add(name, ScopeDef::ModuleDef(ModuleDefId::ModuleId(def))); }); BUILTIN_SCOPE.iter().for_each(|(name, &def)| { @@ -441,7 +490,7 @@ impl Resolver { &Scope::ImplDefScope(impl_) => { if let Some(target_trait) = &db.impl_data(impl_).target_trait { if let Some(TypeNs::TraitId(trait_)) = - self.resolve_path_in_type_ns_fully(db, target_trait.path.mod_path()) + self.resolve_path_in_type_ns_fully(db, &target_trait.path) { traits.insert(trait_); } @@ -536,15 +585,13 @@ impl Resolver { scope_id, })); if let Some(block) = expr_scopes.block(scope_id) { - if let Some(def_map) = db.block_def_map(block) { - let root = def_map.root(); - resolver - .scopes - .push(Scope::BlockScope(ModuleItemMap { def_map, module_id: root })); - // FIXME: This adds as many module scopes as there are blocks, but resolving in each - // already traverses all parents, so this is O(n²). I think we could only store the - // innermost module scope instead? - } + let def_map = db.block_def_map(block); + resolver + .scopes + .push(Scope::BlockScope(ModuleItemMap { def_map, module_id: DefMap::ROOT })); + // FIXME: This adds as many module scopes as there are blocks, but resolving in each + // already traverses all parents, so this is O(n²). I think we could only store the + // innermost module scope instead? } } @@ -592,7 +639,8 @@ impl Resolver { shadow: BuiltinShadowMode, ) -> PerNs { let (item_map, module) = self.item_scope(); - let (module_res, segment_index) = item_map.resolve_path(db, module, path, shadow); + // This method resolves `path` just like import paths, so no expected macro subns is given. + let (module_res, segment_index) = item_map.resolve_path(db, module, path, shadow, None); if segment_index.is_some() { return PerNs::none(); } @@ -705,13 +753,11 @@ fn resolver_for_scope_( for scope in scope_chain.into_iter().rev() { if let Some(block) = scopes.block(scope) { - if let Some(def_map) = db.block_def_map(block) { - let root = def_map.root(); - r = r.push_block_scope(def_map, root); - // FIXME: This adds as many module scopes as there are blocks, but resolving in each - // already traverses all parents, so this is O(n²). I think we could only store the - // innermost module scope instead? - } + let def_map = db.block_def_map(block); + r = r.push_block_scope(def_map, DefMap::ROOT); + // FIXME: This adds as many module scopes as there are blocks, but resolving in each + // already traverses all parents, so this is O(n²). I think we could only store the + // innermost module scope instead? } r = r.push_expr_scope(owner, Arc::clone(&scopes), scope); @@ -1000,6 +1046,12 @@ impl HasResolver for GenericDefId { } } +impl HasResolver for EnumVariantId { + fn resolver(self, db: &dyn DefDatabase) -> Resolver { + self.parent.resolver(db) + } +} + impl HasResolver for VariantId { fn resolver(self, db: &dyn DefDatabase) -> Resolver { match self { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/src.rs b/src/tools/rust-analyzer/crates/hir-def/src/src.rs index f69356cac87d3..6047f770d4d30 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/src.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/src.rs @@ -20,7 +20,7 @@ impl HasSource for AssocItemLoc { fn source(&self, db: &dyn DefDatabase) -> InFile { let tree = self.id.item_tree(db); let ast_id_map = db.ast_id_map(self.id.file_id()); - let root = db.parse_or_expand(self.id.file_id()).unwrap(); + let root = db.parse_or_expand(self.id.file_id()); let node = &tree[self.id.value]; InFile::new(self.id.file_id(), ast_id_map.get(node.ast_id()).to_node(&root)) @@ -33,7 +33,7 @@ impl HasSource for ItemLoc { fn source(&self, db: &dyn DefDatabase) -> InFile { let tree = self.id.item_tree(db); let ast_id_map = db.ast_id_map(self.id.file_id()); - let root = db.parse_or_expand(self.id.file_id()).unwrap(); + let root = db.parse_or_expand(self.id.file_id()); let node = &tree[self.id.value]; InFile::new(self.id.file_id(), ast_id_map.get(node.ast_id()).to_node(&root)) @@ -46,7 +46,7 @@ impl HasSource for Macro2Loc { fn source(&self, db: &dyn DefDatabase) -> InFile { let tree = self.id.item_tree(db); let ast_id_map = db.ast_id_map(self.id.file_id()); - let root = db.parse_or_expand(self.id.file_id()).unwrap(); + let root = db.parse_or_expand(self.id.file_id()); let node = &tree[self.id.value]; InFile::new(self.id.file_id(), ast_id_map.get(node.ast_id()).to_node(&root)) @@ -59,7 +59,7 @@ impl HasSource for MacroRulesLoc { fn source(&self, db: &dyn DefDatabase) -> InFile { let tree = self.id.item_tree(db); let ast_id_map = db.ast_id_map(self.id.file_id()); - let root = db.parse_or_expand(self.id.file_id()).unwrap(); + let root = db.parse_or_expand(self.id.file_id()); let node = &tree[self.id.value]; InFile::new(self.id.file_id(), ast_id_map.get(node.ast_id()).to_node(&root)) @@ -72,7 +72,7 @@ impl HasSource for ProcMacroLoc { fn source(&self, db: &dyn DefDatabase) -> InFile { let tree = self.id.item_tree(db); let ast_id_map = db.ast_id_map(self.id.file_id()); - let root = db.parse_or_expand(self.id.file_id()).unwrap(); + let root = db.parse_or_expand(self.id.file_id()); let node = &tree[self.id.value]; InFile::new(self.id.file_id(), ast_id_map.get(node.ast_id()).to_node(&root)) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs b/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs index ee143b19ae5b3..a6befc8a81a81 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs @@ -1,17 +1,16 @@ //! Database used for testing `hir_def`. -use std::{ - fmt, panic, - sync::{Arc, Mutex}, -}; +use std::{fmt, panic, sync::Mutex}; use base_db::{ - salsa, AnchoredPath, CrateId, FileId, FileLoader, FileLoaderDelegate, FilePosition, - SourceDatabase, Upcast, + salsa::{self, Durability}, + AnchoredPath, CrateId, FileId, FileLoader, FileLoaderDelegate, FilePosition, SourceDatabase, + Upcast, }; use hir_expand::{db::ExpandDatabase, InFile}; -use stdx::hash::NoHashHashSet; +use rustc_hash::FxHashSet; use syntax::{algo, ast, AstNode}; +use triomphe::Arc; use crate::{ db::DefDatabase, @@ -35,7 +34,7 @@ pub(crate) struct TestDB { impl Default for TestDB { fn default() -> Self { let mut this = Self { storage: Default::default(), events: Default::default() }; - this.set_enable_proc_attr_macros(true); + this.set_expand_proc_attr_macros_with_durability(true, Durability::HIGH); this } } @@ -70,13 +69,13 @@ impl fmt::Debug for TestDB { impl panic::RefUnwindSafe for TestDB {} impl FileLoader for TestDB { - fn file_text(&self, file_id: FileId) -> Arc { + fn file_text(&self, file_id: FileId) -> Arc { FileLoaderDelegate(self).file_text(file_id) } fn resolve_path(&self, path: AnchoredPath<'_>) -> Option { FileLoaderDelegate(self).resolve_path(path) } - fn relevant_crates(&self, file_id: FileId) -> Arc> { + fn relevant_crates(&self, file_id: FileId) -> Arc> { FileLoaderDelegate(self).relevant_crates(file_id) } } @@ -111,7 +110,7 @@ impl TestDB { } _ => { // FIXME: handle `mod` inside block expression - return def_map.module_id(def_map.root()); + return def_map.module_id(DefMap::ROOT); } } } @@ -120,7 +119,7 @@ impl TestDB { /// Finds the smallest/innermost module in `def_map` containing `position`. fn mod_at_position(&self, def_map: &DefMap, position: FilePosition) -> LocalModuleId { let mut size = None; - let mut res = def_map.root(); + let mut res = DefMap::ROOT; for (module, data) in def_map.modules() { let src = data.definition_source(self); if src.file_id != position.file_id.into() { @@ -209,13 +208,11 @@ impl TestDB { }); for scope in scope_iter { - let containing_blocks = + let mut containing_blocks = scopes.scope_chain(Some(scope)).filter_map(|scope| scopes.block(scope)); - for block in containing_blocks { - if let Some(def_map) = self.block_def_map(block) { - return Some(def_map); - } + if let Some(block) = containing_blocks.next().map(|block| self.block_def_map(block)) { + return Some(block); } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs b/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs index ab76ed43d3a0e..30f48de61f2e8 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs @@ -1,10 +1,11 @@ //! Defines hir-level representation of visibility (e.g. `pub` and `pub(crate)`). -use std::{iter, sync::Arc}; +use std::iter; use hir_expand::{hygiene::Hygiene, InFile}; use la_arena::ArenaMap; use syntax::ast; +use triomphe::Arc; use crate::{ db::DefDatabase, diff --git a/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml b/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml index 5c684be03cf24..40d8659f25bad 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml @@ -22,6 +22,7 @@ hashbrown = { version = "0.12.1", features = [ "inline-more", ], default-features = false } smallvec.workspace = true +triomphe.workspace = true # local deps stdx.workspace = true diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/ast_id_map.rs b/src/tools/rust-analyzer/crates/hir-expand/src/ast_id_map.rs index 2b27db0e95063..400442de94b98 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/ast_id_map.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/ast_id_map.rs @@ -115,6 +115,7 @@ impl AstIdMap { } } } + res.arena.shrink_to_fit(); res } @@ -123,6 +124,10 @@ impl AstIdMap { FileAstId { raw, _ty: PhantomData } } + pub fn get(&self, id: FileAstId) -> AstPtr { + AstPtr::try_from_raw(self.arena[id.raw].clone()).unwrap() + } + fn erased_ast_id(&self, item: &SyntaxNode) -> ErasedFileAstId { let ptr = SyntaxNodePtr::new(item); let hash = hash_ptr(&ptr); @@ -136,10 +141,6 @@ impl AstIdMap { } } - pub fn get(&self, id: FileAstId) -> AstPtr { - AstPtr::try_from_raw(self.arena[id.raw].clone()).unwrap() - } - fn alloc(&mut self, item: &SyntaxNode) -> ErasedFileAstId { self.arena.alloc(SyntaxNodePtr::new(item)) } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs index 8d1e88725ecbf..0c369a18bb9cc 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs @@ -1,5 +1,5 @@ //! A higher level attributes based on TokenTree, with also some shortcuts. -use std::{fmt, ops, sync::Arc}; +use std::{fmt, ops}; use base_db::CrateId; use cfg::CfgExpr; @@ -8,12 +8,12 @@ use intern::Interned; use mbe::{syntax_node_to_token_tree, DelimiterKind, Punct}; use smallvec::{smallvec, SmallVec}; use syntax::{ast, match_ast, AstNode, SmolStr, SyntaxNode}; +use triomphe::Arc; use crate::{ db::ExpandDatabase, hygiene::Hygiene, - mod_path::{ModPath, PathKind}, - name::AsName, + mod_path::ModPath, tt::{self, Subtree}, InFile, }; @@ -21,6 +21,7 @@ use crate::{ /// Syntactical attributes, without filtering of `cfg_attr`s. #[derive(Default, Debug, Clone, PartialEq, Eq)] pub struct RawAttrs { + // FIXME: Make this a ThinArc entries: Option>, } @@ -50,7 +51,9 @@ impl RawAttrs { path: Interned::new(ModPath::from(crate::name!(doc))), }), }) - .collect::>(); + .collect::>(); + // FIXME: use `Arc::from_iter` when it becomes available + let entries: Arc<[Attr]> = Arc::from(entries); Self { entries: if entries.is_empty() { None } else { Some(entries) } } } @@ -68,7 +71,7 @@ impl RawAttrs { (Some(a), Some(b)) => { let last_ast_index = a.last().map_or(0, |it| it.id.ast_index() + 1) as u32; Self { - entries: Some( + entries: Some(Arc::from( a.iter() .cloned() .chain(b.iter().map(|it| { @@ -78,8 +81,9 @@ impl RawAttrs { << AttrId::AST_INDEX_BITS; it })) - .collect(), - ), + // FIXME: use `Arc::from_iter` when it becomes available + .collect::>(), + )), } } } @@ -96,48 +100,51 @@ impl RawAttrs { } let crate_graph = db.crate_graph(); - let new_attrs = self - .iter() - .flat_map(|attr| -> SmallVec<[_; 1]> { - let is_cfg_attr = - attr.path.as_ident().map_or(false, |name| *name == crate::name![cfg_attr]); - if !is_cfg_attr { - return smallvec![attr.clone()]; - } - - let subtree = match attr.token_tree_value() { - Some(it) => it, - _ => return smallvec![attr.clone()], - }; - - let (cfg, parts) = match parse_cfg_attr_input(subtree) { - Some(it) => it, - None => return smallvec![attr.clone()], - }; - let index = attr.id; - let attrs = - parts.enumerate().take(1 << AttrId::CFG_ATTR_BITS).filter_map(|(idx, attr)| { - let tree = Subtree { - delimiter: tt::Delimiter::unspecified(), - token_trees: attr.to_vec(), - }; - // FIXME hygiene - let hygiene = Hygiene::new_unhygienic(); - Attr::from_tt(db, &tree, &hygiene, index.with_cfg_attr(idx)) - }); - - let cfg_options = &crate_graph[krate].cfg_options; - let cfg = Subtree { delimiter: subtree.delimiter, token_trees: cfg.to_vec() }; - let cfg = CfgExpr::parse(&cfg); - if cfg_options.check(&cfg) == Some(false) { - smallvec![] - } else { - cov_mark::hit!(cfg_attr_active); - - attrs.collect() - } - }) - .collect(); + let new_attrs = Arc::from( + self.iter() + .flat_map(|attr| -> SmallVec<[_; 1]> { + let is_cfg_attr = + attr.path.as_ident().map_or(false, |name| *name == crate::name![cfg_attr]); + if !is_cfg_attr { + return smallvec![attr.clone()]; + } + + let subtree = match attr.token_tree_value() { + Some(it) => it, + _ => return smallvec![attr.clone()], + }; + + let (cfg, parts) = match parse_cfg_attr_input(subtree) { + Some(it) => it, + None => return smallvec![attr.clone()], + }; + let index = attr.id; + let attrs = parts.enumerate().take(1 << AttrId::CFG_ATTR_BITS).filter_map( + |(idx, attr)| { + let tree = Subtree { + delimiter: tt::Delimiter::unspecified(), + token_trees: attr.to_vec(), + }; + // FIXME hygiene + let hygiene = Hygiene::new_unhygienic(); + Attr::from_tt(db, &tree, &hygiene, index.with_cfg_attr(idx)) + }, + ); + + let cfg_options = &crate_graph[krate].cfg_options; + let cfg = Subtree { delimiter: subtree.delimiter, token_trees: cfg.to_vec() }; + let cfg = CfgExpr::parse(&cfg); + if cfg_options.check(&cfg) == Some(false) { + smallvec![] + } else { + cov_mark::hit!(cfg_attr_active); + + attrs.collect() + } + }) + // FIXME: use `Arc::from_iter` when it becomes available + .collect::>(), + ); RawAttrs { entries: Some(new_attrs) } } @@ -266,7 +273,11 @@ impl Attr { } /// Parses this attribute as a token tree consisting of comma separated paths. - pub fn parse_path_comma_token_tree(&self) -> Option + '_> { + pub fn parse_path_comma_token_tree<'a>( + &'a self, + db: &'a dyn ExpandDatabase, + hygiene: &'a Hygiene, + ) -> Option + 'a> { let args = self.token_tree_value()?; if args.delimiter.kind != DelimiterKind::Parenthesis { @@ -275,19 +286,37 @@ impl Attr { let paths = args .token_trees .split(|tt| matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Punct(Punct { char: ',', .. })))) - .filter_map(|tts| { + .filter_map(move |tts| { if tts.is_empty() { return None; } - let segments = tts.iter().filter_map(|tt| match tt { - tt::TokenTree::Leaf(tt::Leaf::Ident(id)) => Some(id.as_name()), - _ => None, - }); - Some(ModPath::from_segments(PathKind::Plain, segments)) + // FIXME: This is necessarily a hack. It'd be nice if we could avoid allocation here. + let subtree = tt::Subtree { + delimiter: tt::Delimiter::unspecified(), + token_trees: tts.into_iter().cloned().collect(), + }; + let (parse, _) = + mbe::token_tree_to_syntax_node(&subtree, mbe::TopEntryPoint::MetaItem); + let meta = ast::Meta::cast(parse.syntax_node())?; + // Only simple paths are allowed. + if meta.eq_token().is_some() || meta.expr().is_some() || meta.token_tree().is_some() + { + return None; + } + let path = meta.path()?; + ModPath::from_src(db, path, hygiene) }); Some(paths) } + + pub fn cfg(&self) -> Option { + if *self.path.as_ident()? == crate::name![cfg] { + self.token_tree_value().map(CfgExpr::parse) + } else { + None + } + } } pub fn collect_attrs( diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_attr_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_attr_macro.rs index 277ecd9399422..80695bc065621 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_attr_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_attr_macro.rs @@ -96,7 +96,7 @@ fn derive_attr_expand( ) -> ExpandResult { let loc = db.lookup_intern_macro_call(id); let derives = match &loc.kind { - MacroCallKind::Attr { attr_args, is_derive: true, .. } => &attr_args.0, + MacroCallKind::Attr { attr_args, .. } if loc.def.is_attribute_derive() => &attr_args.0, _ => return ExpandResult::ok(tt::Subtree::empty()), }; pseudo_derive_attr_expansion(tt, derives) diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs index 5c1a75132ee94..54706943ac4fd 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs @@ -1,11 +1,19 @@ //! Builtin derives. +use ::tt::Ident; use base_db::{CrateOrigin, LangCrateOrigin}; +use itertools::izip; +use mbe::TokenMap; +use std::collections::HashSet; +use stdx::never; use tracing::debug; use crate::tt::{self, TokenId}; use syntax::{ - ast::{self, AstNode, HasGenericParams, HasModuleItem, HasName}, + ast::{ + self, AstNode, FieldList, HasAttrs, HasGenericParams, HasModuleItem, HasName, + HasTypeBounds, PathType, + }, match_ast, }; @@ -58,10 +66,129 @@ pub fn find_builtin_derive(ident: &name::Name) -> Option BuiltinDeriveExpander::find_by_name(ident) } +enum VariantShape { + Struct(Vec), + Tuple(usize), + Unit, +} + +fn tuple_field_iterator(n: usize) -> impl Iterator { + (0..n).map(|x| Ident::new(format!("f{x}"), tt::TokenId::unspecified())) +} + +impl VariantShape { + fn as_pattern(&self, path: tt::Subtree) -> tt::Subtree { + self.as_pattern_map(path, |x| quote!(#x)) + } + + fn field_names(&self) -> Vec { + match self { + VariantShape::Struct(s) => s.clone(), + VariantShape::Tuple(n) => tuple_field_iterator(*n).collect(), + VariantShape::Unit => vec![], + } + } + + fn as_pattern_map( + &self, + path: tt::Subtree, + field_map: impl Fn(&tt::Ident) -> tt::Subtree, + ) -> tt::Subtree { + match self { + VariantShape::Struct(fields) => { + let fields = fields.iter().map(|x| { + let mapped = field_map(x); + quote! { #x : #mapped , } + }); + quote! { + #path { ##fields } + } + } + &VariantShape::Tuple(n) => { + let fields = tuple_field_iterator(n).map(|x| { + let mapped = field_map(&x); + quote! { + #mapped , + } + }); + quote! { + #path ( ##fields ) + } + } + VariantShape::Unit => path, + } + } + + fn from(value: Option, token_map: &TokenMap) -> Result { + let r = match value { + None => VariantShape::Unit, + Some(FieldList::RecordFieldList(x)) => VariantShape::Struct( + x.fields() + .map(|x| x.name()) + .map(|x| name_to_token(token_map, x)) + .collect::>()?, + ), + Some(FieldList::TupleFieldList(x)) => VariantShape::Tuple(x.fields().count()), + }; + Ok(r) + } +} + +enum AdtShape { + Struct(VariantShape), + Enum { variants: Vec<(tt::Ident, VariantShape)>, default_variant: Option }, + Union, +} + +impl AdtShape { + fn as_pattern(&self, name: &tt::Ident) -> Vec { + self.as_pattern_map(name, |x| quote!(#x)) + } + + fn field_names(&self) -> Vec> { + match self { + AdtShape::Struct(s) => { + vec![s.field_names()] + } + AdtShape::Enum { variants, .. } => { + variants.iter().map(|(_, fields)| fields.field_names()).collect() + } + AdtShape::Union => { + never!("using fields of union in derive is always wrong"); + vec![] + } + } + } + + fn as_pattern_map( + &self, + name: &tt::Ident, + field_map: impl Fn(&tt::Ident) -> tt::Subtree, + ) -> Vec { + match self { + AdtShape::Struct(s) => { + vec![s.as_pattern_map(quote! { #name }, field_map)] + } + AdtShape::Enum { variants, .. } => variants + .iter() + .map(|(v, fields)| fields.as_pattern_map(quote! { #name :: #v }, &field_map)) + .collect(), + AdtShape::Union => { + never!("pattern matching on union is always wrong"); + vec![quote! { un }] + } + } + } +} + struct BasicAdtInfo { name: tt::Ident, - /// `Some(ty)` if it's a const param of type `ty`, `None` if it's a type param. - param_types: Vec>, + shape: AdtShape, + /// first field is the name, and + /// second field is `Some(ty)` if it's a const param of type `ty`, `None` if it's a type param. + /// third fields is where bounds, if any + param_types: Vec<(tt::Subtree, Option, Option)>, + associated_types: Vec, } fn parse_adt(tt: &tt::Subtree) -> Result { @@ -75,29 +202,52 @@ fn parse_adt(tt: &tt::Subtree) -> Result { ExpandError::Other("no item found".into()) })?; let node = item.syntax(); - let (name, params) = match_ast! { + let (name, params, shape) = match_ast! { match node { - ast::Struct(it) => (it.name(), it.generic_param_list()), - ast::Enum(it) => (it.name(), it.generic_param_list()), - ast::Union(it) => (it.name(), it.generic_param_list()), + ast::Struct(it) => (it.name(), it.generic_param_list(), AdtShape::Struct(VariantShape::from(it.field_list(), &token_map)?)), + ast::Enum(it) => { + let default_variant = it.variant_list().into_iter().flat_map(|x| x.variants()).position(|x| x.attrs().any(|x| x.simple_name() == Some("default".into()))); + ( + it.name(), + it.generic_param_list(), + AdtShape::Enum { + default_variant, + variants: it.variant_list() + .into_iter() + .flat_map(|x| x.variants()) + .map(|x| Ok((name_to_token(&token_map,x.name())?, VariantShape::from(x.field_list(), &token_map)?))).collect::>()? + } + ) + }, + ast::Union(it) => (it.name(), it.generic_param_list(), AdtShape::Union), _ => { debug!("unexpected node is {:?}", node); return Err(ExpandError::Other("expected struct, enum or union".into())) }, } }; - let name = name.ok_or_else(|| { - debug!("parsed item has no name"); - ExpandError::Other("missing name".into()) - })?; - let name_token_id = - token_map.token_by_range(name.syntax().text_range()).unwrap_or_else(TokenId::unspecified); - let name_token = tt::Ident { span: name_token_id, text: name.text().into() }; + let mut param_type_set: HashSet = HashSet::new(); let param_types = params .into_iter() .flat_map(|param_list| param_list.type_or_const_params()) .map(|param| { - if let ast::TypeOrConstParam::Const(param) = param { + let name = { + let this = param.name(); + match this { + Some(x) => { + param_type_set.insert(x.to_string()); + mbe::syntax_node_to_token_tree(x.syntax()).0 + } + None => tt::Subtree::empty(), + } + }; + let bounds = match ¶m { + ast::TypeOrConstParam::Type(x) => { + x.type_bound_list().map(|x| mbe::syntax_node_to_token_tree(x.syntax()).0) + } + ast::TypeOrConstParam::Const(_) => None, + }; + let ty = if let ast::TypeOrConstParam::Const(param) = param { let ty = param .ty() .map(|ty| mbe::syntax_node_to_token_tree(ty.syntax()).0) @@ -105,27 +255,107 @@ fn parse_adt(tt: &tt::Subtree) -> Result { Some(ty) } else { None - } + }; + (name, ty, bounds) }) .collect(); - Ok(BasicAdtInfo { name: name_token, param_types }) + let is_associated_type = |p: &PathType| { + if let Some(p) = p.path() { + if let Some(parent) = p.qualifier() { + if let Some(x) = parent.segment() { + if let Some(x) = x.path_type() { + if let Some(x) = x.path() { + if let Some(pname) = x.as_single_name_ref() { + if param_type_set.contains(&pname.to_string()) { + // ::Assoc + return true; + } + } + } + } + } + if let Some(pname) = parent.as_single_name_ref() { + if param_type_set.contains(&pname.to_string()) { + // T::Assoc + return true; + } + } + } + } + false + }; + let associated_types = node + .descendants() + .filter_map(PathType::cast) + .filter(is_associated_type) + .map(|x| mbe::syntax_node_to_token_tree(x.syntax()).0) + .collect::>(); + let name_token = name_to_token(&token_map, name)?; + Ok(BasicAdtInfo { name: name_token, shape, param_types, associated_types }) +} + +fn name_to_token(token_map: &TokenMap, name: Option) -> Result { + let name = name.ok_or_else(|| { + debug!("parsed item has no name"); + ExpandError::Other("missing name".into()) + })?; + let name_token_id = + token_map.token_by_range(name.syntax().text_range()).unwrap_or_else(TokenId::unspecified); + let name_token = tt::Ident { span: name_token_id, text: name.text().into() }; + Ok(name_token) } -fn expand_simple_derive(tt: &tt::Subtree, trait_path: tt::Subtree) -> ExpandResult { +/// Given that we are deriving a trait `DerivedTrait` for a type like: +/// +/// ```ignore (only-for-syntax-highlight) +/// struct Struct<'a, ..., 'z, A, B: DeclaredTrait, C, ..., Z> where C: WhereTrait { +/// a: A, +/// b: B::Item, +/// b1: ::Item, +/// c1: ::Item, +/// c2: Option<::Item>, +/// ... +/// } +/// ``` +/// +/// create an impl like: +/// +/// ```ignore (only-for-syntax-highlight) +/// impl<'a, ..., 'z, A, B: DeclaredTrait, C, ... Z> where +/// C: WhereTrait, +/// A: DerivedTrait + B1 + ... + BN, +/// B: DerivedTrait + B1 + ... + BN, +/// C: DerivedTrait + B1 + ... + BN, +/// B::Item: DerivedTrait + B1 + ... + BN, +/// ::Item: DerivedTrait + B1 + ... + BN, +/// ... +/// { +/// ... +/// } +/// ``` +/// +/// where B1, ..., BN are the bounds given by `bounds_paths`.'. Z is a phantom type, and +/// therefore does not get bound by the derived trait. +fn expand_simple_derive( + tt: &tt::Subtree, + trait_path: tt::Subtree, + trait_body: impl FnOnce(&BasicAdtInfo) -> tt::Subtree, +) -> ExpandResult { let info = match parse_adt(tt) { Ok(info) => info, - Err(e) => return ExpandResult::with_err(tt::Subtree::empty(), e), + Err(e) => return ExpandResult::new(tt::Subtree::empty(), e), }; + let trait_body = trait_body(&info); + let mut where_block = vec![]; let (params, args): (Vec<_>, Vec<_>) = info .param_types .into_iter() - .enumerate() - .map(|(idx, param_ty)| { - let ident = tt::Leaf::Ident(tt::Ident { - span: tt::TokenId::unspecified(), - text: format!("T{idx}").into(), - }); + .map(|(ident, param_ty, bound)| { let ident_ = ident.clone(); + if let Some(b) = bound { + let ident = ident.clone(); + where_block.push(quote! { #ident : #b , }); + } if let Some(ty) = param_ty { (quote! { const #ident : #ty , }, quote! { #ident_ , }) } else { @@ -134,9 +364,16 @@ fn expand_simple_derive(tt: &tt::Subtree, trait_path: tt::Subtree) -> ExpandResu } }) .unzip(); + + where_block.extend(info.associated_types.iter().map(|x| { + let x = x.clone(); + let bound = trait_path.clone(); + quote! { #x : #bound , } + })); + let name = info.name; let expanded = quote! { - impl < ##params > #trait_path for #name < ##args > {} + impl < ##params > #trait_path for #name < ##args > where ##where_block { #trait_body } }; ExpandResult::ok(expanded) } @@ -163,7 +400,7 @@ fn copy_expand( tt: &tt::Subtree, ) -> ExpandResult { let krate = find_builtin_crate(db, id); - expand_simple_derive(tt, quote! { #krate::marker::Copy }) + expand_simple_derive(tt, quote! { #krate::marker::Copy }, |_| quote! {}) } fn clone_expand( @@ -172,7 +409,63 @@ fn clone_expand( tt: &tt::Subtree, ) -> ExpandResult { let krate = find_builtin_crate(db, id); - expand_simple_derive(tt, quote! { #krate::clone::Clone }) + expand_simple_derive(tt, quote! { #krate::clone::Clone }, |adt| { + if matches!(adt.shape, AdtShape::Union) { + let star = tt::Punct { + char: '*', + spacing: ::tt::Spacing::Alone, + span: tt::TokenId::unspecified(), + }; + return quote! { + fn clone(&self) -> Self { + #star self + } + }; + } + if matches!(&adt.shape, AdtShape::Enum { variants, .. } if variants.is_empty()) { + let star = tt::Punct { + char: '*', + spacing: ::tt::Spacing::Alone, + span: tt::TokenId::unspecified(), + }; + return quote! { + fn clone(&self) -> Self { + match #star self {} + } + }; + } + let name = &adt.name; + let patterns = adt.shape.as_pattern(name); + let exprs = adt.shape.as_pattern_map(name, |x| quote! { #x .clone() }); + let arms = patterns.into_iter().zip(exprs.into_iter()).map(|(pat, expr)| { + let fat_arrow = fat_arrow(); + quote! { + #pat #fat_arrow #expr, + } + }); + + quote! { + fn clone(&self) -> Self { + match self { + ##arms + } + } + } + }) +} + +/// This function exists since `quote! { => }` doesn't work. +fn fat_arrow() -> ::tt::Subtree { + let eq = + tt::Punct { char: '=', spacing: ::tt::Spacing::Joint, span: tt::TokenId::unspecified() }; + quote! { #eq> } +} + +/// This function exists since `quote! { && }` doesn't work. +fn and_and() -> ::tt::Subtree { + let and = + tt::Punct { char: '&', spacing: ::tt::Spacing::Joint, span: tt::TokenId::unspecified() }; + quote! { #and& } } fn default_expand( @@ -180,8 +473,38 @@ fn default_expand( id: MacroCallId, tt: &tt::Subtree, ) -> ExpandResult { - let krate = find_builtin_crate(db, id); - expand_simple_derive(tt, quote! { #krate::default::Default }) + let krate = &find_builtin_crate(db, id); + expand_simple_derive(tt, quote! { #krate::default::Default }, |adt| { + let body = match &adt.shape { + AdtShape::Struct(fields) => { + let name = &adt.name; + fields + .as_pattern_map(quote!(#name), |_| quote!(#krate::default::Default::default())) + } + AdtShape::Enum { default_variant, variants } => { + if let Some(d) = default_variant { + let (name, fields) = &variants[*d]; + let adt_name = &adt.name; + fields.as_pattern_map( + quote!(#adt_name :: #name), + |_| quote!(#krate::default::Default::default()), + ) + } else { + // FIXME: Return expand error here + quote!() + } + } + AdtShape::Union => { + // FIXME: Return expand error here + quote!() + } + }; + quote! { + fn default() -> Self { + #body + } + } + }) } fn debug_expand( @@ -189,8 +512,79 @@ fn debug_expand( id: MacroCallId, tt: &tt::Subtree, ) -> ExpandResult { - let krate = find_builtin_crate(db, id); - expand_simple_derive(tt, quote! { #krate::fmt::Debug }) + let krate = &find_builtin_crate(db, id); + expand_simple_derive(tt, quote! { #krate::fmt::Debug }, |adt| { + let for_variant = |name: String, v: &VariantShape| match v { + VariantShape::Struct(fields) => { + let for_fields = fields.iter().map(|x| { + let x_string = x.to_string(); + quote! { + .field(#x_string, & #x) + } + }); + quote! { + f.debug_struct(#name) ##for_fields .finish() + } + } + VariantShape::Tuple(n) => { + let for_fields = tuple_field_iterator(*n).map(|x| { + quote! { + .field( & #x) + } + }); + quote! { + f.debug_tuple(#name) ##for_fields .finish() + } + } + VariantShape::Unit => quote! { + f.write_str(#name) + }, + }; + if matches!(&adt.shape, AdtShape::Enum { variants, .. } if variants.is_empty()) { + let star = tt::Punct { + char: '*', + spacing: ::tt::Spacing::Alone, + span: tt::TokenId::unspecified(), + }; + return quote! { + fn fmt(&self, f: &mut #krate::fmt::Formatter) -> #krate::fmt::Result { + match #star self {} + } + }; + } + let arms = match &adt.shape { + AdtShape::Struct(fields) => { + let fat_arrow = fat_arrow(); + let name = &adt.name; + let pat = fields.as_pattern(quote!(#name)); + let expr = for_variant(name.to_string(), fields); + vec![quote! { #pat #fat_arrow #expr }] + } + AdtShape::Enum { variants, .. } => variants + .iter() + .map(|(name, v)| { + let fat_arrow = fat_arrow(); + let adt_name = &adt.name; + let pat = v.as_pattern(quote!(#adt_name :: #name)); + let expr = for_variant(name.to_string(), v); + quote! { + #pat #fat_arrow #expr , + } + }) + .collect(), + AdtShape::Union => { + // FIXME: Return expand error here + vec![] + } + }; + quote! { + fn fmt(&self, f: &mut #krate::fmt::Formatter) -> #krate::fmt::Result { + match self { + ##arms + } + } + } + }) } fn hash_expand( @@ -198,8 +592,47 @@ fn hash_expand( id: MacroCallId, tt: &tt::Subtree, ) -> ExpandResult { - let krate = find_builtin_crate(db, id); - expand_simple_derive(tt, quote! { #krate::hash::Hash }) + let krate = &find_builtin_crate(db, id); + expand_simple_derive(tt, quote! { #krate::hash::Hash }, |adt| { + if matches!(adt.shape, AdtShape::Union) { + // FIXME: Return expand error here + return quote! {}; + } + if matches!(&adt.shape, AdtShape::Enum { variants, .. } if variants.is_empty()) { + let star = tt::Punct { + char: '*', + spacing: ::tt::Spacing::Alone, + span: tt::TokenId::unspecified(), + }; + return quote! { + fn hash(&self, state: &mut H) { + match #star self {} + } + }; + } + let arms = adt.shape.as_pattern(&adt.name).into_iter().zip(adt.shape.field_names()).map( + |(pat, names)| { + let expr = { + let it = names.iter().map(|x| quote! { #x . hash(state); }); + quote! { { + ##it + } } + }; + let fat_arrow = fat_arrow(); + quote! { + #pat #fat_arrow #expr , + } + }, + ); + quote! { + fn hash(&self, state: &mut H) { + #krate::mem::discriminant(self).hash(state); + match self { + ##arms + } + } + } + }) } fn eq_expand( @@ -208,7 +641,7 @@ fn eq_expand( tt: &tt::Subtree, ) -> ExpandResult { let krate = find_builtin_crate(db, id); - expand_simple_derive(tt, quote! { #krate::cmp::Eq }) + expand_simple_derive(tt, quote! { #krate::cmp::Eq }, |_| quote! {}) } fn partial_eq_expand( @@ -217,7 +650,65 @@ fn partial_eq_expand( tt: &tt::Subtree, ) -> ExpandResult { let krate = find_builtin_crate(db, id); - expand_simple_derive(tt, quote! { #krate::cmp::PartialEq }) + expand_simple_derive(tt, quote! { #krate::cmp::PartialEq }, |adt| { + if matches!(adt.shape, AdtShape::Union) { + // FIXME: Return expand error here + return quote! {}; + } + let name = &adt.name; + + let (self_patterns, other_patterns) = self_and_other_patterns(adt, name); + let arms = izip!(self_patterns, other_patterns, adt.shape.field_names()).map( + |(pat1, pat2, names)| { + let fat_arrow = fat_arrow(); + let body = match &*names { + [] => { + quote!(true) + } + [first, rest @ ..] => { + let rest = rest.iter().map(|x| { + let t1 = Ident::new(format!("{}_self", x.text), x.span); + let t2 = Ident::new(format!("{}_other", x.text), x.span); + let and_and = and_and(); + quote!(#and_and #t1 .eq( #t2 )) + }); + let first = { + let t1 = Ident::new(format!("{}_self", first.text), first.span); + let t2 = Ident::new(format!("{}_other", first.text), first.span); + quote!(#t1 .eq( #t2 )) + }; + quote!(#first ##rest) + } + }; + quote! { ( #pat1 , #pat2 ) #fat_arrow #body , } + }, + ); + + let fat_arrow = fat_arrow(); + quote! { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + ##arms + _unused #fat_arrow false + } + } + } + }) +} + +fn self_and_other_patterns( + adt: &BasicAdtInfo, + name: &tt::Ident, +) -> (Vec, Vec) { + let self_patterns = adt.shape.as_pattern_map(name, |x| { + let t = Ident::new(format!("{}_self", x.text), x.span); + quote!(#t) + }); + let other_patterns = adt.shape.as_pattern_map(name, |x| { + let t = Ident::new(format!("{}_other", x.text), x.span); + quote!(#t) + }); + (self_patterns, other_patterns) } fn ord_expand( @@ -225,8 +716,63 @@ fn ord_expand( id: MacroCallId, tt: &tt::Subtree, ) -> ExpandResult { - let krate = find_builtin_crate(db, id); - expand_simple_derive(tt, quote! { #krate::cmp::Ord }) + let krate = &find_builtin_crate(db, id); + expand_simple_derive(tt, quote! { #krate::cmp::Ord }, |adt| { + fn compare( + krate: &tt::TokenTree, + left: tt::Subtree, + right: tt::Subtree, + rest: tt::Subtree, + ) -> tt::Subtree { + let fat_arrow1 = fat_arrow(); + let fat_arrow2 = fat_arrow(); + quote! { + match #left.cmp(&#right) { + #krate::cmp::Ordering::Equal #fat_arrow1 { + #rest + } + c #fat_arrow2 return c, + } + } + } + if matches!(adt.shape, AdtShape::Union) { + // FIXME: Return expand error here + return quote!(); + } + let left = quote!(#krate::intrinsics::discriminant_value(self)); + let right = quote!(#krate::intrinsics::discriminant_value(other)); + + let (self_patterns, other_patterns) = self_and_other_patterns(adt, &adt.name); + let arms = izip!(self_patterns, other_patterns, adt.shape.field_names()).map( + |(pat1, pat2, fields)| { + let mut body = quote!(#krate::cmp::Ordering::Equal); + for f in fields.into_iter().rev() { + let t1 = Ident::new(format!("{}_self", f.text), f.span); + let t2 = Ident::new(format!("{}_other", f.text), f.span); + body = compare(krate, quote!(#t1), quote!(#t2), body); + } + let fat_arrow = fat_arrow(); + quote! { ( #pat1 , #pat2 ) #fat_arrow #body , } + }, + ); + let fat_arrow = fat_arrow(); + let body = compare( + krate, + left, + right, + quote! { + match (self, other) { + ##arms + _unused #fat_arrow #krate::cmp::Ordering::Equal + } + }, + ); + quote! { + fn cmp(&self, other: &Self) -> #krate::cmp::Ordering { + #body + } + } + }) } fn partial_ord_expand( @@ -234,6 +780,61 @@ fn partial_ord_expand( id: MacroCallId, tt: &tt::Subtree, ) -> ExpandResult { - let krate = find_builtin_crate(db, id); - expand_simple_derive(tt, quote! { #krate::cmp::PartialOrd }) + let krate = &find_builtin_crate(db, id); + expand_simple_derive(tt, quote! { #krate::cmp::PartialOrd }, |adt| { + fn compare( + krate: &tt::TokenTree, + left: tt::Subtree, + right: tt::Subtree, + rest: tt::Subtree, + ) -> tt::Subtree { + let fat_arrow1 = fat_arrow(); + let fat_arrow2 = fat_arrow(); + quote! { + match #left.partial_cmp(&#right) { + #krate::option::Option::Some(#krate::cmp::Ordering::Equal) #fat_arrow1 { + #rest + } + c #fat_arrow2 return c, + } + } + } + if matches!(adt.shape, AdtShape::Union) { + // FIXME: Return expand error here + return quote!(); + } + let left = quote!(#krate::intrinsics::discriminant_value(self)); + let right = quote!(#krate::intrinsics::discriminant_value(other)); + + let (self_patterns, other_patterns) = self_and_other_patterns(adt, &adt.name); + let arms = izip!(self_patterns, other_patterns, adt.shape.field_names()).map( + |(pat1, pat2, fields)| { + let mut body = quote!(#krate::option::Option::Some(#krate::cmp::Ordering::Equal)); + for f in fields.into_iter().rev() { + let t1 = Ident::new(format!("{}_self", f.text), f.span); + let t2 = Ident::new(format!("{}_other", f.text), f.span); + body = compare(krate, quote!(#t1), quote!(#t2), body); + } + let fat_arrow = fat_arrow(); + quote! { ( #pat1 , #pat2 ) #fat_arrow #body , } + }, + ); + let fat_arrow = fat_arrow(); + let body = compare( + krate, + left, + right, + quote! { + match (self, other) { + ##arms + _unused #fat_arrow #krate::option::Option::Some(#krate::cmp::Ordering::Equal) + } + }, + ); + quote! { + fn partial_cmp(&self, other: &Self) -> #krate::option::Option::Option<#krate::cmp::Ordering> { + #body + } + } + }) } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs index a9c5e1488aac0..c7643bd0a18eb 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs @@ -1,9 +1,13 @@ //! Builtin macro +use std::mem; + +use ::tt::Ident; use base_db::{AnchoredPath, Edition, FileId}; use cfg::CfgExpr; use either::Either; -use mbe::{parse_exprs_with_sep, parse_to_token_tree}; +use mbe::{parse_exprs_with_sep, parse_to_token_tree, TokenMap}; +use rustc_hash::FxHashMap; use syntax::{ ast::{self, AstToken}, SmolStr, @@ -67,7 +71,7 @@ macro_rules! register_builtin { pub struct ExpandedEager { pub(crate) subtree: tt::Subtree, /// The included file ID of the include macro. - pub(crate) included_file: Option, + pub(crate) included_file: Option<(FileId, TokenMap)>, } impl ExpandedEager { @@ -90,11 +94,6 @@ register_builtin! { (module_path, ModulePath) => module_path_expand, (assert, Assert) => assert_expand, (stringify, Stringify) => stringify_expand, - (format_args, FormatArgs) => format_args_expand, - (const_format_args, ConstFormatArgs) => format_args_expand, - // format_args_nl only differs in that it adds a newline in the end, - // so we use the same stub expansion for now - (format_args_nl, FormatArgsNl) => format_args_expand, (llvm_asm, LlvmAsm) => asm_expand, (asm, Asm) => asm_expand, (global_asm, GlobalAsm) => global_asm_expand, @@ -106,6 +105,9 @@ register_builtin! { (trace_macros, TraceMacros) => trace_macros_expand, EAGER: + (format_args, FormatArgs) => format_args_expand, + (const_format_args, ConstFormatArgs) => format_args_expand, + (format_args_nl, FormatArgsNl) => format_args_nl_expand, (compile_error, CompileError) => compile_error_expand, (concat, Concat) => concat_expand, (concat_idents, ConcatIdents) => concat_idents_expand, @@ -135,9 +137,8 @@ fn line_expand( _tt: &tt::Subtree, ) -> ExpandResult { // dummy implementation for type-checking purposes - let line_num = 0; let expanded = quote! { - #line_num + 0 as u32 }; ExpandResult::ok(expanded) @@ -179,9 +180,8 @@ fn column_expand( _tt: &tt::Subtree, ) -> ExpandResult { // dummy implementation for type-checking purposes - let col_num = 0; let expanded = quote! { - #col_num + 0 as u32 }; ExpandResult::ok(expanded) @@ -234,45 +234,173 @@ fn file_expand( } fn format_args_expand( + db: &dyn ExpandDatabase, + id: MacroCallId, + tt: &tt::Subtree, +) -> ExpandResult { + format_args_expand_general(db, id, tt, "") + .map(|x| ExpandedEager { subtree: x, included_file: None }) +} + +fn format_args_nl_expand( + db: &dyn ExpandDatabase, + id: MacroCallId, + tt: &tt::Subtree, +) -> ExpandResult { + format_args_expand_general(db, id, tt, "\\n") + .map(|x| ExpandedEager { subtree: x, included_file: None }) +} + +fn format_args_expand_general( _db: &dyn ExpandDatabase, _id: MacroCallId, tt: &tt::Subtree, + end_string: &str, ) -> ExpandResult { - // We expand `format_args!("", a1, a2)` to - // ``` - // $crate::fmt::Arguments::new_v1(&[], &[ - // $crate::fmt::Argument::new(&arg1,$crate::fmt::Display::fmt), - // $crate::fmt::Argument::new(&arg2,$crate::fmt::Display::fmt), - // ]) - // ```, - // which is still not really correct, but close enough for now - let mut args = parse_exprs_with_sep(tt, ','); + let args = parse_exprs_with_sep(tt, ','); + + let expand_error = + ExpandResult::new(tt::Subtree::empty(), mbe::ExpandError::NoMatchingRule.into()); if args.is_empty() { - return ExpandResult::with_err( - tt::Subtree::empty(), - mbe::ExpandError::NoMatchingRule.into(), - ); + return expand_error; } - for arg in &mut args { + let mut key_args = FxHashMap::default(); + let mut args = args.into_iter().filter_map(|mut arg| { // Remove `key =`. if matches!(arg.token_trees.get(1), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=') { // but not with `==` - if !matches!(arg.token_trees.get(2), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=' ) + if !matches!(arg.token_trees.get(2), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=') { - arg.token_trees.drain(..2); + let key = arg.token_trees.drain(..2).next().unwrap(); + key_args.insert(key.to_string(), arg); + return None; } } + Some(arg) + }).collect::>().into_iter(); + // ^^^^^^^ we need this collect, to enforce the side effect of the filter_map closure (building the `key_args`) + let format_subtree = args.next().unwrap(); + let format_string = (|| { + let token_tree = format_subtree.token_trees.get(0)?; + match token_tree { + tt::TokenTree::Leaf(l) => match l { + tt::Leaf::Literal(l) => { + if let Some(mut text) = l.text.strip_prefix('r') { + let mut raw_sharps = String::new(); + while let Some(t) = text.strip_prefix('#') { + text = t; + raw_sharps.push('#'); + } + text = + text.strip_suffix(&raw_sharps)?.strip_prefix('"')?.strip_suffix('"')?; + Some((text, l.span, Some(raw_sharps))) + } else { + let text = l.text.strip_prefix('"')?.strip_suffix('"')?; + let span = l.span; + Some((text, span, None)) + } + } + _ => None, + }, + tt::TokenTree::Subtree(_) => None, + } + })(); + let Some((format_string, _format_string_span, raw_sharps)) = format_string else { + return expand_error; + }; + let mut format_iter = format_string.chars().peekable(); + let mut parts = vec![]; + let mut last_part = String::new(); + let mut arg_tts = vec![]; + let mut err = None; + while let Some(c) = format_iter.next() { + // Parsing the format string. See https://doc.rust-lang.org/std/fmt/index.html#syntax for the grammar and more info + match c { + '{' => { + if format_iter.peek() == Some(&'{') { + format_iter.next(); + last_part.push('{'); + continue; + } + let mut argument = String::new(); + while ![Some(&'}'), Some(&':')].contains(&format_iter.peek()) { + argument.push(match format_iter.next() { + Some(c) => c, + None => return expand_error, + }); + } + let format_spec = match format_iter.next().unwrap() { + '}' => "".to_owned(), + ':' => { + let mut s = String::new(); + while let Some(c) = format_iter.next() { + if c == '}' { + break; + } + s.push(c); + } + s + } + _ => unreachable!(), + }; + parts.push(mem::take(&mut last_part)); + let arg_tree = if argument.is_empty() { + match args.next() { + Some(x) => x, + None => { + err = Some(mbe::ExpandError::NoMatchingRule.into()); + tt::Subtree::empty() + } + } + } else if let Some(tree) = key_args.get(&argument) { + tree.clone() + } else { + // FIXME: we should pick the related substring of the `_format_string_span` as the span. You + // can use `.char_indices()` instead of `.char()` for `format_iter` to find the substring interval. + let ident = Ident::new(argument, tt::TokenId::unspecified()); + quote!(#ident) + }; + let formatter = match &*format_spec { + "?" => quote!(::core::fmt::Debug::fmt), + "" => quote!(::core::fmt::Display::fmt), + _ => { + // FIXME: implement the rest and return expand error here + quote!(::core::fmt::Display::fmt) + } + }; + arg_tts.push(quote! { ::core::fmt::Argument::new(&(#arg_tree), #formatter), }); + } + '}' => { + if format_iter.peek() == Some(&'}') { + format_iter.next(); + last_part.push('}'); + } else { + return expand_error; + } + } + _ => last_part.push(c), + } + } + last_part += end_string; + if !last_part.is_empty() { + parts.push(last_part); } - let _format_string = args.remove(0); - let arg_tts = args.into_iter().flat_map(|arg| { - quote! { #DOLLAR_CRATE::fmt::Argument::new(&(#arg), #DOLLAR_CRATE::fmt::Display::fmt), } - }.token_trees); + let part_tts = parts.into_iter().map(|x| { + let text = if let Some(raw) = &raw_sharps { + format!("r{raw}\"{}\"{raw}", x).into() + } else { + format!("\"{}\"", x).into() + }; + let l = tt::Literal { span: tt::TokenId::unspecified(), text }; + quote!(#l ,) + }); + let arg_tts = arg_tts.into_iter().flat_map(|arg| arg.token_trees); let expanded = quote! { - #DOLLAR_CRATE::fmt::Arguments::new_v1(&[], &[##arg_tts]) + ::core::fmt::Arguments::new_v1(&[##part_tts], &[##arg_tts]) }; - ExpandResult::ok(expanded) + ExpandResult { value: expanded, err } } fn asm_expand( @@ -566,16 +694,16 @@ fn include_expand( let path = parse_string(tt)?; let file_id = relative_file(db, arg_id, &path, false)?; - let subtree = - parse_to_token_tree(&db.file_text(file_id)).ok_or(mbe::ExpandError::ConversionError)?.0; - Ok((subtree, file_id)) + let (subtree, map) = + parse_to_token_tree(&db.file_text(file_id)).ok_or(mbe::ExpandError::ConversionError)?; + Ok((subtree, map, file_id)) })(); match res { - Ok((subtree, file_id)) => { - ExpandResult::ok(ExpandedEager { subtree, included_file: Some(file_id) }) + Ok((subtree, map, file_id)) => { + ExpandResult::ok(ExpandedEager { subtree, included_file: Some((file_id, map)) }) } - Err(e) => ExpandResult::with_err( + Err(e) => ExpandResult::new( ExpandedEager { subtree: tt::Subtree::empty(), included_file: None }, e, ), @@ -588,7 +716,7 @@ fn include_bytes_expand( tt: &tt::Subtree, ) -> ExpandResult { if let Err(e) = parse_string(tt) { - return ExpandResult::with_err( + return ExpandResult::new( ExpandedEager { subtree: tt::Subtree::empty(), included_file: None }, e, ); @@ -613,7 +741,7 @@ fn include_str_expand( let path = match parse_string(tt) { Ok(it) => it, Err(e) => { - return ExpandResult::with_err( + return ExpandResult::new( ExpandedEager { subtree: tt::Subtree::empty(), included_file: None }, e, ) @@ -650,7 +778,7 @@ fn env_expand( let key = match parse_string(tt) { Ok(it) => it, Err(e) => { - return ExpandResult::with_err( + return ExpandResult::new( ExpandedEager { subtree: tt::Subtree::empty(), included_file: None }, e, ) @@ -686,16 +814,16 @@ fn option_env_expand( let key = match parse_string(tt) { Ok(it) => it, Err(e) => { - return ExpandResult::with_err( + return ExpandResult::new( ExpandedEager { subtree: tt::Subtree::empty(), included_file: None }, e, ) } }; - + // FIXME: Use `DOLLAR_CRATE` when that works in eager macros. let expanded = match get_env_inner(db, arg_id, &key) { - None => quote! { #DOLLAR_CRATE::option::Option::None::<&str> }, - Some(s) => quote! { #DOLLAR_CRATE::option::Option::Some(#s) }, + None => quote! { ::core::option::Option::None::<&str> }, + Some(s) => quote! { ::core::option::Option::Some(#s) }, }; ExpandResult::ok(ExpandedEager::new(expanded)) diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs index 45572499e8426..965dfa824d8f9 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs @@ -1,22 +1,22 @@ //! Defines database & queries for macro expansion. -use std::sync::Arc; - -use base_db::{salsa, SourceDatabase}; +use base_db::{salsa, Edition, SourceDatabase}; use either::Either; use limit::Limit; use mbe::syntax_node_to_token_tree; use rustc_hash::FxHashSet; use syntax::{ ast::{self, HasAttrs, HasDocComments}, - AstNode, GreenNode, Parse, SyntaxNode, SyntaxToken, T, + AstNode, GreenNode, Parse, SyntaxError, SyntaxNode, SyntaxToken, T, }; +use triomphe::Arc; use crate::{ - ast_id_map::AstIdMap, builtin_attr_macro::pseudo_derive_attr_expansion, fixup, - hygiene::HygieneFrame, tt, BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander, - ExpandError, ExpandResult, ExpandTo, HirFileId, HirFileIdRepr, MacroCallId, MacroCallKind, - MacroCallLoc, MacroDefId, MacroDefKind, MacroFile, ProcMacroExpander, + ast_id_map::AstIdMap, builtin_attr_macro::pseudo_derive_attr_expansion, + builtin_fn_macro::EagerExpander, fixup, hygiene::HygieneFrame, tt, BuiltinAttrExpander, + BuiltinDeriveExpander, BuiltinFnLikeExpander, ExpandError, ExpandResult, ExpandTo, HirFileId, + HirFileIdRepr, MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind, MacroFile, + ProcMacroExpander, }; /// Total limit on the number of tokens produced by any macro invocation. @@ -33,6 +33,8 @@ pub enum TokenExpander { DeclarativeMacro { mac: mbe::DeclarativeMacro, def_site_token_map: mbe::TokenMap }, /// Stuff like `line!` and `file!`. Builtin(BuiltinFnLikeExpander), + /// Built-in eagerly expanded fn-like macros (`include!`, `concat!`, etc.) + BuiltinEager(EagerExpander), /// `global_allocator` and such. BuiltinAttr(BuiltinAttrExpander), /// `derive(Copy)` and such. @@ -51,6 +53,9 @@ impl TokenExpander { match self { TokenExpander::DeclarativeMacro { mac, .. } => mac.expand(tt).map_err(Into::into), TokenExpander::Builtin(it) => it.expand(db, id, tt).map_err(Into::into), + TokenExpander::BuiltinEager(it) => { + it.expand(db, id, tt).map_err(Into::into).map(|res| res.subtree) + } TokenExpander::BuiltinAttr(it) => it.expand(db, id, tt), TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt), TokenExpander::ProcMacro(_) => { @@ -66,6 +71,7 @@ impl TokenExpander { match self { TokenExpander::DeclarativeMacro { mac, .. } => mac.map_id_down(id), TokenExpander::Builtin(..) + | TokenExpander::BuiltinEager(..) | TokenExpander::BuiltinAttr(..) | TokenExpander::BuiltinDerive(..) | TokenExpander::ProcMacro(..) => id, @@ -76,6 +82,7 @@ impl TokenExpander { match self { TokenExpander::DeclarativeMacro { mac, .. } => mac.map_id_up(id), TokenExpander::Builtin(..) + | TokenExpander::BuiltinEager(..) | TokenExpander::BuiltinAttr(..) | TokenExpander::BuiltinDerive(..) | TokenExpander::ProcMacro(..) => (id, mbe::Origin::Call), @@ -90,12 +97,15 @@ pub trait ExpandDatabase: SourceDatabase { /// Main public API -- parses a hir file, not caring whether it's a real /// file or a macro expansion. #[salsa::transparent] - fn parse_or_expand(&self, file_id: HirFileId) -> Option; + fn parse_or_expand(&self, file_id: HirFileId) -> SyntaxNode; + #[salsa::transparent] + fn parse_or_expand_with_err(&self, file_id: HirFileId) -> ExpandResult>; /// Implementation for the macro case. + // This query is LRU cached fn parse_macro_expansion( &self, macro_file: MacroFile, - ) -> ExpandResult, Arc)>>; + ) -> ExpandResult<(Parse, Arc)>; /// Macro ids. That's probably the tricksiest bit in rust-analyzer, and the /// reason why we use salsa at all. @@ -119,15 +129,19 @@ pub trait ExpandDatabase: SourceDatabase { /// just fetches procedural ones. fn macro_def(&self, id: MacroDefId) -> Result, mbe::ParseError>; - /// Expand macro call to a token tree. This query is LRUed (we keep 128 or so results in memory) - fn macro_expand(&self, macro_call: MacroCallId) -> ExpandResult>>; + /// Expand macro call to a token tree. + // This query is LRU cached + fn macro_expand(&self, macro_call: MacroCallId) -> ExpandResult>; /// Special case of the previous query for procedural macros. We can't LRU /// proc macros, since they are not deterministic in general, and - /// non-determinism breaks salsa in a very, very, very bad way. @edwin0cheng - /// heroically debugged this once! + /// non-determinism breaks salsa in a very, very, very bad way. + /// @edwin0cheng heroically debugged this once! fn expand_proc_macro(&self, call: MacroCallId) -> ExpandResult; - /// Firewall query that returns the error from the `macro_expand` query. - fn macro_expand_error(&self, macro_call: MacroCallId) -> Option; + /// Firewall query that returns the errors from the `parse_macro_expansion` query. + fn parse_macro_expansion_error( + &self, + macro_call: MacroCallId, + ) -> ExpandResult>; fn hygiene_frame(&self, file_id: HirFileId) -> Arc; } @@ -159,8 +173,8 @@ pub fn expand_speculative( ); let (attr_arg, token_id) = match loc.kind { - MacroCallKind::Attr { invoc_attr_index, is_derive, .. } => { - let attr = if is_derive { + MacroCallKind::Attr { invoc_attr_index, .. } => { + let attr = if loc.def.is_attribute_derive() { // for pseudo-derive expansion we actually pass the attribute itself only ast::Attr::cast(speculative_args.clone()) } else { @@ -236,17 +250,26 @@ pub fn expand_speculative( } fn ast_id_map(db: &dyn ExpandDatabase, file_id: HirFileId) -> Arc { - let map = db.parse_or_expand(file_id).map(|it| AstIdMap::from_source(&it)).unwrap_or_default(); - Arc::new(map) + Arc::new(AstIdMap::from_source(&db.parse_or_expand(file_id))) } -fn parse_or_expand(db: &dyn ExpandDatabase, file_id: HirFileId) -> Option { +fn parse_or_expand(db: &dyn ExpandDatabase, file_id: HirFileId) -> SyntaxNode { match file_id.repr() { - HirFileIdRepr::FileId(file_id) => Some(db.parse(file_id).tree().syntax().clone()), + HirFileIdRepr::FileId(file_id) => db.parse(file_id).tree().syntax().clone(), HirFileIdRepr::MacroFile(macro_file) => { - // FIXME: Note how we convert from `Parse` to `SyntaxNode` here, - // forgetting about parse errors. - db.parse_macro_expansion(macro_file).value.map(|(it, _)| it.syntax_node()) + db.parse_macro_expansion(macro_file).value.0.syntax_node() + } + } +} + +fn parse_or_expand_with_err( + db: &dyn ExpandDatabase, + file_id: HirFileId, +) -> ExpandResult> { + match file_id.repr() { + HirFileIdRepr::FileId(file_id) => ExpandResult::ok(db.parse(file_id).to_syntax()), + HirFileIdRepr::MacroFile(macro_file) => { + db.parse_macro_expansion(macro_file).map(|(it, _)| it) } } } @@ -254,35 +277,34 @@ fn parse_or_expand(db: &dyn ExpandDatabase, file_id: HirFileId) -> Option ExpandResult, Arc)>> { +) -> ExpandResult<(Parse, Arc)> { let _p = profile::span("parse_macro_expansion"); - let mbe::ValueResult { value, err } = db.macro_expand(macro_file.macro_call_id); + let mbe::ValueResult { value: tt, err } = db.macro_expand(macro_file.macro_call_id); if let Some(err) = &err { - // Note: - // The final goal we would like to make all parse_macro success, - // such that the following log will not call anyway. - let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id); - let node = loc.kind.to_node(db); - - // collect parent information for warning log - let parents = - std::iter::successors(loc.kind.file_id().call_node(db), |it| it.file_id.call_node(db)) - .map(|n| format!("{:#}", n.value)) - .collect::>() - .join("\n"); - - tracing::debug!( - "fail on macro_parse: (reason: {:?} macro_call: {:#}) parents: {}", - err, - node.value, - parents - ); + if tracing::enabled!(tracing::Level::DEBUG) { + // Note: + // The final goal we would like to make all parse_macro success, + // such that the following log will not call anyway. + let loc = db.lookup_intern_macro_call(macro_file.macro_call_id); + let node = loc.to_node(db); + + // collect parent information for warning log + let parents = std::iter::successors(loc.kind.file_id().call_node(db), |it| { + it.file_id.call_node(db) + }) + .map(|n| format!("{:#}", n.value)) + .collect::>() + .join("\n"); + + tracing::debug!( + "fail on macro_parse: (reason: {:?} macro_call: {:#}) parents: {}", + err, + node.value, + parents + ); + } } - let tt = match value { - Some(tt) => tt, - None => return ExpandResult { value: None, err }, - }; let expand_to = macro_expand_to(db, macro_file.macro_call_id); @@ -291,7 +313,7 @@ fn parse_macro_expansion( let (parse, rev_token_map) = token_tree_to_syntax_node(&tt, expand_to); - ExpandResult { value: Some((parse, Arc::new(rev_token_map))), err } + ExpandResult { value: (parse, Arc::new(rev_token_map)), err } } fn macro_arg( @@ -339,7 +361,7 @@ fn censor_for_macro_input(loc: &MacroCallLoc, node: &SyntaxNode) -> FxHashSet return None, + MacroCallKind::Attr { .. } if loc.def.is_attribute_derive() => return None, MacroCallKind::Attr { invoc_attr_index, .. } => { cov_mark::hit!(attribute_macro_attr_censoring); ast::Item::cast(node.clone())? @@ -385,13 +407,14 @@ fn macro_def( ) -> Result, mbe::ParseError> { match id.kind { MacroDefKind::Declarative(ast_id) => { + let is_2021 = db.crate_graph()[id.krate].edition >= Edition::Edition2021; let (mac, def_site_token_map) = match ast_id.to_node(db) { ast::Macro::MacroRules(macro_rules) => { let arg = macro_rules .token_tree() .ok_or_else(|| mbe::ParseError::Expected("expected a token tree".into()))?; let (tt, def_site_token_map) = mbe::syntax_node_to_token_tree(arg.syntax()); - let mac = mbe::DeclarativeMacro::parse_macro_rules(&tt)?; + let mac = mbe::DeclarativeMacro::parse_macro_rules(&tt, is_2021)?; (mac, def_site_token_map) } ast::Macro::MacroDef(macro_def) => { @@ -399,7 +422,7 @@ fn macro_def( .body() .ok_or_else(|| mbe::ParseError::Expected("expected a token tree".into()))?; let (tt, def_site_token_map) = mbe::syntax_node_to_token_tree(arg.syntax()); - let mac = mbe::DeclarativeMacro::parse_macro2(&tt)?; + let mac = mbe::DeclarativeMacro::parse_macro2(&tt, is_2021)?; (mac, def_site_token_map) } }; @@ -412,82 +435,96 @@ fn macro_def( MacroDefKind::BuiltInDerive(expander, _) => { Ok(Arc::new(TokenExpander::BuiltinDerive(expander))) } - MacroDefKind::BuiltInEager(..) => { - // FIXME: Return a random error here just to make the types align. - // This obviously should do something real instead. - Err(mbe::ParseError::UnexpectedToken("unexpected eager macro".into())) + MacroDefKind::BuiltInEager(expander, ..) => { + Ok(Arc::new(TokenExpander::BuiltinEager(expander))) } MacroDefKind::ProcMacro(expander, ..) => Ok(Arc::new(TokenExpander::ProcMacro(expander))), } } -fn macro_expand( - db: &dyn ExpandDatabase, - id: MacroCallId, -) -> ExpandResult>> { +fn macro_expand(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult> { let _p = profile::span("macro_expand"); - let loc: MacroCallLoc = db.lookup_intern_macro_call(id); + let loc = db.lookup_intern_macro_call(id); if let Some(eager) = &loc.eager { - return ExpandResult { - value: Some(eager.arg_or_expansion.clone()), - // FIXME: There could be errors here! - err: None, - }; + return ExpandResult { value: eager.arg_or_expansion.clone(), err: eager.error.clone() }; } - let macro_arg = match db.macro_arg(id) { - Some(it) => it, - None => { - return ExpandResult::only_err(ExpandError::Other( - "Failed to lower macro args to token tree".into(), - )) - } - }; - let expander = match db.macro_def(loc.def) { Ok(it) => it, // FIXME: This is weird -- we effectively report macro *definition* // errors lazily, when we try to expand the macro. Instead, they should - // be reported at the definition site (when we construct a def map). + // be reported at the definition site when we construct a def map. + // (Note we do report them also at the definition site in the late diagnostic pass) Err(err) => { - return ExpandResult::only_err(ExpandError::Other( - format!("invalid macro definition: {err}").into(), - )) + return ExpandResult { + value: Arc::new(tt::Subtree { + delimiter: tt::Delimiter::UNSPECIFIED, + token_trees: vec![], + }), + err: Some(ExpandError::Other(format!("invalid macro definition: {err}").into())), + } } }; + let Some(macro_arg) = db.macro_arg(id) else { + return ExpandResult { + value: Arc::new( + tt::Subtree { + delimiter: tt::Delimiter::UNSPECIFIED, + token_trees: Vec::new(), + }, + ), + err: Some(ExpandError::Other( + "invalid token tree" + .into(), + )), + }; + }; let ExpandResult { value: mut tt, err } = expander.expand(db, id, ¯o_arg.0); // Set a hard limit for the expanded tt let count = tt.count(); if TOKEN_LIMIT.check(count).is_err() { - return ExpandResult::only_err(ExpandError::Other( - format!( - "macro invocation exceeds token limit: produced {} tokens, limit is {}", - count, - TOKEN_LIMIT.inner(), - ) - .into(), - )); + return ExpandResult { + value: Arc::new(tt::Subtree { + delimiter: tt::Delimiter::UNSPECIFIED, + token_trees: vec![], + }), + err: Some(ExpandError::Other( + format!( + "macro invocation exceeds token limit: produced {} tokens, limit is {}", + count, + TOKEN_LIMIT.inner(), + ) + .into(), + )), + }; } fixup::reverse_fixups(&mut tt, ¯o_arg.1, ¯o_arg.2); - ExpandResult { value: Some(Arc::new(tt)), err } + ExpandResult { value: Arc::new(tt), err } } -fn macro_expand_error(db: &dyn ExpandDatabase, macro_call: MacroCallId) -> Option { - db.macro_expand(macro_call).err +fn parse_macro_expansion_error( + db: &dyn ExpandDatabase, + macro_call_id: MacroCallId, +) -> ExpandResult> { + db.parse_macro_expansion(MacroFile { macro_call_id }) + .map(|it| it.0.errors().to_vec().into_boxed_slice()) } fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult { - let loc: MacroCallLoc = db.lookup_intern_macro_call(id); - let macro_arg = match db.macro_arg(id) { - Some(it) => it, - None => { - return ExpandResult::with_err( - tt::Subtree::empty(), - ExpandError::Other("No arguments for proc-macro".into()), - ) - } + let loc = db.lookup_intern_macro_call(id); + let Some(macro_arg) = db.macro_arg(id) else { + return ExpandResult { + value: tt::Subtree { + delimiter: tt::Delimiter::UNSPECIFIED, + token_trees: Vec::new(), + }, + err: Some(ExpandError::Other( + "invalid token tree" + .into(), + )), + }; }; let expander = match loc.def.kind { @@ -512,8 +549,7 @@ fn hygiene_frame(db: &dyn ExpandDatabase, file_id: HirFileId) -> Arc ExpandTo { - let loc: MacroCallLoc = db.lookup_intern_macro_call(id); - loc.kind.expand_to() + db.lookup_intern_macro_call(id).expand_to() } fn token_tree_to_syntax_node( diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs b/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs index aca41b11f926e..59a92ff0ab612 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs @@ -18,10 +18,9 @@ //! //! //! See the full discussion : -use std::sync::Arc; - use base_db::CrateId; -use syntax::{ted, SyntaxNode}; +use syntax::{ted, Parse, SyntaxNode}; +use triomphe::Arc; use crate::{ ast::{self, AstNode}, @@ -32,77 +31,16 @@ use crate::{ MacroCallLoc, MacroDefId, MacroDefKind, UnresolvedMacro, }; -#[derive(Debug)] -pub struct ErrorEmitted { - _private: (), -} - -pub trait ErrorSink { - fn emit(&mut self, err: ExpandError); - - fn option( - &mut self, - opt: Option, - error: impl FnOnce() -> ExpandError, - ) -> Result { - match opt { - Some(it) => Ok(it), - None => { - self.emit(error()); - Err(ErrorEmitted { _private: () }) - } - } - } - - fn option_with( - &mut self, - opt: impl FnOnce() -> Option, - error: impl FnOnce() -> ExpandError, - ) -> Result { - self.option(opt(), error) - } - - fn result(&mut self, res: Result) -> Result { - match res { - Ok(it) => Ok(it), - Err(e) => { - self.emit(e); - Err(ErrorEmitted { _private: () }) - } - } - } - - fn expand_result_option(&mut self, res: ExpandResult>) -> Result { - match (res.value, res.err) { - (None, Some(err)) => { - self.emit(err); - Err(ErrorEmitted { _private: () }) - } - (Some(value), opt_err) => { - if let Some(err) = opt_err { - self.emit(err); - } - Ok(value) - } - (None, None) => unreachable!("`ExpandResult` without value or error"), - } - } -} - -impl ErrorSink for &'_ mut dyn FnMut(ExpandError) { - fn emit(&mut self, err: ExpandError) { - self(err); - } -} - pub fn expand_eager_macro( db: &dyn ExpandDatabase, krate: CrateId, macro_call: InFile, def: MacroDefId, resolver: &dyn Fn(ModPath) -> Option, - diagnostic_sink: &mut dyn FnMut(ExpandError), -) -> Result, UnresolvedMacro> { +) -> Result>, UnresolvedMacro> { + let MacroDefKind::BuiltInEager(eager, _) = def.kind else { + panic!("called `expand_eager_macro` on non-eager macro def {def:?}") + }; let hygiene = Hygiene::new(db, macro_call.file_id); let parsed_args = macro_call .value @@ -115,60 +53,54 @@ pub fn expand_eager_macro( let expand_to = ExpandTo::from_call_site(¯o_call.value); // Note: - // When `lazy_expand` is called, its *parent* file must be already exists. - // Here we store an eager macro id for the argument expanded subtree here + // When `lazy_expand` is called, its *parent* file must already exist. + // Here we store an eager macro id for the argument expanded subtree // for that purpose. let arg_id = db.intern_macro_call(MacroCallLoc { def, krate, - eager: Some(EagerCallInfo { + eager: Some(Box::new(EagerCallInfo { arg_or_expansion: Arc::new(parsed_args.clone()), included_file: None, - }), + error: None, + })), kind: MacroCallKind::FnLike { ast_id: call_id, expand_to: ExpandTo::Expr }, }); let parsed_args = mbe::token_tree_to_syntax_node(&parsed_args, mbe::TopEntryPoint::Expr).0; - let result = match eager_macro_recur( + let ExpandResult { value, mut err } = eager_macro_recur( db, &hygiene, InFile::new(arg_id.as_file(), parsed_args.syntax_node()), krate, resolver, - diagnostic_sink, - ) { - Ok(Ok(it)) => it, - Ok(Err(err)) => return Ok(Err(err)), - Err(err) => return Err(err), + )?; + let Some(value ) = value else { + return Ok(ExpandResult { value: None, err }) + }; + let subtree = { + let mut subtree = mbe::syntax_node_to_token_tree(&value).0; + subtree.delimiter = crate::tt::Delimiter::unspecified(); + subtree }; - let subtree = to_subtree(&result); - - if let MacroDefKind::BuiltInEager(eager, _) = def.kind { - let res = eager.expand(db, arg_id, &subtree); - if let Some(err) = res.err { - diagnostic_sink(err); - } - - let loc = MacroCallLoc { - def, - krate, - eager: Some(EagerCallInfo { - arg_or_expansion: Arc::new(res.value.subtree), - included_file: res.value.included_file, - }), - kind: MacroCallKind::FnLike { ast_id: call_id, expand_to }, - }; - Ok(Ok(db.intern_macro_call(loc))) - } else { - panic!("called `expand_eager_macro` on non-eager macro def {def:?}"); + let res = eager.expand(db, arg_id, &subtree); + if err.is_none() { + err = res.err; } -} -fn to_subtree(node: &SyntaxNode) -> crate::tt::Subtree { - let mut subtree = mbe::syntax_node_to_token_tree(node).0; - subtree.delimiter = crate::tt::Delimiter::unspecified(); - subtree + let loc = MacroCallLoc { + def, + krate, + eager: Some(Box::new(EagerCallInfo { + arg_or_expansion: Arc::new(res.value.subtree), + included_file: res.value.included_file, + error: err.clone(), + })), + kind: MacroCallKind::FnLike { ast_id: call_id, expand_to }, + }; + + Ok(ExpandResult { value: Some(db.intern_macro_call(loc)), err }) } fn lazy_expand( @@ -176,7 +108,7 @@ fn lazy_expand( def: &MacroDefId, macro_call: InFile, krate: CrateId, -) -> ExpandResult>> { +) -> ExpandResult>> { let ast_id = db.ast_id_map(macro_call.file_id).ast_id(¯o_call.value); let expand_to = ExpandTo::from_call_site(¯o_call.value); @@ -186,10 +118,8 @@ fn lazy_expand( MacroCallKind::FnLike { ast_id: macro_call.with_value(ast_id), expand_to }, ); - let err = db.macro_expand_error(id); - let value = db.parse_or_expand(id.as_file()).map(|node| InFile::new(id.as_file(), node)); - - ExpandResult { value, err } + let file_id = id.as_file(); + db.parse_or_expand_with_err(file_id).map(|parse| InFile::new(file_id, parse)) } fn eager_macro_recur( @@ -198,23 +128,25 @@ fn eager_macro_recur( curr: InFile, krate: CrateId, macro_resolver: &dyn Fn(ModPath) -> Option, - mut diagnostic_sink: &mut dyn FnMut(ExpandError), -) -> Result, UnresolvedMacro> { +) -> Result>, UnresolvedMacro> { let original = curr.value.clone_for_update(); let children = original.descendants().filter_map(ast::MacroCall::cast); let mut replacements = Vec::new(); + // Note: We only report a single error inside of eager expansions + let mut error = None; + // Collect replacement for child in children { let def = match child.path().and_then(|path| ModPath::from_src(db, path, hygiene)) { Some(path) => macro_resolver(path.clone()).ok_or(UnresolvedMacro { path })?, None => { - diagnostic_sink(ExpandError::Other("malformed macro invocation".into())); + error = Some(ExpandError::Other("malformed macro invocation".into())); continue; } }; - let insert = match def.kind { + let ExpandResult { value, err } = match def.kind { MacroDefKind::BuiltInEager(..) => { let id = match expand_eager_macro( db, @@ -222,45 +154,49 @@ fn eager_macro_recur( curr.with_value(child.clone()), def, macro_resolver, - diagnostic_sink, ) { - Ok(Ok(it)) => it, - Ok(Err(err)) => return Ok(Err(err)), + Ok(it) => it, Err(err) => return Err(err), }; - db.parse_or_expand(id.as_file()) - .expect("successful macro expansion should be parseable") - .clone_for_update() + id.map(|call| { + call.map(|call| db.parse_or_expand(call.as_file()).clone_for_update()) + }) } MacroDefKind::Declarative(_) | MacroDefKind::BuiltIn(..) | MacroDefKind::BuiltInAttr(..) | MacroDefKind::BuiltInDerive(..) | MacroDefKind::ProcMacro(..) => { - let res = lazy_expand(db, &def, curr.with_value(child.clone()), krate); - let val = match diagnostic_sink.expand_result_option(res) { - Ok(it) => it, - Err(err) => return Ok(Err(err)), - }; + let ExpandResult { value, err } = + lazy_expand(db, &def, curr.with_value(child.clone()), krate); // replace macro inside - let hygiene = Hygiene::new(db, val.file_id); - match eager_macro_recur(db, &hygiene, val, krate, macro_resolver, diagnostic_sink) { - Ok(Ok(it)) => it, - Ok(Err(err)) => return Ok(Err(err)), - Err(err) => return Err(err), - } + let hygiene = Hygiene::new(db, value.file_id); + let ExpandResult { value, err: error } = eager_macro_recur( + db, + &hygiene, + // FIXME: We discard parse errors here + value.map(|it| it.syntax_node()), + krate, + macro_resolver, + )?; + let err = if err.is_none() { error } else { err }; + ExpandResult { value, err } } }; - + if err.is_some() { + error = err; + } // check if the whole original syntax is replaced if child.syntax() == &original { - return Ok(Ok(insert)); + return Ok(ExpandResult { value, err: error }); } - replacements.push((child, insert)); + if let Some(insert) = value { + replacements.push((child, insert)); + } } replacements.into_iter().rev().for_each(|(old, new)| ted::replace(old.syntax(), new)); - Ok(Ok(original)) + Ok(ExpandResult { value: Some(original), err: error }) } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs b/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs index b273f21768c68..00796e7c0dbb0 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs @@ -14,7 +14,7 @@ use tt::token_id::Subtree; /// The result of calculating fixes for a syntax node -- a bunch of changes /// (appending to and replacing nodes), the information that is needed to /// reverse those changes afterwards, and a token map. -#[derive(Debug)] +#[derive(Debug, Default)] pub(crate) struct SyntaxFixups { pub(crate) append: FxHashMap>, pub(crate) replace: FxHashMap>, @@ -24,7 +24,7 @@ pub(crate) struct SyntaxFixups { } /// This is the information needed to reverse the fixups. -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, Default, PartialEq, Eq)] pub struct SyntaxFixupUndoInfo { original: Vec, } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs b/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs index 2eb56fc9e8b26..10f8fe9cec428 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs @@ -2,8 +2,6 @@ //! //! Specifically, `ast` + `Hygiene` allows you to create a `Name`. Note that, at //! this moment, this is horribly incomplete and handles only `$crate`. -use std::sync::Arc; - use base_db::CrateId; use db::TokenExpander; use either::Either; @@ -12,6 +10,7 @@ use syntax::{ ast::{self, HasDocComments}, AstNode, SyntaxKind, SyntaxNode, TextRange, TextSize, }; +use triomphe::Arc; use crate::{ db::{self, ExpandDatabase}, @@ -200,8 +199,14 @@ fn make_hygiene_info( }); let macro_def = db.macro_def(loc.def).ok()?; - let (_, exp_map) = db.parse_macro_expansion(macro_file).value?; - let macro_arg = db.macro_arg(macro_file.macro_call_id)?; + let (_, exp_map) = db.parse_macro_expansion(macro_file).value; + let macro_arg = db.macro_arg(macro_file.macro_call_id).unwrap_or_else(|| { + Arc::new(( + tt::Subtree { delimiter: tt::Delimiter::UNSPECIFIED, token_trees: Vec::new() }, + Default::default(), + Default::default(), + )) + }); Some(HygieneInfo { file: macro_file, diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs index 5e99eacc1b619..c8373778d32b0 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs @@ -20,11 +20,13 @@ pub mod mod_path; pub mod attrs; mod fixup; +use mbe::TokenMap; pub use mbe::{Origin, ValueResult}; use ::tt::token_id as tt; +use triomphe::Arc; -use std::{fmt, hash::Hash, iter, sync::Arc}; +use std::{fmt, hash::Hash, iter}; use base_db::{ impl_intern_key, @@ -51,11 +53,11 @@ use crate::{ pub type ExpandResult = ValueResult; -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone, Hash)] pub enum ExpandError { UnresolvedProcMacro(CrateId), Mbe(mbe::ExpandError), - RecursionOverflowPosioned, + RecursionOverflowPoisoned, Other(Box), } @@ -70,7 +72,7 @@ impl fmt::Display for ExpandError { match self { ExpandError::UnresolvedProcMacro(_) => f.write_str("unresolved proc-macro"), ExpandError::Mbe(it) => it.fmt(f), - ExpandError::RecursionOverflowPosioned => { + ExpandError::RecursionOverflowPoisoned => { f.write_str("overflow expanding the original macro") } ExpandError::Other(it) => f.write_str(it), @@ -113,7 +115,7 @@ impl_intern_key!(MacroCallId); pub struct MacroCallLoc { pub def: MacroDefId, pub(crate) krate: CrateId, - eager: Option, + eager: Option>, pub kind: MacroCallKind, } @@ -139,7 +141,8 @@ pub enum MacroDefKind { struct EagerCallInfo { /// NOTE: This can be *either* the expansion result, *or* the argument to the eager macro! arg_or_expansion: Arc, - included_file: Option, + included_file: Option<(FileId, TokenMap)>, + error: Option, } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -166,8 +169,6 @@ pub enum MacroCallKind { /// Outer attributes are counted first, then inner attributes. This does not support /// out-of-line modules, which may have attributes spread across 2 files! invoc_attr_index: AttrId, - /// Whether this attribute is the `#[derive]` attribute. - is_derive: bool, }, } @@ -205,8 +206,8 @@ impl HirFileId { HirFileIdRepr::FileId(id) => break id, HirFileIdRepr::MacroFile(MacroFile { macro_call_id }) => { let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_call_id); - file_id = match loc.eager { - Some(EagerCallInfo { included_file: Some(file), .. }) => file.into(), + file_id = match loc.eager.as_deref() { + Some(&EagerCallInfo { included_file: Some((file, _)), .. }) => file.into(), _ => loc.kind.file_id(), }; } @@ -230,18 +231,17 @@ impl HirFileId { pub fn call_node(self, db: &dyn db::ExpandDatabase) -> Option> { let macro_file = self.macro_file()?; let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id); - Some(loc.kind.to_node(db)) + Some(loc.to_node(db)) } /// If this is a macro call, returns the syntax node of the very first macro call this file resides in. pub fn original_call_node(self, db: &dyn db::ExpandDatabase) -> Option<(FileId, SyntaxNode)> { - let mut call = - db.lookup_intern_macro_call(self.macro_file()?.macro_call_id).kind.to_node(db); + let mut call = db.lookup_intern_macro_call(self.macro_file()?.macro_call_id).to_node(db); loop { match call.file_id.repr() { HirFileIdRepr::FileId(file_id) => break Some((file_id, call.value)), HirFileIdRepr::MacroFile(MacroFile { macro_call_id }) => { - call = db.lookup_intern_macro_call(macro_call_id).kind.to_node(db); + call = db.lookup_intern_macro_call(macro_call_id).to_node(db); } } } @@ -255,8 +255,14 @@ impl HirFileId { let arg_tt = loc.kind.arg(db)?; let macro_def = db.macro_def(loc.def).ok()?; - let (parse, exp_map) = db.parse_macro_expansion(macro_file).value?; - let macro_arg = db.macro_arg(macro_file.macro_call_id)?; + let (parse, exp_map) = db.parse_macro_expansion(macro_file).value; + let macro_arg = db.macro_arg(macro_file.macro_call_id).unwrap_or_else(|| { + Arc::new(( + tt::Subtree { delimiter: tt::Delimiter::UNSPECIFIED, token_trees: Vec::new() }, + Default::default(), + Default::default(), + )) + }); let def = loc.def.ast_id().left().and_then(|id| { let def_tt = match id.to_node(db) { @@ -298,7 +304,7 @@ impl HirFileId { let macro_file = self.macro_file()?; let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id); let attr = match loc.def.kind { - MacroDefKind::BuiltInDerive(..) => loc.kind.to_node(db), + MacroDefKind::BuiltInDerive(..) => loc.to_node(db), _ => return None, }; Some(attr.with_value(ast::Attr::cast(attr.value.clone())?)) @@ -319,7 +325,7 @@ impl HirFileId { match self.macro_file() { Some(macro_file) => { let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id); - matches!(loc.eager, Some(EagerCallInfo { included_file: Some(_), .. })) + matches!(loc.eager.as_deref(), Some(EagerCallInfo { included_file: Some(..), .. })) } _ => false, } @@ -342,7 +348,7 @@ impl HirFileId { match self.macro_file() { Some(macro_file) => { let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id); - matches!(loc.kind, MacroCallKind::Attr { is_derive: true, .. }) + loc.def.is_attribute_derive() } None => false, } @@ -413,22 +419,15 @@ impl MacroDefId { MacroDefKind::BuiltInAttr(..) | MacroDefKind::ProcMacro(_, ProcMacroKind::Attr, _) ) } -} - -// FIXME: attribute indices do not account for nested `cfg_attr` -impl MacroCallKind { - /// Returns the file containing the macro invocation. - fn file_id(&self) -> HirFileId { - match *self { - MacroCallKind::FnLike { ast_id: InFile { file_id, .. }, .. } - | MacroCallKind::Derive { ast_id: InFile { file_id, .. }, .. } - | MacroCallKind::Attr { ast_id: InFile { file_id, .. }, .. } => file_id, - } + pub fn is_attribute_derive(&self) -> bool { + matches!(self.kind, MacroDefKind::BuiltInAttr(expander, ..) if expander.is_derive()) } +} +impl MacroCallLoc { pub fn to_node(&self, db: &dyn db::ExpandDatabase) -> InFile { - match self { + match self.kind { MacroCallKind::FnLike { ast_id, .. } => { ast_id.with_value(ast_id.to_node(db).syntax().clone()) } @@ -444,23 +443,49 @@ impl MacroCallKind { .unwrap_or_else(|| it.syntax().clone()) }) } - MacroCallKind::Attr { ast_id, is_derive: true, invoc_attr_index, .. } => { - // FIXME: handle `cfg_attr` - ast_id.with_value(ast_id.to_node(db)).map(|it| { - it.doc_comments_and_attrs() - .nth(invoc_attr_index.ast_index()) - .and_then(|it| match it { - Either::Left(attr) => Some(attr.syntax().clone()), - Either::Right(_) => None, - }) - .unwrap_or_else(|| it.syntax().clone()) - }) + MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => { + if self.def.is_attribute_derive() { + // FIXME: handle `cfg_attr` + ast_id.with_value(ast_id.to_node(db)).map(|it| { + it.doc_comments_and_attrs() + .nth(invoc_attr_index.ast_index()) + .and_then(|it| match it { + Either::Left(attr) => Some(attr.syntax().clone()), + Either::Right(_) => None, + }) + .unwrap_or_else(|| it.syntax().clone()) + }) + } else { + ast_id.with_value(ast_id.to_node(db).syntax().clone()) + } } - MacroCallKind::Attr { ast_id, .. } => { - ast_id.with_value(ast_id.to_node(db).syntax().clone()) + } + } + + fn expand_to(&self) -> ExpandTo { + match self.kind { + MacroCallKind::FnLike { expand_to, .. } => expand_to, + MacroCallKind::Derive { .. } => ExpandTo::Items, + MacroCallKind::Attr { .. } if self.def.is_attribute_derive() => ExpandTo::Statements, + MacroCallKind::Attr { .. } => { + // is this always correct? + ExpandTo::Items } } } +} + +// FIXME: attribute indices do not account for nested `cfg_attr` + +impl MacroCallKind { + /// Returns the file containing the macro invocation. + fn file_id(&self) -> HirFileId { + match *self { + MacroCallKind::FnLike { ast_id: InFile { file_id, .. }, .. } + | MacroCallKind::Derive { ast_id: InFile { file_id, .. }, .. } + | MacroCallKind::Attr { ast_id: InFile { file_id, .. }, .. } => file_id, + } + } /// Returns the original file range that best describes the location of this macro call. /// @@ -538,15 +563,6 @@ impl MacroCallKind { MacroCallKind::Attr { ast_id, .. } => Some(ast_id.to_node(db).syntax().clone()), } } - - fn expand_to(&self) -> ExpandTo { - match self { - MacroCallKind::FnLike { expand_to, .. } => *expand_to, - MacroCallKind::Derive { .. } => ExpandTo::Items, - MacroCallKind::Attr { is_derive: true, .. } => ExpandTo::Statements, - MacroCallKind::Attr { .. } => ExpandTo::Items, // is this always correct? - } - } } impl MacroCallId { @@ -610,7 +626,7 @@ impl ExpansionInfo { let token_range = token.value.text_range(); match &loc.kind { - MacroCallKind::Attr { attr_args, invoc_attr_index, is_derive, .. } => { + MacroCallKind::Attr { attr_args, invoc_attr_index, .. } => { // FIXME: handle `cfg_attr` let attr = item .doc_comments_and_attrs() @@ -626,7 +642,8 @@ impl ExpansionInfo { token.value.text_range().checked_sub(attr_input_start)?; // shift by the item's tree's max id let token_id = attr_args.1.token_by_range(relative_range)?; - let token_id = if *is_derive { + + let token_id = if loc.def.is_attribute_derive() { // we do not shift for `#[derive]`, as we only need to downmap the derive attribute tokens token_id } else { @@ -677,20 +694,31 @@ impl ExpansionInfo { let call_id = self.expanded.file_id.macro_file()?.macro_call_id; let loc = db.lookup_intern_macro_call(call_id); + if let Some((file, map)) = loc.eager.and_then(|e| e.included_file) { + // Special case: map tokens from `include!` expansions to the included file + let range = map.first_range_by_token(token_id, token.value.kind())?; + let source = db.parse(file); + + let token = source.syntax_node().covering_element(range).into_token()?; + + return Some((InFile::new(file.into(), token), Origin::Call)); + } + // Attributes are a bit special for us, they have two inputs, the input tokentree and the annotated item. let (token_map, tt) = match &loc.kind { - MacroCallKind::Attr { attr_args, is_derive: true, .. } => { - (&attr_args.1, self.attr_input_or_mac_def.clone()?.syntax().cloned()) - } MacroCallKind::Attr { attr_args, .. } => { - // try unshifting the the token id, if unshifting fails, the token resides in the non-item attribute input - // note that the `TokenExpander::map_id_up` earlier only unshifts for declarative macros, so we don't double unshift with this - match self.macro_arg_shift.unshift(token_id) { - Some(unshifted) => { - token_id = unshifted; - (&attr_args.1, self.attr_input_or_mac_def.clone()?.syntax().cloned()) + if loc.def.is_attribute_derive() { + (&attr_args.1, self.attr_input_or_mac_def.clone()?.syntax().cloned()) + } else { + // try unshifting the token id, if unshifting fails, the token resides in the non-item attribute input + // note that the `TokenExpander::map_id_up` earlier only unshifts for declarative macros, so we don't double unshift with this + match self.macro_arg_shift.unshift(token_id) { + Some(unshifted) => { + token_id = unshifted; + (&attr_args.1, self.attr_input_or_mac_def.clone()?.syntax().cloned()) + } + None => (&self.macro_arg.1, self.arg.clone()), } - None => (&self.macro_arg.1, self.arg.clone()), } } _ => match origin { @@ -718,7 +746,7 @@ pub type AstId = InFile>; impl AstId { pub fn to_node(&self, db: &dyn db::ExpandDatabase) -> N { - let root = db.parse_or_expand(self.file_id).unwrap(); + let root = db.parse_or_expand(self.file_id); db.ast_id_map(self.file_id).get(self.value).to_node(&root) } } @@ -754,7 +782,7 @@ impl InFile { } pub fn file_syntax(&self, db: &dyn db::ExpandDatabase) -> SyntaxNode { - db.parse_or_expand(self.file_id).expect("source created from invalid file") + db.parse_or_expand(self.file_id) } } @@ -950,6 +978,7 @@ fn ascend_node_border_tokens( let first_token = |node: &SyntaxNode| skip_trivia_token(node.first_token()?, Direction::Next); let last_token = |node: &SyntaxNode| skip_trivia_token(node.last_token()?, Direction::Prev); + // FIXME: Once the token map rewrite is done, this shouldnt need to rely on syntax nodes and tokens anymore let first = first_token(node)?; let last = last_token(node)?; let first = ascend_call_token(db, &expansion, InFile::new(file_id, first))?; @@ -977,6 +1006,7 @@ impl InFile { self.value.syntax().descendants().filter_map(T::cast).map(move |n| self.with_value(n)) } + // FIXME: this should return `Option>` pub fn original_ast_node(self, db: &dyn db::ExpandDatabase) -> Option> { // This kind of upmapping can only be achieved in attribute expanded files, // as we don't have node inputs otherwise and therefore can't find an `N` node in the input diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs index e9393cc89aedf..47a8ab7de77f9 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs @@ -1,7 +1,7 @@ //! A lowering for `use`-paths (more generally, paths without angle-bracketed segments). use std::{ - fmt::{self, Display}, + fmt::{self, Display as _}, iter, }; @@ -24,6 +24,12 @@ pub struct ModPath { #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct UnescapedModPath<'a>(&'a ModPath); +impl<'a> UnescapedModPath<'a> { + pub fn display(&'a self, db: &'a dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a { + UnescapedDisplay { db, path: self } + } +} + #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum PathKind { Plain, @@ -110,52 +116,30 @@ impl ModPath { UnescapedModPath(self) } - fn _fmt(&self, f: &mut fmt::Formatter<'_>, escaped: bool) -> fmt::Result { - let mut first_segment = true; - let mut add_segment = |s| -> fmt::Result { - if !first_segment { - f.write_str("::")?; - } - first_segment = false; - f.write_str(s)?; - Ok(()) - }; - match self.kind { - PathKind::Plain => {} - PathKind::Super(0) => add_segment("self")?, - PathKind::Super(n) => { - for _ in 0..n { - add_segment("super")?; - } - } - PathKind::Crate => add_segment("crate")?, - PathKind::Abs => add_segment("")?, - PathKind::DollarCrate(_) => add_segment("$crate")?, - } - for segment in &self.segments { - if !first_segment { - f.write_str("::")?; - } - first_segment = false; - if escaped { - segment.fmt(f)? - } else { - segment.unescaped().fmt(f)? - }; - } - Ok(()) + pub fn display<'a>(&'a self, db: &'a dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a { + Display { db, path: self } } } -impl Display for ModPath { +struct Display<'a> { + db: &'a dyn ExpandDatabase, + path: &'a ModPath, +} + +impl<'a> fmt::Display for Display<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self._fmt(f, true) + display_fmt_path(self.db, self.path, f, true) } } -impl<'a> Display for UnescapedModPath<'a> { +struct UnescapedDisplay<'a> { + db: &'a dyn ExpandDatabase, + path: &'a UnescapedModPath<'a>, +} + +impl<'a> fmt::Display for UnescapedDisplay<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0._fmt(f, false) + display_fmt_path(self.db, self.path.0, f, false) } } @@ -164,6 +148,46 @@ impl From for ModPath { ModPath::from_segments(PathKind::Plain, iter::once(name)) } } +fn display_fmt_path( + db: &dyn ExpandDatabase, + path: &ModPath, + f: &mut fmt::Formatter<'_>, + escaped: bool, +) -> fmt::Result { + let mut first_segment = true; + let mut add_segment = |s| -> fmt::Result { + if !first_segment { + f.write_str("::")?; + } + first_segment = false; + f.write_str(s)?; + Ok(()) + }; + match path.kind { + PathKind::Plain => {} + PathKind::Super(0) => add_segment("self")?, + PathKind::Super(n) => { + for _ in 0..n { + add_segment("super")?; + } + } + PathKind::Crate => add_segment("crate")?, + PathKind::Abs => add_segment("")?, + PathKind::DollarCrate(_) => add_segment("$crate")?, + } + for segment in &path.segments { + if !first_segment { + f.write_str("::")?; + } + first_segment = false; + if escaped { + segment.display(db).fmt(f)?; + } else { + segment.unescaped().display(db).fmt(f)?; + } + } + Ok(()) +} fn convert_path( db: &dyn ExpandDatabase, diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs index c3462beac73c5..f8dbb842775c3 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs @@ -24,27 +24,6 @@ enum Repr { TupleField(usize), } -impl fmt::Display for Name { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match &self.0 { - Repr::Text(text) => fmt::Display::fmt(&text, f), - Repr::TupleField(idx) => fmt::Display::fmt(&idx, f), - } - } -} - -impl<'a> fmt::Display for UnescapedName<'a> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match &self.0 .0 { - Repr::Text(text) => { - let text = text.strip_prefix("r#").unwrap_or(text); - fmt::Display::fmt(&text, f) - } - Repr::TupleField(idx) => fmt::Display::fmt(&idx, f), - } - } -} - 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. @@ -60,6 +39,11 @@ impl<'a> UnescapedName<'a> { Repr::TupleField(it) => SmolStr::new(it.to_string()), } } + + pub fn display(&'a self, db: &dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a { + _ = db; + UnescapedDisplay { name: self } + } } impl Name { @@ -78,7 +62,7 @@ impl Name { Self::new_text(lt.text().into()) } - /// Shortcut to create inline plain text name + /// Shortcut to create inline plain text name. Panics if `text.len() > 22` const fn new_inline(text: &str) -> Name { Name::new_text(SmolStr::new_inline(text)) } @@ -112,6 +96,17 @@ impl Name { Name::new_inline("[missing name]") } + /// Generates a new name which is only equal to itself, by incrementing a counter. Due + /// its implementation, it should not be used in things that salsa considers, like + /// type names or field names, and it should be only used in names of local variables + /// and labels and similar things. + pub fn generate_new_name() -> Name { + use std::sync::atomic::{AtomicUsize, Ordering}; + static CNT: AtomicUsize = AtomicUsize::new(0); + let c = CNT.fetch_add(1, Ordering::Relaxed); + Name::new_text(format!("{c}").into()) + } + /// Returns the tuple index this name represents if it is a tuple field. pub fn as_tuple_index(&self) -> Option { match self.0 { @@ -156,6 +151,40 @@ impl Name { Repr::TupleField(_) => false, } } + + pub fn display<'a>(&'a self, db: &dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a { + _ = db; + Display { name: self } + } +} + +struct Display<'a> { + name: &'a Name, +} + +impl<'a> fmt::Display for Display<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match &self.name.0 { + Repr::Text(text) => fmt::Display::fmt(&text, f), + Repr::TupleField(idx) => fmt::Display::fmt(&idx, f), + } + } +} + +struct UnescapedDisplay<'a> { + name: &'a UnescapedName<'a>, +} + +impl<'a> fmt::Display for UnescapedDisplay<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match &self.name.0 .0 { + Repr::Text(text) => { + let text = text.strip_prefix("r#").unwrap_or(text); + fmt::Display::fmt(&text, f) + } + Repr::TupleField(idx) => fmt::Display::fmt(&idx, f), + } + } } pub trait AsName { @@ -337,18 +366,24 @@ pub mod known { crate_type, derive, global_allocator, + no_core, + no_std, test, test_case, recursion_limit, feature, // known methods of lang items call_once, + call_mut, + call, eq, ne, ge, gt, le, lt, + // known fields of lang items + pieces, // lang items add_assign, add, @@ -363,6 +398,7 @@ pub mod known { deref, div_assign, div, + drop, fn_mut, fn_once, future_trait, diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs index d758e9302cd87..c9539210abf65 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs @@ -7,20 +7,23 @@ use crate::{db::ExpandDatabase, tt, ExpandError, ExpandResult}; #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] pub struct ProcMacroExpander { - proc_macro_id: Option, + proc_macro_id: ProcMacroId, } +const DUMMY_ID: u32 = !0; + impl ProcMacroExpander { pub fn new(proc_macro_id: ProcMacroId) -> Self { - Self { proc_macro_id: Some(proc_macro_id) } + assert_ne!(proc_macro_id.0, DUMMY_ID); + Self { proc_macro_id } } pub fn dummy() -> Self { - Self { proc_macro_id: None } + Self { proc_macro_id: ProcMacroId(DUMMY_ID) } } pub fn is_dummy(&self) -> bool { - self.proc_macro_id.is_none() + self.proc_macro_id.0 == DUMMY_ID } pub fn expand( @@ -32,33 +35,37 @@ impl ProcMacroExpander { attr_arg: Option<&tt::Subtree>, ) -> ExpandResult { match self.proc_macro_id { - Some(id) => { - let krate_graph = db.crate_graph(); - let proc_macros = match &krate_graph[def_crate].proc_macro { - Ok(proc_macros) => proc_macros, - Err(_) => { + ProcMacroId(DUMMY_ID) => { + ExpandResult::new(tt::Subtree::empty(), ExpandError::UnresolvedProcMacro(def_crate)) + } + ProcMacroId(id) => { + let proc_macros = db.proc_macros(); + let proc_macros = match proc_macros.get(&def_crate) { + Some(Ok(proc_macros)) => proc_macros, + Some(Err(_)) | None => { never!("Non-dummy expander even though there are no proc macros"); - return ExpandResult::with_err( + return ExpandResult::new( tt::Subtree::empty(), ExpandError::Other("Internal error".into()), ); } }; - let proc_macro = match proc_macros.get(id.0 as usize) { + let proc_macro = match proc_macros.get(id as usize) { Some(proc_macro) => proc_macro, None => { never!( "Proc macro index out of bounds: the length is {} but the index is {}", proc_macros.len(), - id.0 + id ); - return ExpandResult::with_err( + return ExpandResult::new( tt::Subtree::empty(), ExpandError::Other("Internal error".into()), ); } }; + let krate_graph = db.crate_graph(); // Proc macros have access to the environment variables of the invoking crate. let env = &krate_graph[calling_crate].env; match proc_macro.expander.expand(tt, attr_arg, env) { @@ -74,17 +81,12 @@ impl ProcMacroExpander { } } ProcMacroExpansionError::System(text) - | ProcMacroExpansionError::Panic(text) => ExpandResult::with_err( - tt::Subtree::empty(), - ExpandError::Other(text.into()), - ), + | ProcMacroExpansionError::Panic(text) => { + ExpandResult::new(tt::Subtree::empty(), ExpandError::Other(text.into())) + } }, } } - None => ExpandResult::with_err( - tt::Subtree::empty(), - ExpandError::UnresolvedProcMacro(def_crate), - ), } } } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/quote.rs b/src/tools/rust-analyzer/crates/hir-expand/src/quote.rs index 63586f9daf069..ab3809abc7a26 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/quote.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/quote.rs @@ -162,6 +162,12 @@ impl ToTokenTree for crate::tt::TokenTree { } } +impl ToTokenTree for &crate::tt::TokenTree { + fn to_token(self) -> crate::tt::TokenTree { + self.clone() + } +} + impl ToTokenTree for crate::tt::Subtree { fn to_token(self) -> crate::tt::TokenTree { self.into() diff --git a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml index 9b3296df2508a..6ca0dbb850399 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml @@ -15,7 +15,7 @@ doctest = false cov-mark = "2.0.0-pre.1" itertools = "0.10.5" arrayvec = "0.7.2" -bitflags = "1.3.2" +bitflags = "2.1.0" smallvec.workspace = true ena = "0.14.0" either = "1.7.0" @@ -28,6 +28,8 @@ chalk-recursive = { version = "0.89.0", default-features = false } chalk-derive = "0.89.0" la-arena = { version = "0.3.0", path = "../../lib/la-arena" } once_cell = "1.17.0" +triomphe.workspace = true +nohash-hasher.workspace = true typed-arena = "2.0.1" rustc_index = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_index", default-features = false } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs index 58744dd0c0f98..f5b3f176b12e5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs @@ -3,12 +3,11 @@ //! reference to a type with the field `bar`. This is an approximation of the //! logic in rustc (which lives in rustc_hir_analysis/check/autoderef.rs). -use std::sync::Arc; - use chalk_ir::cast::Cast; use hir_def::lang_item::LangItem; use hir_expand::name::name; use limit::Limit; +use triomphe::Arc; use crate::{ db::HirDatabase, infer::unify::InferenceTable, Canonical, Goal, Interner, ProjectionTyExt, @@ -23,6 +22,21 @@ pub(crate) enum AutoderefKind { Overloaded, } +pub fn autoderef( + db: &dyn HirDatabase, + env: Arc, + ty: Canonical, +) -> impl Iterator> + '_ { + let mut table = InferenceTable::new(db, env); + let ty = table.instantiate_canonical(ty); + let mut autoderef = Autoderef::new(&mut table, ty); + let mut v = Vec::new(); + while let Some((ty, _steps)) = autoderef.next() { + v.push(autoderef.table.canonicalize(ty).value); + } + v.into_iter() +} + #[derive(Debug)] pub(crate) struct Autoderef<'a, 'db> { pub(crate) table: &'a mut InferenceTable<'db>, @@ -76,49 +90,43 @@ pub(crate) fn autoderef_step( table: &mut InferenceTable<'_>, ty: Ty, ) -> Option<(AutoderefKind, Ty)> { - if let Some(derefed) = builtin_deref(&ty) { + if let Some(derefed) = builtin_deref(table, &ty, false) { Some((AutoderefKind::Builtin, table.resolve_ty_shallow(derefed))) } else { Some((AutoderefKind::Overloaded, deref_by_trait(table, ty)?)) } } -// FIXME: replace uses of this with Autoderef above -pub fn autoderef( - db: &dyn HirDatabase, - env: Arc, - ty: Canonical, -) -> impl Iterator> + '_ { - let mut table = InferenceTable::new(db, env); - let ty = table.instantiate_canonical(ty); - let mut autoderef = Autoderef::new(&mut table, ty); - let mut v = Vec::new(); - while let Some((ty, _steps)) = autoderef.next() { - v.push(autoderef.table.canonicalize(ty).value); - } - v.into_iter() -} - -pub(crate) fn deref(table: &mut InferenceTable<'_>, ty: Ty) -> Option { - let _p = profile::span("deref"); - autoderef_step(table, ty).map(|(_, ty)| ty) -} - -fn builtin_deref(ty: &Ty) -> Option<&Ty> { +pub(crate) fn builtin_deref<'ty>( + table: &mut InferenceTable<'_>, + ty: &'ty Ty, + explicit: bool, +) -> Option<&'ty Ty> { match ty.kind(Interner) { - TyKind::Ref(.., ty) | TyKind::Raw(.., ty) => Some(ty), + TyKind::Ref(.., ty) => Some(ty), + // FIXME: Maybe accept this but diagnose if its not explicit? + TyKind::Raw(.., ty) if explicit => Some(ty), + &TyKind::Adt(chalk_ir::AdtId(adt), ref substs) => { + if crate::lang_items::is_box(table.db, adt) { + substs.at(Interner, 0).ty(Interner) + } else { + None + } + } _ => None, } } -fn deref_by_trait(table: &mut InferenceTable<'_>, ty: Ty) -> Option { +pub(crate) fn deref_by_trait( + table @ &mut InferenceTable { db, .. }: &mut InferenceTable<'_>, + ty: Ty, +) -> Option { let _p = profile::span("deref_by_trait"); if table.resolve_ty_shallow(&ty).inference_var(Interner).is_some() { // don't try to deref unknown variables return None; } - let db = table.db; let deref_trait = db.lang_item(table.trait_env.krate, LangItem::Deref).and_then(|l| l.as_trait())?; let target = db.trait_data(deref_trait).associated_type_by_name(&name![Target])?; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs b/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs index 03e9443599d8c..eec57ba3f80f1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs @@ -18,7 +18,6 @@ use crate::{ consteval::unknown_const_as_generic, db::HirDatabase, infer::unify::InferenceTable, primitive, to_assoc_type_id, to_chalk_trait_id, utils::generics, Binders, BoundVar, CallableSig, GenericArg, Interner, ProjectionTy, Substitution, TraitRef, Ty, TyDefId, TyExt, TyKind, - ValueTyDefId, }; #[derive(Debug, Clone, PartialEq, Eq)] @@ -195,6 +194,19 @@ impl TyBuilder<()> { params.placeholder_subst(db) } + pub fn unknown_subst(db: &dyn HirDatabase, def: impl Into) -> Substitution { + let params = generics(db.upcast(), def.into()); + Substitution::from_iter( + Interner, + params.iter_id().map(|id| match id { + either::Either::Left(_) => TyKind::Error.intern(Interner).cast(Interner), + either::Either::Right(id) => { + unknown_const_as_generic(db.const_param_ty(id)).cast(Interner) + } + }), + ) + } + pub fn subst_for_def( db: &dyn HirDatabase, def: impl Into, @@ -233,6 +245,25 @@ impl TyBuilder<()> { TyBuilder::new((), params, parent_subst) } + pub fn subst_for_closure( + db: &dyn HirDatabase, + parent: DefWithBodyId, + sig_ty: Ty, + ) -> Substitution { + let sig_ty = sig_ty.cast(Interner); + let self_subst = iter::once(&sig_ty); + let Some(parent) = parent.as_generic_def_id() else { + return Substitution::from_iter(Interner, self_subst); + }; + Substitution::from_iter( + Interner, + self_subst + .chain(generics(db.upcast(), parent).placeholder_subst(db).iter(Interner)) + .cloned() + .collect::>(), + ) + } + pub fn build(self) -> Substitution { let ((), subst) = self.build_internal(); subst @@ -362,21 +393,4 @@ impl TyBuilder> { pub fn impl_self_ty(db: &dyn HirDatabase, def: hir_def::ImplId) -> TyBuilder> { TyBuilder::subst_for_def(db, def, None).with_data(db.impl_self_ty(def)) } - - pub fn value_ty( - db: &dyn HirDatabase, - def: ValueTyDefId, - parent_subst: Option, - ) -> TyBuilder> { - let poly_value_ty = db.value_ty(def); - let id = match def.to_generic_def_id() { - Some(id) => id, - None => { - // static items - assert!(parent_subst.is_none()); - return TyBuilder::new_empty(poly_value_ty); - } - }; - TyBuilder::subst_for_def(db, id, parent_subst).with_data(poly_value_ty) - } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs index 28ae4c349f83c..ac962c9e3e1f0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs @@ -1,8 +1,8 @@ //! The implementation of `RustIrDatabase` for Chalk, which provides information //! about the code that Chalk needs. -use std::sync::Arc; +use core::ops; +use std::{iter, sync::Arc}; -use cov_mark::hit; use tracing::debug; use chalk_ir::{cast::Cast, fold::shift::Shift, CanonicalVarKinds}; @@ -10,9 +10,9 @@ use chalk_solve::rust_ir::{self, OpaqueTyDatumBound, WellKnownTrait}; use base_db::CrateId; use hir_def::{ - expr::Movability, + hir::Movability, lang_item::{lang_attr, LangItem, LangItemTarget}, - AssocItemId, GenericDefId, HasModule, ItemContainerId, Lookup, ModuleId, TypeAliasId, + AssocItemId, BlockId, GenericDefId, HasModule, ItemContainerId, Lookup, TypeAliasId, }; use hir_expand::name::name; @@ -25,7 +25,7 @@ use crate::{ method_resolution::{TraitImpls, TyFingerprint, ALL_FLOAT_FPS, ALL_INT_FPS}, to_assoc_type_id, to_chalk_trait_id, traits::ChalkContext, - utils::generics, + utils::{generics, ClosureSubst}, wrap_empty_binders, AliasEq, AliasTy, BoundVar, CallableDefId, DebruijnIndex, FnDefId, Interner, ProjectionTy, ProjectionTyExt, QuantifiedWhereClause, Substitution, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, WhereClause, @@ -108,17 +108,6 @@ impl<'a> chalk_solve::RustIrDatabase for ChalkContext<'a> { _ => self_ty_fp.as_ref().map(std::slice::from_ref).unwrap_or(&[]), }; - fn local_impls(db: &dyn HirDatabase, module: ModuleId) -> Option> { - let block = module.containing_block()?; - hit!(block_local_impls); - db.trait_impls_in_block(block) - } - - // Note: Since we're using impls_for_trait, only impls where the trait - // can be resolved should ever reach Chalk. impl_datum relies on that - // and will panic if the trait can't be resolved. - let in_deps = self.db.trait_impls_in_deps(self.krate); - let in_self = self.db.trait_impls_in_crate(self.krate); let trait_module = trait_.module(self.db.upcast()); let type_module = match self_ty_fp { Some(TyFingerprint::Adt(adt_id)) => Some(adt_id.module(self.db.upcast())), @@ -128,33 +117,62 @@ impl<'a> chalk_solve::RustIrDatabase for ChalkContext<'a> { Some(TyFingerprint::Dyn(trait_id)) => Some(trait_id.module(self.db.upcast())), _ => None, }; - let impl_maps = [ - Some(in_deps), - Some(in_self), - local_impls(self.db, trait_module), - type_module.and_then(|m| local_impls(self.db, m)), - ]; - let id_to_chalk = |id: hir_def::ImplId| id.to_chalk(self.db); + let mut def_blocks = + [trait_module.containing_block(), type_module.and_then(|it| it.containing_block())]; - let result: Vec<_> = if fps.is_empty() { - debug!("Unrestricted search for {:?} impls...", trait_); - impl_maps - .iter() - .filter_map(|o| o.as_ref()) - .flat_map(|impls| impls.for_trait(trait_).map(id_to_chalk)) - .collect() - } else { - impl_maps - .iter() - .filter_map(|o| o.as_ref()) - .flat_map(|impls| { - fps.iter().flat_map(move |fp| { - impls.for_trait_and_self_ty(trait_, *fp).map(id_to_chalk) - }) - }) - .collect() - }; + // Note: Since we're using impls_for_trait, only impls where the trait + // can be resolved should ever reach Chalk. impl_datum relies on that + // and will panic if the trait can't be resolved. + let in_deps = self.db.trait_impls_in_deps(self.krate); + let in_self = self.db.trait_impls_in_crate(self.krate); + + let block_impls = iter::successors(self.block, |&block_id| { + cov_mark::hit!(block_local_impls); + self.db.block_def_map(block_id).parent().and_then(|module| module.containing_block()) + }) + .inspect(|&block_id| { + // make sure we don't search the same block twice + def_blocks.iter_mut().for_each(|block| { + if *block == Some(block_id) { + *block = None; + } + }); + }) + .map(|block_id| self.db.trait_impls_in_block(block_id)); + + let id_to_chalk = |id: hir_def::ImplId| id.to_chalk(self.db); + let mut result = vec![]; + match fps { + [] => { + debug!("Unrestricted search for {:?} impls...", trait_); + let mut f = |impls: &TraitImpls| { + result.extend(impls.for_trait(trait_).map(id_to_chalk)); + }; + f(&in_self); + in_deps.iter().map(ops::Deref::deref).for_each(&mut f); + block_impls.for_each(|it| f(&it)); + def_blocks + .into_iter() + .flatten() + .for_each(|it| f(&self.db.trait_impls_in_block(it))); + } + fps => { + let mut f = + |impls: &TraitImpls| { + result.extend(fps.iter().flat_map(|fp| { + impls.for_trait_and_self_ty(trait_, *fp).map(id_to_chalk) + })); + }; + f(&in_self); + in_deps.iter().map(ops::Deref::deref).for_each(&mut f); + block_impls.for_each(|it| f(&it)); + def_blocks + .into_iter() + .flatten() + .for_each(|it| f(&self.db.trait_impls_in_block(it))); + } + } debug!("impls_for_trait returned {} impls", result.len()); result @@ -193,7 +211,7 @@ impl<'a> chalk_solve::RustIrDatabase for ChalkContext<'a> { &self, environment: &chalk_ir::Environment, ) -> chalk_ir::ProgramClauses { - self.db.program_clauses_for_chalk_env(self.krate, environment.clone()) + self.db.program_clauses_for_chalk_env(self.krate, self.block, environment.clone()) } fn opaque_ty_data(&self, id: chalk_ir::OpaqueTyId) -> Arc { @@ -321,7 +339,7 @@ impl<'a> chalk_solve::RustIrDatabase for ChalkContext<'a> { _closure_id: chalk_ir::ClosureId, substs: &chalk_ir::Substitution, ) -> chalk_ir::Binders> { - let sig_ty = substs.at(Interner, 0).assert_ty_ref(Interner).clone(); + let sig_ty = ClosureSubst(substs).sig_ty(); let sig = &sig_ty.callable_sig(self.db).expect("first closure param should be fn ptr"); let io = rust_ir::FnDefInputsAndOutputDatum { argument_types: sig.params().to_vec(), @@ -347,13 +365,19 @@ impl<'a> chalk_solve::RustIrDatabase for ChalkContext<'a> { fn trait_name(&self, trait_id: chalk_ir::TraitId) -> String { let id = from_chalk_trait_id(trait_id); - self.db.trait_data(id).name.to_string() + self.db.trait_data(id).name.display(self.db.upcast()).to_string() } fn adt_name(&self, chalk_ir::AdtId(adt_id): AdtId) -> String { match adt_id { - hir_def::AdtId::StructId(id) => self.db.struct_data(id).name.to_string(), - hir_def::AdtId::EnumId(id) => self.db.enum_data(id).name.to_string(), - hir_def::AdtId::UnionId(id) => self.db.union_data(id).name.to_string(), + hir_def::AdtId::StructId(id) => { + self.db.struct_data(id).name.display(self.db.upcast()).to_string() + } + hir_def::AdtId::EnumId(id) => { + self.db.enum_data(id).name.display(self.db.upcast()).to_string() + } + hir_def::AdtId::UnionId(id) => { + self.db.union_data(id).name.display(self.db.upcast()).to_string() + } } } fn adt_size_align(&self, _id: chalk_ir::AdtId) -> Arc { @@ -362,7 +386,7 @@ impl<'a> chalk_solve::RustIrDatabase for ChalkContext<'a> { } fn assoc_type_name(&self, assoc_ty_id: chalk_ir::AssocTypeId) -> String { let id = self.db.associated_ty_data(assoc_ty_id).name; - self.db.type_alias_data(id).name.to_string() + self.db.type_alias_data(id).name.display(self.db.upcast()).to_string() } fn opaque_type_name(&self, opaque_ty_id: chalk_ir::OpaqueTyId) -> String { format!("Opaque_{}", opaque_ty_id.0) @@ -373,7 +397,7 @@ impl<'a> chalk_solve::RustIrDatabase for ChalkContext<'a> { fn generator_datum( &self, id: chalk_ir::GeneratorId, - ) -> std::sync::Arc> { + ) -> Arc> { let (parent, expr) = self.db.lookup_intern_generator(id.into()); // We fill substitution with unknown type, because we only need to know whether the generic @@ -398,8 +422,8 @@ impl<'a> chalk_solve::RustIrDatabase for ChalkContext<'a> { let input_output = crate::make_type_and_const_binders(it, input_output); let movability = match self.db.body(parent)[expr] { - hir_def::expr::Expr::Closure { - closure_kind: hir_def::expr::ClosureKind::Generator(movability), + hir_def::hir::Expr::Closure { + closure_kind: hir_def::hir::ClosureKind::Generator(movability), .. } => movability, _ => unreachable!("non generator expression interned as generator"), @@ -414,7 +438,7 @@ impl<'a> chalk_solve::RustIrDatabase for ChalkContext<'a> { fn generator_witness_datum( &self, id: chalk_ir::GeneratorId, - ) -> std::sync::Arc> { + ) -> Arc> { // FIXME: calculate inner types let inner_types = rust_ir::GeneratorWitnessExistential { types: wrap_empty_binders(vec![]) }; @@ -435,7 +459,7 @@ impl<'a> chalk_solve::RustIrDatabase for ChalkContext<'a> { } } -impl<'a> chalk_ir::UnificationDatabase for &'a dyn HirDatabase { +impl chalk_ir::UnificationDatabase for &dyn HirDatabase { fn fn_def_variance( &self, fn_def_id: chalk_ir::FnDefId, @@ -451,9 +475,10 @@ impl<'a> chalk_ir::UnificationDatabase for &'a dyn HirDatabase { pub(crate) fn program_clauses_for_chalk_env_query( db: &dyn HirDatabase, krate: CrateId, + block: Option, environment: chalk_ir::Environment, ) -> chalk_ir::ProgramClauses { - chalk_solve::program_clauses_for_env(&ChalkContext { db, krate }, &environment) + chalk_solve::program_clauses_for_env(&ChalkContext { db, krate, block }, &environment) } pub(crate) fn associated_ty_data_query( @@ -786,17 +811,17 @@ pub(crate) fn adt_variance_query( ) } +/// Returns instantiated predicates. pub(super) fn convert_where_clauses( db: &dyn HirDatabase, def: GenericDefId, substs: &Substitution, ) -> Vec> { - let generic_predicates = db.generic_predicates(def); - let mut result = Vec::with_capacity(generic_predicates.len()); - for pred in generic_predicates.iter() { - result.push(pred.clone().substitute(Interner, substs)); - } - result + db.generic_predicates(def) + .iter() + .cloned() + .map(|pred| pred.substitute(Interner, substs)) + .collect() } pub(super) fn generic_predicate_to_inline_bound( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs index 2141894922f7b..a8071591adac9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs @@ -1,24 +1,28 @@ //! Various extensions traits for Chalk types. -use chalk_ir::{FloatTy, IntTy, Mutability, Scalar, TyVariableKind, UintTy}; +use chalk_ir::{cast::Cast, FloatTy, IntTy, Mutability, Scalar, TyVariableKind, UintTy}; use hir_def::{ builtin_type::{BuiltinFloat, BuiltinInt, BuiltinType, BuiltinUint}, generics::TypeOrConstParamData, lang_item::LangItem, type_ref::Rawness, - FunctionId, GenericDefId, HasModule, ItemContainerId, Lookup, TraitId, + DefWithBodyId, FunctionId, GenericDefId, HasModule, ItemContainerId, Lookup, TraitId, }; use crate::{ - db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, - from_placeholder_idx, to_chalk_trait_id, utils::generics, AdtId, AliasEq, AliasTy, Binders, - CallableDefId, CallableSig, DynTy, FnPointer, ImplTraitId, Interner, Lifetime, ProjectionTy, + db::HirDatabase, + from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, from_placeholder_idx, + to_chalk_trait_id, + utils::{generics, ClosureSubst}, + AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, + ClosureId, DynTy, FnPointer, ImplTraitId, InEnvironment, Interner, Lifetime, ProjectionTy, QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyKind, TypeFlags, WhereClause, }; pub trait TyExt { fn is_unit(&self) -> bool; fn is_integral(&self) -> bool; + fn is_scalar(&self) -> bool; fn is_floating_point(&self) -> bool; fn is_never(&self) -> bool; fn is_unknown(&self) -> bool; @@ -28,8 +32,10 @@ pub trait TyExt { fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)>; fn as_builtin(&self) -> Option; fn as_tuple(&self) -> Option<&Substitution>; + fn as_closure(&self) -> Option; fn as_fn_def(&self, db: &dyn HirDatabase) -> Option; fn as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)>; + fn as_raw_ptr(&self) -> Option<(&Ty, Mutability)>; fn as_reference_or_ptr(&self) -> Option<(&Ty, Rawness, Mutability)>; fn as_generic_def(&self, db: &dyn HirDatabase) -> Option; @@ -44,6 +50,7 @@ pub trait TyExt { fn impl_trait_bounds(&self, db: &dyn HirDatabase) -> Option>; fn associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option; + fn is_copy(self, db: &dyn HirDatabase, owner: DefWithBodyId) -> bool; /// FIXME: Get rid of this, it's not a good abstraction fn equals_ctor(&self, other: &Ty) -> bool; @@ -62,6 +69,10 @@ impl TyExt for Ty { ) } + fn is_scalar(&self) -> bool { + matches!(self.kind(Interner), TyKind::Scalar(_)) + } + fn is_floating_point(&self) -> bool { matches!( self.kind(Interner), @@ -128,12 +139,20 @@ impl TyExt for Ty { } } + fn as_closure(&self) -> Option { + match self.kind(Interner) { + TyKind::Closure(id, _) => Some(*id), + _ => None, + } + } + fn as_fn_def(&self, db: &dyn HirDatabase) -> Option { match self.callable_def(db) { Some(CallableDefId::FunctionId(func)) => Some(func), Some(CallableDefId::StructId(_) | CallableDefId::EnumVariantId(_)) | None => None, } } + fn as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)> { match self.kind(Interner) { TyKind::Ref(mutability, lifetime, ty) => Some((ty, lifetime.clone(), *mutability)), @@ -141,6 +160,13 @@ impl TyExt for Ty { } } + fn as_raw_ptr(&self) -> Option<(&Ty, Mutability)> { + match self.kind(Interner) { + TyKind::Raw(mutability, ty) => Some((ty, *mutability)), + _ => None, + } + } + fn as_reference_or_ptr(&self) -> Option<(&Ty, Rawness, Mutability)> { match self.kind(Interner) { TyKind::Ref(mutability, _, ty) => Some((ty, Rawness::Ref, *mutability)), @@ -176,10 +202,7 @@ impl TyExt for Ty { let sig = db.callable_item_signature(callable_def); Some(sig.substitute(Interner, parameters)) } - TyKind::Closure(.., substs) => { - let sig_param = substs.at(Interner, 0).assert_ty_ref(Interner); - sig_param.callable_sig(db) - } + TyKind::Closure(.., substs) => ClosureSubst(substs).sig_ty().callable_sig(db), _ => None, } } @@ -318,6 +341,20 @@ impl TyExt for Ty { } } + fn is_copy(self, db: &dyn HirDatabase, owner: DefWithBodyId) -> bool { + let crate_id = owner.module(db.upcast()).krate(); + let Some(copy_trait) = db.lang_item(crate_id, LangItem::Copy).and_then(|x| x.as_trait()) else { + return false; + }; + let trait_ref = TyBuilder::trait_ref(db, copy_trait).push(self).build(); + let env = db.trait_environment_for_body(owner); + let goal = Canonical { + value: InEnvironment::new(&env.env, trait_ref.cast(Interner)), + binders: CanonicalVarKinds::empty(Interner), + }; + db.trait_solve(crate_id, None, goal).is_some() + } + fn equals_ctor(&self, other: &Ty) -> bool { match (self.kind(Interner), other.kind(Interner)) { (TyKind::Adt(adt, ..), TyKind::Adt(adt2, ..)) => adt == adt2, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs index 5830c48988fe5..40b63b17b5aa4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -3,19 +3,20 @@ use base_db::CrateId; use chalk_ir::{BoundVar, DebruijnIndex, GenericArgData}; use hir_def::{ - expr::Expr, - path::ModPath, + hir::Expr, + path::Path, resolver::{Resolver, ValueNs}, type_ref::ConstRef, - ConstId, EnumVariantId, + EnumVariantId, GeneralConstId, StaticId, }; use la_arena::{Idx, RawIdx}; use stdx::never; +use triomphe::Arc; use crate::{ - db::HirDatabase, infer::InferenceContext, layout::layout_of_ty, lower::ParamLoweringMode, - to_placeholder_idx, utils::Generics, Const, ConstData, ConstScalar, ConstValue, GenericArg, - Interner, MemoryMap, Ty, TyBuilder, + db::HirDatabase, infer::InferenceContext, lower::ParamLoweringMode, + mir::monomorphize_mir_body_bad, to_placeholder_idx, utils::Generics, Const, ConstData, + ConstScalar, ConstValue, GenericArg, Interner, MemoryMap, Substitution, Ty, TyBuilder, }; use super::mir::{interpret_mir, lower_to_mir, pad16, MirEvalError, MirLowerError}; @@ -57,7 +58,7 @@ pub enum ConstEvalError { impl From for ConstEvalError { fn from(value: MirLowerError) -> Self { match value { - MirLowerError::ConstEvalError(e) => *e, + MirLowerError::ConstEvalError(_, e) => *e, _ => ConstEvalError::MirLowerError(value), } } @@ -72,10 +73,11 @@ impl From for ConstEvalError { pub(crate) fn path_to_const( db: &dyn HirDatabase, resolver: &Resolver, - path: &ModPath, + path: &Path, mode: ParamLoweringMode, args_lazy: impl FnOnce() -> Generics, debruijn: DebruijnIndex, + expected_ty: Ty, ) -> Option { match resolver.resolve_path_in_value_ns_fully(db.upcast(), path) { Some(ValueNs::GenericParam(p)) => { @@ -89,7 +91,7 @@ pub(crate) fn path_to_const( Some(x) => ConstValue::BoundVar(BoundVar::new(debruijn, x)), None => { never!( - "Generic list doesn't contain this param: {:?}, {}, {:?}", + "Generic list doesn't contain this param: {:?}, {:?}, {:?}", args, path, p @@ -100,6 +102,10 @@ pub(crate) fn path_to_const( }; Some(ConstData { ty, value }.intern(Interner)) } + Some(ValueNs::ConstId(c)) => Some(intern_const_scalar( + ConstScalar::UnevaluatedConst(c.into(), Substitution::empty(Interner)), + expected_ty, + )), _ => None, } } @@ -124,14 +130,15 @@ pub fn intern_const_scalar(value: ConstScalar, ty: Ty) -> Const { /// Interns a constant scalar with the given type pub fn intern_const_ref(db: &dyn HirDatabase, value: &ConstRef, ty: Ty, krate: CrateId) -> Const { + let layout = db.layout_of_ty(ty.clone(), krate); let bytes = match value { ConstRef::Int(i) => { // FIXME: We should handle failure of layout better. - let size = layout_of_ty(db, &ty, krate).map(|x| x.size.bytes_usize()).unwrap_or(16); + let size = layout.map(|x| x.size.bytes_usize()).unwrap_or(16); ConstScalar::Bytes(i.to_le_bytes()[0..size].to_vec(), MemoryMap::default()) } ConstRef::UInt(i) => { - let size = layout_of_ty(db, &ty, krate).map(|x| x.size.bytes_usize()).unwrap_or(16); + let size = layout.map(|x| x.size.bytes_usize()).unwrap_or(16); ConstScalar::Bytes(i.to_le_bytes()[0..size].to_vec(), MemoryMap::default()) } ConstRef::Bool(b) => ConstScalar::Bytes(vec![*b as u8], MemoryMap::default()), @@ -153,13 +160,17 @@ pub fn usize_const(db: &dyn HirDatabase, value: Option, krate: CrateId) -> ) } -pub fn try_const_usize(c: &Const) -> Option { +pub fn try_const_usize(db: &dyn HirDatabase, c: &Const) -> Option { match &c.data(Interner).value { chalk_ir::ConstValue::BoundVar(_) => None, chalk_ir::ConstValue::InferenceVar(_) => None, chalk_ir::ConstValue::Placeholder(_) => None, chalk_ir::ConstValue::Concrete(c) => match &c.interned { ConstScalar::Bytes(x, _) => Some(u128::from_le_bytes(pad16(&x, false))), + ConstScalar::UnevaluatedConst(c, subst) => { + let ec = db.const_eval(*c, subst.clone()).ok()?; + try_const_usize(db, &ec) + } _ => None, }, } @@ -168,7 +179,16 @@ pub fn try_const_usize(c: &Const) -> Option { pub(crate) fn const_eval_recover( _: &dyn HirDatabase, _: &[String], - _: &ConstId, + _: &GeneralConstId, + _: &Substitution, +) -> Result { + Err(ConstEvalError::MirLowerError(MirLowerError::Loop)) +} + +pub(crate) fn const_eval_static_recover( + _: &dyn HirDatabase, + _: &[String], + _: &StaticId, ) -> Result { Err(ConstEvalError::MirLowerError(MirLowerError::Loop)) } @@ -183,11 +203,39 @@ pub(crate) fn const_eval_discriminant_recover( pub(crate) fn const_eval_query( db: &dyn HirDatabase, - const_id: ConstId, + def: GeneralConstId, + subst: Substitution, +) -> Result { + let body = match def { + GeneralConstId::ConstId(c) => { + db.monomorphized_mir_body(c.into(), subst, db.trait_environment(c.into()))? + } + GeneralConstId::AnonymousConstId(c) => { + let (def, root) = db.lookup_intern_anonymous_const(c); + let body = db.body(def); + let infer = db.infer(def); + Arc::new(monomorphize_mir_body_bad( + db, + lower_to_mir(db, def, &body, &infer, root)?, + subst, + db.trait_environment_for_body(def), + )?) + } + }; + let c = interpret_mir(db, &body, false).0?; + Ok(c) +} + +pub(crate) fn const_eval_static_query( + db: &dyn HirDatabase, + def: StaticId, ) -> Result { - let def = const_id.into(); - let body = db.mir_body(def)?; - let c = interpret_mir(db, &body, false)?; + let body = db.monomorphized_mir_body( + def.into(), + Substitution::empty(Interner), + db.trait_environment_for_body(def.into()), + )?; + let c = interpret_mir(db, &body, false).0?; Ok(c) } @@ -209,9 +257,13 @@ pub(crate) fn const_eval_discriminant_variant( }; return Ok(value); } - let mir_body = db.mir_body(def)?; - let c = interpret_mir(db, &mir_body, false)?; - let c = try_const_usize(&c).unwrap() as i128; + let mir_body = db.monomorphized_mir_body( + def, + Substitution::empty(Interner), + db.trait_environment_for_body(def), + )?; + let c = interpret_mir(db, &mir_body, false).0?; + let c = try_const_usize(db, &c).unwrap() as i128; Ok(c) } @@ -226,15 +278,16 @@ pub(crate) fn eval_to_const( debruijn: DebruijnIndex, ) -> Const { let db = ctx.db; + let infer = ctx.clone().resolve_all(); if let Expr::Path(p) = &ctx.body.exprs[expr] { let resolver = &ctx.resolver; - if let Some(c) = path_to_const(db, resolver, p.mod_path(), mode, args, debruijn) { + if let Some(c) = path_to_const(db, resolver, p, mode, args, debruijn, infer[expr].clone()) { return c; } } let infer = ctx.clone().resolve_all(); if let Ok(mir_body) = lower_to_mir(ctx.db, ctx.owner, &ctx.body, &infer, expr) { - if let Ok(result) = interpret_mir(db, &mir_body, true) { + if let Ok(result) = interpret_mir(db, &mir_body, true).0 { return result; } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs index 6a29e8ce52e62..06fff08b7d35b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs @@ -1,8 +1,10 @@ -use base_db::fixture::WithFixture; +use base_db::{fixture::WithFixture, FileId}; +use chalk_ir::Substitution; use hir_def::db::DefDatabase; use crate::{ - consteval::try_const_usize, db::HirDatabase, test_db::TestDB, Const, ConstScalar, Interner, + consteval::try_const_usize, db::HirDatabase, mir::pad16, test_db::TestDB, Const, ConstScalar, + Interner, }; use super::{ @@ -10,9 +12,11 @@ use super::{ ConstEvalError, }; +mod intrinsics; + fn simplify(e: ConstEvalError) -> ConstEvalError { match e { - ConstEvalError::MirEvalError(MirEvalError::InFunction(_, e)) => { + ConstEvalError::MirEvalError(MirEvalError::InFunction(_, e, _, _)) => { simplify(ConstEvalError::MirEvalError(*e)) } _ => e, @@ -20,17 +24,35 @@ fn simplify(e: ConstEvalError) -> ConstEvalError { } #[track_caller] -fn check_fail(ra_fixture: &str, error: ConstEvalError) { - assert_eq!(eval_goal(ra_fixture).map_err(simplify), Err(error)); +fn check_fail(ra_fixture: &str, error: impl FnOnce(ConstEvalError) -> bool) { + let (db, file_id) = TestDB::with_single_file(ra_fixture); + match eval_goal(&db, file_id) { + Ok(_) => panic!("Expected fail, but it succeeded"), + Err(e) => { + assert!(error(simplify(e.clone())), "Actual error was: {}", pretty_print_err(e, db)) + } + } } #[track_caller] fn check_number(ra_fixture: &str, answer: i128) { - let r = eval_goal(ra_fixture).unwrap(); + let (db, file_id) = TestDB::with_single_file(ra_fixture); + let r = match eval_goal(&db, file_id) { + Ok(t) => t, + Err(e) => { + let err = pretty_print_err(e, db); + panic!("Error in evaluating goal: {}", err); + } + }; match &r.data(Interner).value { chalk_ir::ConstValue::Concrete(c) => match &c.interned { ConstScalar::Bytes(b, _) => { - assert_eq!(b, &answer.to_le_bytes()[0..b.len()]); + assert_eq!( + b, + &answer.to_le_bytes()[0..b.len()], + "Bytes differ. In decimal form: actual = {}, expected = {answer}", + i128::from_le_bytes(pad16(b, true)) + ); } x => panic!("Expected number but found {:?}", x), }, @@ -38,16 +60,26 @@ fn check_number(ra_fixture: &str, answer: i128) { } } -fn eval_goal(ra_fixture: &str) -> Result { - let (db, file_id) = TestDB::with_single_file(ra_fixture); +fn pretty_print_err(e: ConstEvalError, db: TestDB) -> String { + let mut err = String::new(); + let span_formatter = |file, range| format!("{:?} {:?}", file, range); + match e { + ConstEvalError::MirLowerError(e) => e.pretty_print(&mut err, &db, span_formatter), + ConstEvalError::MirEvalError(e) => e.pretty_print(&mut err, &db, span_formatter), + } + .unwrap(); + err +} + +fn eval_goal(db: &TestDB, file_id: FileId) -> Result { let module_id = db.module_for_file(file_id); - let def_map = module_id.def_map(&db); + let def_map = module_id.def_map(db); let scope = &def_map[module_id.local_id].scope; let const_id = scope .declarations() .find_map(|x| match x { hir_def::ModuleDefId::ConstId(x) => { - if db.const_data(x).name.as_ref()?.to_string() == "GOAL" { + if db.const_data(x).name.as_ref()?.display(db).to_string() == "GOAL" { Some(x) } else { None @@ -56,7 +88,7 @@ fn eval_goal(ra_fixture: &str) -> Result { _ => None, }) .unwrap(); - db.const_eval(const_id) + db.const_eval(const_id.into(), Substitution::empty(Interner)) } #[test] @@ -72,8 +104,98 @@ fn bit_op() { check_number(r#"const GOAL: u8 = !0 & !(!0 >> 1)"#, 128); check_number(r#"const GOAL: i8 = !0 & !(!0 >> 1)"#, 0); check_number(r#"const GOAL: i8 = 1 << 7"#, (1i8 << 7) as i128); - // FIXME: report panic here - check_number(r#"const GOAL: i8 = 1 << 8"#, 0); + check_number(r#"const GOAL: i8 = -1 << 2"#, (-1i8 << 2) as i128); + check_fail(r#"const GOAL: i8 = 1 << 8"#, |e| { + e == ConstEvalError::MirEvalError(MirEvalError::Panic("Overflow in Shl".to_string())) + }); +} + +#[test] +fn floating_point() { + check_number( + r#"const GOAL: f64 = 2.0 + 3.0 * 5.5 - 8.;"#, + i128::from_le_bytes(pad16(&f64::to_le_bytes(10.5), true)), + ); + check_number( + r#"const GOAL: f32 = 2.0 + 3.0 * 5.5 - 8.;"#, + i128::from_le_bytes(pad16(&f32::to_le_bytes(10.5), true)), + ); + check_number( + r#"const GOAL: f32 = -90.0 + 36.0;"#, + i128::from_le_bytes(pad16(&f32::to_le_bytes(-54.0), true)), + ); +} + +#[test] +fn casts() { + check_number(r#"const GOAL: usize = 12 as *const i32 as usize"#, 12); + check_number( + r#" + //- minicore: coerce_unsized, index, slice + const GOAL: i32 = { + let a = [10, 20, 3, 15]; + let x: &[i32] = &a; + let y: *const [i32] = x; + let z = y as *const i32; + unsafe { *z } + }; + "#, + 10, + ); + check_number( + r#" + //- minicore: coerce_unsized, index, slice + const GOAL: i16 = { + let a = &mut 5; + let z = a as *mut _; + unsafe { *z } + }; + "#, + 5, + ); + check_number( + r#" + //- minicore: coerce_unsized, index, slice + const GOAL: usize = { + let a = &[10, 20, 30, 40] as &[i32]; + a.len() + }; + "#, + 4, + ); + check_number( + r#" + //- minicore: coerce_unsized, index, slice + const GOAL: usize = { + let a = [10, 20, 3, 15]; + let x: &[i32] = &a; + let y: *const [i32] = x; + let z = y as *const [u8]; // slice fat pointer cast don't touch metadata + let q = z as *const str; + let p = q as *const [u8]; + let w = unsafe { &*z }; + w.len() + }; + "#, + 4, + ); + check_number(r#"const GOAL: i32 = -12i8 as i32"#, -12); +} + +#[test] +fn raw_pointer_equality() { + check_number( + r#" + //- minicore: copy, eq + const GOAL: bool = { + let a = 2; + let p1 = a as *const i32; + let p2 = a as *const i32; + p1 == p2 + }; + "#, + 1, + ); } #[test] @@ -166,8 +288,7 @@ fn reference_autoderef() { #[test] fn overloaded_deref() { - // FIXME: We should support this. - check_fail( + check_number( r#" //- minicore: deref_mut struct Foo; @@ -185,9 +306,7 @@ fn overloaded_deref() { *y + *x }; "#, - ConstEvalError::MirLowerError(MirLowerError::NotSupported( - "explicit overloaded deref".into(), - )), + 10, ); } @@ -218,6 +337,117 @@ fn overloaded_deref_autoref() { ); } +#[test] +fn overloaded_index() { + check_number( + r#" + //- minicore: index + struct Foo; + + impl core::ops::Index for Foo { + type Output = i32; + fn index(&self, index: usize) -> &i32 { + if index == 7 { + &700 + } else { + &1000 + } + } + } + + impl core::ops::IndexMut for Foo { + fn index_mut(&mut self, index: usize) -> &mut i32 { + if index == 7 { + &mut 7 + } else { + &mut 10 + } + } + } + + const GOAL: i32 = { + (Foo[2]) + (Foo[7]) + (*&Foo[2]) + (*&Foo[7]) + (*&mut Foo[2]) + (*&mut Foo[7]) + }; + "#, + 3417, + ); +} + +#[test] +fn overloaded_binop() { + check_number( + r#" + //- minicore: add + enum Color { + Red, + Green, + Yellow, + } + + use Color::*; + + impl core::ops::Add for Color { + type Output = Color; + fn add(self, rhs: Color) -> Self::Output { + Yellow + } + } + + impl core::ops::AddAssign for Color { + fn add_assign(&mut self, rhs: Color) { + *self = Red; + } + } + + const GOAL: bool = { + let x = Red + Green; + let mut y = Green; + y += x; + x == Yellow && y == Red && Red + Green == Yellow && Red + Red == Yellow && Yellow + Green == Yellow + }; + "#, + 1, + ); + check_number( + r#" + //- minicore: add + impl core::ops::Add for usize { + type Output = usize; + fn add(self, rhs: usize) -> Self::Output { + self + rhs + } + } + + impl core::ops::AddAssign for usize { + fn add_assign(&mut self, rhs: usize) { + *self += rhs; + } + } + + #[lang = "shl"] + pub trait Shl { + type Output; + + fn shl(self, rhs: Rhs) -> Self::Output; + } + + impl Shl for usize { + type Output = usize; + + fn shl(self, rhs: u8) -> Self::Output { + self << rhs + } + } + + const GOAL: usize = { + let mut x = 10; + x += 20; + 2 + 2 + (x << 1u8) + };"#, + 64, + ); +} + #[test] fn function_call() { check_number( @@ -240,20 +470,6 @@ fn function_call() { ); } -#[test] -fn intrinsics() { - check_number( - r#" - extern "rust-intrinsic" { - pub fn size_of() -> usize; - } - - const GOAL: usize = size_of::(); - "#, - 4, - ); -} - #[test] fn trait_basic() { check_number( @@ -300,6 +516,35 @@ fn trait_method() { ); } +#[test] +fn trait_method_inside_block() { + check_number( + r#" +trait Twait { + fn a(&self) -> i32; +} + +fn outer() -> impl Twait { + struct Stwuct; + + impl Twait for Stwuct { + fn a(&self) -> i32 { + 5 + } + } + fn f() -> impl Twait { + let s = Stwuct; + s + } + f() +} + +const GOAL: i32 = outer().a(); + "#, + 5, + ); +} + #[test] fn generic_fn() { check_number( @@ -355,6 +600,16 @@ fn generic_fn() { "#, 12, ); + check_number( + r#" + const fn y(b: T) -> (T, ) { + let alloc = b; + (alloc, ) + } + const GOAL: u8 = y(2).0; + "#, + 2, + ); check_number( r#" //- minicore: coerce_unsized, index, slice @@ -483,6 +738,66 @@ fn loops() { "#, 4, ); + check_number( + r#" + const GOAL: u8 = { + let mut x = 0; + loop { + x = x + 1; + if x == 5 { + break x + 2; + } + } + }; + "#, + 7, + ); + check_number( + r#" + const GOAL: u8 = { + 'a: loop { + let x = 'b: loop { + let x = 'c: loop { + let x = 'd: loop { + let x = 'e: loop { + break 'd 1; + }; + break 2 + x; + }; + break 3 + x; + }; + break 'a 4 + x; + }; + break 5 + x; + } + }; + "#, + 8, + ); + check_number( + r#" + //- minicore: add + const GOAL: u8 = { + let mut x = 0; + 'a: loop { + 'b: loop { + 'c: while x < 20 { + 'd: while x < 5 { + 'e: loop { + x += 1; + continue 'c; + }; + }; + x += 1; + }; + break 'a; + }; + } + x + }; + "#, + 20, + ); } #[test] @@ -522,6 +837,18 @@ fn for_loops() { ); } +#[test] +fn ranges() { + check_number( + r#" + //- minicore: range + const GOAL: i32 = (1..2).start + (20..10).end + (100..=200).start + (2000..=1000).end + + (10000..).start + (..100000).end + (..=1000000).end; + "#, + 1111111, + ); +} + #[test] fn recursion() { check_number( @@ -555,6 +882,38 @@ fn structs() { "#, 17, ); + check_number( + r#" + struct Point { + x: i32, + y: i32, + } + + const GOAL: i32 = { + let p = Point { x: 5, y: 2 }; + let p2 = Point { x: 3, ..p }; + p.x * 1000 + p.y * 100 + p2.x * 10 + p2.y + }; + "#, + 5232, + ); + check_number( + r#" + struct Point { + x: i32, + y: i32, + } + + const GOAL: i32 = { + let p = Point { x: 5, y: 2 }; + let Point { x, y } = p; + let Point { x: x2, .. } = p; + let Point { y: y2, .. } = p; + x * 1000 + y * 100 + x2 * 10 + y2 + }; + "#, + 5252, + ); } #[test] @@ -599,13 +958,14 @@ fn tuples() { ); check_number( r#" - struct TupleLike(i32, u8, i64, u16); - const GOAL: u8 = { + struct TupleLike(i32, i64, u8, u16); + const GOAL: i64 = { let a = TupleLike(10, 20, 3, 15); - a.1 + let TupleLike(b, .., c) = a; + a.1 * 100 + b as i64 + c as i64 }; "#, - 20, + 2025, ); check_number( r#" @@ -638,11 +998,17 @@ fn path_pattern_matching() { use Season::*; + const MY_SEASON: Season = Summer; + + impl Season { + const FALL: Season = Fall; + } + const fn f(x: Season) -> i32 { match x { Spring => 1, - Summer => 2, - Fall => 3, + MY_SEASON => 2, + Season::FALL => 3, Winter => 4, } } @@ -653,42 +1019,137 @@ fn path_pattern_matching() { } #[test] -fn pattern_matching_ergonomics() { +fn pattern_matching_literal() { check_number( r#" - const fn f(x: &(u8, u8)) -> u8 { + const fn f(x: i32) -> i32 { match x { - (a, b) => *a + *b + -1 => 1, + 1 => 10, + _ => 100, } } - const GOAL: u8 = f(&(2, 3)); + const GOAL: i32 = f(-1) + f(1) + f(0) + f(-5); "#, - 5, + 211, ); -} - -#[test] -fn let_else() { check_number( r#" - const fn f(x: &(u8, u8)) -> u8 { - let (a, b) = x; - *a + *b + const fn f(x: &str) -> i32 { + match x { + "f" => 1, + "foo" => 10, + "" => 100, + "bar" => 1000, + _ => 10000, + } } - const GOAL: u8 = f(&(2, 3)); + const GOAL: i32 = f("f") + f("foo") * 2 + f("") * 3 + f("bar") * 4; "#, - 5, + 4321, ); +} + +#[test] +fn pattern_matching_range() { check_number( r#" - enum SingleVariant { - Var(u8, u8), + pub const L: i32 = 6; + mod x { + pub const R: i32 = 100; } - const fn f(x: &&&&&SingleVariant) -> u8 { - let SingleVariant::Var(a, b) = x; - *a + *b + const fn f(x: i32) -> i32 { + match x { + -1..=5 => x * 10, + L..=x::R => x * 100, + _ => x, + } } - const GOAL: u8 = f(&&&&&SingleVariant::Var(2, 3)); + const GOAL: i32 = f(-1) + f(2) + f(100) + f(-2) + f(1000); + "#, + 11008, + ); +} + +#[test] +fn pattern_matching_slice() { + check_number( + r#" + //- minicore: slice, index, coerce_unsized, copy + const fn f(x: &[usize]) -> usize { + match x { + [a, b @ .., c, d] => *a + b.len() + *c + *d, + } + } + const GOAL: usize = f(&[10, 20, 3, 15, 1000, 60, 16]); + "#, + 10 + 4 + 60 + 16, + ); + check_number( + r#" + //- minicore: slice, index, coerce_unsized, copy + const fn f(x: &[usize]) -> usize { + match x { + [] => 0, + [a] => *a, + &[a, b] => a + b, + [a, b @ .., c, d] => *a + b.len() + *c + *d, + } + } + const GOAL: usize = f(&[]) + f(&[10]) + f(&[100, 100]) + + f(&[1000, 1000, 1000]) + f(&[10000, 57, 34, 46, 10000, 10000]); + "#, + 33213, + ); +} + +#[test] +fn pattern_matching_ergonomics() { + check_number( + r#" + const fn f(x: &(u8, u8)) -> u8 { + match x { + (a, b) => *a + *b + } + } + const GOAL: u8 = f(&(2, 3)); + "#, + 5, + ); + check_number( + r#" + const GOAL: u8 = { + let a = &(2, 3); + let &(x, y) = a; + x + y + }; + "#, + 5, + ); +} + +#[test] +fn let_else() { + check_number( + r#" + const fn f(x: &(u8, u8)) -> u8 { + let (a, b) = x; + *a + *b + } + const GOAL: u8 = f(&(2, 3)); + "#, + 5, + ); + check_number( + r#" + enum SingleVariant { + Var(u8, u8), + } + const fn f(x: &&&&&SingleVariant) -> u8 { + let SingleVariant::Var(a, b) = x; + *a + *b + } + const GOAL: u8 = f(&&&&&SingleVariant::Var(2, 3)); "#, 5, ); @@ -748,6 +1209,77 @@ fn function_param_patterns() { ); } +#[test] +fn match_guards() { + check_number( + r#" + //- minicore: option + fn f(x: Option) -> i32 { + match x { + y if let Some(42) = y => 42000, + Some(y) => y, + None => 10 + } + } + const GOAL: i32 = f(Some(42)) + f(Some(2)) + f(None); + "#, + 42012, + ); +} + +#[test] +fn result_layout_niche_optimization() { + check_number( + r#" + //- minicore: option, result + const GOAL: i32 = match Some(2).ok_or(Some(2)) { + Ok(x) => x, + Err(_) => 1000, + }; + "#, + 2, + ); + check_number( + r#" + //- minicore: result + pub enum AlignmentEnum64 { + _Align1Shl0 = 1 << 0, + _Align1Shl1 = 1 << 1, + _Align1Shl2 = 1 << 2, + _Align1Shl3 = 1 << 3, + _Align1Shl4 = 1 << 4, + _Align1Shl5 = 1 << 5, + } + const GOAL: Result = { + let align = Err(()); + align + }; + "#, + 0, // It is 0 since result is niche encoded and 1 is valid for `AlignmentEnum64` + ); + check_number( + r#" + //- minicore: result + pub enum AlignmentEnum64 { + _Align1Shl0 = 1 << 0, + _Align1Shl1 = 1 << 1, + _Align1Shl2 = 1 << 2, + _Align1Shl3 = 1 << 3, + _Align1Shl4 = 1 << 4, + _Align1Shl5 = 1 << 5, + } + const GOAL: i32 = { + let align = Ok::<_, ()>(AlignmentEnum64::_Align1Shl0); + match align { + Ok(_) => 2, + Err(_) => 1, + } + }; + "#, + 2, + ); +} + #[test] fn options() { check_number( @@ -761,81 +1293,604 @@ fn options() { } }; "#, - 4, + 4, + ); + check_number( + r#" + //- minicore: option + fn f(x: Option>) -> i32 { + if let Some(y) = x && let Some(z) = y { + z + } else if let Some(y) = x { + 1 + } else { + 0 + } + } + const GOAL: i32 = f(Some(Some(10))) + f(Some(None)) + f(None); + "#, + 11, + ); + check_number( + r#" + //- minicore: option + const GOAL: u8 = { + let x = None; + match x { + Some(y) => 2 * y, + _ => 10, + } + }; + "#, + 10, + ); + check_number( + r#" + //- minicore: option + const GOAL: Option<&u8> = None; + "#, + 0, + ); +} + +#[test] +fn from_trait() { + check_number( + r#" + //- minicore: from + struct E1(i32); + struct E2(i32); + + impl From for E2 { + fn from(E1(x): E1) -> Self { + E2(1000 * x) + } + } + const GOAL: i32 = { + let x: E2 = E1(2).into(); + x.0 + }; + "#, + 2000, + ); +} + +#[test] +fn builtin_derive_macro() { + check_number( + r#" + //- minicore: clone, derive, builtin_impls + #[derive(Clone)] + enum Z { + Foo(Y), + Bar, + } + #[derive(Clone)] + struct X(i32, Z, i64) + #[derive(Clone)] + struct Y { + field1: i32, + field2: u8, + } + + const GOAL: u8 = { + let x = X(2, Z::Foo(Y { field1: 4, field2: 5 }), 8); + let x = x.clone(); + let Z::Foo(t) = x.1; + t.field2 + }; + "#, + 5, + ); + check_number( + r#" + //- minicore: default, derive, builtin_impls + #[derive(Default)] + struct X(i32, Y, i64) + #[derive(Default)] + struct Y { + field1: i32, + field2: u8, + } + + const GOAL: u8 = { + let x = X::default(); + x.1.field2 + }; + "#, + 0, + ); +} + +#[test] +fn try_operator() { + check_number( + r#" + //- minicore: option, try + const fn f(x: Option, y: Option) -> Option { + Some(x? * y?) + } + const fn g(x: Option, y: Option) -> i32 { + match f(x, y) { + Some(k) => k, + None => 5, + } + } + const GOAL: i32 = g(Some(10), Some(20)) + g(Some(30), None) + g(None, Some(40)) + g(None, None); + "#, + 215, + ); + check_number( + r#" + //- minicore: result, try, from + struct E1(i32); + struct E2(i32); + + impl From for E2 { + fn from(E1(x): E1) -> Self { + E2(1000 * x) + } + } + + const fn f(x: Result) -> Result { + Ok(x? * 10) + } + const fn g(x: Result) -> i32 { + match f(x) { + Ok(k) => 7 * k, + Err(E2(k)) => 5 * k, + } + } + const GOAL: i32 = g(Ok(2)) + g(Err(E1(3))); + "#, + 15140, + ); +} + +#[test] +fn try_block() { + check_number( + r#" + //- minicore: option, try + const fn g(x: Option, y: Option) -> i32 { + let r = try { x? * y? }; + match r { + Some(k) => k, + None => 5, + } + } + const GOAL: i32 = g(Some(10), Some(20)) + g(Some(30), None) + g(None, Some(40)) + g(None, None); + "#, + 215, + ); +} + +#[test] +fn closures() { + check_number( + r#" + //- minicore: fn, copy + const GOAL: i32 = { + let y = 5; + let c = |x| x + y; + c(2) + }; + "#, + 7, + ); + check_number( + r#" + //- minicore: fn, copy + const GOAL: i32 = { + let y = 5; + let c = |(a, b): &(i32, i32)| *a + *b + y; + c(&(2, 3)) + }; + "#, + 10, + ); + check_number( + r#" + //- minicore: fn, copy + const GOAL: i32 = { + let mut y = 5; + let c = |x| { + y = y + x; + }; + c(2); + c(3); + y + }; + "#, + 10, + ); + check_number( + r#" + //- minicore: fn, copy + const GOAL: i32 = { + let c: fn(i32) -> i32 = |x| 2 * x; + c(2) + c(10) + }; + "#, + 24, + ); + check_number( + r#" + //- minicore: fn, copy + struct X(i32); + impl X { + fn mult(&mut self, n: i32) { + self.0 = self.0 * n + } + } + const GOAL: i32 = { + let x = X(1); + let c = || { + x.mult(2); + || { + x.mult(3); + || { + || { + x.mult(4); + || { + x.mult(x.0); + || { + x.0 + } + } + } + } + } + }; + let r = c()()()()()(); + r + x.0 + }; + "#, + 24 * 24 * 2, + ); +} + +#[test] +fn closure_and_impl_fn() { + check_number( + r#" + //- minicore: fn, copy + fn closure_wrapper i32>(c: F) -> impl FnOnce() -> F { + || c + } + + const GOAL: i32 = { + let y = 5; + let c = closure_wrapper(|| y); + c()() + }; + "#, + 5, + ); + check_number( + r#" + //- minicore: fn, copy + fn f T>(t: F) -> impl Fn() -> T { + move || t() + } + + const GOAL: i32 = f(|| 2)(); + "#, + 2, + ); +} + +#[test] +fn or_pattern() { + check_number( + r#" + const GOAL: u8 = { + let (a | a) = 2; + a + }; + "#, + 2, + ); + check_number( + r#" + //- minicore: option + const fn f(x: Option) -> i32 { + let (Some(a) | Some(a)) = x else { return 2; }; + a + } + const GOAL: i32 = f(Some(10)) + f(None); + "#, + 12, + ); + check_number( + r#" + //- minicore: option + const fn f(x: Option, y: Option) -> i32 { + match (x, y) { + (Some(x), Some(y)) => x * y, + (Some(a), _) | (_, Some(a)) => a, + _ => 10, + } + } + const GOAL: i32 = f(Some(10), Some(20)) + f(Some(30), None) + f(None, Some(40)) + f(None, None); + "#, + 280, + ); +} + +#[test] +fn function_pointer_in_constants() { + check_number( + r#" + struct Foo { + f: fn(u8) -> u8, + } + const FOO: Foo = Foo { f: add2 }; + fn add2(x: u8) -> u8 { + x + 2 + } + const GOAL: u8 = (FOO.f)(3); + "#, + 5, + ); +} + +#[test] +fn function_pointer() { + check_number( + r#" + fn add2(x: u8) -> u8 { + x + 2 + } + const GOAL: u8 = { + let plus2 = add2; + plus2(3) + }; + "#, + 5, + ); + check_number( + r#" + fn add2(x: u8) -> u8 { + x + 2 + } + const GOAL: u8 = { + let plus2: fn(u8) -> u8 = add2; + plus2(3) + }; + "#, + 5, + ); + check_number( + r#" + //- minicore: coerce_unsized, index, slice + fn add2(x: u8) -> u8 { + x + 2 + } + fn mult3(x: u8) -> u8 { + x * 3 + } + const GOAL: u8 = { + let x = [add2, mult3]; + x[0](1) + x[1](5) + }; + "#, + 18, + ); +} + +#[test] +fn enum_variant_as_function() { + check_number( + r#" + //- minicore: option + const GOAL: u8 = { + let f = Some; + f(3).unwrap_or(2) + }; + "#, + 3, + ); + check_number( + r#" + //- minicore: option + const GOAL: u8 = { + let f: fn(u8) -> Option = Some; + f(3).unwrap_or(2) + }; + "#, + 3, + ); + check_number( + r#" + //- minicore: coerce_unsized, index, slice + enum Foo { + Add2(u8), + Mult3(u8), + } + use Foo::*; + const fn f(x: Foo) -> u8 { + match x { + Add2(x) => x + 2, + Mult3(x) => x * 3, + } + } + const GOAL: u8 = { + let x = [Add2, Mult3]; + f(x[0](1)) + f(x[1](5)) + }; + "#, + 18, ); +} + +#[test] +fn function_traits() { check_number( r#" - //- minicore: option - fn f(x: Option>) -> i32 { - if let Some(y) = x && let Some(z) = y { - z - } else if let Some(y) = x { - 1 - } else { - 0 - } + //- minicore: fn + fn add2(x: u8) -> u8 { + x + 2 } - const GOAL: i32 = f(Some(Some(10))) + f(Some(None)) + f(None); + fn call(f: impl Fn(u8) -> u8, x: u8) -> u8 { + f(x) + } + fn call_mut(mut f: impl FnMut(u8) -> u8, x: u8) -> u8 { + f(x) + } + fn call_once(f: impl FnOnce(u8) -> u8, x: u8) -> u8 { + f(x) + } + const GOAL: u8 = call(add2, 3) + call_mut(add2, 3) + call_once(add2, 3); "#, - 11, + 15, ); check_number( r#" - //- minicore: option + //- minicore: coerce_unsized, fn + fn add2(x: u8) -> u8 { + x + 2 + } + fn call(f: &dyn Fn(u8) -> u8, x: u8) -> u8 { + f(x) + } + fn call_mut(f: &mut dyn FnMut(u8) -> u8, x: u8) -> u8 { + f(x) + } + const GOAL: u8 = call(&add2, 3) + call_mut(&mut add2, 3); + "#, + 10, + ); + check_number( + r#" + //- minicore: fn + fn add2(x: u8) -> u8 { + x + 2 + } + fn call(f: impl Fn(u8) -> u8, x: u8) -> u8 { + f(x) + } + fn call_mut(mut f: impl FnMut(u8) -> u8, x: u8) -> u8 { + f(x) + } + fn call_once(f: impl FnOnce(u8) -> u8, x: u8) -> u8 { + f(x) + } const GOAL: u8 = { - let x = None; - match x { - Some(y) => 2 * y, - _ => 10, - } + let add2: fn(u8) -> u8 = add2; + call(add2, 3) + call_mut(add2, 3) + call_once(add2, 3) }; "#, - 10, + 15, ); check_number( r#" - //- minicore: option - const GOAL: Option<&u8> = None; + //- minicore: fn + fn add2(x: u8) -> u8 { + x + 2 + } + fn call(f: &&&&&impl Fn(u8) -> u8, x: u8) -> u8 { + f(x) + } + const GOAL: u8 = call(&&&&&add2, 3); "#, - 0, + 5, ); } #[test] -fn or_pattern() { +fn dyn_trait() { check_number( r#" + //- minicore: coerce_unsized, index, slice + trait Foo { + fn foo(&self) -> u8 { 10 } + } + struct S1; + struct S2; + struct S3; + impl Foo for S1 { + fn foo(&self) -> u8 { 1 } + } + impl Foo for S2 { + fn foo(&self) -> u8 { 2 } + } + impl Foo for S3 {} const GOAL: u8 = { - let (a | a) = 2; - a + let x: &[&dyn Foo] = &[&S1, &S2, &S3]; + x[0].foo() + x[1].foo() + x[2].foo() }; "#, - 2, + 13, ); check_number( r#" - //- minicore: option - const fn f(x: Option) -> i32 { - let (Some(a) | Some(a)) = x else { return 2; }; - a + //- minicore: coerce_unsized, index, slice + trait Foo { + fn foo(&self) -> i32 { 10 } } - const GOAL: i32 = f(Some(10)) + f(None); + trait Bar { + fn bar(&self) -> i32 { 20 } + } + + struct S; + impl Foo for S { + fn foo(&self) -> i32 { 200 } + } + impl Bar for dyn Foo { + fn bar(&self) -> i32 { 700 } + } + const GOAL: i32 = { + let x: &dyn Foo = &S; + x.bar() + x.foo() + }; "#, - 12, + 900, ); +} + +#[test] +fn boxes() { check_number( r#" - //- minicore: option - const fn f(x: Option, y: Option) -> i32 { - match (x, y) { - (Some(x), Some(y)) => x * y, - (Some(a), _) | (_, Some(a)) => a, - _ => 10, - } +//- minicore: coerce_unsized, deref_mut, slice +use core::ops::{Deref, DerefMut}; +use core::{marker::Unsize, ops::CoerceUnsized}; + +#[lang = "owned_box"] +pub struct Box { + inner: *mut T, +} +impl Box { + fn new(t: T) -> Self { + #[rustc_box] + Box::new(t) } - const GOAL: i32 = f(Some(10), Some(20)) + f(Some(30), None) + f(None, Some(40)) + f(None, None); - "#, - 280, +} + +impl Deref for Box { + type Target = T; + + fn deref(&self) -> &T { + &**self + } +} + +impl DerefMut for Box { + fn deref_mut(&mut self) -> &mut T { + &mut **self + } +} + +impl, U: ?Sized> CoerceUnsized> for Box {} + +const GOAL: usize = { + let x = Box::new(5); + let y: Box<[i32]> = Box::new([1, 2, 3]); + *x + y.len() +}; +"#, + 8, ); } @@ -867,9 +1922,42 @@ fn array_and_index() { check_number( r#" //- minicore: coerce_unsized, index, slice + const GOAL: usize = { + let a = [1, 2, 3]; + let x: &[i32] = &a; + let y = &*x; + y.len() + };"#, + 3, + ); + check_number( + r#" + //- minicore: coerce_unsized, index, slice const GOAL: usize = [1, 2, 3, 4, 5].len();"#, 5, ); + check_number( + r#" + //- minicore: coerce_unsized, index, slice + const GOAL: [u16; 5] = [1, 2, 3, 4, 5];"#, + 1 + (2 << 16) + (3 << 32) + (4 << 48) + (5 << 64), + ); + check_number( + r#" + //- minicore: coerce_unsized, index, slice + const GOAL: [u16; 5] = [12; 5];"#, + 12 + (12 << 16) + (12 << 32) + (12 << 48) + (12 << 64), + ); + check_number( + r#" + //- minicore: coerce_unsized, index, slice + const LEN: usize = 4; + const GOAL: u16 = { + let x = [7; LEN]; + x[2] + }"#, + 7, + ); } #[test] @@ -887,6 +1975,38 @@ fn byte_string() { ); } +#[test] +fn c_string() { + check_number( + r#" +//- minicore: index, slice +#[lang = "CStr"] +pub struct CStr { + inner: [u8] +} +const GOAL: u8 = { + let a = c"hello"; + a.inner[0] +}; + "#, + 104, + ); + check_number( + r#" +//- minicore: index, slice +#[lang = "CStr"] +pub struct CStr { + inner: [u8] +} +const GOAL: u8 = { + let a = c"hello"; + a.inner[6] +}; + "#, + 0, + ); +} + #[test] fn consts() { check_number( @@ -900,6 +2020,37 @@ fn consts() { ); } +#[test] +fn statics() { + check_number( + r#" + //- minicore: cell + use core::cell::Cell; + fn f() -> i32 { + static S: Cell = Cell::new(10); + S.set(S.get() + 1); + S.get() + } + const GOAL: i32 = f() + f() + f(); + "#, + 36, + ); +} + +#[test] +fn extern_weak_statics() { + check_number( + r#" + extern "C" { + #[linkage = "extern_weak"] + static __dso_handle: *mut u8; + } + const GOAL: usize = __dso_handle as usize; + "#, + 0, + ); +} + #[test] fn enums() { check_number( @@ -927,14 +2078,14 @@ fn enums() { "#, 0, ); - let r = eval_goal( + let (db, file_id) = TestDB::with_single_file( r#" enum E { A = 1, B } const GOAL: E = E::A; "#, - ) - .unwrap(); - assert_eq!(try_const_usize(&r), Some(1)); + ); + let r = eval_goal(&db, file_id).unwrap(); + assert_eq!(try_const_usize(&db, &r), Some(1)); } #[test] @@ -946,7 +2097,7 @@ fn const_loop() { const F2: i32 = 2 * F1; const GOAL: i32 = F3; "#, - ConstEvalError::MirLowerError(MirLowerError::Loop), + |e| e == ConstEvalError::MirLowerError(MirLowerError::Loop), ); } @@ -962,6 +2113,29 @@ fn const_transfer_memory() { ); } +#[test] +fn anonymous_const_block() { + check_number( + r#" + extern "rust-intrinsic" { + pub fn size_of() -> usize; + } + + const fn f() -> usize { + let r = const { size_of::() }; + r + } + + const GOAL: usize = { + let x = const { 2 + const { 3 } }; + let y = f::(); + x + y + }; + "#, + 9, + ); +} + #[test] fn const_impl_assoc() { check_number( @@ -970,9 +2144,9 @@ fn const_impl_assoc() { impl U5 { const VAL: usize = 5; } - const GOAL: usize = U5::VAL; + const GOAL: usize = U5::VAL + ::VAL; "#, - 5, + 10, ); } @@ -987,12 +2161,61 @@ fn const_generic_subst_fn() { "#, 11, ); + check_number( + r#" + fn f(x: [i32; N]) -> usize { + N + } + + trait ArrayExt { + fn f(self) -> usize; + } + + impl ArrayExt for [T; N] { + fn g(self) -> usize { + f(self) + } + } + + const GOAL: usize = f([1, 2, 5]); + "#, + 3, + ); +} + +#[test] +fn layout_of_type_with_associated_type_field_defined_inside_body() { + check_number( + r#" +trait Tr { + type Ty; +} + +struct St(T::Ty); + +const GOAL: i64 = { + // if we move `St2` out of body, the test will fail, as we don't see the impl anymore. That + // case will probably be rejected by rustc in some later edition, but we should support this + // case. + struct St2; + + impl Tr for St2 { + type Ty = i64; + } + + struct Goal(St); + + let x = Goal(St(5)); + x.0.0 +}; +"#, + 5, + ); } #[test] fn const_generic_subst_assoc_const_impl() { - // FIXME: this should evaluate to 5 - check_fail( + check_number( r#" struct Adder; impl Adder { @@ -1000,14 +2223,42 @@ fn const_generic_subst_assoc_const_impl() { } const GOAL: usize = Adder::<2, 3>::VAL; "#, - ConstEvalError::MirEvalError(MirEvalError::TypeError("missing generic arg")), + 5, + ); +} + +#[test] +fn associated_types() { + check_number( + r#" + trait Tr { + type Item; + fn get_item(&self) -> Self::Item; + } + + struct X(i32); + struct Y(i32); + + impl Tr for X { + type Item = Y; + fn get_item(&self) -> Self::Item { + Y(self.0 + 2) + } + } + + fn my_get_item(x: T) -> ::Item { + x.get_item() + } + + const GOAL: i32 = my_get_item(X(3)).0; + "#, + 5, ); } #[test] fn const_trait_assoc() { - // FIXME: this should evaluate to 0 - check_fail( + check_number( r#" struct U0; trait ToConst { @@ -1016,9 +2267,49 @@ fn const_trait_assoc() { impl ToConst for U0 { const VAL: usize = 0; } - const GOAL: usize = U0::VAL; + impl ToConst for i32 { + const VAL: usize = 32; + } + const GOAL: usize = U0::VAL + i32::VAL; + "#, + 32, + ); + check_number( + r#" + struct S(*mut T); + + trait MySized: Sized { + const SIZE: S = S(1 as *mut Self); + } + + impl MySized for i32 { + const SIZE: S = S(10 as *mut i32); + } + + impl MySized for i64 { + } + + const fn f() -> usize { + T::SIZE.0 as usize + } + + const GOAL: usize = f::() + f::() * 2; + "#, + 12, + ); +} + +#[test] +fn panic_messages() { + check_fail( + r#" + //- minicore: panic + const GOAL: u8 = { + let x: u16 = 2; + panic!("hello"); + }; "#, - ConstEvalError::MirLowerError(MirLowerError::IncompleteExpr), + |e| e == ConstEvalError::MirEvalError(MirEvalError::Panic("hello".to_string())), ); } @@ -1028,7 +2319,7 @@ fn exec_limits() { r#" const GOAL: usize = loop {}; "#, - ConstEvalError::MirEvalError(MirEvalError::ExecutionLimitExceeded), + |e| e == ConstEvalError::MirEvalError(MirEvalError::ExecutionLimitExceeded), ); check_fail( r#" @@ -1037,7 +2328,7 @@ fn exec_limits() { } const GOAL: i32 = f(0); "#, - ConstEvalError::MirEvalError(MirEvalError::StackOverflow), + |e| e == ConstEvalError::MirEvalError(MirEvalError::StackOverflow), ); // Reasonable code should still work check_number( @@ -1062,7 +2353,7 @@ fn exec_limits() { #[test] fn type_error() { - let e = eval_goal( + check_fail( r#" const GOAL: u8 = { let x: u16 = 2; @@ -1070,6 +2361,25 @@ fn type_error() { y.0 }; "#, + |e| matches!(e, ConstEvalError::MirLowerError(MirLowerError::TypeMismatch(_))), + ); +} + +#[test] +fn unsized_local() { + check_fail( + r#" + //- minicore: coerce_unsized, index, slice + const fn x() -> SomeUnknownTypeThatDereferenceToSlice { + SomeUnknownTypeThatDereferenceToSlice + } + + const GOAL: u16 = { + let y = x(); + let z: &[u16] = &y; + z[1] + }; + "#, + |e| matches!(e, ConstEvalError::MirLowerError(MirLowerError::UnsizedTemporary(_))), ); - assert!(matches!(e, Err(ConstEvalError::MirLowerError(MirLowerError::TypeMismatch(_))))); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs new file mode 100644 index 0000000000000..e05d824dbacfc --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs @@ -0,0 +1,377 @@ +use super::*; + +#[test] +fn size_of() { + check_number( + r#" + extern "rust-intrinsic" { + pub fn size_of() -> usize; + } + + const GOAL: usize = size_of::(); + "#, + 4, + ); +} + +#[test] +fn transmute() { + check_number( + r#" + extern "rust-intrinsic" { + pub fn transmute(e: T) -> U; + } + + const GOAL: i32 = transmute((1i16, 1i16)); + "#, + 0x00010001, + ); +} + +#[test] +fn const_eval_select() { + check_number( + r#" + extern "rust-intrinsic" { + pub fn const_eval_select(arg: ARG, called_in_const: F, called_at_rt: G) -> RET + where + G: FnOnce, + F: FnOnce; + } + + const fn in_const(x: i32, y: i32) -> i32 { + x + y + } + + fn in_rt(x: i32, y: i32) -> i32 { + x + y + } + + const GOAL: i32 = const_eval_select((2, 3), in_const, in_rt); + "#, + 5, + ); +} + +#[test] +fn wrapping_add() { + check_number( + r#" + extern "rust-intrinsic" { + pub fn wrapping_add(a: T, b: T) -> T; + } + + const GOAL: u8 = wrapping_add(10, 250); + "#, + 4, + ); +} + +#[test] +fn saturating_add() { + check_number( + r#" + extern "rust-intrinsic" { + pub fn saturating_add(a: T, b: T) -> T; + } + + const GOAL: u8 = saturating_add(10, 250); + "#, + 255, + ); + check_number( + r#" + extern "rust-intrinsic" { + pub fn saturating_add(a: T, b: T) -> T; + } + + const GOAL: i8 = saturating_add(5, 8); + "#, + 13, + ); +} + +#[test] +fn allocator() { + check_number( + r#" + extern "Rust" { + #[rustc_allocator] + fn __rust_alloc(size: usize, align: usize) -> *mut u8; + #[rustc_deallocator] + fn __rust_dealloc(ptr: *mut u8, size: usize, align: usize); + #[rustc_reallocator] + fn __rust_realloc(ptr: *mut u8, old_size: usize, align: usize, new_size: usize) -> *mut u8; + #[rustc_allocator_zeroed] + fn __rust_alloc_zeroed(size: usize, align: usize) -> *mut u8; + } + + const GOAL: u8 = unsafe { + let ptr = __rust_alloc(4, 1); + let ptr2 = ((ptr as usize) + 1) as *mut u8; + *ptr = 23; + *ptr2 = 32; + let ptr = __rust_realloc(ptr, 4, 1, 8); + let ptr2 = ((ptr as usize) + 1) as *mut u8; + *ptr + *ptr2 + }; + "#, + 55, + ); +} + +#[test] +fn overflowing_add() { + check_number( + r#" + extern "rust-intrinsic" { + pub fn add_with_overflow(x: T, y: T) -> (T, bool); + } + + const GOAL: u8 = add_with_overflow(1, 2).0; + "#, + 3, + ); + check_number( + r#" + extern "rust-intrinsic" { + pub fn add_with_overflow(x: T, y: T) -> (T, bool); + } + + const GOAL: u8 = add_with_overflow(1, 2).1 as u8; + "#, + 0, + ); +} + +#[test] +fn needs_drop() { + check_number( + r#" + //- minicore: copy, sized + extern "rust-intrinsic" { + pub fn needs_drop() -> bool; + } + struct X; + const GOAL: bool = !needs_drop::() && needs_drop::(); + "#, + 1, + ); +} + +#[test] +fn likely() { + check_number( + r#" + extern "rust-intrinsic" { + pub fn likely(b: bool) -> bool; + pub fn unlikely(b: bool) -> bool; + } + + const GOAL: bool = likely(true) && unlikely(true) && !likely(false) && !unlikely(false); + "#, + 1, + ); +} + +#[test] +fn floating_point() { + check_number( + r#" + extern "rust-intrinsic" { + pub fn sqrtf32(x: f32) -> f32; + pub fn powf32(a: f32, x: f32) -> f32; + pub fn fmaf32(a: f32, b: f32, c: f32) -> f32; + } + + const GOAL: f32 = sqrtf32(1.2) + powf32(3.4, 5.6) + fmaf32(-7.8, 1.3, 2.4); + "#, + i128::from_le_bytes(pad16( + &f32::to_le_bytes(1.2f32.sqrt() + 3.4f32.powf(5.6) + (-7.8f32).mul_add(1.3, 2.4)), + true, + )), + ); + check_number( + r#" + extern "rust-intrinsic" { + pub fn powif64(a: f64, x: i32) -> f64; + pub fn sinf64(x: f64) -> f64; + pub fn minnumf64(x: f64, y: f64) -> f64; + } + + const GOAL: f64 = powif64(1.2, 5) + sinf64(3.4) + minnumf64(-7.8, 1.3); + "#, + i128::from_le_bytes(pad16( + &f64::to_le_bytes(1.2f64.powi(5) + 3.4f64.sin() + (-7.8f64).min(1.3)), + true, + )), + ); +} + +#[test] +fn atomic() { + check_number( + r#" + //- minicore: copy + extern "rust-intrinsic" { + pub fn atomic_load_seqcst(src: *const T) -> T; + pub fn atomic_xchg_acquire(dst: *mut T, src: T) -> T; + pub fn atomic_cxchg_release_seqcst(dst: *mut T, old: T, src: T) -> (T, bool); + pub fn atomic_cxchgweak_acquire_acquire(dst: *mut T, old: T, src: T) -> (T, bool); + pub fn atomic_store_release(dst: *mut T, val: T); + pub fn atomic_xadd_acqrel(dst: *mut T, src: T) -> T; + pub fn atomic_xsub_seqcst(dst: *mut T, src: T) -> T; + pub fn atomic_and_acquire(dst: *mut T, src: T) -> T; + pub fn atomic_nand_seqcst(dst: *mut T, src: T) -> T; + pub fn atomic_or_release(dst: *mut T, src: T) -> T; + pub fn atomic_xor_seqcst(dst: *mut T, src: T) -> T; + } + + fn should_not_reach() { + _ // fails the test if executed + } + + const GOAL: i32 = { + let mut x = 5; + atomic_store_release(&mut x, 10); + let mut y = atomic_xchg_acquire(&mut x, 100); + atomic_xadd_acqrel(&mut y, 20); + if (30, true) != atomic_cxchg_release_seqcst(&mut y, 30, 40) { + should_not_reach(); + } + if (40, false) != atomic_cxchg_release_seqcst(&mut y, 30, 50) { + should_not_reach(); + } + if (40, true) != atomic_cxchgweak_acquire_acquire(&mut y, 40, 30) { + should_not_reach(); + } + let mut z = atomic_xsub_seqcst(&mut x, -200); + atomic_xor_seqcst(&mut x, 1024); + atomic_load_seqcst(&x) + z * 3 + atomic_load_seqcst(&y) * 2 + }; + "#, + 660 + 1024, + ); +} + +#[test] +fn offset() { + check_number( + r#" + //- minicore: coerce_unsized, index, slice + extern "rust-intrinsic" { + pub fn offset(dst: *const T, offset: isize) -> *const T; + } + + const GOAL: u8 = unsafe { + let ar: &[(u8, u8, u8)] = &[ + (10, 11, 12), + (20, 21, 22), + (30, 31, 32), + (40, 41, 42), + (50, 51, 52), + ]; + let ar: *const [(u8, u8, u8)] = ar; + let ar = ar as *const (u8, u8, u8); + let element = *offset(ar, 2); + element.1 + }; + "#, + 31, + ); +} + +#[test] +fn arith_offset() { + check_number( + r#" + //- minicore: coerce_unsized, index, slice + extern "rust-intrinsic" { + pub fn arith_offset(dst: *const T, offset: isize) -> *const T; + } + + const GOAL: u8 = unsafe { + let ar: &[(u8, u8, u8)] = &[ + (10, 11, 12), + (20, 21, 22), + (30, 31, 32), + (40, 41, 42), + (50, 51, 52), + ]; + let ar: *const [(u8, u8, u8)] = ar; + let ar = ar as *const (u8, u8, u8); + let element = *arith_offset(arith_offset(ar, 102), -100); + element.1 + }; + "#, + 31, + ); +} + +#[test] +fn copy_nonoverlapping() { + check_number( + r#" + extern "rust-intrinsic" { + pub fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize); + } + + const GOAL: u8 = unsafe { + let mut x = 2; + let y = 5; + copy_nonoverlapping(&y, &mut x, 1); + x + }; + "#, + 5, + ); +} + +#[test] +fn copy() { + check_number( + r#" + //- minicore: coerce_unsized, index, slice + extern "rust-intrinsic" { + pub fn copy(src: *const T, dst: *mut T, count: usize); + } + + const GOAL: i32 = unsafe { + let mut x = [1i32, 2, 3, 4, 5]; + let y = (&mut x as *mut _) as *mut i32; + let z = (y as usize + 4) as *const i32; + copy(z, y, 4); + x[0] + x[1] + x[2] + x[3] + x[4] + }; + "#, + 19, + ); +} + +#[test] +fn ctpop() { + check_number( + r#" + extern "rust-intrinsic" { + pub fn ctpop(x: T) -> T; + } + + const GOAL: i64 = ctpop(-29); + "#, + 61, + ); +} + +#[test] +fn cttz() { + check_number( + r#" + extern "rust-intrinsic" { + pub fn cttz(x: T) -> T; + } + + const GOAL: i64 = cttz(-24); + "#, + 3, + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index 304c78767f129..ca8a394e360dc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -1,27 +1,27 @@ //! The home of `HirDatabase`, which is the Salsa database containing all the //! type inference-related queries. -use std::sync::Arc; +use std::sync; use base_db::{impl_intern_key, salsa, CrateId, Upcast}; use hir_def::{ - db::DefDatabase, - expr::ExprId, - layout::{Layout, LayoutError, TargetDataLayout}, - AdtId, BlockId, ConstId, ConstParamId, DefWithBodyId, EnumVariantId, FunctionId, GenericDefId, - ImplId, LifetimeParamId, LocalFieldId, TypeOrConstParamId, VariantId, + db::DefDatabase, hir::ExprId, layout::TargetDataLayout, AdtId, BlockId, ConstParamId, + DefWithBodyId, EnumVariantId, FunctionId, GeneralConstId, GenericDefId, ImplId, + LifetimeParamId, LocalFieldId, StaticId, TypeOrConstParamId, VariantId, }; use la_arena::ArenaMap; use smallvec::SmallVec; +use triomphe::Arc; use crate::{ chalk_db, consteval::ConstEvalError, + layout::{Layout, LayoutError}, method_resolution::{InherentImpls, TraitImpls, TyFingerprint}, mir::{BorrowckResult, MirBody, MirLowerError}, - Binders, CallableDefId, Const, FnDefId, GenericArg, ImplTraitId, InferenceResult, Interner, - PolyFnSig, QuantifiedWhereClause, ReturnTypeImplTraits, Substitution, TraitRef, Ty, TyDefId, - ValueTyDefId, + Binders, CallableDefId, ClosureId, Const, FnDefId, GenericArg, ImplTraitId, InferenceResult, + Interner, PolyFnSig, QuantifiedWhereClause, ReturnTypeImplTraits, Substitution, TraitRef, Ty, + TyDefId, ValueTyDefId, }; use hir_expand::name::Name; @@ -38,8 +38,28 @@ pub trait HirDatabase: DefDatabase + Upcast { #[salsa::cycle(crate::mir::mir_body_recover)] fn mir_body(&self, def: DefWithBodyId) -> Result, MirLowerError>; + #[salsa::invoke(crate::mir::mir_body_for_closure_query)] + fn mir_body_for_closure(&self, def: ClosureId) -> Result, MirLowerError>; + + #[salsa::invoke(crate::mir::monomorphized_mir_body_query)] + #[salsa::cycle(crate::mir::monomorphized_mir_body_recover)] + fn monomorphized_mir_body( + &self, + def: DefWithBodyId, + subst: Substitution, + env: Arc, + ) -> Result, MirLowerError>; + + #[salsa::invoke(crate::mir::monomorphized_mir_body_for_closure_query)] + fn monomorphized_mir_body_for_closure( + &self, + def: ClosureId, + subst: Substitution, + env: Arc, + ) -> Result, MirLowerError>; + #[salsa::invoke(crate::mir::borrowck_query)] - fn borrowck(&self, def: DefWithBodyId) -> Result, MirLowerError>; + fn borrowck(&self, def: DefWithBodyId) -> Result, MirLowerError>; #[salsa::invoke(crate::lower::ty_query)] #[salsa::cycle(crate::lower::ty_recover)] @@ -57,7 +77,12 @@ pub trait HirDatabase: DefDatabase + Upcast { #[salsa::invoke(crate::consteval::const_eval_query)] #[salsa::cycle(crate::consteval::const_eval_recover)] - fn const_eval(&self, def: ConstId) -> Result; + fn const_eval(&self, def: GeneralConstId, subst: Substitution) + -> Result; + + #[salsa::invoke(crate::consteval::const_eval_static_query)] + #[salsa::cycle(crate::consteval::const_eval_static_recover)] + fn const_eval_static(&self, def: StaticId) -> Result; #[salsa::invoke(crate::consteval::const_eval_discriminant_variant)] #[salsa::cycle(crate::consteval::const_eval_discriminant_recover)] @@ -71,7 +96,16 @@ pub trait HirDatabase: DefDatabase + Upcast { #[salsa::invoke(crate::layout::layout_of_adt_query)] #[salsa::cycle(crate::layout::layout_of_adt_recover)] - fn layout_of_adt(&self, def: AdtId, subst: Substitution) -> Result; + fn layout_of_adt( + &self, + def: AdtId, + subst: Substitution, + krate: CrateId, + ) -> Result, LayoutError>; + + #[salsa::invoke(crate::layout::layout_of_ty_query)] + #[salsa::cycle(crate::layout::layout_of_ty_recover)] + fn layout_of_ty(&self, ty: Ty, krate: CrateId) -> Result, LayoutError>; #[salsa::invoke(crate::layout::target_data_layout_query)] fn target_data_layout(&self, krate: CrateId) -> Option>; @@ -97,6 +131,10 @@ pub trait HirDatabase: DefDatabase + Upcast { #[salsa::invoke(crate::lower::generic_predicates_query)] fn generic_predicates(&self, def: GenericDefId) -> Arc<[Binders]>; + #[salsa::invoke(crate::lower::trait_environment_for_body_query)] + #[salsa::transparent] + fn trait_environment_for_body(&self, def: DefWithBodyId) -> Arc; + #[salsa::invoke(crate::lower::trait_environment_query)] fn trait_environment(&self, def: GenericDefId) -> Arc; @@ -108,7 +146,7 @@ pub trait HirDatabase: DefDatabase + Upcast { fn inherent_impls_in_crate(&self, krate: CrateId) -> Arc; #[salsa::invoke(InherentImpls::inherent_impls_in_block_query)] - fn inherent_impls_in_block(&self, block: BlockId) -> Option>; + fn inherent_impls_in_block(&self, block: BlockId) -> Arc; /// Collects all crates in the dependency graph that have impls for the /// given fingerprint. This is only used for primitive types and types @@ -125,10 +163,10 @@ pub trait HirDatabase: DefDatabase + Upcast { fn trait_impls_in_crate(&self, krate: CrateId) -> Arc; #[salsa::invoke(TraitImpls::trait_impls_in_block_query)] - fn trait_impls_in_block(&self, krate: BlockId) -> Option>; + fn trait_impls_in_block(&self, block: BlockId) -> Arc; #[salsa::invoke(TraitImpls::trait_impls_in_deps_query)] - fn trait_impls_in_deps(&self, krate: CrateId) -> Arc; + fn trait_impls_in_deps(&self, krate: CrateId) -> Arc<[Arc]>; // Interned IDs for Chalk integration #[salsa::interned] @@ -148,24 +186,34 @@ pub trait HirDatabase: DefDatabase + Upcast { fn intern_generator(&self, id: (DefWithBodyId, ExprId)) -> InternedGeneratorId; #[salsa::invoke(chalk_db::associated_ty_data_query)] - fn associated_ty_data(&self, id: chalk_db::AssocTypeId) -> Arc; + fn associated_ty_data( + &self, + id: chalk_db::AssocTypeId, + ) -> sync::Arc; #[salsa::invoke(chalk_db::trait_datum_query)] - fn trait_datum(&self, krate: CrateId, trait_id: chalk_db::TraitId) - -> Arc; + fn trait_datum( + &self, + krate: CrateId, + trait_id: chalk_db::TraitId, + ) -> sync::Arc; #[salsa::invoke(chalk_db::struct_datum_query)] fn struct_datum( &self, krate: CrateId, struct_id: chalk_db::AdtId, - ) -> Arc; + ) -> sync::Arc; #[salsa::invoke(chalk_db::impl_datum_query)] - fn impl_datum(&self, krate: CrateId, impl_id: chalk_db::ImplId) -> Arc; + fn impl_datum( + &self, + krate: CrateId, + impl_id: chalk_db::ImplId, + ) -> sync::Arc; #[salsa::invoke(chalk_db::fn_def_datum_query)] - fn fn_def_datum(&self, krate: CrateId, fn_def_id: FnDefId) -> Arc; + fn fn_def_datum(&self, krate: CrateId, fn_def_id: FnDefId) -> sync::Arc; #[salsa::invoke(chalk_db::fn_def_variance_query)] fn fn_def_variance(&self, fn_def_id: FnDefId) -> chalk_db::Variances; @@ -178,7 +226,7 @@ pub trait HirDatabase: DefDatabase + Upcast { &self, krate: CrateId, id: chalk_db::AssociatedTyValueId, - ) -> Arc; + ) -> sync::Arc; #[salsa::invoke(crate::traits::normalize_projection_query)] #[salsa::transparent] @@ -193,6 +241,7 @@ pub trait HirDatabase: DefDatabase + Upcast { fn trait_solve( &self, krate: CrateId, + block: Option, goal: crate::Canonical>, ) -> Option; @@ -200,6 +249,7 @@ pub trait HirDatabase: DefDatabase + Upcast { fn trait_solve_query( &self, krate: CrateId, + block: Option, goal: crate::Canonical>, ) -> Option; @@ -207,19 +257,26 @@ pub trait HirDatabase: DefDatabase + Upcast { fn program_clauses_for_chalk_env( &self, krate: CrateId, + block: Option, env: chalk_ir::Environment, ) -> chalk_ir::ProgramClauses; } fn infer_wait(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc { let _p = profile::span("infer:wait").detail(|| match def { - DefWithBodyId::FunctionId(it) => db.function_data(it).name.to_string(), - DefWithBodyId::StaticId(it) => db.static_data(it).name.clone().to_string(), - DefWithBodyId::ConstId(it) => { - db.const_data(it).name.clone().unwrap_or_else(Name::missing).to_string() + DefWithBodyId::FunctionId(it) => db.function_data(it).name.display(db.upcast()).to_string(), + DefWithBodyId::StaticId(it) => { + db.static_data(it).name.clone().display(db.upcast()).to_string() } + DefWithBodyId::ConstId(it) => db + .const_data(it) + .name + .clone() + .unwrap_or_else(Name::missing) + .display(db.upcast()) + .to_string(), DefWithBodyId::VariantId(it) => { - db.enum_data(it.parent).variants[it.local_id].name.to_string() + db.enum_data(it.parent).variants[it.local_id].name.display(db.upcast()).to_string() } }); db.infer_query(def) @@ -228,10 +285,11 @@ fn infer_wait(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc fn trait_solve_wait( db: &dyn HirDatabase, krate: CrateId, + block: Option, goal: crate::Canonical>, ) -> Option { let _p = profile::span("trait_solve::wait"); - db.trait_solve_query(krate, goal) + db.trait_solve_query(krate, block, goal) } #[test] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs index d36b93e3bdde1..1233469b947b4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs @@ -16,8 +16,8 @@ use std::fmt; use base_db::CrateId; use hir_def::{ - adt::VariantData, - expr::{Pat, PatId}, + data::adt::VariantData, + hir::{Pat, PatId}, src::HasSource, AdtId, AttrDefId, ConstId, EnumId, FunctionId, ItemContainerId, Lookup, ModuleDefId, StaticId, StructId, @@ -223,7 +223,7 @@ impl<'a> DeclValidator<'a> { } // Check the function name. - let function_name = data.name.to_string(); + let function_name = data.name.display(self.db.upcast()).to_string(); let fn_name_replacement = to_lower_snake_case(&function_name).map(|new_name| Replacement { current_name: data.name.clone(), suggested_text: new_name, @@ -244,7 +244,9 @@ impl<'a> DeclValidator<'a> { id, Replacement { current_name: bind_name.clone(), - suggested_text: to_lower_snake_case(&bind_name.to_string())?, + suggested_text: to_lower_snake_case( + &bind_name.display(self.db.upcast()).to_string(), + )?, expected_case: CaseType::LowerSnakeCase, }, )) @@ -287,7 +289,7 @@ impl<'a> DeclValidator<'a> { ident_type: IdentType::Function, ident: AstPtr::new(&ast_ptr), expected_case: fn_name_replacement.expected_case, - ident_text: fn_name_replacement.current_name.to_string(), + ident_text: fn_name_replacement.current_name.display(self.db.upcast()).to_string(), suggested_text: fn_name_replacement.suggested_text, }; @@ -343,7 +345,10 @@ impl<'a> DeclValidator<'a> { ident_type, ident: AstPtr::new(&name_ast), expected_case: replacement.expected_case, - ident_text: replacement.current_name.to_string(), + ident_text: replacement + .current_name + .display(self.db.upcast()) + .to_string(), suggested_text: replacement.suggested_text, }; @@ -362,7 +367,7 @@ impl<'a> DeclValidator<'a> { let non_snake_case_allowed = self.allowed(struct_id.into(), allow::NON_SNAKE_CASE, false); // Check the structure name. - let struct_name = data.name.to_string(); + let struct_name = data.name.display(self.db.upcast()).to_string(); let struct_name_replacement = if !non_camel_case_allowed { to_camel_case(&struct_name).map(|new_name| Replacement { current_name: data.name.clone(), @@ -379,7 +384,7 @@ impl<'a> DeclValidator<'a> { if !non_snake_case_allowed { if let VariantData::Record(fields) = data.variant_data.as_ref() { for (_, field) in fields.iter() { - let field_name = field.name.to_string(); + let field_name = field.name.display(self.db.upcast()).to_string(); if let Some(new_name) = to_lower_snake_case(&field_name) { let replacement = Replacement { current_name: field.name.clone(), @@ -434,7 +439,7 @@ impl<'a> DeclValidator<'a> { ident_type: IdentType::Structure, ident: AstPtr::new(&ast_ptr), expected_case: replacement.expected_case, - ident_text: replacement.current_name.to_string(), + ident_text: replacement.current_name.display(self.db.upcast()).to_string(), suggested_text: replacement.suggested_text, }; @@ -479,7 +484,7 @@ impl<'a> DeclValidator<'a> { ident_type: IdentType::Field, ident: AstPtr::new(&ast_ptr), expected_case: field_to_rename.expected_case, - ident_text: field_to_rename.current_name.to_string(), + ident_text: field_to_rename.current_name.display(self.db.upcast()).to_string(), suggested_text: field_to_rename.suggested_text, }; @@ -496,7 +501,7 @@ impl<'a> DeclValidator<'a> { } // Check the enum name. - let enum_name = data.name.to_string(); + let enum_name = data.name.display(self.db.upcast()).to_string(); let enum_name_replacement = to_camel_case(&enum_name).map(|new_name| Replacement { current_name: data.name.clone(), suggested_text: new_name, @@ -510,7 +515,9 @@ impl<'a> DeclValidator<'a> { .filter_map(|(_, variant)| { Some(Replacement { current_name: variant.name.clone(), - suggested_text: to_camel_case(&variant.name.to_string())?, + suggested_text: to_camel_case( + &variant.name.display(self.db.upcast()).to_string(), + )?, expected_case: CaseType::UpperCamelCase, }) }) @@ -558,7 +565,7 @@ impl<'a> DeclValidator<'a> { ident_type: IdentType::Enum, ident: AstPtr::new(&ast_ptr), expected_case: replacement.expected_case, - ident_text: replacement.current_name.to_string(), + ident_text: replacement.current_name.display(self.db.upcast()).to_string(), suggested_text: replacement.suggested_text, }; @@ -603,7 +610,7 @@ impl<'a> DeclValidator<'a> { ident_type: IdentType::Variant, ident: AstPtr::new(&ast_ptr), expected_case: variant_to_rename.expected_case, - ident_text: variant_to_rename.current_name.to_string(), + ident_text: variant_to_rename.current_name.display(self.db.upcast()).to_string(), suggested_text: variant_to_rename.suggested_text, }; @@ -623,7 +630,7 @@ impl<'a> DeclValidator<'a> { None => return, }; - let const_name = name.to_string(); + let const_name = name.display(self.db.upcast()).to_string(); let replacement = if let Some(new_name) = to_upper_snake_case(&const_name) { Replacement { current_name: name.clone(), @@ -648,7 +655,7 @@ impl<'a> DeclValidator<'a> { ident_type: IdentType::Constant, ident: AstPtr::new(&ast_ptr), expected_case: replacement.expected_case, - ident_text: replacement.current_name.to_string(), + ident_text: replacement.current_name.display(self.db.upcast()).to_string(), suggested_text: replacement.suggested_text, }; @@ -668,7 +675,7 @@ impl<'a> DeclValidator<'a> { let name = &data.name; - let static_name = name.to_string(); + let static_name = name.display(self.db.upcast()).to_string(); let replacement = if let Some(new_name) = to_upper_snake_case(&static_name) { Replacement { current_name: name.clone(), @@ -693,7 +700,7 @@ impl<'a> DeclValidator<'a> { ident_type: IdentType::StaticVariable, ident: AstPtr::new(&ast_ptr), expected_case: replacement.expected_case, - ident_text: replacement.current_name.to_string(), + ident_text: replacement.current_name.display(self.db.upcast()).to_string(), suggested_text: replacement.suggested_text, }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs index 2e9066788cf62..ab34dc88d87bc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs @@ -3,7 +3,6 @@ //! fields, etc. use std::fmt; -use std::sync::Arc; use either::Either; use hir_def::lang_item::LangItem; @@ -12,6 +11,7 @@ use hir_def::{ItemContainerId, Lookup}; use hir_expand::name; use itertools::Itertools; use rustc_hash::FxHashSet; +use triomphe::Arc; use typed_arena::Arena; use crate::{ @@ -27,7 +27,7 @@ use crate::{ pub(crate) use hir_def::{ body::Body, - expr::{Expr, ExprId, MatchArm, Pat, PatId}, + hir::{Expr, ExprId, MatchArm, Pat, PatId}, LocalFieldId, VariantId, }; @@ -207,7 +207,7 @@ impl ExprValidator { let report = compute_match_usefulness(&cx, &m_arms, scrut_ty); - // FIXME Report unreacheble arms + // FIXME Report unreachable arms // https://github.com/rust-lang/rust/blob/f31622a50/compiler/rustc_mir_build/src/thir/pattern/check_match.rs#L200 let witnesses = report.non_exhaustiveness_witnesses; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs index 859a37804ae7f..f8cdeaa5e3549 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs @@ -1,6 +1,6 @@ //! Validation of matches. //! -//! This module provides lowering from [hir_def::expr::Pat] to [self::Pat] and match +//! This module provides lowering from [hir_def::hir::Pat] to [self::Pat] and match //! checking algorithm. //! //! It is modeled on the rustc module `rustc_mir_build::thir::pattern`. @@ -12,7 +12,7 @@ pub(crate) mod usefulness; use chalk_ir::Mutability; use hir_def::{ - adt::VariantData, body::Body, expr::PatId, AdtId, EnumVariantId, LocalFieldId, VariantId, + body::Body, data::adt::VariantData, hir::PatId, AdtId, EnumVariantId, LocalFieldId, VariantId, }; use hir_expand::name::Name; use stdx::{always, never}; @@ -125,15 +125,15 @@ impl<'a> PatCtxt<'a> { let variant = self.infer.variant_resolution_for_pat(pat); let kind = match self.body[pat] { - hir_def::expr::Pat::Wild => PatKind::Wild, + hir_def::hir::Pat::Wild => PatKind::Wild, - hir_def::expr::Pat::Lit(expr) => self.lower_lit(expr), + hir_def::hir::Pat::Lit(expr) => self.lower_lit(expr), - hir_def::expr::Pat::Path(ref path) => { + hir_def::hir::Pat::Path(ref path) => { return self.lower_path(pat, path); } - hir_def::expr::Pat::Tuple { ref args, ellipsis } => { + hir_def::hir::Pat::Tuple { ref args, ellipsis } => { let arity = match *ty.kind(Interner) { TyKind::Tuple(arity, _) => arity, _ => { @@ -146,13 +146,14 @@ impl<'a> PatCtxt<'a> { PatKind::Leaf { subpatterns } } - hir_def::expr::Pat::Bind { id, subpat, .. } => { - let bm = self.infer.pat_binding_modes[&pat]; + hir_def::hir::Pat::Bind { id, subpat, .. } => { + let bm = self.infer.binding_modes[id]; + ty = &self.infer[id]; let name = &self.body.bindings[id].name; match (bm, ty.kind(Interner)) { (BindingMode::Ref(_), TyKind::Ref(.., rty)) => ty = rty, (BindingMode::Ref(_), _) => { - never!("`ref {}` has wrong type {:?}", name, ty); + never!("`ref {}` has wrong type {:?}", name.display(self.db.upcast()), ty); self.errors.push(PatternError::UnexpectedType); return Pat { ty: ty.clone(), kind: PatKind::Wild.into() }; } @@ -161,13 +162,13 @@ impl<'a> PatCtxt<'a> { PatKind::Binding { name: name.clone(), subpattern: self.lower_opt_pattern(subpat) } } - hir_def::expr::Pat::TupleStruct { ref args, ellipsis, .. } if variant.is_some() => { + hir_def::hir::Pat::TupleStruct { ref args, ellipsis, .. } if variant.is_some() => { let expected_len = variant.unwrap().variant_data(self.db.upcast()).fields().len(); let subpatterns = self.lower_tuple_subpats(args, expected_len, ellipsis); self.lower_variant_or_leaf(pat, ty, subpatterns) } - hir_def::expr::Pat::Record { ref args, .. } if variant.is_some() => { + hir_def::hir::Pat::Record { ref args, .. } if variant.is_some() => { let variant_data = variant.unwrap().variant_data(self.db.upcast()); let subpatterns = args .iter() @@ -187,12 +188,12 @@ impl<'a> PatCtxt<'a> { } } } - hir_def::expr::Pat::TupleStruct { .. } | hir_def::expr::Pat::Record { .. } => { + hir_def::hir::Pat::TupleStruct { .. } | hir_def::hir::Pat::Record { .. } => { self.errors.push(PatternError::UnresolvedVariant); PatKind::Wild } - hir_def::expr::Pat::Or(ref pats) => PatKind::Or { pats: self.lower_patterns(pats) }, + hir_def::hir::Pat::Or(ref pats) => PatKind::Or { pats: self.lower_patterns(pats) }, _ => { self.errors.push(PatternError::Unimplemented); @@ -279,8 +280,8 @@ impl<'a> PatCtxt<'a> { } } - fn lower_lit(&mut self, expr: hir_def::expr::ExprId) -> PatKind { - use hir_def::expr::{Expr, Literal::Bool}; + fn lower_lit(&mut self, expr: hir_def::hir::ExprId) -> PatKind { + use hir_def::hir::{Expr, Literal::Bool}; match self.body[expr] { Expr::Literal(Bool(value)) => PatKind::LiteralBool { value }, @@ -297,7 +298,7 @@ impl HirDisplay for Pat { match &*self.kind { PatKind::Wild => write!(f, "_"), PatKind::Binding { name, subpattern } => { - write!(f, "{name}")?; + write!(f, "{}", name.display(f.db.upcast()))?; if let Some(subpattern) = subpattern { write!(f, " @ ")?; subpattern.hir_fmt(f)?; @@ -318,10 +319,14 @@ impl HirDisplay for Pat { match variant { VariantId::EnumVariantId(v) => { let data = f.db.enum_data(v.parent); - write!(f, "{}", data.variants[v.local_id].name)?; + write!(f, "{}", data.variants[v.local_id].name.display(f.db.upcast()))?; + } + VariantId::StructId(s) => { + write!(f, "{}", f.db.struct_data(s).name.display(f.db.upcast()))? + } + VariantId::UnionId(u) => { + write!(f, "{}", f.db.union_data(u).name.display(f.db.upcast()))? } - VariantId::StructId(s) => write!(f, "{}", f.db.struct_data(s).name)?, - VariantId::UnionId(u) => write!(f, "{}", f.db.union_data(u).name)?, }; let variant_data = variant.variant_data(f.db.upcast()); @@ -335,7 +340,11 @@ impl HirDisplay for Pat { .map(|p| { printed += 1; WriteWith(move |f| { - write!(f, "{}: ", rec_fields[p.field].name)?; + write!( + f, + "{}: ", + rec_fields[p.field].name.display(f.db.upcast()) + )?; p.pattern.hir_fmt(f) }) }); @@ -379,7 +388,7 @@ impl HirDisplay for Pat { } PatKind::Deref { subpattern } => { match self.ty.kind(Interner) { - TyKind::Adt(adt, _) if is_box(adt.0, f.db) => write!(f, "box ")?, + TyKind::Adt(adt, _) if is_box(f.db, adt.0) => write!(f, "box ")?, &TyKind::Ref(mutbl, ..) => { write!(f, "&{}", if mutbl == Mutability::Mut { "mut " } else { "" })? } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/deconstruct_pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/deconstruct_pat.rs index d130827a77e84..a0f6b9368ee01 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/deconstruct_pat.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/deconstruct_pat.rs @@ -82,7 +82,7 @@ fn expand_or_pat(pat: &Pat) -> Vec<&Pat> { pats } -/// [Constructor] uses this in umimplemented variants. +/// [Constructor] uses this in unimplemented variants. /// It allows porting match expressions from upstream algorithm without losing semantics. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub(super) enum Void {} @@ -384,7 +384,7 @@ impl Constructor { TyKind::Tuple(arity, ..) => arity, TyKind::Ref(..) => 1, TyKind::Adt(adt, ..) => { - if is_box(adt.0, pcx.cx.db) { + if is_box(pcx.cx.db, adt.0) { // The only legal patterns of type `Box` (outside `std`) are `_` and box // patterns. If we're here we can assume this is a box pattern. 1 @@ -772,7 +772,7 @@ impl<'p> Fields<'p> { (0..fields_len).map(|idx| LocalFieldId::from_raw(idx.into())).filter_map(move |fid| { let ty = field_ty[fid].clone().substitute(Interner, substs); - let ty = normalize(cx.db, cx.body, ty); + let ty = normalize(cx.db, cx.db.trait_environment_for_body(cx.body), ty); let is_visible = matches!(adt, hir_def::AdtId::EnumId(..)) || visibility[fid].is_visible_from(cx.db.upcast(), cx.module); let is_uninhabited = cx.is_uninhabited(&ty); @@ -800,7 +800,7 @@ impl<'p> Fields<'p> { } TyKind::Ref(.., rty) => Fields::wildcards_from_tys(cx, once(rty.clone())), &TyKind::Adt(AdtId(adt), ref substs) => { - if is_box(adt, cx.db) { + if is_box(cx.db, adt) { // The only legal patterns of type `Box` (outside `std`) are `_` and box // patterns. If we're here we can assume this is a box pattern. let subst_ty = substs.at(Interner, 0).assert_ty_ref(Interner).clone(); @@ -905,7 +905,7 @@ impl<'p> DeconstructedPat<'p> { } fields = Fields::from_iter(cx, wilds) } - TyKind::Adt(adt, substs) if is_box(adt.0, cx.db) => { + TyKind::Adt(adt, substs) if is_box(cx.db, adt.0) => { // The only legal patterns of type `Box` (outside `std`) are `_` and box // patterns. If we're here we can assume this is a box pattern. // FIXME(Nadrieril): A `Box` can in theory be matched either with `Box(_, @@ -992,7 +992,7 @@ impl<'p> DeconstructedPat<'p> { }) .collect(), }, - TyKind::Adt(adt, _) if is_box(adt.0, cx.db) => { + TyKind::Adt(adt, _) if is_box(cx.db, adt.0) => { // Without `box_patterns`, the only legal pattern of type `Box` is `_` (outside // of `std`). So this branch is only reachable when the feature is enabled and // the pattern is a box pattern. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_util.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_util.rs index b89b4f2bfb7d9..217454499ef6d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_util.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_util.rs @@ -1,4 +1,4 @@ -//! Pattern untilities. +//! Pattern utilities. //! //! Originates from `rustc_hir::pat_util` diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/usefulness.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/usefulness.rs index c4d709a975b02..d737b24ad32bb 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/usefulness.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/usefulness.rs @@ -755,7 +755,7 @@ pub(crate) enum Reachability { /// The arm is reachable. This additionally carries a set of or-pattern branches that have been /// found to be unreachable despite the overall arm being reachable. Used only in the presence /// of or-patterns, otherwise it stays empty. - // FIXME: store ureachable subpattern IDs + // FIXME: store unreachable subpattern IDs Reachable, /// The arm is unreachable. Unreachable, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs index d25c0ccf00dcd..7c38e6583a75f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs @@ -3,7 +3,7 @@ use hir_def::{ body::Body, - expr::{Expr, ExprId, UnaryOp}, + hir::{Expr, ExprId, UnaryOp}, resolver::{resolver_for_expr, ResolveValueResult, ValueNs}, DefWithBodyId, }; @@ -73,7 +73,7 @@ fn walk_unsafe( } Expr::Path(path) => { let resolver = resolver_for_expr(db.upcast(), def, current); - let value_or_partial = resolver.resolve_path_in_value_ns(db.upcast(), path.mod_path()); + let value_or_partial = resolver.resolve_path_in_value_ns(db.upcast(), path); if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id))) = value_or_partial { if db.static_data(id).mutable { unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block }); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index bd3eccfe43dab..f90e025c7cc62 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -7,32 +7,36 @@ use std::fmt::{self, Debug}; use base_db::CrateId; use chalk_ir::{BoundVar, TyKind}; use hir_def::{ - adt::VariantData, - body, + data::adt::VariantData, db::DefDatabase, find_path, generics::{TypeOrConstParamData, TypeParamProvenance}, item_scope::ItemInNs, lang_item::{LangItem, LangItemTarget}, + nameres::DefMap, path::{Path, PathKind}, type_ref::{TraitBoundModifier, TypeBound, TypeRef}, visibility::Visibility, - HasModule, ItemContainerId, LocalFieldId, Lookup, ModuleDefId, ModuleId, TraitId, + EnumVariantId, HasModule, ItemContainerId, LocalFieldId, Lookup, ModuleDefId, ModuleId, + TraitId, }; use hir_expand::{hygiene::Hygiene, name::Name}; use intern::{Internable, Interned}; use itertools::Itertools; +use la_arena::ArenaMap; use smallvec::SmallVec; +use stdx::never; use crate::{ + consteval::try_const_usize, db::HirDatabase, from_assoc_type_id, from_foreign_def_id, from_placeholder_idx, - layout::layout_of_ty, + layout::Layout, lt_from_placeholder_idx, mapping::from_chalk, mir::pad16, primitive, to_assoc_type_id, - utils::{self, generics}, + utils::{self, detect_variant_from_bytes, generics, ClosureSubst}, AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, Const, ConstScalar, ConstValue, DomainGoal, GenericArg, ImplTraitId, Interner, Lifetime, LifetimeData, LifetimeOutlives, MemoryMap, Mutability, OpaqueTy, ProjectionTy, ProjectionTyExt, QuantifiedWhereClause, Scalar, @@ -64,6 +68,7 @@ pub struct HirFormatter<'a> { curr_size: usize, pub(crate) max_size: Option, omit_verbose_types: bool, + closure_style: ClosureStyle, display_target: DisplayTarget, } @@ -87,6 +92,7 @@ pub trait HirDisplay { max_size: Option, omit_verbose_types: bool, display_target: DisplayTarget, + closure_style: ClosureStyle, ) -> HirDisplayWrapper<'a, Self> where Self: Sized, @@ -95,7 +101,14 @@ pub trait HirDisplay { !matches!(display_target, DisplayTarget::SourceCode { .. }), "HirDisplayWrapper cannot fail with DisplaySourceCodeError, use HirDisplay::hir_fmt directly instead" ); - HirDisplayWrapper { db, t: self, max_size, omit_verbose_types, display_target } + HirDisplayWrapper { + db, + t: self, + max_size, + omit_verbose_types, + display_target, + closure_style, + } } /// Returns a `Display`able type that is human-readable. @@ -109,6 +122,7 @@ pub trait HirDisplay { t: self, max_size: None, omit_verbose_types: false, + closure_style: ClosureStyle::ImplFn, display_target: DisplayTarget::Diagnostics, } } @@ -128,6 +142,7 @@ pub trait HirDisplay { t: self, max_size, omit_verbose_types: true, + closure_style: ClosureStyle::ImplFn, display_target: DisplayTarget::Diagnostics, } } @@ -138,6 +153,7 @@ pub trait HirDisplay { &'a self, db: &'a dyn HirDatabase, module_id: ModuleId, + allow_opaque: bool, ) -> Result { let mut result = String::new(); match self.hir_fmt(&mut HirFormatter { @@ -147,7 +163,8 @@ pub trait HirDisplay { curr_size: 0, max_size: None, omit_verbose_types: false, - display_target: DisplayTarget::SourceCode { module_id }, + closure_style: ClosureStyle::ImplFn, + display_target: DisplayTarget::SourceCode { module_id, allow_opaque }, }) { Ok(()) => {} Err(HirDisplayError::FmtError) => panic!("Writing to String can't fail!"), @@ -166,6 +183,7 @@ pub trait HirDisplay { t: self, max_size: None, omit_verbose_types: false, + closure_style: ClosureStyle::ImplFn, display_target: DisplayTarget::Test, } } @@ -235,26 +253,34 @@ pub enum DisplayTarget { Diagnostics, /// Display types for inserting them in source files. /// The generated code should compile, so paths need to be qualified. - SourceCode { module_id: ModuleId }, + SourceCode { module_id: ModuleId, allow_opaque: bool }, /// Only for test purpose to keep real types Test, } impl DisplayTarget { - fn is_source_code(&self) -> bool { + fn is_source_code(self) -> bool { matches!(self, Self::SourceCode { .. }) } - fn is_test(&self) -> bool { + + fn is_test(self) -> bool { matches!(self, Self::Test) } + + fn allows_opaque(self) -> bool { + match self { + Self::SourceCode { allow_opaque, .. } => allow_opaque, + _ => true, + } + } } #[derive(Debug)] pub enum DisplaySourceCodeError { PathNotFound, UnknownType, - Closure, Generator, + OpaqueType, } pub enum HirDisplayError { @@ -274,9 +300,25 @@ pub struct HirDisplayWrapper<'a, T> { t: &'a T, max_size: Option, omit_verbose_types: bool, + closure_style: ClosureStyle, display_target: DisplayTarget, } +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum ClosureStyle { + /// `impl FnX(i32, i32) -> i32`, where `FnX` is the most special trait between `Fn`, `FnMut`, `FnOnce` that the + /// closure implements. This is the default. + ImplFn, + /// `|i32, i32| -> i32` + RANotation, + /// `{closure#14825}`, useful for some diagnostics (like type mismatch) and internal usage. + ClosureWithId, + /// `{closure#14825}`, useful for internal usage. + ClosureWithSubst, + /// `…`, which is the `TYPE_HINT_TRUNCATION` + Hide, +} + impl HirDisplayWrapper<'_, T> { pub fn write_to(&self, f: &mut F) -> Result<(), HirDisplayError> { self.t.hir_fmt(&mut HirFormatter { @@ -287,8 +329,14 @@ impl HirDisplayWrapper<'_, T> { max_size: self.max_size, omit_verbose_types: self.omit_verbose_types, display_target: self.display_target, + closure_style: self.closure_style, }) } + + pub fn with_closure_style(mut self, c: ClosureStyle) -> Self { + self.closure_style = c; + self + } } impl<'a, T> fmt::Display for HirDisplayWrapper<'a, T> @@ -330,7 +378,13 @@ impl HirDisplay for ProjectionTy { let trait_ref = self.trait_ref(f.db); write!(f, "<")?; fmt_trait_ref(f, &trait_ref, true)?; - write!(f, ">::{}", f.db.type_alias_data(from_assoc_type_id(self.associated_ty_id)).name)?; + write!( + f, + ">::{}", + f.db.type_alias_data(from_assoc_type_id(self.associated_ty_id)) + .name + .display(f.db.upcast()) + )?; let proj_params_count = self.substitution.len(Interner) - trait_ref.substitution.len(Interner); let proj_params = &self.substitution.as_slice(Interner)[..proj_params_count]; @@ -373,10 +427,16 @@ impl HirDisplay for Const { let id = from_placeholder_idx(f.db, *idx); let generics = generics(f.db.upcast(), id.parent); let param_data = &generics.params.type_or_consts[id.local_id]; - write!(f, "{}", param_data.name().unwrap()) + write!(f, "{}", param_data.name().unwrap().display(f.db.upcast()))?; + Ok(()) } ConstValue::Concrete(c) => match &c.interned { ConstScalar::Bytes(b, m) => render_const_scalar(f, &b, m, &data.ty), + ConstScalar::UnevaluatedConst(c, parameters) => { + write!(f, "{}", c.name(f.db.upcast()))?; + hir_fmt_generics(f, parameters, c.generic_def(f.db.upcast()))?; + Ok(()) + } ConstScalar::Unknown => f.write_char('_'), }, } @@ -411,8 +471,11 @@ fn render_const_scalar( memory_map: &MemoryMap, ty: &Ty, ) -> Result<(), HirDisplayError> { + // FIXME: We need to get krate from the final callers of the hir display + // infrastructure and have it here as a field on `f`. + let krate = *f.db.crate_graph().crates_in_topological_order().last().unwrap(); match ty.kind(Interner) { - chalk_ir::TyKind::Scalar(s) => match s { + TyKind::Scalar(s) => match s { Scalar::Bool => write!(f, "{}", if b[0] == 0 { false } else { true }), Scalar::Char => { let x = u128::from_le_bytes(pad16(b, false)) as u32; @@ -440,22 +503,54 @@ fn render_const_scalar( } }, }, - chalk_ir::TyKind::Ref(_, _, t) => match t.kind(Interner) { - chalk_ir::TyKind::Str => { + TyKind::Ref(_, _, t) => match t.kind(Interner) { + TyKind::Str => { let addr = usize::from_le_bytes(b[0..b.len() / 2].try_into().unwrap()); - let bytes = memory_map.0.get(&addr).map(|x| &**x).unwrap_or(&[]); - let s = std::str::from_utf8(bytes).unwrap_or(""); + let size = usize::from_le_bytes(b[b.len() / 2..].try_into().unwrap()); + let Some(bytes) = memory_map.get(addr, size) else { + return f.write_str(""); + }; + let s = std::str::from_utf8(&bytes).unwrap_or(""); write!(f, "{s:?}") } - _ => f.write_str(""), + TyKind::Slice(ty) => { + let addr = usize::from_le_bytes(b[0..b.len() / 2].try_into().unwrap()); + let count = usize::from_le_bytes(b[b.len() / 2..].try_into().unwrap()); + let Ok(layout) = f.db.layout_of_ty(ty.clone(), krate) else { + return f.write_str(""); + }; + let size_one = layout.size.bytes_usize(); + let Some(bytes) = memory_map.get(addr, size_one * count) else { + return f.write_str(""); + }; + f.write_str("&[")?; + let mut first = true; + for i in 0..count { + if first { + first = false; + } else { + f.write_str(", ")?; + } + let offset = size_one * i; + render_const_scalar(f, &bytes[offset..offset + size_one], memory_map, &ty)?; + } + f.write_str("]") + } + _ => { + let addr = usize::from_le_bytes(b.try_into().unwrap()); + let Ok(layout) = f.db.layout_of_ty(t.clone(), krate) else { + return f.write_str(""); + }; + let size = layout.size.bytes_usize(); + let Some(bytes) = memory_map.get(addr, size) else { + return f.write_str(""); + }; + f.write_str("&")?; + render_const_scalar(f, bytes, memory_map, t) + } }, - chalk_ir::TyKind::Tuple(_, subst) => { - // FIXME: Remove this line. If the target data layout is independent - // of the krate, the `db.target_data_layout` and its callers like `layout_of_ty` don't need - // to get krate. Otherwise, we need to get krate from the final callers of the hir display - // infrastructure and have it here as a field on `f`. - let krate = *f.db.crate_graph().crates_in_topological_order().last().unwrap(); - let Ok(layout) = layout_of_ty(f.db, ty, krate) else { + TyKind::Tuple(_, subst) => { + let Ok(layout) = f.db.layout_of_ty(ty.clone(), krate) else { return f.write_str(""); }; f.write_str("(")?; @@ -468,7 +563,7 @@ fn render_const_scalar( } let ty = ty.assert_ty_ref(Interner); // Tuple only has type argument let offset = layout.fields.offset(id).bytes_usize(); - let Ok(layout) = layout_of_ty(f.db, &ty, krate) else { + let Ok(layout) = f.db.layout_of_ty(ty.clone(), krate) else { f.write_str("")?; continue; }; @@ -477,62 +572,144 @@ fn render_const_scalar( } f.write_str(")") } - chalk_ir::TyKind::Adt(adt, subst) => match adt.0 { - hir_def::AdtId::StructId(s) => { - let data = f.db.struct_data(s); - let Ok(layout) = f.db.layout_of_adt(adt.0, subst.clone()) else { + TyKind::Adt(adt, subst) => { + let Ok(layout) = f.db.layout_of_adt(adt.0, subst.clone(), krate) else { + return f.write_str(""); + }; + match adt.0 { + hir_def::AdtId::StructId(s) => { + let data = f.db.struct_data(s); + write!(f, "{}", data.name.display(f.db.upcast()))?; + let field_types = f.db.field_types(s.into()); + render_variant_after_name( + &data.variant_data, + f, + &field_types, + adt.0.module(f.db.upcast()).krate(), + &layout, + subst, + b, + memory_map, + ) + } + hir_def::AdtId::UnionId(u) => { + write!(f, "{}", f.db.union_data(u).name.display(f.db.upcast())) + } + hir_def::AdtId::EnumId(e) => { + let Some((var_id, var_layout)) = + detect_variant_from_bytes(&layout, f.db, krate, b, e) else { + return f.write_str(""); + }; + let data = &f.db.enum_data(e).variants[var_id]; + write!(f, "{}", data.name.display(f.db.upcast()))?; + let field_types = + f.db.field_types(EnumVariantId { parent: e, local_id: var_id }.into()); + render_variant_after_name( + &data.variant_data, + f, + &field_types, + adt.0.module(f.db.upcast()).krate(), + &var_layout, + subst, + b, + memory_map, + ) + } + } + } + TyKind::FnDef(..) => ty.hir_fmt(f), + TyKind::Function(_) | TyKind::Raw(_, _) => { + let x = u128::from_le_bytes(pad16(b, false)); + write!(f, "{:#X} as ", x)?; + ty.hir_fmt(f) + } + TyKind::Array(ty, len) => { + let Some(len) = try_const_usize(f.db, len) else { + return f.write_str(""); + }; + let Ok(layout) = f.db.layout_of_ty(ty.clone(), krate) else { + return f.write_str(""); + }; + let size_one = layout.size.bytes_usize(); + f.write_str("[")?; + let mut first = true; + for i in 0..len as usize { + if first { + first = false; + } else { + f.write_str(", ")?; + } + let offset = size_one * i; + render_const_scalar(f, &b[offset..offset + size_one], memory_map, &ty)?; + } + f.write_str("]") + } + TyKind::Never => f.write_str("!"), + TyKind::Closure(_, _) => f.write_str(""), + TyKind::Generator(_, _) => f.write_str(""), + TyKind::GeneratorWitness(_, _) => f.write_str(""), + // The below arms are unreachable, since const eval will bail out before here. + TyKind::Foreign(_) => f.write_str(""), + TyKind::Error + | TyKind::Placeholder(_) + | TyKind::Alias(_) + | TyKind::AssociatedType(_, _) + | TyKind::OpaqueType(_, _) + | TyKind::BoundVar(_) + | TyKind::InferenceVar(_, _) => f.write_str(""), + // The below arms are unreachable, since we handled them in ref case. + TyKind::Slice(_) | TyKind::Str | TyKind::Dyn(_) => f.write_str(""), + } +} + +fn render_variant_after_name( + data: &VariantData, + f: &mut HirFormatter<'_>, + field_types: &ArenaMap>, + krate: CrateId, + layout: &Layout, + subst: &Substitution, + b: &[u8], + memory_map: &MemoryMap, +) -> Result<(), HirDisplayError> { + match data { + VariantData::Record(fields) | VariantData::Tuple(fields) => { + let render_field = |f: &mut HirFormatter<'_>, id: LocalFieldId| { + let offset = layout.fields.offset(u32::from(id.into_raw()) as usize).bytes_usize(); + let ty = field_types[id].clone().substitute(Interner, subst); + let Ok(layout) = f.db.layout_of_ty(ty.clone(), krate) else { return f.write_str(""); }; - match data.variant_data.as_ref() { - VariantData::Record(fields) | VariantData::Tuple(fields) => { - let field_types = f.db.field_types(s.into()); - let krate = adt.0.module(f.db.upcast()).krate(); - let render_field = |f: &mut HirFormatter<'_>, id: LocalFieldId| { - let offset = layout - .fields - .offset(u32::from(id.into_raw()) as usize) - .bytes_usize(); - let ty = field_types[id].clone().substitute(Interner, subst); - let Ok(layout) = layout_of_ty(f.db, &ty, krate) else { - return f.write_str(""); - }; - let size = layout.size.bytes_usize(); - render_const_scalar(f, &b[offset..offset + size], memory_map, &ty) - }; - let mut it = fields.iter(); - if matches!(data.variant_data.as_ref(), VariantData::Record(_)) { - write!(f, "{} {{", data.name)?; - if let Some((id, data)) = it.next() { - write!(f, " {}: ", data.name)?; - render_field(f, id)?; - } - for (id, data) in it { - write!(f, ", {}: ", data.name)?; - render_field(f, id)?; - } - write!(f, " }}")?; - } else { - let mut it = it.map(|x| x.0); - write!(f, "{}(", data.name)?; - if let Some(id) = it.next() { - render_field(f, id)?; - } - for id in it { - write!(f, ", ")?; - render_field(f, id)?; - } - write!(f, ")")?; - } - return Ok(()); - } - VariantData::Unit => write!(f, "{}", data.name), + let size = layout.size.bytes_usize(); + render_const_scalar(f, &b[offset..offset + size], memory_map, &ty) + }; + let mut it = fields.iter(); + if matches!(data, VariantData::Record(_)) { + write!(f, " {{")?; + if let Some((id, data)) = it.next() { + write!(f, " {}: ", data.name.display(f.db.upcast()))?; + render_field(f, id)?; + } + for (id, data) in it { + write!(f, ", {}: ", data.name.display(f.db.upcast()))?; + render_field(f, id)?; + } + write!(f, " }}")?; + } else { + let mut it = it.map(|x| x.0); + write!(f, "(")?; + if let Some(id) = it.next() { + render_field(f, id)?; } + for id in it { + write!(f, ", ")?; + render_field(f, id)?; + } + write!(f, ")")?; } - hir_def::AdtId::UnionId(u) => write!(f, "{}", f.db.union_data(u).name), - hir_def::AdtId::EnumId(_) => f.write_str(""), - }, - chalk_ir::TyKind::FnDef(..) => ty.hir_fmt(f), - _ => f.write_str(""), + return Ok(()); + } + VariantData::Unit => Ok(()), } } @@ -689,11 +866,17 @@ impl HirDisplay for Ty { let sig = db.callable_item_signature(def).substitute(Interner, parameters); f.start_location_link(def.into()); match def { - CallableDefId::FunctionId(ff) => write!(f, "fn {}", db.function_data(ff).name)?, - CallableDefId::StructId(s) => write!(f, "{}", db.struct_data(s).name)?, - CallableDefId::EnumVariantId(e) => { - write!(f, "{}", db.enum_data(e.parent).variants[e.local_id].name)? + CallableDefId::FunctionId(ff) => { + write!(f, "fn {}", db.function_data(ff).name.display(f.db.upcast()))? + } + CallableDefId::StructId(s) => { + write!(f, "{}", db.struct_data(s).name.display(f.db.upcast()))? } + CallableDefId::EnumVariantId(e) => write!( + f, + "{}", + db.enum_data(e.parent).variants[e.local_id].name.display(f.db.upcast()) + )?, }; f.end_location_link(); if parameters.len(Interner) > 0 { @@ -733,16 +916,16 @@ impl HirDisplay for Ty { hir_def::AdtId::UnionId(it) => db.union_data(it).name.clone(), hir_def::AdtId::EnumId(it) => db.enum_data(it).name.clone(), }; - write!(f, "{name}")?; + write!(f, "{}", name.display(f.db.upcast()))?; } - DisplayTarget::SourceCode { module_id } => { + DisplayTarget::SourceCode { module_id, allow_opaque: _ } => { if let Some(path) = find_path::find_path( db.upcast(), ItemInNs::Types((*def_id).into()), module_id, false, ) { - write!(f, "{path}")?; + write!(f, "{}", path.display(f.db.upcast()))?; } else { return Err(HirDisplayError::DisplaySourceCodeError( DisplaySourceCodeError::PathNotFound, @@ -752,82 +935,9 @@ impl HirDisplay for Ty { } f.end_location_link(); - if parameters.len(Interner) > 0 { - let parameters_to_write = if f.display_target.is_source_code() - || f.omit_verbose_types() - { - match self - .as_generic_def(db) - .map(|generic_def_id| db.generic_defaults(generic_def_id)) - .filter(|defaults| !defaults.is_empty()) - { - None => parameters.as_slice(Interner), - Some(default_parameters) => { - fn should_show( - parameter: &GenericArg, - default_parameters: &[Binders], - i: usize, - parameters: &Substitution, - ) -> bool { - if parameter.ty(Interner).map(|x| x.kind(Interner)) - == Some(&TyKind::Error) - { - return true; - } - if let Some(ConstValue::Concrete(c)) = parameter - .constant(Interner) - .map(|x| &x.data(Interner).value) - { - if c.interned == ConstScalar::Unknown { - return true; - } - } - let default_parameter = match default_parameters.get(i) { - Some(x) => x, - None => return true, - }; - let actual_default = - default_parameter.clone().substitute(Interner, ¶meters); - parameter != &actual_default - } - let mut default_from = 0; - for (i, parameter) in parameters.iter(Interner).enumerate() { - if should_show(parameter, &default_parameters, i, parameters) { - default_from = i + 1; - } - } - ¶meters.as_slice(Interner)[0..default_from] - } - } - } else { - parameters.as_slice(Interner) - }; - if !parameters_to_write.is_empty() { - write!(f, "<")?; + let generic_def = self.as_generic_def(db); - if f.display_target.is_source_code() { - let mut first = true; - for generic_arg in parameters_to_write { - if !first { - write!(f, ", ")?; - } - first = false; - - if generic_arg.ty(Interner).map(|ty| ty.kind(Interner)) - == Some(&TyKind::Error) - { - write!(f, "_")?; - } else { - generic_arg.hir_fmt(f)?; - } - } - } else { - f.write_joined(parameters_to_write, ", ")?; - } - - write!(f, ">")?; - } - } + hir_fmt_generics(f, parameters, generic_def)?; } TyKind::AssociatedType(assoc_type_id, parameters) => { let type_alias = from_assoc_type_id(*assoc_type_id); @@ -841,12 +951,12 @@ impl HirDisplay for Ty { // Use placeholder associated types when the target is test (https://rust-lang.github.io/chalk/book/clauses/type_equality.html#placeholder-associated-types) if f.display_target.is_test() { f.start_location_link(trait_.into()); - write!(f, "{}", trait_data.name)?; + write!(f, "{}", trait_data.name.display(f.db.upcast()))?; f.end_location_link(); write!(f, "::")?; f.start_location_link(type_alias.into()); - write!(f, "{}", type_alias_data.name)?; + write!(f, "{}", type_alias_data.name.display(f.db.upcast()))?; f.end_location_link(); // Note that the generic args for the associated type come before those for the // trait (including the self type). @@ -869,10 +979,15 @@ impl HirDisplay for Ty { let alias = from_foreign_def_id(*type_alias); let type_alias = db.type_alias_data(alias); f.start_location_link(alias.into()); - write!(f, "{}", type_alias.name)?; + write!(f, "{}", type_alias.name.display(f.db.upcast()))?; f.end_location_link(); } TyKind::OpaqueType(opaque_ty_id, parameters) => { + if !f.display_target.allows_opaque() { + return Err(HirDisplayError::DisplaySourceCodeError( + DisplaySourceCodeError::OpaqueType, + )); + } let impl_trait_id = db.lookup_intern_impl_trait_id((*opaque_ty_id).into()); match impl_trait_id { ImplTraitId::ReturnTypeImplTrait(func, idx) => { @@ -919,26 +1034,52 @@ impl HirDisplay for Ty { } } } - TyKind::Closure(.., substs) => { + TyKind::Closure(id, substs) => { if f.display_target.is_source_code() { - return Err(HirDisplayError::DisplaySourceCodeError( - DisplaySourceCodeError::Closure, - )); + if !f.display_target.allows_opaque() { + return Err(HirDisplayError::DisplaySourceCodeError( + DisplaySourceCodeError::OpaqueType, + )); + } else if f.closure_style != ClosureStyle::ImplFn { + never!("Only `impl Fn` is valid for displaying closures in source code"); + } } - let sig = substs.at(Interner, 0).assert_ty_ref(Interner).callable_sig(db); + match f.closure_style { + ClosureStyle::Hide => return write!(f, "{TYPE_HINT_TRUNCATION}"), + ClosureStyle::ClosureWithId => { + return write!(f, "{{closure#{:?}}}", id.0.as_u32()) + } + ClosureStyle::ClosureWithSubst => { + write!(f, "{{closure#{:?}}}", id.0.as_u32())?; + return hir_fmt_generics(f, substs, None); + } + _ => (), + } + let sig = ClosureSubst(substs).sig_ty().callable_sig(db); if let Some(sig) = sig { + let (def, _) = db.lookup_intern_closure((*id).into()); + let infer = db.infer(def); + let (_, kind) = infer.closure_info(id); + match f.closure_style { + ClosureStyle::ImplFn => write!(f, "impl {kind:?}(")?, + ClosureStyle::RANotation => write!(f, "|")?, + _ => unreachable!(), + } if sig.params().is_empty() { - write!(f, "||")?; } else if f.should_truncate() { - write!(f, "|{TYPE_HINT_TRUNCATION}|")?; + write!(f, "{TYPE_HINT_TRUNCATION}")?; } else { - write!(f, "|")?; f.write_joined(sig.params(), ", ")?; - write!(f, "|")?; }; - - write!(f, " -> ")?; - sig.ret().hir_fmt(f)?; + match f.closure_style { + ClosureStyle::ImplFn => write!(f, ")")?, + ClosureStyle::RANotation => write!(f, "|")?, + _ => unreachable!(), + } + if f.closure_style == ClosureStyle::RANotation || !sig.ret().is_unit() { + write!(f, " -> ")?; + sig.ret().hir_fmt(f)?; + } } else { write!(f, "{{closure}}")?; } @@ -950,7 +1091,11 @@ impl HirDisplay for Ty { match param_data { TypeOrConstParamData::TypeParamData(p) => match p.provenance { TypeParamProvenance::TypeParamList | TypeParamProvenance::TraitSelf => { - write!(f, "{}", p.name.clone().unwrap_or_else(Name::missing))? + write!( + f, + "{}", + p.name.clone().unwrap_or_else(Name::missing).display(f.db.upcast()) + )? } TypeParamProvenance::ArgumentImplTrait => { let substs = generics.placeholder_subst(db); @@ -979,7 +1124,7 @@ impl HirDisplay for Ty { } }, TypeOrConstParamData::ConstParamData(p) => { - write!(f, "{}", p.name)?; + write!(f, "{}", p.name.display(f.db.upcast()))?; } } } @@ -1004,6 +1149,11 @@ impl HirDisplay for Ty { } TyKind::Alias(AliasTy::Projection(p_ty)) => p_ty.hir_fmt(f)?, TyKind::Alias(AliasTy::Opaque(opaque_ty)) => { + if !f.display_target.allows_opaque() { + return Err(HirDisplayError::DisplaySourceCodeError( + DisplaySourceCodeError::OpaqueType, + )); + } let impl_trait_id = db.lookup_intern_impl_trait_id(opaque_ty.opaque_ty_id.into()); match impl_trait_id { ImplTraitId::ReturnTypeImplTrait(func, idx) => { @@ -1067,6 +1217,88 @@ impl HirDisplay for Ty { } } +fn hir_fmt_generics( + f: &mut HirFormatter<'_>, + parameters: &Substitution, + generic_def: Option, +) -> Result<(), HirDisplayError> { + let db = f.db; + let lifetime_args_count = generic_def.map_or(0, |g| db.generic_params(g).lifetimes.len()); + if parameters.len(Interner) + lifetime_args_count > 0 { + let parameters_to_write = if f.display_target.is_source_code() || f.omit_verbose_types() { + match generic_def + .map(|generic_def_id| db.generic_defaults(generic_def_id)) + .filter(|defaults| !defaults.is_empty()) + { + None => parameters.as_slice(Interner), + Some(default_parameters) => { + fn should_show( + parameter: &GenericArg, + default_parameters: &[Binders], + i: usize, + parameters: &Substitution, + ) -> bool { + if parameter.ty(Interner).map(|x| x.kind(Interner)) == Some(&TyKind::Error) + { + return true; + } + if let Some(ConstValue::Concrete(c)) = + parameter.constant(Interner).map(|x| &x.data(Interner).value) + { + if c.interned == ConstScalar::Unknown { + return true; + } + } + let default_parameter = match default_parameters.get(i) { + Some(x) => x, + None => return true, + }; + let actual_default = + default_parameter.clone().substitute(Interner, ¶meters); + parameter != &actual_default + } + let mut default_from = 0; + for (i, parameter) in parameters.iter(Interner).enumerate() { + if should_show(parameter, &default_parameters, i, parameters) { + default_from = i + 1; + } + } + ¶meters.as_slice(Interner)[0..default_from] + } + } + } else { + parameters.as_slice(Interner) + }; + if !parameters_to_write.is_empty() || lifetime_args_count != 0 { + write!(f, "<")?; + let mut first = true; + for _ in 0..lifetime_args_count { + if !first { + write!(f, ", ")?; + } + first = false; + write!(f, "'_")?; + } + for generic_arg in parameters_to_write { + if !first { + write!(f, ", ")?; + } + first = false; + if f.display_target.is_source_code() + && generic_arg.ty(Interner).map(|ty| ty.kind(Interner)) == Some(&TyKind::Error) + { + write!(f, "_")?; + } else { + generic_arg.hir_fmt(f)?; + } + } + + write!(f, ">")?; + } + } + Ok(()) +} + impl HirDisplay for CallableSig { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { write!(f, "fn(")?; @@ -1170,7 +1402,7 @@ fn write_bounds_like_dyn_trait( // existential) here, which is the only thing that's // possible in actual Rust, and hence don't print it f.start_location_link(trait_.into()); - write!(f, "{}", f.db.trait_data(trait_).name)?; + write!(f, "{}", f.db.trait_data(trait_).name.display(f.db.upcast()))?; f.end_location_link(); if let [_, params @ ..] = &*trait_ref.substitution.as_slice(Interner) { if is_fn_trait { @@ -1209,7 +1441,7 @@ fn write_bounds_like_dyn_trait( let assoc_ty_id = from_assoc_type_id(proj.associated_ty_id); let type_alias = f.db.type_alias_data(assoc_ty_id); f.start_location_link(assoc_ty_id.into()); - write!(f, "{}", type_alias.name)?; + write!(f, "{}", type_alias.name.display(f.db.upcast()))?; f.end_location_link(); let proj_arg_count = generics(f.db.upcast(), assoc_ty_id.into()).len_self(); @@ -1276,7 +1508,7 @@ fn fmt_trait_ref( } let trait_ = tr.hir_trait_id(); f.start_location_link(trait_.into()); - write!(f, "{}", f.db.trait_data(trait_).name)?; + write!(f, "{}", f.db.trait_data(trait_).name.display(f.db.upcast()))?; f.end_location_link(); if tr.substitution.len(Interner) > 1 { write!(f, "<")?; @@ -1306,7 +1538,7 @@ impl HirDisplay for WhereClause { write!(f, ">::",)?; let type_alias = from_assoc_type_id(projection_ty.associated_ty_id); f.start_location_link(type_alias.into()); - write!(f, "{}", f.db.type_alias_data(type_alias).name,)?; + write!(f, "{}", f.db.type_alias_data(type_alias).name.display(f.db.upcast()),)?; f.end_location_link(); write!(f, " = ")?; ty.hir_fmt(f)?; @@ -1344,7 +1576,8 @@ impl HirDisplay for LifetimeData { let id = lt_from_placeholder_idx(f.db, *idx); let generics = generics(f.db.upcast(), id.parent); let param_data = &generics.params.lifetimes[id.local_id]; - write!(f, "{}", param_data.name) + write!(f, "{}", param_data.name.display(f.db.upcast()))?; + Ok(()) } LifetimeData::Static => write!(f, "'static"), LifetimeData::Erased => Ok(()), @@ -1376,7 +1609,7 @@ pub fn write_visibility( Visibility::Public => write!(f, "pub "), Visibility::Module(vis_id) => { let def_map = module_id.def_map(f.db.upcast()); - let root_module_id = def_map.module_id(def_map.root()); + let root_module_id = def_map.module_id(DefMap::ROOT); if vis_id == module_id { // pub(self) or omitted Ok(()) @@ -1420,7 +1653,7 @@ impl HirDisplay for TypeRef { }; write!(f, "&")?; if let Some(lifetime) = lifetime { - write!(f, "{} ", lifetime.name)?; + write!(f, "{} ", lifetime.name.display(f.db.upcast()))?; } write!(f, "{mutability}")?; inner.hir_fmt(f)?; @@ -1428,7 +1661,7 @@ impl HirDisplay for TypeRef { TypeRef::Array(inner, len) => { write!(f, "[")?; inner.hir_fmt(f)?; - write!(f, "; {len}]")?; + write!(f, "; {}]", len.display(f.db.upcast()))?; } TypeRef::Slice(inner) => { write!(f, "[")?; @@ -1445,7 +1678,7 @@ impl HirDisplay for TypeRef { for index in 0..function_parameters.len() { let (param_name, param_type) = &function_parameters[index]; if let Some(name) = param_name { - write!(f, "{name}: ")?; + write!(f, "{}: ", name.display(f.db.upcast()))?; } param_type.hir_fmt(f)?; @@ -1477,7 +1710,10 @@ impl HirDisplay for TypeRef { } TypeRef::Macro(macro_call) => { let macro_call = macro_call.to_node(f.db.upcast()); - let ctx = body::LowerCtx::with_hygiene(f.db.upcast(), &Hygiene::new_unhygienic()); + let ctx = hir_def::lower::LowerCtx::with_hygiene( + f.db.upcast(), + &Hygiene::new_unhygienic(), + ); match macro_call.path() { Some(path) => match Path::from_src(path, &ctx) { Some(path) => path.hir_fmt(f)?, @@ -1503,9 +1739,13 @@ impl HirDisplay for TypeBound { } path.hir_fmt(f) } - TypeBound::Lifetime(lifetime) => write!(f, "{}", lifetime.name), + TypeBound::Lifetime(lifetime) => write!(f, "{}", lifetime.name.display(f.db.upcast())), TypeBound::ForLifetime(lifetimes, path) => { - write!(f, "for<{}> ", lifetimes.iter().format(", "))?; + write!( + f, + "for<{}> ", + lifetimes.iter().map(|it| it.display(f.db.upcast())).format(", ") + )?; path.hir_fmt(f) } TypeBound::Error => write!(f, "{{error}}"), @@ -1551,7 +1791,7 @@ impl HirDisplay for Path { if !matches!(self.kind(), PathKind::Plain) || seg_idx > 0 { write!(f, "::")?; } - write!(f, "{}", segment.name)?; + write!(f, "{}", segment.name.display(f.db.upcast()))?; if let Some(generic_args) = segment.args_and_bindings { // We should be in type context, so format as `Foo` instead of `Foo::`. // Do we actually format expressions? @@ -1598,7 +1838,7 @@ impl HirDisplay for Path { } else { write!(f, ", ")?; } - write!(f, "{}", binding.name)?; + write!(f, "{}", binding.name.display(f.db.upcast()))?; match &binding.type_ref { Some(ty) => { write!(f, " = ")?; @@ -1621,8 +1861,10 @@ impl HirDisplay for hir_def::path::GenericArg { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { match self { hir_def::path::GenericArg::Type(ty) => ty.hir_fmt(f), - hir_def::path::GenericArg::Const(c) => write!(f, "{c}"), - hir_def::path::GenericArg::Lifetime(lifetime) => write!(f, "{}", lifetime.name), + hir_def::path::GenericArg::Const(c) => write!(f, "{}", c.display(f.db.upcast())), + hir_def::path::GenericArg::Lifetime(lifetime) => { + write!(f, "{}", lifetime.name.display(f.db.upcast())) + } } } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 7de5b4295fcc4..80f32e96ee63f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -13,34 +13,38 @@ //! to certain types. To record this, we use the union-find implementation from //! the `ena` crate, which is extracted from rustc. -use std::ops::Index; -use std::sync::Arc; +use std::{convert::identity, ops::Index}; -use chalk_ir::{cast::Cast, ConstValue, DebruijnIndex, Mutability, Safety, Scalar, TypeFlags}; +use chalk_ir::{ + cast::Cast, fold::TypeFoldable, interner::HasInterner, DebruijnIndex, Mutability, Safety, + Scalar, TyKind, TypeFlags, +}; use either::Either; use hir_def::{ body::Body, builtin_type::{BuiltinInt, BuiltinType, BuiltinUint}, data::{ConstData, StaticData}, - expr::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, PatId}, + hir::LabelId, + hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, PatId}, lang_item::{LangItem, LangItemTarget}, layout::Integer, - path::Path, + path::{ModPath, Path}, resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs}, type_ref::TypeRef, - AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, HasModule, - ItemContainerId, Lookup, TraitId, TypeAliasId, VariantId, + AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, ItemContainerId, Lookup, + TraitId, TypeAliasId, VariantId, }; use hir_expand::name::{name, Name}; -use la_arena::ArenaMap; +use la_arena::{ArenaMap, Entry}; use rustc_hash::{FxHashMap, FxHashSet}; -use stdx::always; +use stdx::{always, never}; +use triomphe::Arc; use crate::{ - db::HirDatabase, fold_tys, fold_tys_and_consts, infer::coerce::CoerceMany, - lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Const, DomainGoal, + db::HirDatabase, fold_tys, infer::coerce::CoerceMany, lower::ImplTraitLoweringMode, + static_lifetime, to_assoc_type_id, traits::FnTrait, AliasEq, AliasTy, ClosureId, DomainGoal, GenericArg, Goal, ImplTraitId, InEnvironment, Interner, ProjectionTy, RpitId, Substitution, - TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind, + TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, }; // This lint has a false positive here. See the link below for details. @@ -51,12 +55,15 @@ pub use coerce::could_coerce; #[allow(unreachable_pub)] pub use unify::could_unify; +pub(crate) use self::closure::{CaptureKind, CapturedItem, CapturedItemWithoutTy}; + pub(crate) mod unify; mod path; mod expr; mod pat; mod coerce; -mod closure; +pub(crate) mod closure; +mod mutability; /// The entry point of type inference. pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc { @@ -99,6 +106,10 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc Arc Ty { - if !ty.data(Interner).flags.intersects(TypeFlags::HAS_PROJECTION) { +pub(crate) fn normalize(db: &dyn HirDatabase, trait_env: Arc, ty: Ty) -> Ty { + // FIXME: TypeFlags::HAS_CT_PROJECTION is not implemented in chalk, so TypeFlags::HAS_PROJECTION only + // works for the type case, so we check array unconditionally. Remove the array part + // when the bug in chalk becomes fixed. + if !ty.data(Interner).flags.intersects(TypeFlags::HAS_PROJECTION) + && !matches!(ty.kind(Interner), TyKind::Array(..)) + { return ty; } - let krate = owner.module(db.upcast()).krate(); - let trait_env = owner - .as_generic_def_id() - .map_or_else(|| Arc::new(TraitEnvironment::empty(krate)), |d| db.trait_environment(d)); let mut table = unify::InferenceTable::new(db, trait_env); let ty_with_vars = table.normalize_associated_types_in(ty); @@ -188,7 +200,7 @@ pub enum InferenceDiagnostic { /// Contains the type the field resolves to field_with_same_name: Option, }, - // FIXME: Make this proper + // FIXME: This should be emitted in body lowering BreakOutsideOfLoop { expr: ExprId, is_break: bool, @@ -203,6 +215,10 @@ pub enum InferenceDiagnostic { call_expr: ExprId, found: Ty, }, + TypedHole { + expr: ExprId, + expected: Ty, + }, } /// A mismatch between an expected and an inferred type. @@ -276,6 +292,13 @@ pub struct Adjustment { pub target: Ty, } +impl Adjustment { + pub fn borrow(m: Mutability, ty: Ty) -> Self { + let ty = TyKind::Ref(m, static_lifetime(), ty).intern(Interner); + Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(m)), target: ty } + } +} + #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum Adjust { /// Go from ! to any type. @@ -304,6 +327,13 @@ pub enum AutoBorrow { RawPtr(Mutability), } +impl AutoBorrow { + fn mutability(self) -> Mutability { + let (AutoBorrow::Ref(m) | AutoBorrow::RawPtr(m)) = self; + m + } +} + #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum PointerCast { /// Go from a fn-item type to a fn-pointer type. @@ -337,6 +367,10 @@ pub enum PointerCast { } /// The result of type inference: A mapping from expressions and patterns to types. +/// +/// When you add a field that stores types (including `Substitution` and the like), don't forget +/// `resolve_completely()`'ing them in `InferenceContext::resolve_all()`. Inference variables must +/// not appear in the final inference result. #[derive(Clone, PartialEq, Eq, Debug, Default)] pub struct InferenceResult { /// For each method call expr, records the function it resolves to. @@ -363,8 +397,11 @@ pub struct InferenceResult { standard_types: InternedStandardTypes, /// Stores the types which were implicitly dereferenced in pattern binding modes. pub pat_adjustments: FxHashMap>, - pub pat_binding_modes: FxHashMap, + pub binding_modes: ArenaMap, pub expr_adjustments: FxHashMap>, + pub(crate) closure_info: FxHashMap, FnTrait)>, + // FIXME: remove this field + pub mutated_bindings_in_closure: FxHashSet, } impl InferenceResult { @@ -401,6 +438,9 @@ impl InferenceResult { _ => None, }) } + pub fn closure_info(&self, closure: &ClosureId) -> &(Vec, FnTrait) { + self.closure_info.get(closure).unwrap() + } } impl Index for InferenceResult { @@ -435,7 +475,6 @@ pub(crate) struct InferenceContext<'a> { pub(crate) body: &'a Body, pub(crate) resolver: Resolver, table: unify::InferenceTable<'a>, - trait_env: Arc, /// The traits in scope, disregarding block modules. This is used for caching purposes. traits_in_scope: FxHashSet, pub(crate) result: InferenceResult, @@ -453,6 +492,14 @@ pub(crate) struct InferenceContext<'a> { resume_yield_tys: Option<(Ty, Ty)>, diverges: Diverges, breakables: Vec, + + // fields related to closure capture + current_captures: Vec, + current_closure: Option, + /// Stores the list of closure ids that need to be analyzed before this closure. See the + /// comment on `InferenceContext::sort_closures` + closure_dependencies: FxHashMap>, + deferred_closures: FxHashMap, ExprId)>>, } #[derive(Clone, Debug)] @@ -462,7 +509,7 @@ struct BreakableContext { /// The coercion target of the context. coerce: Option, /// The optional label of the context. - label: Option, + label: Option, kind: BreakableKind, } @@ -477,21 +524,21 @@ enum BreakableKind { fn find_breakable<'c>( ctxs: &'c mut [BreakableContext], - label: Option<&name::Name>, + label: Option, ) -> Option<&'c mut BreakableContext> { let mut ctxs = ctxs .iter_mut() .rev() .take_while(|it| matches!(it.kind, BreakableKind::Block | BreakableKind::Loop)); match label { - Some(_) => ctxs.find(|ctx| ctx.label.as_ref() == label), + Some(_) => ctxs.find(|ctx| ctx.label == label), None => ctxs.find(|ctx| matches!(ctx.kind, BreakableKind::Loop)), } } fn find_continuable<'c>( ctxs: &'c mut [BreakableContext], - label: Option<&name::Name>, + label: Option, ) -> Option<&'c mut BreakableContext> { match label { Some(_) => find_breakable(ctxs, label).filter(|it| matches!(it.kind, BreakableKind::Loop)), @@ -506,14 +553,10 @@ impl<'a> InferenceContext<'a> { body: &'a Body, resolver: Resolver, ) -> Self { - let krate = owner.module(db.upcast()).krate(); - let trait_env = owner - .as_generic_def_id() - .map_or_else(|| Arc::new(TraitEnvironment::empty(krate)), |d| db.trait_environment(d)); + let trait_env = db.trait_environment_for_body(owner); InferenceContext { result: InferenceResult::default(), - table: unify::InferenceTable::new(db, trait_env.clone()), - trait_env, + table: unify::InferenceTable::new(db, trait_env), return_ty: TyKind::Error.intern(Interner), // set in collect_* calls resume_yield_tys: None, return_coercion: None, @@ -524,6 +567,10 @@ impl<'a> InferenceContext<'a> { resolver, diverges: Diverges::Maybe, breakables: Vec::new(), + current_captures: vec![], + current_closure: None, + deferred_closures: FxHashMap::default(), + closure_dependencies: FxHashMap::default(), } } @@ -533,6 +580,30 @@ impl<'a> InferenceContext<'a> { // there is no problem in it being `pub(crate)`, remove this comment. pub(crate) fn resolve_all(self) -> InferenceResult { let InferenceContext { mut table, mut result, .. } = self; + // Destructure every single field so whenever new fields are added to `InferenceResult` we + // don't forget to handle them here. + let InferenceResult { + method_resolutions, + field_resolutions: _, + variant_resolutions: _, + assoc_resolutions, + diagnostics, + type_of_expr, + type_of_pat, + type_of_binding, + type_of_rpit, + type_of_for_iterator, + type_mismatches, + standard_types: _, + pat_adjustments, + binding_modes: _, + expr_adjustments, + // Types in `closure_info` have already been `resolve_completely()`'d during + // `InferenceContext::infer_closures()` (in `HirPlace::ty()` specifically), so no need + // to resolve them here. + closure_info: _, + mutated_bindings_in_closure: _, + } = &mut result; table.fallback_if_possible(); @@ -541,62 +612,63 @@ impl<'a> InferenceContext<'a> { // make sure diverging type variables are marked as such table.propagate_diverging_flag(); - for ty in result.type_of_expr.values_mut() { + for ty in type_of_expr.values_mut() { *ty = table.resolve_completely(ty.clone()); } - for ty in result.type_of_pat.values_mut() { + for ty in type_of_pat.values_mut() { *ty = table.resolve_completely(ty.clone()); } - for ty in result.type_of_binding.values_mut() { + for ty in type_of_binding.values_mut() { *ty = table.resolve_completely(ty.clone()); } - for ty in result.type_of_rpit.values_mut() { + for ty in type_of_rpit.values_mut() { *ty = table.resolve_completely(ty.clone()); } - for ty in result.type_of_for_iterator.values_mut() { + for ty in type_of_for_iterator.values_mut() { *ty = table.resolve_completely(ty.clone()); } - for mismatch in result.type_mismatches.values_mut() { + for mismatch in type_mismatches.values_mut() { mismatch.expected = table.resolve_completely(mismatch.expected.clone()); mismatch.actual = table.resolve_completely(mismatch.actual.clone()); } - result.diagnostics.retain_mut(|diagnostic| { - if let InferenceDiagnostic::ExpectedFunction { found: ty, .. } - | InferenceDiagnostic::UnresolvedField { receiver: ty, .. } - | InferenceDiagnostic::UnresolvedMethodCall { receiver: ty, .. } = diagnostic - { - *ty = table.resolve_completely(ty.clone()); - // FIXME: Remove this when we are on par with rustc in terms of inference - if ty.contains_unknown() { - return false; - } + diagnostics.retain_mut(|diagnostic| { + use InferenceDiagnostic::*; + match diagnostic { + ExpectedFunction { found: ty, .. } + | UnresolvedField { receiver: ty, .. } + | UnresolvedMethodCall { receiver: ty, .. } => { + *ty = table.resolve_completely(ty.clone()); + // FIXME: Remove this when we are on par with rustc in terms of inference + if ty.contains_unknown() { + return false; + } - if let InferenceDiagnostic::UnresolvedMethodCall { field_with_same_name, .. } = - diagnostic - { - let clear = if let Some(ty) = field_with_same_name { - *ty = table.resolve_completely(ty.clone()); - ty.contains_unknown() - } else { - false - }; - if clear { - *field_with_same_name = None; + if let UnresolvedMethodCall { field_with_same_name, .. } = diagnostic { + if let Some(ty) = field_with_same_name { + *ty = table.resolve_completely(ty.clone()); + if ty.contains_unknown() { + *field_with_same_name = None; + } + } } } + TypedHole { expected: ty, .. } => { + *ty = table.resolve_completely(ty.clone()); + } + _ => (), } true }); - for (_, subst) in result.method_resolutions.values_mut() { + for (_, subst) in method_resolutions.values_mut() { *subst = table.resolve_completely(subst.clone()); } - for (_, subst) in result.assoc_resolutions.values_mut() { + for (_, subst) in assoc_resolutions.values_mut() { *subst = table.resolve_completely(subst.clone()); } - for adjustment in result.expr_adjustments.values_mut().flatten() { + for adjustment in expr_adjustments.values_mut().flatten() { adjustment.target = table.resolve_completely(adjustment.target.clone()); } - for adjustment in result.pat_adjustments.values_mut().flatten() { + for adjustment in pat_adjustments.values_mut().flatten() { *adjustment = table.resolve_completely(adjustment.clone()); } result @@ -615,7 +687,7 @@ impl<'a> InferenceContext<'a> { let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver) .with_impl_trait_mode(ImplTraitLoweringMode::Param); let mut param_tys = - data.params.iter().map(|(_, type_ref)| ctx.lower_ty(type_ref)).collect::>(); + data.params.iter().map(|type_ref| ctx.lower_ty(type_ref)).collect::>(); // Check if function contains a va_list, if it does then we append it to the parameter types // that are collected from the function data if data.is_varargs() { @@ -634,12 +706,7 @@ impl<'a> InferenceContext<'a> { self.infer_top_pat(*pat, &ty); } - let error_ty = &TypeRef::Error; - let return_ty = if data.has_async_kw() { - data.async_ret_type.as_deref().unwrap_or(error_ty) - } else { - &*data.ret_type - }; + let return_ty = &*data.ret_type; let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver) .with_impl_trait_mode(ImplTraitLoweringMode::Opaque); @@ -649,36 +716,16 @@ impl<'a> InferenceContext<'a> { let return_ty = if let Some(rpits) = self.db.return_type_impl_traits(func) { // RPIT opaque types use substitution of their parent function. let fn_placeholders = TyBuilder::placeholder_subst(self.db, func); - fold_tys( - return_ty, - |ty, _| { - let opaque_ty_id = match ty.kind(Interner) { - TyKind::OpaqueType(opaque_ty_id, _) => *opaque_ty_id, - _ => return ty, - }; - let idx = match self.db.lookup_intern_impl_trait_id(opaque_ty_id.into()) { - ImplTraitId::ReturnTypeImplTrait(_, idx) => idx, - _ => unreachable!(), - }; - let bounds = (*rpits).map_ref(|rpits| { - rpits.impl_traits[idx].bounds.map_ref(|it| it.into_iter()) - }); - let var = self.table.new_type_var(); - let var_subst = Substitution::from1(Interner, var.clone()); - for bound in bounds { - let predicate = - bound.map(|it| it.cloned()).substitute(Interner, &fn_placeholders); - let (var_predicate, binders) = predicate - .substitute(Interner, &var_subst) - .into_value_and_skipped_binders(); - always!(binders.is_empty(Interner)); // quantified where clauses not yet handled - self.push_obligation(var_predicate.cast(Interner)); - } - self.result.type_of_rpit.insert(idx, var.clone()); - var - }, - DebruijnIndex::INNERMOST, - ) + let result = + self.insert_inference_vars_for_rpit(return_ty, rpits.clone(), fn_placeholders); + let rpits = rpits.skip_binders(); + for (id, _) in rpits.impl_traits.iter() { + if let Entry::Vacant(e) = self.result.type_of_rpit.entry(id) { + never!("Missed RPIT in `insert_inference_vars_for_rpit`"); + e.insert(TyKind::Error.intern(Interner)); + } + } + result } else { return_ty }; @@ -687,6 +734,50 @@ impl<'a> InferenceContext<'a> { self.return_coercion = Some(CoerceMany::new(self.return_ty.clone())); } + fn insert_inference_vars_for_rpit( + &mut self, + t: T, + rpits: Arc>, + fn_placeholders: Substitution, + ) -> T + where + T: crate::HasInterner + crate::TypeFoldable, + { + fold_tys( + t, + |ty, _| { + let opaque_ty_id = match ty.kind(Interner) { + TyKind::OpaqueType(opaque_ty_id, _) => *opaque_ty_id, + _ => return ty, + }; + let idx = match self.db.lookup_intern_impl_trait_id(opaque_ty_id.into()) { + ImplTraitId::ReturnTypeImplTrait(_, idx) => idx, + _ => unreachable!(), + }; + let bounds = (*rpits) + .map_ref(|rpits| rpits.impl_traits[idx].bounds.map_ref(|it| it.into_iter())); + let var = self.table.new_type_var(); + let var_subst = Substitution::from1(Interner, var.clone()); + for bound in bounds { + let predicate = + bound.map(|it| it.cloned()).substitute(Interner, &fn_placeholders); + let (var_predicate, binders) = + predicate.substitute(Interner, &var_subst).into_value_and_skipped_binders(); + always!(binders.is_empty(Interner)); // quantified where clauses not yet handled + let var_predicate = self.insert_inference_vars_for_rpit( + var_predicate, + rpits.clone(), + fn_placeholders.clone(), + ); + self.push_obligation(var_predicate.cast(Interner)); + } + self.result.type_of_rpit.insert(idx, var.clone()); + var + }, + DebruijnIndex::INNERMOST, + ) + } + fn infer_body(&mut self) { match self.return_coercion { Some(_) => self.infer_return(self.body.body_expr), @@ -742,43 +833,16 @@ impl<'a> InferenceContext<'a> { self.result.standard_types.unknown.clone() } - /// Replaces ConstScalar::Unknown by a new type var, so we can maybe still infer it. - fn insert_const_vars_shallow(&mut self, c: Const) -> Const { - let data = c.data(Interner); - match &data.value { - ConstValue::Concrete(cc) => match cc.interned { - crate::ConstScalar::Unknown => self.table.new_const_var(data.ty.clone()), - _ => c, - }, - _ => c, - } - } - /// Replaces `Ty::Error` by a new type var, so we can maybe still infer it. fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty { - match ty.kind(Interner) { - TyKind::Error => self.table.new_type_var(), - TyKind::InferenceVar(..) => { - let ty_resolved = self.resolve_ty_shallow(&ty); - if ty_resolved.is_unknown() { - self.table.new_type_var() - } else { - ty - } - } - _ => ty, - } + self.table.insert_type_vars_shallow(ty) } - fn insert_type_vars(&mut self, ty: Ty) -> Ty { - fold_tys_and_consts( - ty, - |x, _| match x { - Either::Left(ty) => Either::Left(self.insert_type_vars_shallow(ty)), - Either::Right(c) => Either::Right(self.insert_const_vars_shallow(c)), - }, - DebruijnIndex::INNERMOST, - ) + fn insert_type_vars(&mut self, ty: T) -> T + where + T: HasInterner + TypeFoldable, + { + self.table.insert_type_vars(ty) } fn push_obligation(&mut self, o: DomainGoal) { @@ -789,13 +853,75 @@ impl<'a> InferenceContext<'a> { self.table.unify(ty1, ty2) } + /// Attempts to returns the deeply last field of nested structures, but + /// does not apply any normalization in its search. Returns the same type + /// if input `ty` is not a structure at all. + fn struct_tail_without_normalization(&mut self, ty: Ty) -> Ty { + self.struct_tail_with_normalize(ty, identity) + } + + /// Returns the deeply last field of nested structures, or the same type if + /// not a structure at all. Corresponds to the only possible unsized field, + /// and its type can be used to determine unsizing strategy. + /// + /// This is parameterized over the normalization strategy (i.e. how to + /// handle `::Assoc` and `impl Trait`); pass the identity + /// function to indicate no normalization should take place. + fn struct_tail_with_normalize( + &mut self, + mut ty: Ty, + mut normalize: impl FnMut(Ty) -> Ty, + ) -> Ty { + // FIXME: fetch the limit properly + let recursion_limit = 10; + for iteration in 0.. { + if iteration > recursion_limit { + return self.err_ty(); + } + match ty.kind(Interner) { + TyKind::Adt(chalk_ir::AdtId(hir_def::AdtId::StructId(struct_id)), substs) => { + match self.db.field_types((*struct_id).into()).values().next_back().cloned() { + Some(field) => { + ty = field.substitute(Interner, substs); + } + None => break, + } + } + TyKind::Adt(..) => break, + TyKind::Tuple(_, substs) => { + match substs + .as_slice(Interner) + .split_last() + .and_then(|(last_ty, _)| last_ty.ty(Interner)) + { + Some(last_ty) => ty = last_ty.clone(), + None => break, + } + } + TyKind::Alias(..) => { + let normalized = normalize(ty.clone()); + if ty == normalized { + return ty; + } else { + ty = normalized; + } + } + _ => break, + } + } + ty + } + /// Recurses through the given type, normalizing associated types mentioned /// in it by replacing them by type variables and registering obligations to /// resolve later. This should be done once for every type we get from some /// type annotation (e.g. from a let type annotation, field type or function /// call). `make_ty` handles this already, but e.g. for field types we need /// to do it as well. - fn normalize_associated_types_in(&mut self, ty: Ty) -> Ty { + fn normalize_associated_types_in(&mut self, ty: T) -> T + where + T: HasInterner + TypeFoldable, + { self.table.normalize_associated_types_in(ty) } @@ -848,10 +974,8 @@ impl<'a> InferenceContext<'a> { None => return (self.err_ty(), None), }; let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver); - // FIXME: this should resolve assoc items as well, see this example: - // https://play.rust-lang.org/?gist=087992e9e22495446c01c0d4e2d69521 let (resolution, unresolved) = if value_ns { - match self.resolver.resolve_path_in_value_ns(self.db.upcast(), path.mod_path()) { + match self.resolver.resolve_path_in_value_ns(self.db.upcast(), path) { Some(ResolveValueResult::ValueNs(value)) => match value { ValueNs::EnumVariantId(var) => { let substs = ctx.substs_from_path(path, var.into(), true); @@ -872,11 +996,15 @@ impl<'a> InferenceContext<'a> { None => return (self.err_ty(), None), } } else { - match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path.mod_path()) { + match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) { Some(it) => it, None => return (self.err_ty(), None), } }; + let Some(mod_path) = path.mod_path() else { + never!("resolver should always resolve lang item paths"); + return (self.err_ty(), None); + }; return match resolution { TypeNs::AdtId(AdtId::StructId(strukt)) => { let substs = ctx.substs_from_path(path, strukt.into(), true); @@ -899,8 +1027,68 @@ impl<'a> InferenceContext<'a> { TypeNs::SelfType(impl_id) => { let generics = crate::utils::generics(self.db.upcast(), impl_id.into()); let substs = generics.placeholder_subst(self.db); - let ty = self.db.impl_self_ty(impl_id).substitute(Interner, &substs); - self.resolve_variant_on_alias(ty, unresolved, path) + let mut ty = self.db.impl_self_ty(impl_id).substitute(Interner, &substs); + + let Some(mut remaining_idx) = unresolved else { + return self.resolve_variant_on_alias(ty, None, mod_path); + }; + + let mut remaining_segments = path.segments().skip(remaining_idx); + + // We need to try resolving unresolved segments one by one because each may resolve + // to a projection, which `TyLoweringContext` cannot handle on its own. + while !remaining_segments.is_empty() { + let resolved_segment = path.segments().get(remaining_idx - 1).unwrap(); + let current_segment = remaining_segments.take(1); + + // If we can resolve to an enum variant, it takes priority over associated type + // of the same name. + if let Some((AdtId::EnumId(id), _)) = ty.as_adt() { + let enum_data = self.db.enum_data(id); + let name = current_segment.first().unwrap().name; + if let Some(local_id) = enum_data.variant(name) { + let variant = EnumVariantId { parent: id, local_id }; + return if remaining_segments.len() == 1 { + (ty, Some(variant.into())) + } else { + // We still have unresolved paths, but enum variants never have + // associated types! + (self.err_ty(), None) + }; + } + } + + // `lower_partly_resolved_path()` returns `None` as type namespace unless + // `remaining_segments` is empty, which is never the case here. We don't know + // which namespace the new `ty` is in until normalized anyway. + (ty, _) = ctx.lower_partly_resolved_path( + resolution, + resolved_segment, + current_segment, + false, + ); + + ty = self.table.insert_type_vars(ty); + ty = self.table.normalize_associated_types_in(ty); + ty = self.table.resolve_ty_shallow(&ty); + if ty.is_unknown() { + return (self.err_ty(), None); + } + + // FIXME(inherent_associated_types): update `resolution` based on `ty` here. + remaining_idx += 1; + remaining_segments = remaining_segments.skip(1); + } + + let variant = ty.as_adt().and_then(|(id, _)| match id { + AdtId::StructId(s) => Some(VariantId::StructId(s)), + AdtId::UnionId(u) => Some(VariantId::UnionId(u)), + AdtId::EnumId(_) => { + // FIXME Error E0071, expected struct, variant or union type, found enum `Foo` + None + } + }); + (ty, variant) } TypeNs::TypeAliasId(it) => { let container = it.lookup(self.db.upcast()).container; @@ -917,7 +1105,7 @@ impl<'a> InferenceContext<'a> { let ty = TyBuilder::def_ty(self.db, it.into(), parent_subst) .fill_with_inference_vars(&mut self.table) .build(); - self.resolve_variant_on_alias(ty, unresolved, path) + self.resolve_variant_on_alias(ty, unresolved, mod_path) } TypeNs::AdtSelfType(_) => { // FIXME this could happen in array size expressions, once we're checking them @@ -953,9 +1141,9 @@ impl<'a> InferenceContext<'a> { &mut self, ty: Ty, unresolved: Option, - path: &Path, + path: &ModPath, ) -> (Ty, Option) { - let remaining = unresolved.map(|x| path.segments().skip(x).len()).filter(|x| x > &0); + let remaining = unresolved.map(|x| path.segments()[x..].len()).filter(|x| x > &0); match remaining { None => { let variant = ty.as_adt().and_then(|(adt_id, _)| match adt_id { @@ -969,7 +1157,7 @@ impl<'a> InferenceContext<'a> { (ty, variant) } Some(1) => { - let segment = path.mod_path().segments().last().unwrap(); + let segment = path.segments().last().unwrap(); // this could be an enum variant or associated type if let Some((AdtId::EnumId(enum_id), _)) = ty.as_adt() { let enum_data = self.db.enum_data(enum_id); @@ -993,22 +1181,6 @@ impl<'a> InferenceContext<'a> { self.db.lang_item(krate, item) } - fn resolve_into_iter_item(&self) -> Option { - let ItemContainerId::TraitId(trait_) = self.resolve_lang_item(LangItem::IntoIterIntoIter)? - .as_function()? - .lookup(self.db.upcast()).container - else { return None }; - self.db.trait_data(trait_).associated_type_by_name(&name![IntoIter]) - } - - fn resolve_iterator_item(&self) -> Option { - let ItemContainerId::TraitId(trait_) = self.resolve_lang_item(LangItem::IteratorNext)? - .as_function()? - .lookup(self.db.upcast()).container - else { return None }; - self.db.trait_data(trait_).associated_type_by_name(&name![Item]) - } - fn resolve_output_on(&self, trait_: TraitId) -> Option { self.db.trait_data(trait_).associated_type_by_name(&name![Output]) } @@ -1017,10 +1189,6 @@ impl<'a> InferenceContext<'a> { self.resolve_lang_item(lang)?.as_trait() } - fn resolve_ops_try_output(&self) -> Option { - self.resolve_output_on(self.resolve_lang_trait(LangItem::Try)?) - } - fn resolve_ops_neg_output(&self) -> Option { self.resolve_output_on(self.resolve_lang_trait(LangItem::Neg)?) } @@ -1136,9 +1304,8 @@ impl Expectation { /// which still is useful, because it informs integer literals and the like. /// See the test case `test/ui/coerce-expect-unsized.rs` and #20169 /// for examples of where this comes up,. - fn rvalue_hint(table: &mut unify::InferenceTable<'_>, ty: Ty) -> Self { - // FIXME: do struct_tail_without_normalization - match table.resolve_ty_shallow(&ty).kind(Interner) { + fn rvalue_hint(ctx: &mut InferenceContext<'_>, ty: Ty) -> Self { + match ctx.struct_tail_without_normalization(ty.clone()).kind(Interner) { TyKind::Slice(_) | TyKind::Str | TyKind::Dyn(_) => Expectation::RValueLikeUnsized(ty), _ => Expectation::has_type(ty), } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index a6449d019ff6a..23189f383e0e6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -1,12 +1,33 @@ //! Inference of closure parameter types based on the closure's expected type. -use chalk_ir::{cast::Cast, AliasEq, AliasTy, FnSubst, WhereClause}; -use hir_def::{expr::ExprId, HasModule}; +use std::{cmp, collections::HashMap, convert::Infallible, mem}; + +use chalk_ir::{ + cast::Cast, + fold::{FallibleTypeFolder, TypeFoldable}, + AliasEq, AliasTy, BoundVar, DebruijnIndex, FnSubst, Mutability, TyKind, WhereClause, +}; +use hir_def::{ + data::adt::VariantData, + hir::{Array, BinaryOp, BindingId, CaptureBy, Expr, ExprId, Pat, PatId, Statement, UnaryOp}, + lang_item::LangItem, + resolver::{resolver_for_expr, ResolveValueResult, ValueNs}, + DefWithBodyId, FieldId, HasModule, VariantId, +}; +use hir_expand::name; +use rustc_hash::FxHashMap; use smallvec::SmallVec; +use stdx::never; use crate::{ - to_chalk_trait_id, utils, ChalkTraitId, DynTy, FnPointer, FnSig, Interner, Substitution, Ty, - TyExt, TyKind, + db::HirDatabase, + from_placeholder_idx, make_binders, + mir::{BorrowKind, MirSpan, ProjectionElem}, + static_lifetime, to_chalk_trait_id, + traits::FnTrait, + utils::{self, generics, Generics}, + Adjust, Adjustment, Binders, BindingMode, ChalkTraitId, ClosureId, DynTy, FnPointer, FnSig, + Interner, Substitution, Ty, TyExt, }; use super::{Expectation, InferenceContext}; @@ -86,3 +107,906 @@ impl InferenceContext<'_> { None } } + +// The below functions handle capture and closure kind (Fn, FnMut, ..) + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub(crate) struct HirPlace { + pub(crate) local: BindingId, + pub(crate) projections: Vec>, +} + +impl HirPlace { + fn ty(&self, ctx: &mut InferenceContext<'_>) -> Ty { + let mut ty = ctx.table.resolve_completely(ctx.result[self.local].clone()); + for p in &self.projections { + ty = p.projected_ty( + ty, + ctx.db, + |_, _, _| { + unreachable!("Closure field only happens in MIR"); + }, + ctx.owner.module(ctx.db.upcast()).krate(), + ); + } + ty.clone() + } + + fn capture_kind_of_truncated_place( + &self, + mut current_capture: CaptureKind, + len: usize, + ) -> CaptureKind { + match current_capture { + CaptureKind::ByRef(BorrowKind::Mut { .. }) => { + if self.projections[len..].iter().any(|x| *x == ProjectionElem::Deref) { + current_capture = CaptureKind::ByRef(BorrowKind::Unique); + } + } + _ => (), + } + current_capture + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum CaptureKind { + ByRef(BorrowKind), + ByValue, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CapturedItem { + pub(crate) place: HirPlace, + pub(crate) kind: CaptureKind, + pub(crate) span: MirSpan, + pub(crate) ty: Binders, +} + +impl CapturedItem { + pub fn local(&self) -> BindingId { + self.place.local + } + + pub fn ty(&self, subst: &Substitution) -> Ty { + self.ty.clone().substitute(Interner, utils::ClosureSubst(subst).parent_subst()) + } + + pub fn kind(&self) -> CaptureKind { + self.kind + } + + pub fn display_place(&self, owner: DefWithBodyId, db: &dyn HirDatabase) -> String { + let body = db.body(owner); + let mut result = body[self.place.local].name.display(db.upcast()).to_string(); + let mut field_need_paren = false; + for proj in &self.place.projections { + match proj { + ProjectionElem::Deref => { + result = format!("*{result}"); + field_need_paren = true; + } + ProjectionElem::Field(f) => { + if field_need_paren { + result = format!("({result})"); + } + let variant_data = f.parent.variant_data(db.upcast()); + let field = match &*variant_data { + VariantData::Record(fields) => fields[f.local_id] + .name + .as_str() + .unwrap_or("[missing field]") + .to_string(), + VariantData::Tuple(fields) => fields + .iter() + .position(|x| x.0 == f.local_id) + .unwrap_or_default() + .to_string(), + VariantData::Unit => "[missing field]".to_string(), + }; + result = format!("{result}.{field}"); + field_need_paren = false; + } + &ProjectionElem::TupleOrClosureField(field) => { + if field_need_paren { + result = format!("({result})"); + } + result = format!("{result}.{field}"); + field_need_paren = false; + } + ProjectionElem::Index(_) + | ProjectionElem::ConstantIndex { .. } + | ProjectionElem::Subslice { .. } + | ProjectionElem::OpaqueCast(_) => { + never!("Not happen in closure capture"); + continue; + } + } + } + result + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub(crate) struct CapturedItemWithoutTy { + pub(crate) place: HirPlace, + pub(crate) kind: CaptureKind, + pub(crate) span: MirSpan, +} + +impl CapturedItemWithoutTy { + fn with_ty(self, ctx: &mut InferenceContext<'_>) -> CapturedItem { + let ty = self.place.ty(ctx).clone(); + let ty = match &self.kind { + CaptureKind::ByValue => ty, + CaptureKind::ByRef(bk) => { + let m = match bk { + BorrowKind::Mut { .. } => Mutability::Mut, + _ => Mutability::Not, + }; + TyKind::Ref(m, static_lifetime(), ty).intern(Interner) + } + }; + return CapturedItem { + place: self.place, + kind: self.kind, + span: self.span, + ty: replace_placeholder_with_binder(ctx.db, ctx.owner, ty), + }; + + fn replace_placeholder_with_binder( + db: &dyn HirDatabase, + owner: DefWithBodyId, + ty: Ty, + ) -> Binders { + struct Filler<'a> { + db: &'a dyn HirDatabase, + generics: Generics, + } + impl FallibleTypeFolder for Filler<'_> { + type Error = (); + + fn as_dyn(&mut self) -> &mut dyn FallibleTypeFolder { + self + } + + fn interner(&self) -> Interner { + Interner + } + + fn try_fold_free_placeholder_const( + &mut self, + ty: chalk_ir::Ty, + idx: chalk_ir::PlaceholderIndex, + outer_binder: DebruijnIndex, + ) -> Result, Self::Error> { + let x = from_placeholder_idx(self.db, idx); + let Some(idx) = self.generics.param_idx(x) else { + return Err(()); + }; + Ok(BoundVar::new(outer_binder, idx).to_const(Interner, ty)) + } + + fn try_fold_free_placeholder_ty( + &mut self, + idx: chalk_ir::PlaceholderIndex, + outer_binder: DebruijnIndex, + ) -> std::result::Result { + let x = from_placeholder_idx(self.db, idx); + let Some(idx) = self.generics.param_idx(x) else { + return Err(()); + }; + Ok(BoundVar::new(outer_binder, idx).to_ty(Interner)) + } + } + let Some(generic_def) = owner.as_generic_def_id() else { + return Binders::empty(Interner, ty); + }; + let filler = &mut Filler { db, generics: generics(db.upcast(), generic_def) }; + let result = ty.clone().try_fold_with(filler, DebruijnIndex::INNERMOST).unwrap_or(ty); + make_binders(db, &filler.generics, result) + } + } +} + +impl InferenceContext<'_> { + fn place_of_expr(&mut self, tgt_expr: ExprId) -> Option { + let r = self.place_of_expr_without_adjust(tgt_expr)?; + let default = vec![]; + let adjustments = self.result.expr_adjustments.get(&tgt_expr).unwrap_or(&default); + apply_adjusts_to_place(r, adjustments) + } + + fn place_of_expr_without_adjust(&mut self, tgt_expr: ExprId) -> Option { + match &self.body[tgt_expr] { + Expr::Path(p) => { + let resolver = resolver_for_expr(self.db.upcast(), self.owner, tgt_expr); + if let Some(r) = resolver.resolve_path_in_value_ns(self.db.upcast(), p) { + if let ResolveValueResult::ValueNs(v) = r { + if let ValueNs::LocalBinding(b) = v { + return Some(HirPlace { local: b, projections: vec![] }); + } + } + } + } + Expr::Field { expr, name } => { + let mut place = self.place_of_expr(*expr)?; + if let TyKind::Tuple(..) = self.expr_ty(*expr).kind(Interner) { + let index = name.as_tuple_index()?; + place.projections.push(ProjectionElem::TupleOrClosureField(index)) + } else { + let field = self.result.field_resolution(tgt_expr)?; + place.projections.push(ProjectionElem::Field(field)); + } + return Some(place); + } + Expr::UnaryOp { expr, op: UnaryOp::Deref } => { + if matches!( + self.expr_ty_after_adjustments(*expr).kind(Interner), + TyKind::Ref(..) | TyKind::Raw(..) + ) { + let mut place = self.place_of_expr(*expr)?; + place.projections.push(ProjectionElem::Deref); + return Some(place); + } + } + _ => (), + } + None + } + + fn push_capture(&mut self, capture: CapturedItemWithoutTy) { + self.current_captures.push(capture); + } + + fn ref_expr(&mut self, expr: ExprId) { + if let Some(place) = self.place_of_expr(expr) { + self.add_capture(place, CaptureKind::ByRef(BorrowKind::Shared), expr.into()); + } + self.walk_expr(expr); + } + + fn add_capture(&mut self, place: HirPlace, kind: CaptureKind, span: MirSpan) { + if self.is_upvar(&place) { + self.push_capture(CapturedItemWithoutTy { place, kind, span }); + } + } + + fn mutate_expr(&mut self, expr: ExprId) { + if let Some(place) = self.place_of_expr(expr) { + self.add_capture( + place, + CaptureKind::ByRef(BorrowKind::Mut { allow_two_phase_borrow: false }), + expr.into(), + ); + } + self.walk_expr(expr); + } + + fn consume_expr(&mut self, expr: ExprId) { + if let Some(place) = self.place_of_expr(expr) { + self.consume_place(place, expr.into()); + } + self.walk_expr(expr); + } + + fn consume_place(&mut self, place: HirPlace, span: MirSpan) { + if self.is_upvar(&place) { + let ty = place.ty(self).clone(); + let kind = if self.is_ty_copy(ty) { + CaptureKind::ByRef(BorrowKind::Shared) + } else { + CaptureKind::ByValue + }; + self.push_capture(CapturedItemWithoutTy { place, kind, span }); + } + } + + fn walk_expr_with_adjust(&mut self, tgt_expr: ExprId, adjustment: &[Adjustment]) { + if let Some((last, rest)) = adjustment.split_last() { + match last.kind { + Adjust::NeverToAny | Adjust::Deref(None) | Adjust::Pointer(_) => { + self.walk_expr_with_adjust(tgt_expr, rest) + } + Adjust::Deref(Some(m)) => match m.0 { + Some(m) => { + self.ref_capture_with_adjusts(m, tgt_expr, rest); + } + None => unreachable!(), + }, + Adjust::Borrow(b) => { + self.ref_capture_with_adjusts(b.mutability(), tgt_expr, rest); + } + } + } else { + self.walk_expr_without_adjust(tgt_expr); + } + } + + fn ref_capture_with_adjusts(&mut self, m: Mutability, tgt_expr: ExprId, rest: &[Adjustment]) { + let capture_kind = match m { + Mutability::Mut => { + CaptureKind::ByRef(BorrowKind::Mut { allow_two_phase_borrow: false }) + } + Mutability::Not => CaptureKind::ByRef(BorrowKind::Shared), + }; + if let Some(place) = self.place_of_expr_without_adjust(tgt_expr) { + if let Some(place) = apply_adjusts_to_place(place, rest) { + self.add_capture(place, capture_kind, tgt_expr.into()); + } + } + self.walk_expr_with_adjust(tgt_expr, rest); + } + + fn walk_expr(&mut self, tgt_expr: ExprId) { + if let Some(x) = self.result.expr_adjustments.get_mut(&tgt_expr) { + // FIXME: this take is completely unneeded, and just is here to make borrow checker + // happy. Remove it if you can. + let x_taken = mem::take(x); + self.walk_expr_with_adjust(tgt_expr, &x_taken); + *self.result.expr_adjustments.get_mut(&tgt_expr).unwrap() = x_taken; + } else { + self.walk_expr_without_adjust(tgt_expr); + } + } + + fn walk_expr_without_adjust(&mut self, tgt_expr: ExprId) { + match &self.body[tgt_expr] { + Expr::If { condition, then_branch, else_branch } => { + self.consume_expr(*condition); + self.consume_expr(*then_branch); + if let &Some(expr) = else_branch { + self.consume_expr(expr); + } + } + Expr::Async { statements, tail, .. } + | Expr::Unsafe { statements, tail, .. } + | Expr::Block { statements, tail, .. } => { + for s in statements.iter() { + match s { + Statement::Let { pat, type_ref: _, initializer, else_branch } => { + if let Some(else_branch) = else_branch { + self.consume_expr(*else_branch); + if let Some(initializer) = initializer { + self.consume_expr(*initializer); + } + return; + } + if let Some(initializer) = initializer { + self.walk_expr(*initializer); + if let Some(place) = self.place_of_expr(*initializer) { + self.consume_with_pat(place, *pat); + } + } + } + Statement::Expr { expr, has_semi: _ } => { + self.consume_expr(*expr); + } + } + } + if let Some(tail) = tail { + self.consume_expr(*tail); + } + } + Expr::While { condition, body, label: _ } => { + self.consume_expr(*condition); + self.consume_expr(*body); + } + Expr::Call { callee, args, is_assignee_expr: _ } => { + self.consume_expr(*callee); + self.consume_exprs(args.iter().copied()); + } + Expr::MethodCall { receiver, args, .. } => { + self.consume_expr(*receiver); + self.consume_exprs(args.iter().copied()); + } + Expr::Match { expr, arms } => { + for arm in arms.iter() { + self.consume_expr(arm.expr); + if let Some(guard) = arm.guard { + self.consume_expr(guard); + } + } + self.walk_expr(*expr); + if let Some(discr_place) = self.place_of_expr(*expr) { + if self.is_upvar(&discr_place) { + let mut capture_mode = None; + for arm in arms.iter() { + self.walk_pat(&mut capture_mode, arm.pat); + } + if let Some(c) = capture_mode { + self.push_capture(CapturedItemWithoutTy { + place: discr_place, + kind: c, + span: (*expr).into(), + }) + } + } + } + } + Expr::Break { expr, label: _ } + | Expr::Return { expr } + | Expr::Yield { expr } + | Expr::Yeet { expr } => { + if let &Some(expr) = expr { + self.consume_expr(expr); + } + } + Expr::RecordLit { fields, spread, .. } => { + if let &Some(expr) = spread { + self.consume_expr(expr); + } + self.consume_exprs(fields.iter().map(|x| x.expr)); + } + Expr::Field { expr, name: _ } => self.select_from_expr(*expr), + Expr::UnaryOp { expr, op: UnaryOp::Deref } => { + if matches!( + self.expr_ty_after_adjustments(*expr).kind(Interner), + TyKind::Ref(..) | TyKind::Raw(..) + ) { + self.select_from_expr(*expr); + } else if let Some((f, _)) = self.result.method_resolution(tgt_expr) { + let mutability = 'b: { + if let Some(deref_trait) = + self.resolve_lang_item(LangItem::DerefMut).and_then(|x| x.as_trait()) + { + if let Some(deref_fn) = + self.db.trait_data(deref_trait).method_by_name(&name![deref_mut]) + { + break 'b deref_fn == f; + } + } + false + }; + if mutability { + self.mutate_expr(*expr); + } else { + self.ref_expr(*expr); + } + } else { + self.select_from_expr(*expr); + } + } + Expr::UnaryOp { expr, op: _ } + | Expr::Array(Array::Repeat { initializer: expr, repeat: _ }) + | Expr::Await { expr } + | Expr::Loop { body: expr, label: _ } + | Expr::Let { pat: _, expr } + | Expr::Box { expr } + | Expr::Cast { expr, type_ref: _ } => { + self.consume_expr(*expr); + } + Expr::Ref { expr, rawness: _, mutability } => match mutability { + hir_def::type_ref::Mutability::Shared => self.ref_expr(*expr), + hir_def::type_ref::Mutability::Mut => self.mutate_expr(*expr), + }, + Expr::BinaryOp { lhs, rhs, op } => { + let Some(op) = op else { + return; + }; + if matches!(op, BinaryOp::Assignment { .. }) { + self.mutate_expr(*lhs); + self.consume_expr(*rhs); + return; + } + self.consume_expr(*lhs); + self.consume_expr(*rhs); + } + Expr::Range { lhs, rhs, range_type: _ } => { + if let &Some(expr) = lhs { + self.consume_expr(expr); + } + if let &Some(expr) = rhs { + self.consume_expr(expr); + } + } + Expr::Index { base, index } => { + self.select_from_expr(*base); + self.consume_expr(*index); + } + Expr::Closure { .. } => { + let ty = self.expr_ty(tgt_expr); + let TyKind::Closure(id, _) = ty.kind(Interner) else { + never!("closure type is always closure"); + return; + }; + let (captures, _) = + self.result.closure_info.get(id).expect( + "We sort closures, so we should always have data for inner closures", + ); + let mut cc = mem::take(&mut self.current_captures); + cc.extend(captures.iter().filter(|x| self.is_upvar(&x.place)).map(|x| { + CapturedItemWithoutTy { place: x.place.clone(), kind: x.kind, span: x.span } + })); + self.current_captures = cc; + } + Expr::Array(Array::ElementList { elements: exprs, is_assignee_expr: _ }) + | Expr::Tuple { exprs, is_assignee_expr: _ } => { + self.consume_exprs(exprs.iter().copied()) + } + Expr::Missing + | Expr::Continue { .. } + | Expr::Path(_) + | Expr::Literal(_) + | Expr::Const(_) + | Expr::Underscore => (), + } + } + + fn walk_pat(&mut self, result: &mut Option, pat: PatId) { + let mut update_result = |ck: CaptureKind| match result { + Some(r) => { + *r = cmp::max(*r, ck); + } + None => *result = Some(ck), + }; + + self.walk_pat_inner( + pat, + &mut update_result, + BorrowKind::Mut { allow_two_phase_borrow: false }, + ); + } + + fn walk_pat_inner( + &mut self, + p: PatId, + update_result: &mut impl FnMut(CaptureKind), + mut for_mut: BorrowKind, + ) { + match &self.body[p] { + Pat::Ref { .. } + | Pat::Box { .. } + | Pat::Missing + | Pat::Wild + | Pat::Tuple { .. } + | Pat::Or(_) => (), + Pat::TupleStruct { .. } | Pat::Record { .. } => { + if let Some(variant) = self.result.variant_resolution_for_pat(p) { + let adt = variant.adt_id(); + let is_multivariant = match adt { + hir_def::AdtId::EnumId(e) => self.db.enum_data(e).variants.len() != 1, + _ => false, + }; + if is_multivariant { + update_result(CaptureKind::ByRef(BorrowKind::Shared)); + } + } + } + Pat::Slice { .. } + | Pat::ConstBlock(_) + | Pat::Path(_) + | Pat::Lit(_) + | Pat::Range { .. } => { + update_result(CaptureKind::ByRef(BorrowKind::Shared)); + } + Pat::Bind { id, .. } => match self.result.binding_modes[*id] { + crate::BindingMode::Move => { + if self.is_ty_copy(self.result.type_of_binding[*id].clone()) { + update_result(CaptureKind::ByRef(BorrowKind::Shared)); + } else { + update_result(CaptureKind::ByValue); + } + } + crate::BindingMode::Ref(r) => match r { + Mutability::Mut => update_result(CaptureKind::ByRef(for_mut)), + Mutability::Not => update_result(CaptureKind::ByRef(BorrowKind::Shared)), + }, + }, + } + if self.result.pat_adjustments.get(&p).map_or(false, |x| !x.is_empty()) { + for_mut = BorrowKind::Unique; + } + self.body.walk_pats_shallow(p, |p| self.walk_pat_inner(p, update_result, for_mut)); + } + + fn expr_ty(&self, expr: ExprId) -> Ty { + self.result[expr].clone() + } + + fn expr_ty_after_adjustments(&self, e: ExprId) -> Ty { + let mut ty = None; + if let Some(x) = self.result.expr_adjustments.get(&e) { + if let Some(x) = x.last() { + ty = Some(x.target.clone()); + } + } + ty.unwrap_or_else(|| self.expr_ty(e)) + } + + fn is_upvar(&self, place: &HirPlace) -> bool { + let b = &self.body[place.local]; + if let Some(c) = self.current_closure { + let (_, root) = self.db.lookup_intern_closure(c.into()); + return b.is_upvar(root); + } + false + } + + fn is_ty_copy(&mut self, ty: Ty) -> bool { + if let TyKind::Closure(id, _) = ty.kind(Interner) { + // FIXME: We handle closure as a special case, since chalk consider every closure as copy. We + // should probably let chalk know which closures are copy, but I don't know how doing it + // without creating query cycles. + return self.result.closure_info.get(id).map(|x| x.1 == FnTrait::Fn).unwrap_or(true); + } + self.table.resolve_completely(ty).is_copy(self.db, self.owner) + } + + fn select_from_expr(&mut self, expr: ExprId) { + self.walk_expr(expr); + } + + fn adjust_for_move_closure(&mut self) { + for capture in &mut self.current_captures { + if let Some(first_deref) = + capture.place.projections.iter().position(|proj| *proj == ProjectionElem::Deref) + { + capture.place.projections.truncate(first_deref); + } + capture.kind = CaptureKind::ByValue; + } + } + + fn minimize_captures(&mut self) { + self.current_captures.sort_by_key(|x| x.place.projections.len()); + let mut hash_map = HashMap::::new(); + let result = mem::take(&mut self.current_captures); + for item in result { + let mut lookup_place = HirPlace { local: item.place.local, projections: vec![] }; + let mut it = item.place.projections.iter(); + let prev_index = loop { + if let Some(k) = hash_map.get(&lookup_place) { + break Some(*k); + } + match it.next() { + Some(x) => lookup_place.projections.push(x.clone()), + None => break None, + } + }; + match prev_index { + Some(p) => { + let len = self.current_captures[p].place.projections.len(); + let kind_after_truncate = + item.place.capture_kind_of_truncated_place(item.kind, len); + self.current_captures[p].kind = + cmp::max(kind_after_truncate, self.current_captures[p].kind); + } + None => { + hash_map.insert(item.place.clone(), self.current_captures.len()); + self.current_captures.push(item); + } + } + } + } + + fn consume_with_pat(&mut self, mut place: HirPlace, pat: PatId) { + let cnt = self.result.pat_adjustments.get(&pat).map(|x| x.len()).unwrap_or_default(); + place.projections = place + .projections + .iter() + .cloned() + .chain((0..cnt).map(|_| ProjectionElem::Deref)) + .collect::>() + .into(); + match &self.body[pat] { + Pat::Missing | Pat::Wild => (), + Pat::Tuple { args, ellipsis } => { + let (al, ar) = args.split_at(ellipsis.unwrap_or(args.len())); + let field_count = match self.result[pat].kind(Interner) { + TyKind::Tuple(_, s) => s.len(Interner), + _ => return, + }; + let fields = 0..field_count; + let it = al.iter().zip(fields.clone()).chain(ar.iter().rev().zip(fields.rev())); + for (arg, i) in it { + let mut p = place.clone(); + p.projections.push(ProjectionElem::TupleOrClosureField(i)); + self.consume_with_pat(p, *arg); + } + } + Pat::Or(pats) => { + for pat in pats.iter() { + self.consume_with_pat(place.clone(), *pat); + } + } + Pat::Record { args, .. } => { + let Some(variant) = self.result.variant_resolution_for_pat(pat) else { + return; + }; + match variant { + VariantId::EnumVariantId(_) | VariantId::UnionId(_) => { + self.consume_place(place, pat.into()) + } + VariantId::StructId(s) => { + let vd = &*self.db.struct_data(s).variant_data; + for field_pat in args.iter() { + let arg = field_pat.pat; + let Some(local_id) = vd.field(&field_pat.name) else { + continue; + }; + let mut p = place.clone(); + p.projections.push(ProjectionElem::Field(FieldId { + parent: variant.into(), + local_id, + })); + self.consume_with_pat(p, arg); + } + } + } + } + Pat::Range { .. } + | Pat::Slice { .. } + | Pat::ConstBlock(_) + | Pat::Path(_) + | Pat::Lit(_) => self.consume_place(place, pat.into()), + Pat::Bind { id, subpat: _ } => { + let mode = self.result.binding_modes[*id]; + let capture_kind = match mode { + BindingMode::Move => { + self.consume_place(place, pat.into()); + return; + } + BindingMode::Ref(Mutability::Not) => BorrowKind::Shared, + BindingMode::Ref(Mutability::Mut) => { + BorrowKind::Mut { allow_two_phase_borrow: false } + } + }; + self.add_capture(place, CaptureKind::ByRef(capture_kind), pat.into()); + } + Pat::TupleStruct { path: _, args, ellipsis } => { + let Some(variant) = self.result.variant_resolution_for_pat(pat) else { + return; + }; + match variant { + VariantId::EnumVariantId(_) | VariantId::UnionId(_) => { + self.consume_place(place, pat.into()) + } + VariantId::StructId(s) => { + let vd = &*self.db.struct_data(s).variant_data; + let (al, ar) = args.split_at(ellipsis.unwrap_or(args.len())); + let fields = vd.fields().iter(); + let it = + al.iter().zip(fields.clone()).chain(ar.iter().rev().zip(fields.rev())); + for (arg, (i, _)) in it { + let mut p = place.clone(); + p.projections.push(ProjectionElem::Field(FieldId { + parent: variant.into(), + local_id: i, + })); + self.consume_with_pat(p, *arg); + } + } + } + } + Pat::Ref { pat, mutability: _ } => { + place.projections.push(ProjectionElem::Deref); + self.consume_with_pat(place, *pat) + } + Pat::Box { .. } => (), // not supported + } + } + + fn consume_exprs(&mut self, exprs: impl Iterator) { + for expr in exprs { + self.consume_expr(expr); + } + } + + fn closure_kind(&self) -> FnTrait { + let mut r = FnTrait::Fn; + for x in &self.current_captures { + r = cmp::min( + r, + match &x.kind { + CaptureKind::ByRef(BorrowKind::Unique | BorrowKind::Mut { .. }) => { + FnTrait::FnMut + } + CaptureKind::ByRef(BorrowKind::Shallow | BorrowKind::Shared) => FnTrait::Fn, + CaptureKind::ByValue => FnTrait::FnOnce, + }, + ) + } + r + } + + fn analyze_closure(&mut self, closure: ClosureId) -> FnTrait { + let (_, root) = self.db.lookup_intern_closure(closure.into()); + self.current_closure = Some(closure); + let Expr::Closure { body, capture_by, .. } = &self.body[root] else { + unreachable!("Closure expression id is always closure"); + }; + self.consume_expr(*body); + for item in &self.current_captures { + if matches!(item.kind, CaptureKind::ByRef(BorrowKind::Mut { .. })) + && !item.place.projections.contains(&ProjectionElem::Deref) + { + // FIXME: remove the `mutated_bindings_in_closure` completely and add proper fake reads in + // MIR. I didn't do that due duplicate diagnostics. + self.result.mutated_bindings_in_closure.insert(item.place.local); + } + } + // closure_kind should be done before adjust_for_move_closure + let closure_kind = self.closure_kind(); + match capture_by { + CaptureBy::Value => self.adjust_for_move_closure(), + CaptureBy::Ref => (), + } + self.minimize_captures(); + let result = mem::take(&mut self.current_captures); + let captures = result.into_iter().map(|x| x.with_ty(self)).collect::>(); + self.result.closure_info.insert(closure, (captures, closure_kind)); + closure_kind + } + + pub(crate) fn infer_closures(&mut self) { + let deferred_closures = self.sort_closures(); + for (closure, exprs) in deferred_closures.into_iter().rev() { + self.current_captures = vec![]; + let kind = self.analyze_closure(closure); + + for (derefed_callee, callee_ty, params, expr) in exprs { + if let &Expr::Call { callee, .. } = &self.body[expr] { + let mut adjustments = + self.result.expr_adjustments.remove(&callee).unwrap_or_default(); + self.write_fn_trait_method_resolution( + kind, + &derefed_callee, + &mut adjustments, + &callee_ty, + ¶ms, + expr, + ); + self.result.expr_adjustments.insert(callee, adjustments); + } + } + } + } + + /// We want to analyze some closures before others, to have a correct analysis: + /// * We should analyze nested closures before the parent, since the parent should capture some of + /// the things that its children captures. + /// * If a closure calls another closure, we need to analyze the callee, to find out how we should + /// capture it (e.g. by move for FnOnce) + /// + /// These dependencies are collected in the main inference. We do a topological sort in this function. It + /// will consume the `deferred_closures` field and return its content in a sorted vector. + fn sort_closures(&mut self) -> Vec<(ClosureId, Vec<(Ty, Ty, Vec, ExprId)>)> { + let mut deferred_closures = mem::take(&mut self.deferred_closures); + let mut dependents_count: FxHashMap = + deferred_closures.keys().map(|x| (*x, 0)).collect(); + for (_, deps) in &self.closure_dependencies { + for dep in deps { + *dependents_count.entry(*dep).or_default() += 1; + } + } + let mut queue: Vec<_> = + deferred_closures.keys().copied().filter(|x| dependents_count[x] == 0).collect(); + let mut result = vec![]; + while let Some(x) = queue.pop() { + if let Some(d) = deferred_closures.remove(&x) { + result.push((x, d)); + } + for dep in self.closure_dependencies.get(&x).into_iter().flat_map(|x| x.iter()) { + let cnt = dependents_count.get_mut(dep).unwrap(); + *cnt -= 1; + if *cnt == 0 { + queue.push(*dep); + } + } + } + result + } +} + +fn apply_adjusts_to_place(mut r: HirPlace, adjustments: &[Adjustment]) -> Option { + for adj in adjustments { + match &adj.kind { + Adjust::Deref(None) => { + r.projections.push(ProjectionElem::Deref); + } + _ => return None, + } + } + Some(r) +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs index 48c91530266df..05a476f632dc2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs @@ -5,14 +5,15 @@ //! See and //! `rustc_hir_analysis/check/coercion.rs`. -use std::{iter, sync::Arc}; +use std::iter; -use chalk_ir::{cast::Cast, BoundVar, Goal, Mutability, TyVariableKind}; +use chalk_ir::{cast::Cast, BoundVar, Goal, Mutability, TyKind, TyVariableKind}; use hir_def::{ - expr::ExprId, + hir::ExprId, lang_item::{LangItem, LangItemTarget}, }; use stdx::always; +use triomphe::Arc; use crate::{ autoderef::{Autoderef, AutoderefKind}, @@ -21,8 +22,10 @@ use crate::{ Adjust, Adjustment, AutoBorrow, InferOk, InferenceContext, OverloadedDeref, PointerCast, TypeError, TypeMismatch, }, - static_lifetime, Canonical, DomainGoal, FnPointer, FnSig, Guidance, InEnvironment, Interner, - Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, + static_lifetime, + utils::ClosureSubst, + Canonical, DomainGoal, FnPointer, FnSig, Guidance, InEnvironment, Interner, Solution, + Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, }; use super::unify::InferenceTable; @@ -47,15 +50,23 @@ fn success( Ok(InferOk { goals, value: (adj, target) }) } +pub(super) enum CoercionCause { + // FIXME: Make better use of this. Right now things like return and break without a value + // use it to point to themselves, causing us to report a mismatch on those expressions even + // though technically they themselves are `!` + Expr(ExprId), +} + #[derive(Clone, Debug)] pub(super) struct CoerceMany { expected_ty: Ty, final_ty: Option, + expressions: Vec, } impl CoerceMany { pub(super) fn new(expected: Ty) -> Self { - CoerceMany { expected_ty: expected, final_ty: None } + CoerceMany { expected_ty: expected, final_ty: None, expressions: vec![] } } /// Returns the "expected type" with which this coercion was @@ -86,8 +97,12 @@ impl CoerceMany { } } - pub(super) fn coerce_forced_unit(&mut self, ctx: &mut InferenceContext<'_>) { - self.coerce(ctx, None, &ctx.result.standard_types.unit.clone()) + pub(super) fn coerce_forced_unit( + &mut self, + ctx: &mut InferenceContext<'_>, + cause: CoercionCause, + ) { + self.coerce(ctx, None, &ctx.result.standard_types.unit.clone(), cause) } /// Merge two types from different branches, with possible coercion. @@ -102,6 +117,7 @@ impl CoerceMany { ctx: &mut InferenceContext<'_>, expr: Option, expr_ty: &Ty, + cause: CoercionCause, ) { let expr_ty = ctx.resolve_ty_shallow(expr_ty); self.expected_ty = ctx.resolve_ty_shallow(&self.expected_ty); @@ -110,6 +126,8 @@ impl CoerceMany { // pointers to have a chance at getting a match. See // https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916 let sig = match (self.merged_ty().kind(Interner), expr_ty.kind(Interner)) { + (TyKind::FnDef(x, _), TyKind::FnDef(y, _)) if x == y => None, + (TyKind::Closure(x, _), TyKind::Closure(y, _)) if x == y => None, (TyKind::FnDef(..) | TyKind::Closure(..), TyKind::FnDef(..) | TyKind::Closure(..)) => { // FIXME: we're ignoring safety here. To be more correct, if we have one FnDef and one Closure, // we should be coercing the closure to a fn pointer of the safety of the FnDef @@ -125,8 +143,15 @@ impl CoerceMany { let result1 = ctx.table.coerce_inner(self.merged_ty(), &target_ty); let result2 = ctx.table.coerce_inner(expr_ty.clone(), &target_ty); if let (Ok(result1), Ok(result2)) = (result1, result2) { - ctx.table.register_infer_ok(result1); - ctx.table.register_infer_ok(result2); + ctx.table.register_infer_ok(InferOk { value: (), goals: result1.goals }); + for &e in &self.expressions { + ctx.write_expr_adj(e, result1.value.0.clone()); + } + ctx.table.register_infer_ok(InferOk { value: (), goals: result2.goals }); + if let Some(expr) = expr { + ctx.write_expr_adj(expr, result2.value.0); + self.expressions.push(expr); + } return self.final_ty = Some(target_ty); } } @@ -140,14 +165,19 @@ impl CoerceMany { } else if let Ok(res) = ctx.coerce(expr, &self.merged_ty(), &expr_ty) { self.final_ty = Some(res); } else { - if let Some(id) = expr { - ctx.result.type_mismatches.insert( - id.into(), - TypeMismatch { expected: self.merged_ty().clone(), actual: expr_ty.clone() }, - ); + match cause { + CoercionCause::Expr(id) => { + ctx.result.type_mismatches.insert( + id.into(), + TypeMismatch { expected: self.merged_ty(), actual: expr_ty.clone() }, + ); + } } cov_mark::hit!(coerce_merge_fail_fallback); } + if let Some(expr) = expr { + self.expressions.push(expr); + } } } @@ -625,7 +655,7 @@ impl<'a> InferenceTable<'a> { // Need to find out in what cases this is necessary let solution = self .db - .trait_solve(krate, canonicalized.value.clone().cast(Interner)) + .trait_solve(krate, self.trait_env.block, canonicalized.value.clone().cast(Interner)) .ok_or(TypeError)?; match solution { @@ -657,7 +687,7 @@ impl<'a> InferenceTable<'a> { } fn coerce_closure_fn_ty(closure_substs: &Substitution, safety: chalk_ir::Safety) -> Ty { - let closure_sig = closure_substs.at(Interner, 0).assert_ty_ref(Interner).clone(); + let closure_sig = ClosureSubst(closure_substs).sig_ty().clone(); match closure_sig.kind(Interner) { TyKind::Function(fn_ty) => TyKind::Function(FnPointer { num_binders: fn_ty.num_binders, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index ee186673ee130..33e98ac86cf6b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -6,37 +6,43 @@ use std::{ }; use chalk_ir::{ - cast::Cast, fold::Shift, DebruijnIndex, GenericArgData, Mutability, TyKind, TyVariableKind, + cast::Cast, fold::Shift, DebruijnIndex, GenericArgData, Mutability, TyVariableKind, }; use hir_def::{ - expr::{ + generics::TypeOrConstParamData, + hir::{ ArithOp, Array, BinaryOp, ClosureKind, Expr, ExprId, LabelId, Literal, Statement, UnaryOp, }, - generics::TypeOrConstParamData, - lang_item::LangItem, + lang_item::{LangItem, LangItemTarget}, path::{GenericArg, GenericArgs}, - ConstParamId, FieldId, ItemContainerId, Lookup, + BlockId, ConstParamId, FieldId, ItemContainerId, Lookup, }; use hir_expand::name::{name, Name}; use stdx::always; use syntax::ast::RangeOp; +use triomphe::Arc; use crate::{ - autoderef::{self, Autoderef}, + autoderef::{builtin_deref, deref_by_trait, Autoderef}, consteval, infer::{ - coerce::CoerceMany, find_continuable, pat::contains_explicit_ref_binding, BreakableKind, + coerce::{CoerceMany, CoercionCause}, + find_continuable, + pat::contains_explicit_ref_binding, + BreakableKind, }, + lang_items::lang_items_for_bin_op, lower::{ const_or_path_to_chalk, generic_arg_to_chalk, lower_to_chalk_mutability, ParamLoweringMode, }, mapping::{from_chalk, ToChalk}, - method_resolution::{self, lang_items_for_bin_op, VisibleFromModule}, + method_resolution::{self, VisibleFromModule}, primitive::{self, UintTy}, static_lifetime, to_chalk_trait_id, + traits::FnTrait, utils::{generics, Generics}, Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, FnPointer, FnSig, FnSubst, - Interner, Rawness, Scalar, Substitution, TraitRef, Ty, TyBuilder, TyExt, + Interner, Rawness, Scalar, Substitution, TraitRef, Ty, TyBuilder, TyExt, TyKind, }; use super::{ @@ -83,10 +89,10 @@ impl<'a> InferenceContext<'a> { } } - pub(super) fn infer_expr_coerce_never(&mut self, expr: ExprId, expected: &Expectation) -> Ty { + fn infer_expr_coerce_never(&mut self, expr: ExprId, expected: &Expectation) -> Ty { let ty = self.infer_expr_inner(expr, expected); // While we don't allow *arbitrary* coercions here, we *do* allow - // coercions from ! to `expected`. + // coercions from `!` to `expected`. if ty.is_never() { if let Some(adjustments) = self.result.expr_adjustments.get(&expr) { return if let [Adjustment { kind: Adjust::NeverToAny, target }] = &**adjustments { @@ -96,13 +102,22 @@ impl<'a> InferenceContext<'a> { }; } - let adj_ty = self.table.new_type_var(); - self.write_expr_adj( - expr, - vec![Adjustment { kind: Adjust::NeverToAny, target: adj_ty.clone() }], - ); - adj_ty + if let Some(target) = expected.only_has_type(&mut self.table) { + self.coerce(Some(expr), &ty, &target) + .expect("never-to-any coercion should always succeed") + } else { + ty + } } else { + if let Some(expected_ty) = expected.only_has_type(&mut self.table) { + let could_unify = self.unify(&ty, &expected_ty); + if !could_unify { + self.result.type_mismatches.insert( + expr.into(), + TypeMismatch { expected: expected_ty, actual: ty.clone() }, + ); + } + } ty } } @@ -120,24 +135,28 @@ impl<'a> InferenceContext<'a> { ); let condition_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); - let mut both_arms_diverge = Diverges::Always; let then_ty = self.infer_expr_inner(then_branch, expected); - both_arms_diverge &= mem::replace(&mut self.diverges, Diverges::Maybe); + let then_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); let mut coerce = CoerceMany::new(expected.coercion_target_type(&mut self.table)); - coerce.coerce(self, Some(then_branch), &then_ty); + coerce.coerce(self, Some(then_branch), &then_ty, CoercionCause::Expr(then_branch)); match else_branch { Some(else_branch) => { let else_ty = self.infer_expr_inner(else_branch, expected); - coerce.coerce(self, Some(else_branch), &else_ty); + let else_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); + coerce.coerce( + self, + Some(else_branch), + &else_ty, + CoercionCause::Expr(else_branch), + ); + self.diverges = condition_diverges | then_diverges & else_diverges; } None => { - coerce.coerce_forced_unit(self); + coerce.coerce_forced_unit(self, CoercionCause::Expr(tgt_expr)); + self.diverges = condition_diverges; } } - both_arms_diverge &= self.diverges; - - self.diverges = condition_diverges | both_arms_diverge; coerce.complete(self) } @@ -146,67 +165,21 @@ impl<'a> InferenceContext<'a> { self.infer_top_pat(pat, &input_ty); self.result.standard_types.bool_.clone() } - Expr::Block { statements, tail, label, id: _ } => { - self.infer_block(tgt_expr, statements, *tail, *label, expected) + Expr::Block { statements, tail, label, id } => { + self.infer_block(tgt_expr, *id, statements, *tail, *label, expected) } - Expr::Unsafe { id: _, statements, tail } => { - self.infer_block(tgt_expr, statements, *tail, None, expected) + Expr::Unsafe { id, statements, tail } => { + self.infer_block(tgt_expr, *id, statements, *tail, None, expected) } - Expr::Const { id: _, statements, tail } => { + Expr::Const(id) => { self.with_breakable_ctx(BreakableKind::Border, None, None, |this| { - this.infer_block(tgt_expr, statements, *tail, None, expected) + let (_, expr) = this.db.lookup_intern_anonymous_const(*id); + this.infer_expr(expr, expected) }) .1 } - Expr::TryBlock { id: _, statements, tail } => { - // The type that is returned from the try block - let try_ty = self.table.new_type_var(); - if let Some(ty) = expected.only_has_type(&mut self.table) { - self.unify(&try_ty, &ty); - } - - // The ok-ish type that is expected from the last expression - let ok_ty = - self.resolve_associated_type(try_ty.clone(), self.resolve_ops_try_output()); - - self.infer_block( - tgt_expr, - statements, - *tail, - None, - &Expectation::has_type(ok_ty.clone()), - ); - try_ty - } - Expr::Async { id: _, statements, tail } => { - let ret_ty = self.table.new_type_var(); - let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); - let prev_ret_ty = mem::replace(&mut self.return_ty, ret_ty.clone()); - let prev_ret_coercion = - mem::replace(&mut self.return_coercion, Some(CoerceMany::new(ret_ty.clone()))); - - let (_, inner_ty) = - self.with_breakable_ctx(BreakableKind::Border, None, None, |this| { - this.infer_block( - tgt_expr, - statements, - *tail, - None, - &Expectation::has_type(ret_ty), - ) - }); - - self.diverges = prev_diverges; - self.return_ty = prev_ret_ty; - self.return_coercion = prev_ret_coercion; - - // Use the first type parameter as the output type of future. - // existential type AsyncBlockImplTrait: Future - let impl_trait_id = - crate::ImplTraitId::AsyncBlockTypeImplTrait(self.owner, tgt_expr); - let opaque_ty_id = self.db.intern_impl_trait_id(impl_trait_id).into(); - TyKind::OpaqueType(opaque_ty_id, Substitution::from1(Interner, inner_ty)) - .intern(Interner) + Expr::Async { id, statements, tail } => { + self.infer_async_block(tgt_expr, id, statements, tail) } &Expr::Loop { body, label } => { // FIXME: should be: @@ -238,25 +211,7 @@ impl<'a> InferenceContext<'a> { self.diverges = Diverges::Maybe; TyBuilder::unit() } - &Expr::For { iterable, body, pat, label } => { - let iterable_ty = self.infer_expr(iterable, &Expectation::none()); - let into_iter_ty = - self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item()); - let pat_ty = self - .resolve_associated_type(into_iter_ty.clone(), self.resolve_iterator_item()); - - self.result.type_of_for_iterator.insert(tgt_expr, into_iter_ty); - - self.infer_top_pat(pat, &pat_ty); - self.with_breakable_ctx(BreakableKind::Loop, None, label, |this| { - this.infer_expr(body, &Expectation::HasType(TyBuilder::unit())); - }); - - // the body may not run, so it diverging doesn't mean we diverge - self.diverges = Diverges::Maybe; - TyBuilder::unit() - } - Expr::Closure { body, args, ret_type, arg_types, closure_kind } => { + Expr::Closure { body, args, ret_type, arg_types, closure_kind, capture_by: _ } => { assert_eq!(args.len(), arg_types.len()); let mut sig_tys = Vec::with_capacity(arg_types.len() + 1); @@ -276,18 +231,7 @@ impl<'a> InferenceContext<'a> { None => self.table.new_type_var(), }; if let ClosureKind::Async = closure_kind { - // Use the first type parameter as the output type of future. - // existential type AsyncBlockImplTrait: Future - let impl_trait_id = - crate::ImplTraitId::AsyncBlockTypeImplTrait(self.owner, *body); - let opaque_ty_id = self.db.intern_impl_trait_id(impl_trait_id).into(); - sig_tys.push( - TyKind::OpaqueType( - opaque_ty_id, - Substitution::from1(Interner, ret_ty.clone()), - ) - .intern(Interner), - ); + sig_tys.push(self.lower_async_block_type_impl_trait(ret_ty.clone(), *body)); } else { sig_tys.push(ret_ty.clone()); } @@ -302,7 +246,7 @@ impl<'a> InferenceContext<'a> { }) .intern(Interner); - let (ty, resume_yield_tys) = match closure_kind { + let (id, ty, resume_yield_tys) = match closure_kind { ClosureKind::Generator(_) => { // FIXME: report error when there are more than 1 parameter. let resume_ty = match sig_tys.first() { @@ -322,17 +266,20 @@ impl<'a> InferenceContext<'a> { let generator_id = self.db.intern_generator((self.owner, tgt_expr)).into(); let generator_ty = TyKind::Generator(generator_id, subst).intern(Interner); - (generator_ty, Some((resume_ty, yield_ty))) + (None, generator_ty, Some((resume_ty, yield_ty))) } ClosureKind::Closure | ClosureKind::Async => { let closure_id = self.db.intern_closure((self.owner, tgt_expr)).into(); let closure_ty = TyKind::Closure( closure_id, - Substitution::from1(Interner, sig_ty.clone()), + TyBuilder::subst_for_closure(self.db, self.owner, sig_ty.clone()), ) .intern(Interner); - - (closure_ty, None) + self.deferred_closures.entry(closure_id).or_default(); + if let Some(c) = self.current_closure { + self.closure_dependencies.entry(c).or_default().push(closure_id); + } + (Some(closure_id), closure_ty, None) } }; @@ -348,9 +295,10 @@ impl<'a> InferenceContext<'a> { // FIXME: lift these out into a struct let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); + let prev_closure = mem::replace(&mut self.current_closure, id); let prev_ret_ty = mem::replace(&mut self.return_ty, ret_ty.clone()); let prev_ret_coercion = - mem::replace(&mut self.return_coercion, Some(CoerceMany::new(ret_ty.clone()))); + mem::replace(&mut self.return_coercion, Some(CoerceMany::new(ret_ty))); let prev_resume_yield_tys = mem::replace(&mut self.resume_yield_tys, resume_yield_tys); @@ -361,6 +309,7 @@ impl<'a> InferenceContext<'a> { self.diverges = prev_diverges; self.return_ty = prev_ret_ty; self.return_coercion = prev_ret_coercion; + self.current_closure = prev_closure; self.resume_yield_tys = prev_resume_yield_tys; ty @@ -385,16 +334,31 @@ impl<'a> InferenceContext<'a> { || res.is_none(); let (param_tys, ret_ty) = match res { Some((func, params, ret_ty)) => { - let adjustments = auto_deref_adjust_steps(&derefs); - // FIXME: Handle call adjustments for Fn/FnMut - self.write_expr_adj(*callee, adjustments); - if let Some((trait_, func)) = func { - let subst = TyBuilder::subst_for_def(self.db, trait_, None) - .push(callee_ty.clone()) - .push(TyBuilder::tuple_with(params.iter().cloned())) - .build(); - self.write_method_resolution(tgt_expr, func, subst.clone()); + let mut adjustments = auto_deref_adjust_steps(&derefs); + if let TyKind::Closure(c, _) = + self.table.resolve_completely(callee_ty.clone()).kind(Interner) + { + if let Some(par) = self.current_closure { + self.closure_dependencies.entry(par).or_default().push(*c); + } + self.deferred_closures.entry(*c).or_default().push(( + derefed_callee.clone(), + callee_ty.clone(), + params.clone(), + tgt_expr, + )); } + if let Some(fn_x) = func { + self.write_fn_trait_method_resolution( + fn_x, + &derefed_callee, + &mut adjustments, + &callee_ty, + ¶ms, + tgt_expr, + ); + } + self.write_expr_adj(*callee, adjustments); (params, ret_ty) } None => { @@ -470,7 +434,7 @@ impl<'a> InferenceContext<'a> { let arm_ty = self.infer_expr_inner(arm.expr, &expected); all_arms_diverge &= self.diverges; - coerce.coerce(self, Some(arm.expr), &arm_ty); + coerce.coerce(self, Some(arm.expr), &arm_ty, CoercionCause::Expr(arm.expr)); } self.diverges = matchee_diverges | all_arms_diverge; @@ -484,8 +448,8 @@ impl<'a> InferenceContext<'a> { self.resolver.reset_to_guard(g); ty } - Expr::Continue { label } => { - if let None = find_continuable(&mut self.breakables, label.as_ref()) { + &Expr::Continue { label } => { + if let None = find_continuable(&mut self.breakables, label) { self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop { expr: tgt_expr, is_break: false, @@ -494,9 +458,9 @@ impl<'a> InferenceContext<'a> { }; self.result.standard_types.never.clone() } - Expr::Break { expr, label } => { - let val_ty = if let Some(expr) = *expr { - let opt_coerce_to = match find_breakable(&mut self.breakables, label.as_ref()) { + &Expr::Break { expr, label } => { + let val_ty = if let Some(expr) = expr { + let opt_coerce_to = match find_breakable(&mut self.breakables, label) { Some(ctxt) => match &ctxt.coerce { Some(coerce) => coerce.expected_ty(), None => { @@ -515,13 +479,17 @@ impl<'a> InferenceContext<'a> { TyBuilder::unit() }; - match find_breakable(&mut self.breakables, label.as_ref()) { + match find_breakable(&mut self.breakables, label) { Some(ctxt) => match ctxt.coerce.take() { Some(mut coerce) => { - coerce.coerce(self, *expr, &val_ty); + let cause = match expr { + Some(expr) => CoercionCause::Expr(expr), + None => CoercionCause::Expr(tgt_expr), + }; + coerce.coerce(self, expr, &val_ty, cause); // Avoiding borrowck - let ctxt = find_breakable(&mut self.breakables, label.as_ref()) + let ctxt = find_breakable(&mut self.breakables, label) .expect("breakable stack changed during coercion"); ctxt.may_break = true; ctxt.coerce = Some(coerce); @@ -538,7 +506,7 @@ impl<'a> InferenceContext<'a> { } self.result.standard_types.never.clone() } - &Expr::Return { expr } => self.infer_expr_return(expr), + &Expr::Return { expr } => self.infer_expr_return(tgt_expr, expr), Expr::Yield { expr } => { if let Some((resume_ty, yield_ty)) = self.resume_yield_tys.clone() { if let Some(expr) = expr { @@ -589,6 +557,9 @@ impl<'a> InferenceContext<'a> { let field_ty = field_def.map_or(self.err_ty(), |it| { field_types[it.local_id].clone().substitute(Interner, &substs) }); + // Field type might have some unknown types + // FIXME: we may want to emit a single type variable for all instance of type fields? + let field_ty = self.insert_type_vars(field_ty); self.infer_expr_coerce(field.expr, &Expectation::has_type(field_ty)); } if let Some(expr) = spread { @@ -601,26 +572,18 @@ impl<'a> InferenceContext<'a> { let inner_ty = self.infer_expr_inner(*expr, &Expectation::none()); self.resolve_associated_type(inner_ty, self.resolve_future_future_output()) } - Expr::Try { expr } => { - let inner_ty = self.infer_expr_inner(*expr, &Expectation::none()); - if let Some(trait_) = self.resolve_lang_trait(LangItem::Try) { - if let Some(func) = self.db.trait_data(trait_).method_by_name(&name!(branch)) { - let subst = TyBuilder::subst_for_def(self.db, trait_, None) - .push(inner_ty.clone()) - .build(); - self.write_method_resolution(tgt_expr, func, subst.clone()); - } - let try_output = self.resolve_output_on(trait_); - self.resolve_associated_type(inner_ty, try_output) - } else { - self.err_ty() - } - } Expr::Cast { expr, type_ref } => { let cast_ty = self.make_ty(type_ref); // FIXME: propagate the "castable to" expectation - let _inner_ty = self.infer_expr_no_expect(*expr); - // FIXME check the cast... + let inner_ty = self.infer_expr_no_expect(*expr); + match (inner_ty.kind(Interner), cast_ty.kind(Interner)) { + (TyKind::Ref(_, _, inner), TyKind::Raw(_, cast)) => { + // FIXME: record invalid cast diagnostic in case of mismatch + self.unify(inner, cast); + } + // FIXME check the other kinds of cast... + _ => (), + } cast_ty } Expr::Ref { expr, rawness, mutability } => { @@ -638,7 +601,7 @@ impl<'a> InferenceContext<'a> { // FIXME: record type error - expected reference but found ptr, // which cannot be coerced } - Expectation::rvalue_hint(&mut self.table, Ty::clone(exp_inner)) + Expectation::rvalue_hint(self, Ty::clone(exp_inner)) } else { Expectation::none() }; @@ -656,7 +619,25 @@ impl<'a> InferenceContext<'a> { // FIXME: Note down method resolution her match op { UnaryOp::Deref => { - autoderef::deref(&mut self.table, inner_ty).unwrap_or_else(|| self.err_ty()) + if let Some(deref_trait) = self.resolve_lang_trait(LangItem::Deref) { + if let Some(deref_fn) = + self.db.trait_data(deref_trait).method_by_name(&name![deref]) + { + // FIXME: this is wrong in multiple ways, subst is empty, and we emit it even for builtin deref (note that + // the mutability is not wrong, and will be fixed in `self.infer_mut`). + self.write_method_resolution( + tgt_expr, + deref_fn, + Substitution::empty(Interner), + ); + } + } + if let Some(derefed) = builtin_deref(&mut self.table, &inner_ty, true) { + self.resolve_ty_shallow(derefed) + } else { + deref_by_trait(&mut self.table, inner_ty) + .unwrap_or_else(|| self.err_ty()) + } } UnaryOp::Neg => { match inner_ty.kind(Interner) { @@ -767,14 +748,16 @@ impl<'a> InferenceContext<'a> { let canonicalized = self.canonicalize(base_ty.clone()); let receiver_adjustments = method_resolution::resolve_indexing_op( self.db, - self.trait_env.clone(), + self.table.trait_env.clone(), canonicalized.value, index_trait, ); - let (self_ty, adj) = receiver_adjustments + let (self_ty, mut adj) = receiver_adjustments .map_or((self.err_ty(), Vec::new()), |adj| { adj.apply(&mut self.table, base_ty) }); + // mutability will be fixed up in `InferenceContext::infer_mut`; + adj.push(Adjustment::borrow(Mutability::Not, self_ty.clone())); self.write_expr_adj(*base, adj); if let Some(func) = self.db.trait_data(index_trait).method_by_name(&name!(index)) @@ -783,7 +766,7 @@ impl<'a> InferenceContext<'a> { .push(self_ty.clone()) .push(index_ty.clone()) .build(); - self.write_method_resolution(tgt_expr, func, substs.clone()); + self.write_method_resolution(tgt_expr, func, substs); } self.resolve_associated_type_with_params( self_ty, @@ -834,6 +817,20 @@ impl<'a> InferenceContext<'a> { let array_type = TyKind::Array(byte_type, len).intern(Interner); TyKind::Ref(Mutability::Not, static_lifetime(), array_type).intern(Interner) } + Literal::CString(..) => TyKind::Ref( + Mutability::Not, + static_lifetime(), + self.resolve_lang_item(LangItem::CStr) + .and_then(LangItemTarget::as_struct) + .map_or_else( + || self.err_ty(), + |strukt| { + TyKind::Adt(AdtId(strukt.into()), Substitution::empty(Interner)) + .intern(Interner) + }, + ), + ) + .intern(Interner), Literal::Char(..) => TyKind::Scalar(Scalar::Char).intern(Interner), Literal::Int(_v, ty) => match ty { Some(int_ty) => { @@ -859,9 +856,15 @@ impl<'a> InferenceContext<'a> { }, Expr::Underscore => { // Underscore expressions may only appear in assignee expressions, - // which are handled by `infer_assignee_expr()`, so any underscore - // expression reaching this branch is an error. - self.err_ty() + // which are handled by `infer_assignee_expr()`. + // Any other underscore expression is an error, we render a specialized diagnostic + // to let the user know what type is expected though. + let expected = expected.to_option(&mut self.table).unwrap_or_else(|| self.err_ty()); + self.push_diagnostic(InferenceDiagnostic::TypedHole { + expr: tgt_expr, + expected: expected.clone(), + }); + expected } }; // use a new type variable if we got unknown here @@ -874,6 +877,88 @@ impl<'a> InferenceContext<'a> { ty } + fn infer_async_block( + &mut self, + tgt_expr: ExprId, + id: &Option, + statements: &[Statement], + tail: &Option, + ) -> Ty { + let ret_ty = self.table.new_type_var(); + let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); + let prev_ret_ty = mem::replace(&mut self.return_ty, ret_ty.clone()); + let prev_ret_coercion = + mem::replace(&mut self.return_coercion, Some(CoerceMany::new(ret_ty.clone()))); + + let (_, inner_ty) = self.with_breakable_ctx(BreakableKind::Border, None, None, |this| { + this.infer_block(tgt_expr, *id, statements, *tail, None, &Expectation::has_type(ret_ty)) + }); + + self.diverges = prev_diverges; + self.return_ty = prev_ret_ty; + self.return_coercion = prev_ret_coercion; + + self.lower_async_block_type_impl_trait(inner_ty, tgt_expr) + } + + pub(crate) fn lower_async_block_type_impl_trait( + &mut self, + inner_ty: Ty, + tgt_expr: ExprId, + ) -> Ty { + // Use the first type parameter as the output type of future. + // existential type AsyncBlockImplTrait: Future + let impl_trait_id = crate::ImplTraitId::AsyncBlockTypeImplTrait(self.owner, tgt_expr); + let opaque_ty_id = self.db.intern_impl_trait_id(impl_trait_id).into(); + TyKind::OpaqueType(opaque_ty_id, Substitution::from1(Interner, inner_ty)).intern(Interner) + } + + pub(crate) fn write_fn_trait_method_resolution( + &mut self, + fn_x: FnTrait, + derefed_callee: &Ty, + adjustments: &mut Vec, + callee_ty: &Ty, + params: &Vec, + tgt_expr: ExprId, + ) { + match fn_x { + FnTrait::FnOnce => (), + FnTrait::FnMut => { + if let TyKind::Ref(Mutability::Mut, _, inner) = derefed_callee.kind(Interner) { + if adjustments + .last() + .map(|x| matches!(x.kind, Adjust::Borrow(_))) + .unwrap_or(true) + { + // prefer reborrow to move + adjustments + .push(Adjustment { kind: Adjust::Deref(None), target: inner.clone() }); + adjustments.push(Adjustment::borrow(Mutability::Mut, inner.clone())) + } + } else { + adjustments.push(Adjustment::borrow(Mutability::Mut, derefed_callee.clone())); + } + } + FnTrait::Fn => { + if !matches!(derefed_callee.kind(Interner), TyKind::Ref(Mutability::Not, _, _)) { + adjustments.push(Adjustment::borrow(Mutability::Not, derefed_callee.clone())); + } + } + } + let Some(trait_) = fn_x.get_id(self.db, self.table.trait_env.krate) else { + return; + }; + let trait_data = self.db.trait_data(trait_); + if let Some(func) = trait_data.method_by_name(&fn_x.method_name()) { + let subst = TyBuilder::subst_for_def(self.db, trait_, None) + .push(callee_ty.clone()) + .push(TyBuilder::tuple_with(params.iter().cloned())) + .build(); + self.write_method_resolution(tgt_expr, func, subst.clone()); + } + } + fn infer_expr_array( &mut self, array: &Array, @@ -892,10 +977,10 @@ impl<'a> InferenceContext<'a> { (elem_ty, consteval::usize_const(self.db, Some(0), krate)) } Array::ElementList { elements, .. } => { - let mut coerce = CoerceMany::new(elem_ty.clone()); + let mut coerce = CoerceMany::new(elem_ty); for &expr in elements.iter() { let cur_elem_ty = self.infer_expr_inner(expr, &expected); - coerce.coerce(self, Some(expr), &cur_elem_ty); + coerce.coerce(self, Some(expr), &cur_elem_ty, CoercionCause::Expr(expr)); } ( coerce.complete(self), @@ -904,12 +989,13 @@ impl<'a> InferenceContext<'a> { } &Array::Repeat { initializer, repeat } => { self.infer_expr_coerce(initializer, &Expectation::has_type(elem_ty.clone())); - self.infer_expr( - repeat, - &Expectation::HasType( - TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner), - ), - ); + let usize = TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner); + match self.body[repeat] { + Expr::Underscore => { + self.write_expr_ty(repeat, usize); + } + _ => _ = self.infer_expr(repeat, &Expectation::HasType(usize)), + } ( elem_ty, @@ -928,7 +1014,8 @@ impl<'a> InferenceContext<'a> { ) } }; - + // Try to evaluate unevaluated constant, and insert variable if is not possible. + let len = self.table.insert_const_vars_shallow(len); TyKind::Array(elem_ty, len).intern(Interner) } @@ -940,18 +1027,18 @@ impl<'a> InferenceContext<'a> { .expected_ty(); let return_expr_ty = self.infer_expr_inner(expr, &Expectation::HasType(ret_ty)); let mut coerce_many = self.return_coercion.take().unwrap(); - coerce_many.coerce(self, Some(expr), &return_expr_ty); + coerce_many.coerce(self, Some(expr), &return_expr_ty, CoercionCause::Expr(expr)); self.return_coercion = Some(coerce_many); } - fn infer_expr_return(&mut self, expr: Option) -> Ty { + fn infer_expr_return(&mut self, ret: ExprId, expr: Option) -> Ty { match self.return_coercion { Some(_) => { if let Some(expr) = expr { self.infer_return(expr); } else { let mut coerce = self.return_coercion.take().unwrap(); - coerce.coerce_forced_unit(self); + coerce.coerce_forced_unit(self, CoercionCause::Expr(ret)); self.return_coercion = Some(coerce); } } @@ -976,7 +1063,7 @@ impl<'a> InferenceContext<'a> { .filter(|(e_adt, _)| e_adt == &box_id) .map(|(_, subts)| { let g = subts.at(Interner, 0); - Expectation::rvalue_hint(table, Ty::clone(g.assert_ty_ref(Interner))) + Expectation::rvalue_hint(self, Ty::clone(g.assert_ty_ref(Interner))) }) .unwrap_or_else(Expectation::none); @@ -1185,6 +1272,7 @@ impl<'a> InferenceContext<'a> { fn infer_block( &mut self, expr: ExprId, + block_id: Option, statements: &[Statement], tail: Option, label: Option, @@ -1192,9 +1280,14 @@ impl<'a> InferenceContext<'a> { ) -> Ty { let coerce_ty = expected.coercion_target_type(&mut self.table); let g = self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, expr); + let prev_env = block_id.map(|block_id| { + let prev_env = self.table.trait_env.clone(); + Arc::make_mut(&mut self.table.trait_env).block = Some(block_id); + prev_env + }); let (break_ty, ty) = - self.with_breakable_ctx(BreakableKind::Block, Some(coerce_ty.clone()), label, |this| { + self.with_breakable_ctx(BreakableKind::Block, Some(coerce_ty), label, |this| { for stmt in statements { match stmt { Statement::Let { pat, type_ref, initializer, else_branch } => { @@ -1280,6 +1373,9 @@ impl<'a> InferenceContext<'a> { } }); self.resolver.reset_to_guard(g); + if let Some(prev_env) = prev_env { + self.table.trait_env = prev_env; + } break_ty.unwrap_or(ty) } @@ -1378,7 +1474,7 @@ impl<'a> InferenceContext<'a> { method_resolution::lookup_method( self.db, &canonicalized_receiver.value, - self.trait_env.clone(), + self.table.trait_env.clone(), self.get_traits_in_scope().as_ref().left_or_else(|&it| it), VisibleFromModule::Filter(self.resolver.module()), name, @@ -1411,7 +1507,7 @@ impl<'a> InferenceContext<'a> { let resolved = method_resolution::lookup_method( self.db, &canonicalized_receiver.value, - self.trait_env.clone(), + self.table.trait_env.clone(), self.get_traits_in_scope().as_ref().left_or_else(|&it| it), VisibleFromModule::Filter(self.resolver.module()), method_name, @@ -1562,7 +1658,7 @@ impl<'a> InferenceContext<'a> { // the parameter to coerce to the expected type (for example in // `coerce_unsize_expected_type_4`). let param_ty = self.normalize_associated_types_in(param_ty); - let expected = Expectation::rvalue_hint(&mut self.table, expected_ty); + let expected = Expectation::rvalue_hint(self, expected_ty); // infer with the expected type we have... let ty = self.infer_expr_inner(arg, &expected); @@ -1575,9 +1671,10 @@ impl<'a> InferenceContext<'a> { } else { param_ty }; - if !coercion_target.is_unknown() - && self.coerce(Some(arg), &ty, &coercion_target).is_err() - { + // The function signature may contain some unknown types, so we need to insert + // type vars here to avoid type mismatch false positive. + let coercion_target = self.insert_type_vars(coercion_target); + if self.coerce(Some(arg), &ty, &coercion_target).is_err() { self.result.type_mismatches.insert( arg.into(), TypeMismatch { expected: coercion_target, actual: ty.clone() }, @@ -1868,7 +1965,6 @@ impl<'a> InferenceContext<'a> { cb: impl FnOnce(&mut Self) -> T, ) -> (Option, T) { self.breakables.push({ - let label = label.map(|label| self.body[label].name.clone()); BreakableContext { kind, may_break: false, coerce: ty.map(CoerceMany::new), label } }); let res = cb(self); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs new file mode 100644 index 0000000000000..447834243908a --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs @@ -0,0 +1,218 @@ +//! Finds if an expression is an immutable context or a mutable context, which is used in selecting +//! between `Deref` and `DerefMut` or `Index` and `IndexMut` or similar. + +use chalk_ir::Mutability; +use hir_def::{ + hir::{Array, BinaryOp, BindingAnnotation, Expr, ExprId, PatId, Statement, UnaryOp}, + lang_item::LangItem, +}; +use hir_expand::name; + +use crate::{lower::lower_to_chalk_mutability, Adjust, Adjustment, AutoBorrow, OverloadedDeref}; + +use super::InferenceContext; + +impl<'a> InferenceContext<'a> { + pub(crate) fn infer_mut_body(&mut self) { + self.infer_mut_expr(self.body.body_expr, Mutability::Not); + } + + fn infer_mut_expr(&mut self, tgt_expr: ExprId, mut mutability: Mutability) { + if let Some(adjustments) = self.result.expr_adjustments.get_mut(&tgt_expr) { + for adj in adjustments.iter_mut().rev() { + match &mut adj.kind { + Adjust::NeverToAny | Adjust::Deref(None) | Adjust::Pointer(_) => (), + Adjust::Deref(Some(d)) => *d = OverloadedDeref(Some(mutability)), + Adjust::Borrow(b) => match b { + AutoBorrow::Ref(m) | AutoBorrow::RawPtr(m) => mutability = *m, + }, + } + } + } + self.infer_mut_expr_without_adjust(tgt_expr, mutability); + } + + fn infer_mut_expr_without_adjust(&mut self, tgt_expr: ExprId, mutability: Mutability) { + match &self.body[tgt_expr] { + Expr::Missing => (), + &Expr::If { condition, then_branch, else_branch } => { + self.infer_mut_expr(condition, Mutability::Not); + self.infer_mut_expr(then_branch, Mutability::Not); + if let Some(else_branch) = else_branch { + self.infer_mut_expr(else_branch, Mutability::Not); + } + } + Expr::Const(id) => { + let (_, expr) = self.db.lookup_intern_anonymous_const(*id); + self.infer_mut_expr(expr, Mutability::Not); + } + Expr::Let { pat, expr } => self.infer_mut_expr(*expr, self.pat_bound_mutability(*pat)), + Expr::Block { id: _, statements, tail, label: _ } + | Expr::Async { id: _, statements, tail } + | Expr::Unsafe { id: _, statements, tail } => { + for st in statements.iter() { + match st { + Statement::Let { pat, type_ref: _, initializer, else_branch } => { + if let Some(i) = initializer { + self.infer_mut_expr(*i, self.pat_bound_mutability(*pat)); + } + if let Some(e) = else_branch { + self.infer_mut_expr(*e, Mutability::Not); + } + } + Statement::Expr { expr, has_semi: _ } => { + self.infer_mut_expr(*expr, Mutability::Not); + } + } + } + if let Some(tail) = tail { + self.infer_mut_expr(*tail, Mutability::Not); + } + } + &Expr::While { condition: c, body, label: _ } => { + self.infer_mut_expr(c, Mutability::Not); + self.infer_mut_expr(body, Mutability::Not); + } + Expr::MethodCall { receiver: x, method_name: _, args, generic_args: _ } + | Expr::Call { callee: x, args, is_assignee_expr: _ } => { + self.infer_mut_not_expr_iter(args.iter().copied().chain(Some(*x))); + } + Expr::Match { expr, arms } => { + let m = self.pat_iter_bound_mutability(arms.iter().map(|x| x.pat)); + self.infer_mut_expr(*expr, m); + for arm in arms.iter() { + self.infer_mut_expr(arm.expr, Mutability::Not); + if let Some(g) = arm.guard { + self.infer_mut_expr(g, Mutability::Not); + } + } + } + Expr::Yield { expr } + | Expr::Yeet { expr } + | Expr::Return { expr } + | Expr::Break { expr, label: _ } => { + if let &Some(expr) = expr { + self.infer_mut_expr(expr, Mutability::Not); + } + } + Expr::RecordLit { path: _, fields, spread, ellipsis: _, is_assignee_expr: _ } => { + self.infer_mut_not_expr_iter(fields.iter().map(|x| x.expr).chain(*spread)) + } + &Expr::Index { base, index } => { + if mutability == Mutability::Mut { + if let Some((f, _)) = self.result.method_resolutions.get_mut(&tgt_expr) { + if let Some(index_trait) = self + .db + .lang_item(self.table.trait_env.krate, LangItem::IndexMut) + .and_then(|l| l.as_trait()) + { + if let Some(index_fn) = + self.db.trait_data(index_trait).method_by_name(&name![index_mut]) + { + *f = index_fn; + let base_adjustments = self + .result + .expr_adjustments + .get_mut(&base) + .and_then(|it| it.last_mut()); + if let Some(Adjustment { + kind: Adjust::Borrow(AutoBorrow::Ref(mutability)), + .. + }) = base_adjustments + { + *mutability = Mutability::Mut; + } + } + } + } + } + self.infer_mut_expr(base, mutability); + self.infer_mut_expr(index, Mutability::Not); + } + Expr::UnaryOp { expr, op: UnaryOp::Deref } => { + if let Some((f, _)) = self.result.method_resolutions.get_mut(&tgt_expr) { + if mutability == Mutability::Mut { + if let Some(deref_trait) = self + .db + .lang_item(self.table.trait_env.krate, LangItem::DerefMut) + .and_then(|l| l.as_trait()) + { + if let Some(deref_fn) = + self.db.trait_data(deref_trait).method_by_name(&name![deref_mut]) + { + *f = deref_fn; + } + } + } + } + self.infer_mut_expr(*expr, mutability); + } + Expr::Field { expr, name: _ } => { + self.infer_mut_expr(*expr, mutability); + } + Expr::UnaryOp { expr, op: _ } + | Expr::Range { lhs: Some(expr), rhs: None, range_type: _ } + | Expr::Range { rhs: Some(expr), lhs: None, range_type: _ } + | Expr::Await { expr } + | Expr::Box { expr } + | Expr::Loop { body: expr, label: _ } + | Expr::Cast { expr, type_ref: _ } => { + self.infer_mut_expr(*expr, Mutability::Not); + } + Expr::Ref { expr, rawness: _, mutability } => { + let mutability = lower_to_chalk_mutability(*mutability); + self.infer_mut_expr(*expr, mutability); + } + Expr::BinaryOp { lhs, rhs, op: Some(BinaryOp::Assignment { .. }) } => { + self.infer_mut_expr(*lhs, Mutability::Mut); + self.infer_mut_expr(*rhs, Mutability::Not); + } + Expr::Array(Array::Repeat { initializer: lhs, repeat: rhs }) + | Expr::BinaryOp { lhs, rhs, op: _ } + | Expr::Range { lhs: Some(lhs), rhs: Some(rhs), range_type: _ } => { + self.infer_mut_expr(*lhs, Mutability::Not); + self.infer_mut_expr(*rhs, Mutability::Not); + } + Expr::Closure { body, .. } => { + self.infer_mut_expr(*body, Mutability::Not); + } + Expr::Tuple { exprs, is_assignee_expr: _ } + | Expr::Array(Array::ElementList { elements: exprs, is_assignee_expr: _ }) => { + self.infer_mut_not_expr_iter(exprs.iter().copied()); + } + // These don't need any action, as they don't have sub expressions + Expr::Range { lhs: None, rhs: None, range_type: _ } + | Expr::Literal(_) + | Expr::Path(_) + | Expr::Continue { .. } + | Expr::Underscore => (), + } + } + + fn infer_mut_not_expr_iter(&mut self, exprs: impl Iterator) { + for expr in exprs { + self.infer_mut_expr(expr, Mutability::Not); + } + } + + fn pat_iter_bound_mutability(&self, mut pat: impl Iterator) -> Mutability { + if pat.any(|p| self.pat_bound_mutability(p) == Mutability::Mut) { + Mutability::Mut + } else { + Mutability::Not + } + } + + /// Checks if the pat contains a `ref mut` binding. Such paths makes the context of bounded expressions + /// mutable. For example in `let (ref mut x0, ref x1) = *x;` we need to use `DerefMut` for `*x` but in + /// `let (ref x0, ref x1) = *x;` we should use `Deref`. + fn pat_bound_mutability(&self, pat: PatId) -> Mutability { + let mut r = Mutability::Not; + self.body.walk_bindings_in_pat(pat, |b| { + if self.body.bindings[b].mode == BindingAnnotation::RefMut { + r = Mutability::Mut; + } + }); + r + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs index 5f839fc307aab..2480f8babac4f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs @@ -5,7 +5,7 @@ use std::iter::repeat_with; use chalk_ir::Mutability; use hir_def::{ body::Body, - expr::{Binding, BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, Literal, Pat, PatId}, + hir::{Binding, BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, Literal, Pat, PatId}, path::Path, }; use hir_expand::name::Name; @@ -255,15 +255,15 @@ impl<'a> InferenceContext<'a> { self.infer_slice_pat(&expected, prefix, slice, suffix, default_bm) } Pat::Wild => expected.clone(), - Pat::Range { start, end } => { - let start_ty = self.infer_expr(*start, &Expectation::has_type(expected.clone())); - self.infer_expr(*end, &Expectation::has_type(start_ty)) + Pat::Range { .. } => { + // FIXME: do some checks here. + expected.clone() } &Pat::Lit(expr) => { // Don't emit type mismatches again, the expression lowering already did that. let ty = self.infer_lit_pat(expr, &expected); self.write_pat_ty(pat, ty.clone()); - return ty; + return self.pat_ty_after_adjustment(pat); } Pat::Box { inner } => match self.resolve_boxed_box() { Some(box_adt) => { @@ -298,22 +298,38 @@ impl<'a> InferenceContext<'a> { .type_mismatches .insert(pat.into(), TypeMismatch { expected, actual: ty.clone() }); } - self.write_pat_ty(pat, ty.clone()); - ty + self.write_pat_ty(pat, ty); + self.pat_ty_after_adjustment(pat) + } + + fn pat_ty_after_adjustment(&self, pat: PatId) -> Ty { + self.result + .pat_adjustments + .get(&pat) + .and_then(|x| x.first()) + .unwrap_or(&self.result.type_of_pat[pat]) + .clone() } fn infer_ref_pat( &mut self, - pat: PatId, + inner_pat: PatId, mutability: Mutability, expected: &Ty, default_bm: BindingMode, ) -> Ty { let expectation = match expected.as_reference() { Some((inner_ty, _lifetime, _exp_mut)) => inner_ty.clone(), - _ => self.result.standard_types.unknown.clone(), + None => { + let inner_ty = self.table.new_type_var(); + let ref_ty = + TyKind::Ref(mutability, static_lifetime(), inner_ty.clone()).intern(Interner); + // Unification failure will be reported by the caller. + self.unify(&ref_ty, expected); + inner_ty + } }; - let subty = self.infer_pat(pat, &expectation, default_bm); + let subty = self.infer_pat(inner_pat, &expectation, default_bm); TyKind::Ref(mutability, static_lifetime(), subty).intern(Interner) } @@ -331,7 +347,7 @@ impl<'a> InferenceContext<'a> { } else { BindingMode::convert(mode) }; - self.result.pat_binding_modes.insert(pat, mode); + self.result.binding_modes.insert(binding, mode); let inner_ty = match subpat { Some(subpat) => self.infer_pat(subpat, &expected, default_bm), @@ -345,7 +361,7 @@ impl<'a> InferenceContext<'a> { } BindingMode::Move => inner_ty.clone(), }; - self.write_pat_ty(pat, bound_ty.clone()); + self.write_pat_ty(pat, inner_ty.clone()); self.write_binding_ty(binding, bound_ty); return inner_ty; } @@ -370,7 +386,7 @@ impl<'a> InferenceContext<'a> { if let &Some(slice_pat_id) = slice { let rest_pat_ty = match expected.kind(Interner) { TyKind::Array(_, length) => { - let len = try_const_usize(length); + let len = try_const_usize(self.db, length); let len = len.and_then(|len| len.checked_sub((prefix.len() + suffix.len()) as u128)); TyKind::Array(elem_ty.clone(), usize_const(self.db, len, self.resolver.krate())) @@ -419,17 +435,10 @@ 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(..) | Literal::ByteString(..))) - } - Pat::Bind { id, subpat: Some(subpat), .. } - if matches!( - body.bindings[*id].mode, - BindingAnnotation::Mutable | BindingAnnotation::Unannotated - ) => - { - is_non_ref_pat(body, *subpat) - } + Pat::Lit(expr) => !matches!( + body[*expr], + Expr::Literal(Literal::String(..) | Literal::CString(..) | Literal::ByteString(..)) + ), Pat::Wild | Pat::Bind { .. } | Pat::Ref { .. } | Pat::Box { .. } | Pat::Missing => false, } } @@ -437,7 +446,7 @@ fn is_non_ref_pat(body: &hir_def::body::Body, pat: PatId) -> bool { pub(super) fn contains_explicit_ref_binding(body: &Body, pat_id: PatId) -> bool { let mut res = false; body.walk_pats(pat_id, &mut |pat| { - res |= matches!(pat, Pat::Bind { id, .. } if body.bindings[*id].mode == BindingAnnotation::Ref); + res |= matches!(body[pat], Pat::Bind { id, .. } if body.bindings[id].mode == BindingAnnotation::Ref); }); res } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs index 2267fedaa8e92..95a20f983f1b4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs @@ -4,7 +4,7 @@ use chalk_ir::cast::Cast; use hir_def::{ path::{Path, PathSegment}, resolver::{ResolveValueResult, TypeNs, ValueNs}, - AdtId, AssocItemId, EnumVariantId, ItemContainerId, Lookup, + AdtId, AssocItemId, EnumVariantId, GenericDefId, ItemContainerId, Lookup, }; use hir_expand::name::Name; use stdx::never; @@ -13,6 +13,7 @@ use crate::{ builder::ParamKind, consteval, method_resolution::{self, VisibleFromModule}, + to_chalk_trait_id, utils::generics, InferenceDiagnostic, Interner, Substitution, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, ValueTyDefId, @@ -20,26 +21,43 @@ use crate::{ use super::{ExprOrPatId, InferenceContext, TraitRef}; -impl<'a> InferenceContext<'a> { +impl InferenceContext<'_> { pub(super) fn infer_path(&mut self, path: &Path, id: ExprOrPatId) -> Option { - let ty = self.resolve_value_path(path, id)?; - let ty = self.insert_type_vars(ty); + let (value_def, generic_def, substs) = match self.resolve_value_path(path, id)? { + ValuePathResolution::GenericDef(value_def, generic_def, substs) => { + (value_def, generic_def, substs) + } + ValuePathResolution::NonGeneric(ty) => return Some(ty), + }; + let substs = self.insert_type_vars(substs); + let substs = self.normalize_associated_types_in(substs); + + self.add_required_obligations_for_value_path(generic_def, &substs); + + let ty = self.db.value_ty(value_def).substitute(Interner, &substs); let ty = self.normalize_associated_types_in(ty); Some(ty) } - fn resolve_value_path(&mut self, path: &Path, id: ExprOrPatId) -> Option { + fn resolve_value_path(&mut self, path: &Path, id: ExprOrPatId) -> Option { let (value, self_subst) = if let Some(type_ref) = path.type_anchor() { - let Some(last) = path.segments().last() else { return None }; - let ty = self.make_ty(type_ref); - let remaining_segments_for_ty = path.segments().take(path.segments().len() - 1); + let last = path.segments().last()?; + + // Don't use `self.make_ty()` here as we need `orig_ns`. let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver); - let (ty, _) = ctx.lower_ty_relative_path(ty, None, remaining_segments_for_ty); + let (ty, orig_ns) = ctx.lower_ty_ext(type_ref); + let ty = self.table.insert_type_vars(ty); + let ty = self.table.normalize_associated_types_in(ty); + + let remaining_segments_for_ty = path.segments().take(path.segments().len() - 1); + let (ty, _) = ctx.lower_ty_relative_path(ty, orig_ns, remaining_segments_for_ty); + let ty = self.table.insert_type_vars(ty); + let ty = self.table.normalize_associated_types_in(ty); self.resolve_ty_assoc_item(ty, last.name, id).map(|(it, substs)| (it, Some(substs)))? } else { // FIXME: report error, unresolved first path segment let value_or_partial = - self.resolver.resolve_path_in_value_ns(self.db.upcast(), path.mod_path())?; + self.resolver.resolve_path_in_value_ns(self.db.upcast(), path)?; match value_or_partial { ResolveValueResult::ValueNs(it) => (it, None), @@ -49,9 +67,9 @@ impl<'a> InferenceContext<'a> { } }; - let typable: ValueTyDefId = match value { + let value_def = match value { ValueNs::LocalBinding(pat) => match self.result.type_of_binding.get(pat) { - Some(ty) => return Some(ty.clone()), + Some(ty) => return Some(ValuePathResolution::NonGeneric(ty.clone())), None => { never!("uninferred pattern?"); return None; @@ -75,28 +93,45 @@ impl<'a> InferenceContext<'a> { let substs = generics.placeholder_subst(self.db); let ty = self.db.impl_self_ty(impl_id).substitute(Interner, &substs); if let Some((AdtId::StructId(struct_id), substs)) = ty.as_adt() { - let ty = self.db.value_ty(struct_id.into()).substitute(Interner, &substs); - return Some(ty); + return Some(ValuePathResolution::GenericDef( + struct_id.into(), + struct_id.into(), + substs.clone(), + )); } else { // FIXME: report error, invalid Self reference return None; } } - ValueNs::GenericParam(it) => return Some(self.db.const_param_ty(it)), + ValueNs::GenericParam(it) => { + return Some(ValuePathResolution::NonGeneric(self.db.const_param_ty(it))) + } }; let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver); - let substs = ctx.substs_from_path(path, typable, true); + let substs = ctx.substs_from_path(path, value_def, true); let substs = substs.as_slice(Interner); let parent_substs = self_subst.or_else(|| { - let generics = generics(self.db.upcast(), typable.to_generic_def_id()?); + let generics = generics(self.db.upcast(), value_def.to_generic_def_id()?); let parent_params_len = generics.parent_generics()?.len(); let parent_args = &substs[substs.len() - parent_params_len..]; Some(Substitution::from_iter(Interner, parent_args)) }); let parent_substs_len = parent_substs.as_ref().map_or(0, |s| s.len(Interner)); let mut it = substs.iter().take(substs.len() - parent_substs_len).cloned(); - let ty = TyBuilder::value_ty(self.db, typable, parent_substs) + + let Some(generic_def) = value_def.to_generic_def_id() else { + // `value_def` is the kind of item that can never be generic (i.e. statics, at least + // currently). We can just skip the binders to get its type. + let (ty, binders) = self.db.value_ty(value_def).into_value_and_skipped_binders(); + stdx::always!( + parent_substs.is_none() && binders.is_empty(Interner), + "non-empty binders for non-generic def", + ); + return Some(ValuePathResolution::NonGeneric(ty)); + }; + let builder = TyBuilder::subst_for_def(self.db, generic_def, parent_substs); + let substs = builder .fill(|x| { it.next().unwrap_or_else(|| match x { ParamKind::Type => self.result.standard_types.unknown.clone().cast(Interner), @@ -104,7 +139,35 @@ impl<'a> InferenceContext<'a> { }) }) .build(); - Some(ty) + + Some(ValuePathResolution::GenericDef(value_def, generic_def, substs)) + } + + fn add_required_obligations_for_value_path(&mut self, def: GenericDefId, subst: &Substitution) { + let predicates = self.db.generic_predicates(def); + for predicate in predicates.iter() { + let (predicate, binders) = + predicate.clone().substitute(Interner, &subst).into_value_and_skipped_binders(); + // Quantified where clauses are not yet handled. + stdx::always!(binders.is_empty(Interner)); + self.push_obligation(predicate.cast(Interner)); + } + + // We need to add `Self: Trait` obligation when `def` is a trait assoc item. + let container = match def { + GenericDefId::FunctionId(id) => id.lookup(self.db.upcast()).container, + GenericDefId::ConstId(id) => id.lookup(self.db.upcast()).container, + _ => return, + }; + + if let ItemContainerId::TraitId(trait_) = container { + let param_len = generics(self.db.upcast(), def).len_self(); + let parent_subst = + Substitution::from_iter(Interner, subst.iter(Interner).skip(param_len)); + let trait_ref = + TraitRef { trait_id: to_chalk_trait_id(trait_), substitution: parent_subst }; + self.push_obligation(trait_ref.cast(Interner)); + } } fn resolve_assoc_item( @@ -169,7 +232,7 @@ impl<'a> InferenceContext<'a> { ) -> Option<(ValueNs, Substitution)> { let trait_ = trait_ref.hir_trait_id(); let item = - self.db.trait_data(trait_).items.iter().map(|(_name, id)| (*id)).find_map(|item| { + self.db.trait_data(trait_).items.iter().map(|(_name, id)| *id).find_map(|item| { match item { AssocItemId::FunctionId(func) => { if segment.name == &self.db.function_data(func).name { @@ -288,7 +351,7 @@ impl<'a> InferenceContext<'a> { name: &Name, id: ExprOrPatId, ) -> Option<(ValueNs, Substitution)> { - let ty = self.resolve_ty_shallow(ty); + let ty = self.resolve_ty_shallow(&ty); let (enum_id, subst) = match ty.as_adt() { Some((AdtId::EnumId(e), subst)) => (e, subst), _ => return None, @@ -300,3 +363,10 @@ impl<'a> InferenceContext<'a> { Some((ValueNs::EnumVariantId(variant), subst.clone())) } } + +enum ValuePathResolution { + // It's awkward to wrap a single ID in two enums, but we need both and this saves fallible + // conversion between them + `unwrap()`. + GenericDef(ValueTyDefId, GenericDefId, Substitution), + NonGeneric(Ty), +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index 504f0743aa947..e33d8f1795e2e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -1,23 +1,25 @@ //! Unification and canonicalization logic. -use std::{fmt, iter, mem, sync::Arc}; +use std::{fmt, iter, mem}; use chalk_ir::{ cast::Cast, fold::TypeFoldable, interner::HasInterner, zip::Zip, CanonicalVarKind, FloatTy, IntTy, TyVariableKind, UniverseIndex, }; use chalk_solve::infer::ParameterEnaVariableExt; +use either::Either; use ena::unify::UnifyKey; -use hir_def::{FunctionId, TraitId}; use hir_expand::name; use stdx::never; +use triomphe::Arc; use super::{InferOk, InferResult, InferenceContext, TypeError}; use crate::{ - db::HirDatabase, fold_tys, static_lifetime, traits::FnTrait, AliasEq, AliasTy, BoundVar, - Canonical, Const, DebruijnIndex, GenericArg, GenericArgData, Goal, Guidance, InEnvironment, - InferenceVar, Interner, Lifetime, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, Solution, - Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind, + consteval::unknown_const, db::HirDatabase, fold_tys_and_consts, static_lifetime, + to_chalk_trait_id, traits::FnTrait, AliasEq, AliasTy, BoundVar, Canonical, Const, ConstValue, + DebruijnIndex, GenericArg, GenericArgData, Goal, Guidance, InEnvironment, InferenceVar, + Interner, Lifetime, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, Solution, Substitution, + TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind, }; impl<'a> InferenceContext<'a> { @@ -130,7 +132,7 @@ pub(crate) fn unify( } bitflags::bitflags! { - #[derive(Default)] + #[derive(Default, Clone, Copy)] pub(crate) struct TypeVariableFlags: u8 { const DIVERGING = 1 << 0; const INTEGER = 1 << 1; @@ -230,14 +232,40 @@ impl<'a> InferenceTable<'a> { /// type annotation (e.g. from a let type annotation, field type or function /// call). `make_ty` handles this already, but e.g. for field types we need /// to do it as well. - pub(crate) fn normalize_associated_types_in(&mut self, ty: Ty) -> Ty { - fold_tys( + pub(crate) fn normalize_associated_types_in(&mut self, ty: T) -> T + where + T: HasInterner + TypeFoldable, + { + fold_tys_and_consts( ty, - |ty, _| match ty.kind(Interner) { - TyKind::Alias(AliasTy::Projection(proj_ty)) => { - self.normalize_projection_ty(proj_ty.clone()) - } - _ => ty, + |e, _| match e { + Either::Left(ty) => Either::Left(match ty.kind(Interner) { + TyKind::Alias(AliasTy::Projection(proj_ty)) => { + self.normalize_projection_ty(proj_ty.clone()) + } + _ => ty, + }), + Either::Right(c) => Either::Right(match &c.data(Interner).value { + chalk_ir::ConstValue::Concrete(cc) => match &cc.interned { + crate::ConstScalar::UnevaluatedConst(c_id, subst) => { + // FIXME: Ideally here we should do everything that we do with type alias, i.e. adding a variable + // and registering an obligation. But it needs chalk support, so we handle the most basic + // case (a non associated const without generic parameters) manually. + if subst.len(Interner) == 0 { + if let Ok(eval) = self.db.const_eval((*c_id).into(), subst.clone()) + { + eval + } else { + unknown_const(c.data(Interner).ty.clone()) + } + } else { + unknown_const(c.data(Interner).ty.clone()) + } + } + _ => c, + }, + _ => c, + }), }, DebruijnIndex::INNERMOST, ) @@ -463,7 +491,8 @@ impl<'a> InferenceTable<'a> { pub(crate) fn try_obligation(&mut self, goal: Goal) -> Option { let in_env = InEnvironment::new(&self.trait_env.env, goal); let canonicalized = self.canonicalize(in_env); - let solution = self.db.trait_solve(self.trait_env.krate, canonicalized.value); + let solution = + self.db.trait_solve(self.trait_env.krate, self.trait_env.block, canonicalized.value); solution } @@ -598,7 +627,11 @@ impl<'a> InferenceTable<'a> { &mut self, canonicalized: &Canonicalized>, ) -> bool { - let solution = self.db.trait_solve(self.trait_env.krate, canonicalized.value.clone()); + let solution = self.db.trait_solve( + self.trait_env.krate, + self.trait_env.block, + canonicalized.value.clone(), + ); match solution { Some(Solution::Unique(canonical_subst)) => { @@ -631,10 +664,13 @@ impl<'a> InferenceTable<'a> { &mut self, ty: &Ty, num_args: usize, - ) -> Option<(Option<(TraitId, FunctionId)>, Vec, Ty)> { + ) -> Option<(Option, Vec, Ty)> { match ty.callable_sig(self.db) { Some(sig) => Some((None, sig.params().to_vec(), sig.ret().clone())), - None => self.callable_sig_from_fn_trait(ty, num_args), + None => { + let (f, args_ty, return_ty) = self.callable_sig_from_fn_trait(ty, num_args)?; + Some((Some(f), args_ty, return_ty)) + } } } @@ -642,7 +678,7 @@ impl<'a> InferenceTable<'a> { &mut self, ty: &Ty, num_args: usize, - ) -> Option<(Option<(TraitId, FunctionId)>, Vec, Ty)> { + ) -> Option<(FnTrait, Vec, Ty)> { let krate = self.trait_env.krate; let fn_once_trait = FnTrait::FnOnce.get_id(self.db, krate)?; let trait_data = self.db.trait_data(fn_once_trait); @@ -676,23 +712,90 @@ impl<'a> InferenceTable<'a> { }; let trait_env = self.trait_env.env.clone(); + let mut trait_ref = projection.trait_ref(self.db); let obligation = InEnvironment { - goal: projection.trait_ref(self.db).cast(Interner), - environment: trait_env, + goal: trait_ref.clone().cast(Interner), + environment: trait_env.clone(), }; let canonical = self.canonicalize(obligation.clone()); - if self.db.trait_solve(krate, canonical.value.cast(Interner)).is_some() { + if self + .db + .trait_solve(krate, self.trait_env.block, canonical.value.cast(Interner)) + .is_some() + { self.register_obligation(obligation.goal); let return_ty = self.normalize_projection_ty(projection); - Some(( - Some(fn_once_trait).zip(trait_data.method_by_name(&name!(call_once))), - arg_tys, - return_ty, - )) + for fn_x in [FnTrait::Fn, FnTrait::FnMut, FnTrait::FnOnce] { + let fn_x_trait = fn_x.get_id(self.db, krate)?; + trait_ref.trait_id = to_chalk_trait_id(fn_x_trait); + let obligation: chalk_ir::InEnvironment> = InEnvironment { + goal: trait_ref.clone().cast(Interner), + environment: trait_env.clone(), + }; + let canonical = self.canonicalize(obligation.clone()); + if self + .db + .trait_solve(krate, self.trait_env.block, canonical.value.cast(Interner)) + .is_some() + { + return Some((fn_x, arg_tys, return_ty)); + } + } + unreachable!("It should at least implement FnOnce at this point"); } else { None } } + + pub(super) fn insert_type_vars(&mut self, ty: T) -> T + where + T: HasInterner + TypeFoldable, + { + fold_tys_and_consts( + ty, + |x, _| match x { + Either::Left(ty) => Either::Left(self.insert_type_vars_shallow(ty)), + Either::Right(c) => Either::Right(self.insert_const_vars_shallow(c)), + }, + DebruijnIndex::INNERMOST, + ) + } + + /// Replaces `Ty::Error` by a new type var, so we can maybe still infer it. + pub(super) fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty { + match ty.kind(Interner) { + TyKind::Error => self.new_type_var(), + TyKind::InferenceVar(..) => { + let ty_resolved = self.resolve_ty_shallow(&ty); + if ty_resolved.is_unknown() { + self.new_type_var() + } else { + ty + } + } + _ => ty, + } + } + + /// Replaces ConstScalar::Unknown by a new type var, so we can maybe still infer it. + pub(super) fn insert_const_vars_shallow(&mut self, c: Const) -> Const { + let data = c.data(Interner); + match &data.value { + ConstValue::Concrete(cc) => match &cc.interned { + crate::ConstScalar::Unknown => self.new_const_var(data.ty.clone()), + // try to evaluate unevaluated const. Replace with new var if const eval failed. + crate::ConstScalar::UnevaluatedConst(id, subst) => { + if let Ok(eval) = self.db.const_eval(*id, subst.clone()) { + eval + } else { + self.new_const_var(data.ty.clone()) + } + } + _ => c, + }, + _ => c, + } + } } impl<'a> fmt::Debug for InferenceTable<'a> { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs b/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs index 36af78153d43a..e5038543b68e6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs @@ -6,9 +6,10 @@ use chalk_ir::{ DebruijnIndex, }; use hir_def::{ - adt::VariantData, attr::Attrs, visibility::Visibility, AdtId, EnumVariantId, HasModule, Lookup, - ModuleId, VariantId, + attr::Attrs, data::adt::VariantData, visibility::Visibility, AdtId, EnumVariantId, HasModule, + Lookup, ModuleId, VariantId, }; +use rustc_hash::FxHashSet; use crate::{ consteval::try_const_usize, db::HirDatabase, Binders, Interner, Substitution, Ty, TyKind, @@ -16,7 +17,8 @@ use crate::{ /// Checks whether a type is visibly uninhabited from a particular module. pub(crate) fn is_ty_uninhabited_from(ty: &Ty, target_mod: ModuleId, db: &dyn HirDatabase) -> bool { - let mut uninhabited_from = UninhabitedFrom { target_mod, db }; + let mut uninhabited_from = + UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: FxHashSet::default() }; let inhabitedness = ty.visit_with(&mut uninhabited_from, DebruijnIndex::INNERMOST); inhabitedness == BREAK_VISIBLY_UNINHABITED } @@ -32,7 +34,8 @@ pub(crate) fn is_enum_variant_uninhabited_from( let vars_attrs = db.variants_attrs(variant.parent); let is_local = variant.parent.lookup(db.upcast()).container.krate() == target_mod.krate(); - let mut uninhabited_from = UninhabitedFrom { target_mod, db }; + let mut uninhabited_from = + UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: FxHashSet::default() }; let inhabitedness = uninhabited_from.visit_variant( variant.into(), &enum_data.variants[variant.local_id].variant_data, @@ -45,6 +48,9 @@ pub(crate) fn is_enum_variant_uninhabited_from( struct UninhabitedFrom<'a> { target_mod: ModuleId, + recursive_ty: FxHashSet, + // guard for preventing stack overflow in non trivial non terminating types + max_depth: usize, db: &'a dyn HirDatabase, } @@ -65,17 +71,27 @@ impl TypeVisitor for UninhabitedFrom<'_> { ty: &Ty, outer_binder: DebruijnIndex, ) -> ControlFlow { - match ty.kind(Interner) { + if self.recursive_ty.contains(ty) || self.max_depth == 0 { + // rustc considers recursive types always inhabited. I think it is valid to consider + // recursive types as always uninhabited, but we should do what rustc is doing. + return CONTINUE_OPAQUELY_INHABITED; + } + self.recursive_ty.insert(ty.clone()); + self.max_depth -= 1; + let r = match ty.kind(Interner) { TyKind::Adt(adt, subst) => self.visit_adt(adt.0, subst), TyKind::Never => BREAK_VISIBLY_UNINHABITED, TyKind::Tuple(..) => ty.super_visit_with(self, outer_binder), - TyKind::Array(item_ty, len) => match try_const_usize(len) { + TyKind::Array(item_ty, len) => match try_const_usize(self.db, len) { Some(0) | None => CONTINUE_OPAQUELY_INHABITED, Some(1..) => item_ty.super_visit_with(self, outer_binder), }, TyKind::Ref(..) | _ => CONTINUE_OPAQUELY_INHABITED, - } + }; + self.recursive_ty.remove(ty); + self.max_depth += 1; + r } fn interner(&self) -> Interner { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/interner.rs index aea7e9762fdf4..89f7d9c4f4abc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/interner.rs @@ -7,7 +7,8 @@ use chalk_ir::{Goal, GoalData}; use hir_def::TypeAliasId; use intern::{impl_internable, Interned}; use smallvec::SmallVec; -use std::{fmt, sync::Arc}; +use std::fmt; +use triomphe::Arc; #[derive(Debug, Copy, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)] pub struct Interner; @@ -43,7 +44,7 @@ impl_internable!( ); impl chalk_ir::interner::Interner for Interner { - type InternedType = Interned>>; + type InternedType = Interned>>; type InternedLifetime = Interned>>; type InternedConst = Interned>>; type InternedConcreteConst = ConstScalar; @@ -51,8 +52,8 @@ impl chalk_ir::interner::Interner for Interner { type InternedGoal = Arc>; type InternedGoals = Vec>; type InternedSubstitution = Interned>>; - type InternedProgramClause = chalk_ir::ProgramClauseData; type InternedProgramClauses = Interned>>>; + type InternedProgramClause = chalk_ir::ProgramClauseData; type InternedQuantifiedWhereClauses = Interned>>>; type InternedVariableKinds = Interned>>>; @@ -86,6 +87,27 @@ impl chalk_ir::interner::Interner for Interner { tls::with_current_program(|prog| Some(prog?.debug_assoc_type_id(id, fmt))) } + fn debug_opaque_ty_id( + opaque_ty_id: chalk_ir::OpaqueTyId, + fmt: &mut fmt::Formatter<'_>, + ) -> Option { + Some(write!(fmt, "OpaqueTy#{}", opaque_ty_id.0)) + } + + fn debug_fn_def_id( + fn_def_id: chalk_ir::FnDefId, + fmt: &mut fmt::Formatter<'_>, + ) -> Option { + tls::with_current_program(|prog| Some(prog?.debug_fn_def_id(fn_def_id, fmt))) + } + + fn debug_closure_id( + _fn_def_id: chalk_ir::ClosureId, + _fmt: &mut fmt::Formatter<'_>, + ) -> Option { + None + } + fn debug_alias( alias: &chalk_ir::AliasTy, fmt: &mut fmt::Formatter<'_>, @@ -113,13 +135,6 @@ impl chalk_ir::interner::Interner for Interner { Some(write!(fmt, "{:?}", opaque_ty.opaque_ty_id)) } - fn debug_opaque_ty_id( - opaque_ty_id: chalk_ir::OpaqueTyId, - fmt: &mut fmt::Formatter<'_>, - ) -> Option { - Some(write!(fmt, "OpaqueTy#{}", opaque_ty_id.0)) - } - fn debug_ty(ty: &chalk_ir::Ty, fmt: &mut fmt::Formatter<'_>) -> Option { Some(write!(fmt, "{:?}", ty.data(Interner))) } @@ -131,76 +146,56 @@ impl chalk_ir::interner::Interner for Interner { Some(write!(fmt, "{:?}", lifetime.data(Interner))) } - fn debug_generic_arg( - parameter: &GenericArg, - fmt: &mut fmt::Formatter<'_>, - ) -> Option { - Some(write!(fmt, "{:?}", parameter.data(Interner).inner_debug())) - } - - fn debug_goal(goal: &Goal, fmt: &mut fmt::Formatter<'_>) -> Option { - let goal_data = goal.data(Interner); - Some(write!(fmt, "{goal_data:?}")) - } - - fn debug_goals( - goals: &chalk_ir::Goals, - fmt: &mut fmt::Formatter<'_>, - ) -> Option { - Some(write!(fmt, "{:?}", goals.debug(Interner))) - } - - fn debug_program_clause_implication( - pci: &chalk_ir::ProgramClauseImplication, - fmt: &mut fmt::Formatter<'_>, - ) -> Option { - Some(write!(fmt, "{:?}", pci.debug(Interner))) - } - - fn debug_substitution( - substitution: &chalk_ir::Substitution, + fn debug_const( + constant: &chalk_ir::Const, fmt: &mut fmt::Formatter<'_>, ) -> Option { - Some(write!(fmt, "{:?}", substitution.debug(Interner))) + Some(write!(fmt, "{:?}", constant.data(Interner))) } - fn debug_separator_trait_ref( - separator_trait_ref: &chalk_ir::SeparatorTraitRef<'_, Interner>, + fn debug_generic_arg( + parameter: &GenericArg, fmt: &mut fmt::Formatter<'_>, ) -> Option { - Some(write!(fmt, "{:?}", separator_trait_ref.debug(Interner))) + Some(write!(fmt, "{:?}", parameter.data(Interner).inner_debug())) } - fn debug_fn_def_id( - fn_def_id: chalk_ir::FnDefId, - fmt: &mut fmt::Formatter<'_>, - ) -> Option { - tls::with_current_program(|prog| Some(prog?.debug_fn_def_id(fn_def_id, fmt))) - } - fn debug_const( - constant: &chalk_ir::Const, - fmt: &mut fmt::Formatter<'_>, - ) -> Option { - Some(write!(fmt, "{:?}", constant.data(Interner))) - } fn debug_variable_kinds( variable_kinds: &chalk_ir::VariableKinds, fmt: &mut fmt::Formatter<'_>, ) -> Option { Some(write!(fmt, "{:?}", variable_kinds.as_slice(Interner))) } + fn debug_variable_kinds_with_angles( variable_kinds: &chalk_ir::VariableKinds, fmt: &mut fmt::Formatter<'_>, ) -> Option { Some(write!(fmt, "{:?}", variable_kinds.inner_debug(Interner))) } + fn debug_canonical_var_kinds( canonical_var_kinds: &chalk_ir::CanonicalVarKinds, fmt: &mut fmt::Formatter<'_>, ) -> Option { Some(write!(fmt, "{:?}", canonical_var_kinds.as_slice(Interner))) } + fn debug_goal(goal: &Goal, fmt: &mut fmt::Formatter<'_>) -> Option { + let goal_data = goal.data(Interner); + Some(write!(fmt, "{goal_data:?}")) + } + fn debug_goals( + goals: &chalk_ir::Goals, + fmt: &mut fmt::Formatter<'_>, + ) -> Option { + Some(write!(fmt, "{:?}", goals.debug(Interner))) + } + fn debug_program_clause_implication( + pci: &chalk_ir::ProgramClauseImplication, + fmt: &mut fmt::Formatter<'_>, + ) -> Option { + Some(write!(fmt, "{:?}", pci.debug(Interner))) + } fn debug_program_clause( clause: &chalk_ir::ProgramClause, fmt: &mut fmt::Formatter<'_>, @@ -213,6 +208,19 @@ impl chalk_ir::interner::Interner for Interner { ) -> Option { Some(write!(fmt, "{:?}", clauses.as_slice(Interner))) } + fn debug_substitution( + substitution: &chalk_ir::Substitution, + fmt: &mut fmt::Formatter<'_>, + ) -> Option { + Some(write!(fmt, "{:?}", substitution.debug(Interner))) + } + fn debug_separator_trait_ref( + separator_trait_ref: &chalk_ir::SeparatorTraitRef<'_, Interner>, + fmt: &mut fmt::Formatter<'_>, + ) -> Option { + Some(write!(fmt, "{:?}", separator_trait_ref.debug(Interner))) + } + fn debug_quantified_where_clauses( clauses: &chalk_ir::QuantifiedWhereClauses, fmt: &mut fmt::Formatter<'_>, @@ -220,6 +228,13 @@ impl chalk_ir::interner::Interner for Interner { Some(write!(fmt, "{:?}", clauses.as_slice(Interner))) } + fn debug_constraints( + _clauses: &chalk_ir::Constraints, + _fmt: &mut fmt::Formatter<'_>, + ) -> Option { + None + } + fn intern_ty(self, kind: chalk_ir::TyKind) -> Self::InternedType { let flags = kind.compute_flags(self); Interned::new(InternedWrapper(chalk_ir::TyData { kind, flags })) @@ -272,6 +287,10 @@ impl chalk_ir::interner::Interner for Interner { Arc::new(goal) } + fn goal_data(self, goal: &Self::InternedGoal) -> &GoalData { + goal + } + fn intern_goals( self, data: impl IntoIterator, E>>, @@ -279,10 +298,6 @@ impl chalk_ir::interner::Interner for Interner { data.into_iter().collect() } - fn goal_data(self, goal: &Self::InternedGoal) -> &GoalData { - goal - } - fn goals_data(self, goals: &Self::InternedGoals) -> &[Goal] { goals } @@ -367,32 +382,18 @@ impl chalk_ir::interner::Interner for Interner { ) -> &[chalk_ir::CanonicalVarKind] { canonical_var_kinds } - fn intern_constraints( self, data: impl IntoIterator>, E>>, ) -> Result { data.into_iter().collect() } - fn constraints_data( self, constraints: &Self::InternedConstraints, ) -> &[chalk_ir::InEnvironment>] { constraints } - fn debug_closure_id( - _fn_def_id: chalk_ir::ClosureId, - _fmt: &mut fmt::Formatter<'_>, - ) -> Option { - None - } - fn debug_constraints( - _clauses: &chalk_ir::Constraints, - _fmt: &mut fmt::Formatter<'_>, - ) -> Option { - None - } fn intern_variances( self, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lang_items.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lang_items.rs index 5308c72161b26..85ed46b96321a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lang_items.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lang_items.rs @@ -1,19 +1,65 @@ //! Functions to detect special lang items -use hir_def::{lang_item::LangItem, AdtId, HasModule}; +use hir_def::{data::adt::StructFlags, lang_item::LangItem, AdtId}; +use hir_expand::name::Name; use crate::db::HirDatabase; -pub fn is_box(adt: AdtId, db: &dyn HirDatabase) -> bool { - let krate = adt.module(db.upcast()).krate(); - let box_adt = - db.lang_item(krate, LangItem::OwnedBox).and_then(|it| it.as_struct()).map(AdtId::from); - Some(adt) == box_adt +pub fn is_box(db: &dyn HirDatabase, adt: AdtId) -> bool { + let AdtId::StructId(id) = adt else { return false }; + db.struct_data(id).flags.contains(StructFlags::IS_BOX) } -pub fn is_unsafe_cell(adt: AdtId, db: &dyn HirDatabase) -> bool { - let krate = adt.module(db.upcast()).krate(); - let box_adt = - db.lang_item(krate, LangItem::UnsafeCell).and_then(|it| it.as_struct()).map(AdtId::from); - Some(adt) == box_adt +pub fn is_unsafe_cell(db: &dyn HirDatabase, adt: AdtId) -> bool { + let AdtId::StructId(id) = adt else { return false }; + db.struct_data(id).flags.contains(StructFlags::IS_UNSAFE_CELL) +} + +pub fn lang_items_for_bin_op(op: syntax::ast::BinaryOp) -> Option<(Name, LangItem)> { + 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], LangItem::Add), + ArithOp::Mul => (name![mul], LangItem::Mul), + ArithOp::Sub => (name![sub], LangItem::Sub), + ArithOp::Div => (name![div], LangItem::Div), + ArithOp::Rem => (name![rem], LangItem::Rem), + ArithOp::Shl => (name![shl], LangItem::Shl), + ArithOp::Shr => (name![shr], LangItem::Shr), + ArithOp::BitXor => (name![bitxor], LangItem::BitXor), + ArithOp::BitOr => (name![bitor], LangItem::BitOr), + ArithOp::BitAnd => (name![bitand], LangItem::BitAnd), + }, + BinaryOp::Assignment { op: Some(aop) } => match aop { + ArithOp::Add => (name![add_assign], LangItem::AddAssign), + ArithOp::Mul => (name![mul_assign], LangItem::MulAssign), + ArithOp::Sub => (name![sub_assign], LangItem::SubAssign), + ArithOp::Div => (name![div_assign], LangItem::DivAssign), + ArithOp::Rem => (name![rem_assign], LangItem::RemAssign), + ArithOp::Shl => (name![shl_assign], LangItem::ShlAssign), + ArithOp::Shr => (name![shr_assign], LangItem::ShrAssign), + ArithOp::BitXor => (name![bitxor_assign], LangItem::BitXorAssign), + ArithOp::BitOr => (name![bitor_assign], LangItem::BitOrAssign), + ArithOp::BitAnd => (name![bitand_assign], LangItem::BitAndAssign), + }, + BinaryOp::CmpOp(cop) => match cop { + CmpOp::Eq { negated: false } => (name![eq], LangItem::PartialEq), + CmpOp::Eq { negated: true } => (name![ne], LangItem::PartialEq), + CmpOp::Ord { ordering: Ordering::Less, strict: false } => { + (name![le], LangItem::PartialOrd) + } + CmpOp::Ord { ordering: Ordering::Less, strict: true } => { + (name![lt], LangItem::PartialOrd) + } + CmpOp::Ord { ordering: Ordering::Greater, strict: false } => { + (name![ge], LangItem::PartialOrd) + } + CmpOp::Ord { ordering: Ordering::Greater, strict: true } => { + (name![gt], LangItem::PartialOrd) + } + }, + BinaryOp::Assignment { op: None } => return None, + }) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index b95bb01fcefeb..35d3407c16c13 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -1,19 +1,23 @@ //! Compute the binary representation of a type use base_db::CrateId; -use chalk_ir::{AdtId, TyKind}; +use chalk_ir::{AdtId, FloatTy, IntTy, TyKind, UintTy}; use hir_def::{ layout::{ - Abi, FieldsShape, Integer, Layout, LayoutCalculator, LayoutError, Primitive, ReprOptions, - RustcEnumVariantIdx, Scalar, Size, StructKind, TargetDataLayout, Variants, WrappingRange, + Abi, FieldsShape, Integer, LayoutCalculator, LayoutS, Primitive, ReprOptions, Scalar, Size, + StructKind, TargetDataLayout, WrappingRange, }, - LocalFieldId, + LocalEnumVariantId, LocalFieldId, }; +use la_arena::{Idx, RawIdx}; use stdx::never; +use triomphe::Arc; -use crate::{consteval::try_const_usize, db::HirDatabase, Interner, Substitution, Ty}; +use crate::{ + consteval::try_const_usize, db::HirDatabase, infer::normalize, layout::adt::struct_variant_idx, + utils::ClosureSubst, Interner, Substitution, TraitEnvironment, Ty, +}; -use self::adt::struct_variant_idx; pub use self::{ adt::{layout_of_adt_query, layout_of_adt_recover}, target::target_data_layout_query, @@ -28,6 +32,34 @@ macro_rules! user_error { mod adt; mod target; +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct RustcEnumVariantIdx(pub LocalEnumVariantId); + +impl rustc_index::vec::Idx for RustcEnumVariantIdx { + fn new(idx: usize) -> Self { + RustcEnumVariantIdx(Idx::from_raw(RawIdx::from(idx as u32))) + } + + fn index(self) -> usize { + u32::from(self.0.into_raw()) as usize + } +} + +pub type Layout = LayoutS; +pub type TagEncoding = hir_def::layout::TagEncoding; +pub type Variants = hir_def::layout::Variants; + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum LayoutError { + UserError(String), + SizeOverflow, + TargetLayoutNotAvailable, + HasPlaceholder, + HasErrorType, + NotImplemented, + Unknown, +} + struct LayoutCx<'a> { krate: CrateId, target: &'a TargetDataLayout, @@ -45,20 +77,18 @@ impl<'a> LayoutCalculator for LayoutCx<'a> { } } -fn scalar_unit(dl: &TargetDataLayout, value: Primitive) -> Scalar { - Scalar::Initialized { value, valid_range: WrappingRange::full(value.size(dl)) } -} - -fn scalar(dl: &TargetDataLayout, value: Primitive) -> Layout { - Layout::scalar(dl, scalar_unit(dl, value)) -} - -pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result { +pub fn layout_of_ty_query( + db: &dyn HirDatabase, + ty: Ty, + krate: CrateId, +) -> Result, LayoutError> { let Some(target) = db.target_data_layout(krate) else { return Err(LayoutError::TargetLayoutNotAvailable) }; let cx = LayoutCx { krate, target: &target }; let dl = &*cx.current_data_layout(); - Ok(match ty.kind(Interner) { - TyKind::Adt(AdtId(def), subst) => db.layout_of_adt(*def, subst.clone())?, + let trait_env = Arc::new(TraitEnvironment::empty(krate)); + let ty = normalize(db, trait_env, ty.clone()); + let result = match ty.kind(Interner) { + TyKind::Adt(AdtId(def), subst) => return db.layout_of_adt(*def, subst.clone(), krate), TyKind::Scalar(s) => match s { chalk_ir::Scalar::Bool => Layout::scalar( dl, @@ -78,12 +108,12 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result dl.ptr_sized_integer(), - chalk_ir::IntTy::I8 => Integer::I8, - chalk_ir::IntTy::I16 => Integer::I16, - chalk_ir::IntTy::I32 => Integer::I32, - chalk_ir::IntTy::I64 => Integer::I64, - chalk_ir::IntTy::I128 => Integer::I128, + IntTy::Isize => dl.ptr_sized_integer(), + IntTy::I8 => Integer::I8, + IntTy::I16 => Integer::I16, + IntTy::I32 => Integer::I32, + IntTy::I64 => Integer::I64, + IntTy::I128 => Integer::I128, }, true, ), @@ -92,12 +122,12 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result dl.ptr_sized_integer(), - chalk_ir::UintTy::U8 => Integer::I8, - chalk_ir::UintTy::U16 => Integer::I16, - chalk_ir::UintTy::U32 => Integer::I32, - chalk_ir::UintTy::U64 => Integer::I64, - chalk_ir::UintTy::U128 => Integer::I128, + UintTy::Usize => dl.ptr_sized_integer(), + UintTy::U8 => Integer::I8, + UintTy::U16 => Integer::I16, + UintTy::U32 => Integer::I32, + UintTy::U64 => Integer::I64, + UintTy::U128 => Integer::I128, }, false, ), @@ -105,8 +135,8 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result scalar( dl, match f { - chalk_ir::FloatTy::F32 => Primitive::F32, - chalk_ir::FloatTy::F64 => Primitive::F64, + FloatTy::F32 => Primitive::F32, + FloatTy::F64 => Primitive::F64, }, ), }, @@ -115,17 +145,17 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result, _>>()?; - let fields = fields.iter().collect::>(); + let fields = fields.iter().map(|x| &**x).collect::>(); let fields = fields.iter().collect::>(); cx.univariant(dl, &fields, &ReprOptions::default(), kind).ok_or(LayoutError::Unknown)? } TyKind::Array(element, count) => { - let count = try_const_usize(&count).ok_or(LayoutError::UserError( - "mismatched type of const generic parameter".to_string(), + let count = try_const_usize(db, &count).ok_or(LayoutError::UserError( + "unevaluated or mistyped const generic parameter".to_string(), ))? as u64; - let element = layout_of_ty(db, element, krate)?; + let element = db.layout_of_ty(element.clone(), krate)?; let size = element.size.checked_mul(count, dl).ok_or(LayoutError::SizeOverflow)?; let abi = if count != 0 && matches!(element.abi, Abi::Uninhabited) { @@ -146,7 +176,7 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result { - let element = layout_of_ty(db, element, krate)?; + let element = db.layout_of_ty(element.clone(), krate)?; Layout { variants: Variants::Single { index: struct_variant_idx() }, fields: FieldsShape::Array { stride: element.size, count: 0 }, @@ -180,7 +210,7 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result { // pointee is sized - return Ok(Layout::scalar(dl, data_ptr)); + return Ok(Arc::new(Layout::scalar(dl, data_ptr))); } }; @@ -222,23 +252,51 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result { let infer = db.infer(func.into()); - layout_of_ty(db, &infer.type_of_rpit[idx], krate)? + return db.layout_of_ty(infer.type_of_rpit[idx].clone(), krate); } crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => { return Err(LayoutError::NotImplemented) } } } - TyKind::Closure(_, _) | TyKind::Generator(_, _) | TyKind::GeneratorWitness(_, _) => { + TyKind::Closure(c, subst) => { + let (def, _) = db.lookup_intern_closure((*c).into()); + let infer = db.infer(def); + let (captures, _) = infer.closure_info(c); + let fields = captures + .iter() + .map(|x| { + db.layout_of_ty( + x.ty.clone().substitute(Interner, ClosureSubst(subst).parent_subst()), + krate, + ) + }) + .collect::, _>>()?; + let fields = fields.iter().map(|x| &**x).collect::>(); + let fields = fields.iter().collect::>(); + cx.univariant(dl, &fields, &ReprOptions::default(), StructKind::AlwaysSized) + .ok_or(LayoutError::Unknown)? + } + TyKind::Generator(_, _) | TyKind::GeneratorWitness(_, _) => { return Err(LayoutError::NotImplemented) } + TyKind::Error => return Err(LayoutError::HasErrorType), TyKind::AssociatedType(_, _) - | TyKind::Error | TyKind::Alias(_) | TyKind::Placeholder(_) | TyKind::BoundVar(_) | TyKind::InferenceVar(_, _) => return Err(LayoutError::HasPlaceholder), - }) + }; + Ok(Arc::new(result)) +} + +pub fn layout_of_ty_recover( + _: &dyn HirDatabase, + _: &[String], + _: &Ty, + _: &CrateId, +) -> Result, LayoutError> { + user_error!("infinite sized recursive type"); } fn layout_of_unit(cx: &LayoutCx<'_>, dl: &TargetDataLayout) -> Result { @@ -274,5 +332,13 @@ fn field_ty( db.field_types(def)[fd].clone().substitute(Interner, subst) } +fn scalar_unit(dl: &TargetDataLayout, value: Primitive) -> Scalar { + Scalar::Initialized { value, valid_range: WrappingRange::full(value.size(dl)) } +} + +fn scalar(dl: &TargetDataLayout, value: Primitive) -> Layout { + Layout::scalar(dl, scalar_unit(dl, value)) +} + #[cfg(test)] mod tests; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs index b22d0fe8ded11..bd2752a7119ab 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs @@ -1,18 +1,25 @@ //! Compute the binary representation of structs, unions and enums -use std::ops::Bound; +use std::{cmp, ops::Bound}; +use base_db::CrateId; use hir_def::{ - adt::VariantData, - layout::{Integer, IntegerExt, Layout, LayoutCalculator, LayoutError, RustcEnumVariantIdx}, - AdtId, EnumVariantId, HasModule, LocalEnumVariantId, VariantId, + data::adt::VariantData, + layout::{Integer, LayoutCalculator, ReprOptions, TargetDataLayout}, + AdtId, EnumVariantId, LocalEnumVariantId, VariantId, }; use la_arena::RawIdx; use smallvec::SmallVec; +use triomphe::Arc; -use crate::{db::HirDatabase, lang_items::is_unsafe_cell, layout::field_ty, Substitution}; +use crate::{ + db::HirDatabase, + lang_items::is_unsafe_cell, + layout::{field_ty, Layout, LayoutError, RustcEnumVariantIdx}, + Substitution, +}; -use super::{layout_of_ty, LayoutCx}; +use super::LayoutCx; pub(crate) fn struct_variant_idx() -> RustcEnumVariantIdx { RustcEnumVariantIdx(LocalEnumVariantId::from_raw(RawIdx::from(0))) @@ -22,29 +29,29 @@ pub fn layout_of_adt_query( db: &dyn HirDatabase, def: AdtId, subst: Substitution, -) -> Result { - let krate = def.module(db.upcast()).krate(); + krate: CrateId, +) -> Result, LayoutError> { let Some(target) = db.target_data_layout(krate) else { return Err(LayoutError::TargetLayoutNotAvailable) }; let cx = LayoutCx { krate, target: &target }; let dl = cx.current_data_layout(); let handle_variant = |def: VariantId, var: &VariantData| { var.fields() .iter() - .map(|(fd, _)| layout_of_ty(db, &field_ty(db, def, fd, &subst), cx.krate)) + .map(|(fd, _)| db.layout_of_ty(field_ty(db, def, fd, &subst), cx.krate)) .collect::, _>>() }; - let (variants, is_enum, is_union, repr) = match def { + let (variants, repr) = match def { AdtId::StructId(s) => { let data = db.struct_data(s); let mut r = SmallVec::<[_; 1]>::new(); r.push(handle_variant(s.into(), &data.variant_data)?); - (r, false, false, data.repr.unwrap_or_default()) + (r, data.repr.unwrap_or_default()) } AdtId::UnionId(id) => { let data = db.union_data(id); let mut r = SmallVec::new(); r.push(handle_variant(id.into(), &data.variant_data)?); - (r, false, true, data.repr.unwrap_or_default()) + (r, data.repr.unwrap_or_default()) } AdtId::EnumId(e) => { let data = db.enum_data(e); @@ -58,22 +65,24 @@ pub fn layout_of_adt_query( ) }) .collect::, _>>()?; - (r, true, false, data.repr.unwrap_or_default()) + (r, data.repr.unwrap_or_default()) } }; - let variants = - variants.iter().map(|x| x.iter().collect::>()).collect::>(); + let variants = variants + .iter() + .map(|x| x.iter().map(|x| &**x).collect::>()) + .collect::>(); let variants = variants.iter().map(|x| x.iter().collect()).collect(); - if is_union { - cx.layout_of_union(&repr, &variants).ok_or(LayoutError::Unknown) + let result = if matches!(def, AdtId::UnionId(..)) { + cx.layout_of_union(&repr, &variants).ok_or(LayoutError::Unknown)? } else { cx.layout_of_struct_or_enum( &repr, &variants, - is_enum, - is_unsafe_cell(def, db), + matches!(def, AdtId::EnumId(..)), + is_unsafe_cell(db, def), layout_scalar_valid_range(db, def), - |min, max| Integer::repr_discr(&dl, &repr, min, max).unwrap_or((Integer::I8, false)), + |min, max| repr_discr(&dl, &repr, min, max).unwrap_or((Integer::I8, false)), variants.iter_enumerated().filter_map(|(id, _)| { let AdtId::EnumId(e) = def else { return None }; let d = @@ -90,15 +99,16 @@ pub fn layout_of_adt_query( // .iter_enumerated() // .any(|(i, v)| v.discr != ty::VariantDiscr::Relative(i.as_u32())) repr.inhibit_enum_layout_opt(), - !is_enum + !matches!(def, AdtId::EnumId(..)) && variants .iter() .next() .and_then(|x| x.last().map(|x| x.is_unsized())) .unwrap_or(true), ) - .ok_or(LayoutError::SizeOverflow) - } + .ok_or(LayoutError::SizeOverflow)? + }; + Ok(Arc::new(result)) } fn layout_scalar_valid_range(db: &dyn HirDatabase, def: AdtId) -> (Bound, Bound) { @@ -122,6 +132,54 @@ pub fn layout_of_adt_recover( _: &[String], _: &AdtId, _: &Substitution, -) -> Result { + _: &CrateId, +) -> Result, LayoutError> { user_error!("infinite sized recursive type"); } + +/// Finds the appropriate Integer type and signedness for the given +/// signed discriminant range and `#[repr]` attribute. +/// N.B.: `u128` values above `i128::MAX` will be treated as signed, but +/// that shouldn't affect anything, other than maybe debuginfo. +fn repr_discr( + dl: &TargetDataLayout, + repr: &ReprOptions, + min: i128, + max: i128, +) -> Result<(Integer, bool), LayoutError> { + // Theoretically, negative values could be larger in unsigned representation + // than the unsigned representation of the signed minimum. However, if there + // are any negative values, the only valid unsigned representation is u128 + // which can fit all i128 values, so the result remains unaffected. + let unsigned_fit = Integer::fit_unsigned(cmp::max(min as u128, max as u128)); + let signed_fit = cmp::max(Integer::fit_signed(min), Integer::fit_signed(max)); + + if let Some(ity) = repr.int { + let discr = Integer::from_attr(dl, ity); + let fit = if ity.is_signed() { signed_fit } else { unsigned_fit }; + if discr < fit { + return Err(LayoutError::UserError( + "Integer::repr_discr: `#[repr]` hint too small for \ + discriminant range of enum " + .to_string(), + )); + } + return Ok((discr, ity.is_signed())); + } + + let at_least = if repr.c() { + // This is usually I32, however it can be different on some platforms, + // notably hexagon and arm-none/thumb-none + dl.c_enum_min_size + } else { + // repr(Rust) enums try to be as small as possible + Integer::I8 + }; + + // If there are no negative values, we can use the unsigned fit. + Ok(if min >= 0 { + (cmp::max(unsigned_fit, at_least), false) + } else { + (cmp::max(signed_fit, at_least), true) + }) +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs index adfae0a1abb38..04b940afbe8f8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs @@ -1,9 +1,8 @@ //! Target dependent parameters needed for layouts -use std::sync::Arc; - use base_db::CrateId; use hir_def::layout::TargetDataLayout; +use triomphe::Arc; use crate::db::HirDatabase; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs index a8971fde3c21e..fca2e09ff0a99 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs @@ -2,49 +2,55 @@ use std::collections::HashMap; use base_db::fixture::WithFixture; use chalk_ir::{AdtId, TyKind}; -use hir_def::{ - db::DefDatabase, +use hir_def::db::DefDatabase; +use triomphe::Arc; + +use crate::{ + db::HirDatabase, layout::{Layout, LayoutError}, + test_db::TestDB, + Interner, Substitution, }; -use crate::{db::HirDatabase, test_db::TestDB, Interner, Substitution}; - -use super::layout_of_ty; +mod closure; fn current_machine_data_layout() -> String { project_model::target_data_layout::get(None, None, &HashMap::default()).unwrap() } -fn eval_goal(ra_fixture: &str, minicore: &str) -> Result { +fn eval_goal(ra_fixture: &str, minicore: &str) -> Result, LayoutError> { let target_data_layout = current_machine_data_layout(); let ra_fixture = format!( "{minicore}//- /main.rs crate:test target_data_layout:{target_data_layout}\n{ra_fixture}", ); - let (db, file_id) = TestDB::with_single_file(&ra_fixture); - let module_id = db.module_for_file(file_id); - let def_map = module_id.def_map(&db); - let scope = &def_map[module_id.local_id].scope; - let adt_id = scope - .declarations() - .find_map(|x| match x { - hir_def::ModuleDefId::AdtId(x) => { - let name = match x { - hir_def::AdtId::StructId(x) => db.struct_data(x).name.to_smol_str(), - hir_def::AdtId::UnionId(x) => db.union_data(x).name.to_smol_str(), - hir_def::AdtId::EnumId(x) => db.enum_data(x).name.to_smol_str(), - }; - (name == "Goal").then_some(x) - } - _ => None, + let (db, file_ids) = TestDB::with_many_files(&ra_fixture); + let (adt_id, module_id) = file_ids + .into_iter() + .find_map(|file_id| { + let module_id = db.module_for_file(file_id); + let def_map = module_id.def_map(&db); + let scope = &def_map[module_id.local_id].scope; + let adt_id = scope.declarations().find_map(|x| match x { + hir_def::ModuleDefId::AdtId(x) => { + let name = match x { + hir_def::AdtId::StructId(x) => db.struct_data(x).name.to_smol_str(), + hir_def::AdtId::UnionId(x) => db.union_data(x).name.to_smol_str(), + hir_def::AdtId::EnumId(x) => db.enum_data(x).name.to_smol_str(), + }; + (name == "Goal").then_some(x) + } + _ => None, + })?; + Some((adt_id, module_id)) }) .unwrap(); let goal_ty = TyKind::Adt(AdtId(adt_id), Substitution::empty(Interner)).intern(Interner); - layout_of_ty(&db, &goal_ty, module_id.krate()) + db.layout_of_ty(goal_ty, module_id.krate()) } /// A version of `eval_goal` for types that can not be expressed in ADTs, like closures and `impl Trait` -fn eval_expr(ra_fixture: &str, minicore: &str) -> Result { +fn eval_expr(ra_fixture: &str, minicore: &str) -> Result, LayoutError> { let target_data_layout = current_machine_data_layout(); let ra_fixture = format!( "{minicore}//- /main.rs crate:test target_data_layout:{target_data_layout}\nfn main(){{let goal = {{{ra_fixture}}};}}", @@ -68,7 +74,7 @@ fn eval_expr(ra_fixture: &str, minicore: &str) -> Result { let b = hir_body.bindings.iter().find(|x| x.1.name.to_smol_str() == "goal").unwrap().0; let infer = db.infer(adt_id.into()); let goal_ty = infer.type_of_binding[b].clone(); - layout_of_ty(&db, &goal_ty, module_id.krate()) + db.layout_of_ty(goal_ty, module_id.krate()) } #[track_caller] @@ -81,8 +87,8 @@ fn check_size_and_align(ra_fixture: &str, minicore: &str, size: u64, align: u64) #[track_caller] fn check_size_and_align_expr(ra_fixture: &str, minicore: &str, size: u64, align: u64) { let l = eval_expr(ra_fixture, minicore).unwrap(); - assert_eq!(l.size.bytes(), size); - assert_eq!(l.align.abi.bytes(), align); + assert_eq!(l.size.bytes(), size, "size mismatch"); + assert_eq!(l.align.abi.bytes(), align, "align mismatch"); } #[track_caller] @@ -118,13 +124,31 @@ macro_rules! size_and_align { }; } +#[macro_export] macro_rules! size_and_align_expr { + (minicore: $($x:tt),*; stmts: [$($s:tt)*] $($t:tt)*) => { + { + #[allow(dead_code)] + #[allow(unused_must_use)] + #[allow(path_statements)] + { + $($s)* + let val = { $($t)* }; + $crate::layout::tests::check_size_and_align_expr( + &format!("{{ {} let val = {{ {} }}; val }}", stringify!($($s)*), stringify!($($t)*)), + &format!("//- minicore: {}\n", stringify!($($x),*)), + ::std::mem::size_of_val(&val) as u64, + ::std::mem::align_of_val(&val) as u64, + ); + } + } + }; ($($t:tt)*) => { { #[allow(dead_code)] { let val = { $($t)* }; - check_size_and_align_expr( + $crate::layout::tests::check_size_and_align_expr( stringify!($($t)*), "", ::std::mem::size_of_val(&val) as u64, @@ -196,6 +220,44 @@ fn generic() { } } +#[test] +fn associated_types() { + size_and_align! { + trait Tr { + type Ty; + } + + impl Tr for i32 { + type Ty = i64; + } + + struct Foo(::Ty); + struct Bar(A::Ty); + struct Goal(Foo, Bar, ::Ty); + } + check_size_and_align( + r#" +//- /b/mod.rs crate:b +pub trait Tr { + type Ty; +} +pub struct Foo(::Ty); + +//- /a/mod.rs crate:a deps:b +use b::{Tr, Foo}; + +struct S; +impl Tr for S { + type Ty = i64; +} +struct Goal(Foo); + "#, + "", + 8, + 8, + ); +} + #[test] fn return_position_impl_trait() { size_and_align_expr! { @@ -212,6 +274,45 @@ fn return_position_impl_trait() { fn foo() -> (impl T, impl T, impl T) { (2i64, 5i32, 7i32) } foo() } + size_and_align_expr! { + minicore: iterators; + stmts: [] + trait Tr {} + impl Tr for i32 {} + fn foo() -> impl Iterator { + [1, 2, 3].into_iter() + } + let mut iter = foo(); + let item = iter.next(); + (iter, item) + } + size_and_align_expr! { + minicore: future; + stmts: [] + use core::{future::Future, task::{Poll, Context}, pin::pin}; + use std::{task::Wake, sync::Arc}; + trait Tr {} + impl Tr for i32 {} + async fn f() -> impl Tr { + 2 + } + fn unwrap_fut(inp: impl Future) -> Poll { + // In a normal test we could use `loop {}` or `panic!()` here, + // but rustc actually runs this code. + let pinned = pin!(inp); + struct EmptyWaker; + impl Wake for EmptyWaker { + fn wake(self: Arc) { + } + } + let waker = Arc::new(EmptyWaker).into(); + let mut context = Context::from_waker(&waker); + let x = pinned.poll(&mut context); + x + } + let x = unwrap_fut(f()); + x + } size_and_align_expr! { struct Foo(T, T, (T, T)); trait T {} @@ -276,6 +377,14 @@ fn niche_optimization() { } } +#[test] +fn const_eval() { + size_and_align! { + const X: usize = 5; + struct Goal([i32; X]); + } +} + #[test] fn enums_with_discriminants() { size_and_align! { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests/closure.rs new file mode 100644 index 0000000000000..576e7f3fc619c --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests/closure.rs @@ -0,0 +1,257 @@ +use crate::size_and_align_expr; + +#[test] +fn zero_capture_simple() { + size_and_align_expr! { + |x: i32| x + 2 + } +} + +#[test] +fn move_simple() { + size_and_align_expr! { + minicore: copy; + stmts: [] + let y: i32 = 5; + move |x: i32| { + x + y + } + } +} + +#[test] +fn ref_simple() { + size_and_align_expr! { + minicore: copy; + stmts: [ + let y: i32 = 5; + ] + |x: i32| { + x + y + } + } + size_and_align_expr! { + minicore: copy; + stmts: [ + let mut y: i32 = 5; + ] + |x: i32| { + y = y + x; + y + } + } + size_and_align_expr! { + minicore: copy, deref_mut; + stmts: [ + let y: &mut i32 = &mut 5; + ] + |x: i32| { + *y += x; + } + } + size_and_align_expr! { + minicore: copy; + stmts: [ + struct X(i32, i64); + let x: X = X(2, 6); + ] + || { + x + } + } + size_and_align_expr! { + minicore: copy, deref_mut; + stmts: [ + struct X(i32, i64); + let x: &mut X = &mut X(2, 6); + ] + || { + (*x).0 as i64 + x.1 + } + } +} + +#[test] +fn ref_then_mut_then_move() { + size_and_align_expr! { + minicore: copy; + stmts: [ + struct X(i32, i64); + let mut x: X = X(2, 6); + ] + || { + &x; + &mut x; + x; + } + } +} + +#[test] +fn nested_closures() { + size_and_align_expr! { + || { + || { + || { + let x = 2; + move || { + move || { + x + } + } + } + } + } + } +} + +#[test] +fn capture_specific_fields2() { + size_and_align_expr! { + minicore: copy; + stmts: [ + let x = &mut 2; + ] + || { + *x = 5; + &x; + } + } +} + +#[test] +fn capture_specific_fields() { + size_and_align_expr! { + struct X(i64, i32, (u8, i128)); + let y: X = X(2, 5, (7, 3)); + move |x: i64| { + y.0 + x + (y.2 .0 as i64) + } + } + size_and_align_expr! { + struct X(i64, i32, (u8, i128)); + let y: X = X(2, 5, (7, 3)); + move |x: i64| { + let _ = &y; + y.0 + x + (y.2 .0 as i64) + } + } + size_and_align_expr! { + minicore: copy; + stmts: [ + struct X(i64, i32, (u8, i128)); + let y: X = X(2, 5, (7, 3)); + ] + let y = &y; + move |x: i64| { + y.0 + x + (y.2 .0 as i64) + } + } + size_and_align_expr! { + struct X(i64, i32, (u8, i128)); + let y: X = X(2, 5, (7, 3)); + move |x: i64| { + let X(a, _, (b, _)) = y; + a + x + (b as i64) + } + } + size_and_align_expr! { + struct X(i64, i32, (u8, i128)); + let y = &&X(2, 5, (7, 3)); + move |x: i64| { + let X(a, _, (b, _)) = y; + *a + x + (*b as i64) + } + } + size_and_align_expr! { + struct X(i64, i32, (u8, i128)); + let y: X = X(2, 5, (7, 3)); + move |x: i64| { + match y { + X(a, _, (b, _)) => a + x + (b as i64), + } + } + } + size_and_align_expr! { + struct X(i64, i32, (u8, i128)); + let y: X = X(2, 5, (7, 3)); + move |x: i64| { + let X(a @ 2, _, (b, _)) = y else { return 5 }; + a + x + (b as i64) + } + } +} + +#[test] +fn match_pattern() { + size_and_align_expr! { + struct X(i64, i32, (u8, i128)); + let y: X = X(2, 5, (7, 3)); + move |x: i64| { + match y { + _ => x, + } + } + } + size_and_align_expr! { + minicore: copy; + stmts: [ + struct X(i64, i32, (u8, i128)); + let y: X = X(2, 5, (7, 3)); + ] + |x: i64| { + match y { + X(_a, _, _c) => x, + } + } + } + size_and_align_expr! { + minicore: copy; + stmts: [ + struct X(i64, i32, (u8, i128)); + let y: X = X(2, 5, (7, 3)); + ] + |x: i64| { + match y { + _y => x, + } + } + } + size_and_align_expr! { + minicore: copy; + stmts: [ + struct X(i64, i32, (u8, i128)); + let y: X = X(2, 5, (7, 3)); + ] + |x: i64| { + match y { + ref _y => x, + } + } + } +} + +#[test] +fn ellipsis_pattern() { + size_and_align_expr! { + struct X(i8, u16, i32, u64, i128, u8); + let y: X = X(1, 2, 3, 4, 5, 6); + move |_: i64| { + let X(_a, .., _b, _c) = y; + } + } + size_and_align_expr! { + struct X { a: i32, b: u8, c: i128} + let y: X = X { a: 1, b: 2, c: 3 }; + move |_: i64| { + let X { a, b, .. } = y; + _ = (a, b); + } + } + size_and_align_expr! { + let y: (&&&(i8, u16, i32, u64, i128, u8), u16, i32, u64, i128, u8) = (&&&(1, 2, 3, 4, 5, 6), 2, 3, 4, 5, 6); + move |_: i64| { + let ((_a, .., _b, _c), .., _e, _f) = y; + } + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 9c63d67ab19a4..1a4d003bf5e99 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -1,6 +1,5 @@ //! The type system. We currently use this to infer types for completion, hover //! information and various assists. - #![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] #[allow(unused)] @@ -8,12 +7,9 @@ macro_rules! eprintln { ($($tt:tt)*) => { stdx::eprintln!($($tt)*) }; } -mod autoderef; mod builder; mod chalk_db; mod chalk_ext; -pub mod consteval; -pub mod mir; mod infer; mod inhabitedness; mod interner; @@ -21,21 +17,28 @@ mod lower; mod mapping; mod tls; mod utils; + +pub mod autoderef; +pub mod consteval; pub mod db; pub mod diagnostics; pub mod display; +pub mod lang_items; +pub mod layout; pub mod method_resolution; +pub mod mir; pub mod primitive; pub mod traits; -pub mod layout; -pub mod lang_items; #[cfg(test)] mod tests; #[cfg(test)] mod test_db; -use std::{collections::HashMap, hash::Hash, sync::Arc}; +use std::{ + collections::{hash_map::Entry, HashMap}, + hash::Hash, +}; use chalk_ir::{ fold::{Shift, TypeFoldable}, @@ -44,12 +47,13 @@ use chalk_ir::{ NoSolution, TyData, }; use either::Either; -use hir_def::{expr::ExprId, type_ref::Rawness, TypeOrConstParamId}; +use hir_def::{hir::ExprId, type_ref::Rawness, GeneralConstId, TypeOrConstParamId}; use hir_expand::name; use la_arena::{Arena, Idx}; -use mir::MirEvalError; +use mir::{MirEvalError, VTableMap}; use rustc_hash::FxHashSet; use traits::FnTrait; +use triomphe::Arc; use utils::Generics; use crate::{ @@ -60,6 +64,7 @@ pub use autoderef::autoderef; pub use builder::{ParamKind, TyBuilder}; pub use chalk_ext::*; pub use infer::{ + closure::{CaptureKind, CapturedItem}, could_coerce, could_unify, Adjust, Adjustment, AutoBorrow, BindingMode, InferenceDiagnostic, InferenceResult, OverloadedDeref, PointerCast, }; @@ -148,14 +153,26 @@ pub type Guidance = chalk_solve::Guidance; pub type WhereClause = chalk_ir::WhereClause; /// A constant can have reference to other things. Memory map job is holding -/// the neccessary bits of memory of the const eval session to keep the constant +/// the necessary bits of memory of the const eval session to keep the constant /// meaningful. #[derive(Debug, Default, Clone, PartialEq, Eq)] -pub struct MemoryMap(pub HashMap>); +pub struct MemoryMap { + pub memory: HashMap>, + pub vtable: VTableMap, +} impl MemoryMap { fn insert(&mut self, addr: usize, x: Vec) { - self.0.insert(addr, x); + match self.memory.entry(addr) { + Entry::Occupied(mut e) => { + if e.get().len() < x.len() { + e.insert(x); + } + } + Entry::Vacant(e) => { + e.insert(x); + } + } } /// This functions convert each address by a function `f` which gets the byte intervals and assign an address @@ -165,7 +182,15 @@ impl MemoryMap { &self, mut f: impl FnMut(&[u8]) -> Result, ) -> Result, MirEvalError> { - self.0.iter().map(|x| Ok((*x.0, f(x.1)?))).collect() + self.memory.iter().map(|x| Ok((*x.0, f(x.1)?))).collect() + } + + fn get<'a>(&'a self, addr: usize, size: usize) -> Option<&'a [u8]> { + if size == 0 { + Some(&[]) + } else { + self.memory.get(&addr)?.get(0..size) + } } } @@ -173,6 +198,9 @@ impl MemoryMap { #[derive(Debug, Clone, PartialEq, Eq)] pub enum ConstScalar { Bytes(Vec, MemoryMap), + // FIXME: this is a hack to get around chalk not being able to represent unevaluatable + // constants + UnevaluatedConst(GeneralConstId, Substitution), /// Case of an unknown value that rustc might know but we don't // FIXME: this is a hack to get around chalk not being able to represent unevaluatable // constants @@ -283,16 +311,19 @@ impl CallableSig { pub fn from_fn_ptr(fn_ptr: &FnPointer) -> CallableSig { CallableSig { // FIXME: what to do about lifetime params? -> return PolyFnSig - params_and_return: fn_ptr - .substitution - .clone() - .shifted_out_to(Interner, DebruijnIndex::ONE) - .expect("unexpected lifetime vars in fn ptr") - .0 - .as_slice(Interner) - .iter() - .map(|arg| arg.assert_ty_ref(Interner).clone()) - .collect(), + // FIXME: use `Arc::from_iter` when it becomes available + params_and_return: Arc::from( + fn_ptr + .substitution + .clone() + .shifted_out_to(Interner, DebruijnIndex::ONE) + .expect("unexpected lifetime vars in fn ptr") + .0 + .as_slice(Interner) + .iter() + .map(|arg| arg.assert_ty_ref(Interner).clone()) + .collect::>(), + ), is_varargs: fn_ptr.sig.variadic, safety: fn_ptr.sig.safety, } @@ -576,15 +607,19 @@ where } pub fn callable_sig_from_fnonce( - self_ty: &Ty, + mut self_ty: &Ty, env: Arc, db: &dyn HirDatabase, ) -> Option { + if let Some((ty, _, _)) = self_ty.as_reference() { + // This will happen when it implements fn or fn mut, since we add a autoborrow adjustment + self_ty = ty; + } 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 table = InferenceTable::new(db, env.clone()); + let mut table = InferenceTable::new(db, env); let b = TyBuilder::trait_ref(db, fn_once_trait); if b.remaining() != 2 { return None; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index 23b15087e3165..0c68891fe4932 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -8,7 +8,6 @@ use std::{ cell::{Cell, RefCell, RefMut}, iter, - sync::Arc, }; use base_db::CrateId; @@ -18,19 +17,20 @@ use chalk_ir::{ use either::Either; use hir_def::{ - adt::StructKind, - body::{Expander, LowerCtx}, builtin_type::BuiltinType, + data::adt::StructKind, + expander::Expander, generics::{ TypeOrConstParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget, }, lang_item::{lang_attr, LangItem}, - path::{GenericArg, ModPath, Path, PathKind, PathSegment, PathSegments}, + nameres::MacroSubNs, + path::{GenericArg, GenericArgs, ModPath, Path, PathKind, PathSegment, PathSegments}, resolver::{HasResolver, Resolver, TypeNs}, type_ref::{ConstRefOrPath, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound, TypeRef}, - AdtId, AssocItemId, ConstId, ConstParamId, EnumId, EnumVariantId, FunctionId, GenericDefId, - HasModule, ImplId, ItemContainerId, LocalFieldId, Lookup, ModuleDefId, StaticId, StructId, - TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId, VariantId, + AdtId, AssocItemId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, FunctionId, + GenericDefId, HasModule, ImplId, ItemContainerId, LocalFieldId, Lookup, ModuleDefId, StaticId, + StructId, TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId, VariantId, }; use hir_expand::{name::Name, ExpandResult}; use intern::Interned; @@ -39,6 +39,7 @@ use rustc_hash::FxHashSet; use smallvec::SmallVec; use stdx::{impl_from, never}; use syntax::ast; +use triomphe::Arc; use crate::{ all_super_traits, @@ -103,7 +104,7 @@ impl ImplTraitLoweringState { #[derive(Debug)] pub struct TyLoweringContext<'a> { pub db: &'a dyn HirDatabase, - pub resolver: &'a Resolver, + resolver: &'a Resolver, in_binders: DebruijnIndex, /// Note: Conceptually, it's thinkable that we could be in a location where /// some type params should be represented as placeholders, and others @@ -378,10 +379,19 @@ impl<'a> TyLoweringContext<'a> { }; let ty = { let macro_call = macro_call.to_node(self.db.upcast()); - match expander.enter_expand::(self.db.upcast(), macro_call) { + let resolver = |path| { + self.resolver.resolve_path_as_macro( + self.db.upcast(), + &path, + Some(MacroSubNs::Bang), + ) + }; + match expander.enter_expand::(self.db.upcast(), macro_call, resolver) + { Ok(ExpandResult { value: Some((mark, expanded)), .. }) => { - let ctx = LowerCtx::new(self.db.upcast(), expander.current_file_id()); - let type_ref = TypeRef::from_ast(&ctx, expanded); + let ctx = expander.ctx(self.db.upcast()); + // FIXME: Report syntax errors in expansion here + let type_ref = TypeRef::from_ast(&ctx, expanded.tree()); drop(expander); let ty = self.lower_ty(&type_ref); @@ -425,11 +435,10 @@ impl<'a> TyLoweringContext<'a> { if path.segments().len() > 1 { return None; } - let resolution = - match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path.mod_path()) { - Some((it, None)) => it, - _ => return None, - }; + let resolution = match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) { + Some((it, None)) => it, + _ => return None, + }; match resolution { TypeNs::GenericParam(param_id) => Some(param_id.into()), _ => None, @@ -608,7 +617,7 @@ impl<'a> TyLoweringContext<'a> { } let (resolution, remaining_index) = - match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path.mod_path()) { + match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) { Some(it) => it, None => return (TyKind::Error.intern(Interner), None), }; @@ -716,7 +725,7 @@ impl<'a> TyLoweringContext<'a> { resolved: ValueTyDefId, infer_args: bool, ) -> Substitution { - let last = path.segments().last().expect("path should have at least one segment"); + let last = path.segments().last(); let (segment, generic_def) = match resolved { ValueTyDefId::FunctionId(it) => (last, Some(it.into())), ValueTyDefId::StructId(it) => (last, Some(it.into())), @@ -732,13 +741,20 @@ impl<'a> TyLoweringContext<'a> { let len = path.segments().len(); let penultimate = len.checked_sub(2).and_then(|idx| path.segments().get(idx)); let segment = match penultimate { - Some(segment) if segment.args_and_bindings.is_some() => segment, + Some(segment) if segment.args_and_bindings.is_some() => Some(segment), _ => last, }; (segment, Some(var.parent.into())) } }; - self.substs_from_path_segment(segment, generic_def, infer_args, None) + if let Some(segment) = segment { + self.substs_from_path_segment(segment, generic_def, infer_args, None) + } else if let Some(generic_def) = generic_def { + // lang item + self.substs_from_args_and_bindings(None, Some(generic_def), infer_args, None) + } else { + Substitution::empty(Interner) + } } fn substs_from_path_segment( @@ -747,6 +763,21 @@ impl<'a> TyLoweringContext<'a> { def: Option, infer_args: bool, explicit_self_ty: Option, + ) -> Substitution { + self.substs_from_args_and_bindings( + segment.args_and_bindings, + def, + infer_args, + explicit_self_ty, + ) + } + + fn substs_from_args_and_bindings( + &self, + args_and_bindings: Option<&GenericArgs>, + def: Option, + infer_args: bool, + explicit_self_ty: Option, ) -> Substitution { // Remember that the item's own generic args come before its parent's. let mut substs = Vec::new(); @@ -780,7 +811,7 @@ impl<'a> TyLoweringContext<'a> { }; let mut had_explicit_args = false; - if let Some(generic_args) = &segment.args_and_bindings { + if let Some(generic_args) = &args_and_bindings { if !generic_args.has_self_type { fill_self_params(); } @@ -879,12 +910,11 @@ impl<'a> TyLoweringContext<'a> { path: &Path, explicit_self_ty: Option, ) -> Option { - let resolved = - match self.resolver.resolve_path_in_type_ns_fully(self.db.upcast(), path.mod_path())? { - // FIXME(trait_alias): We need to handle trait alias here. - TypeNs::TraitId(tr) => tr, - _ => return None, - }; + let resolved = match self.resolver.resolve_path_in_type_ns_fully(self.db.upcast(), path)? { + // FIXME(trait_alias): We need to handle trait alias here. + TypeNs::TraitId(tr) => tr, + _ => return None, + }; let segment = path.segments().last().expect("path should have at least one segment"); Some(self.lower_trait_ref_from_resolved_path(resolved, segment, explicit_self_ty)) } @@ -968,7 +998,7 @@ impl<'a> TyLoweringContext<'a> { // ignore `T: Drop` or `T: Destruct` bounds. // - `T: ~const Drop` has a special meaning in Rust 1.61 that we don't implement. // (So ideally, we'd only ignore `~const Drop` here) - // - `Destruct` impls are built-in in 1.62 (current nightlies as of 08-04-2022), so until + // - `Destruct` impls are built-in in 1.62 (current nightly as of 08-04-2022), so until // the builtin impls are supported by Chalk, we ignore them here. if let Some(lang) = lang_attr(self.db.upcast(), tr.hir_trait_id()) { if matches!(lang, LangItem::Drop | LangItem::Destruct) { @@ -1062,23 +1092,23 @@ impl<'a> TyLoweringContext<'a> { associated_ty_id: to_assoc_type_id(associated_ty), substitution, }; - let mut preds: SmallVec<[_; 1]> = SmallVec::with_capacity( + let mut predicates: SmallVec<[_; 1]> = SmallVec::with_capacity( binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(), ); if let Some(type_ref) = &binding.type_ref { let ty = self.lower_ty(type_ref); let alias_eq = AliasEq { alias: AliasTy::Projection(projection_ty.clone()), ty }; - preds.push(crate::wrap_empty_binders(WhereClause::AliasEq(alias_eq))); + predicates.push(crate::wrap_empty_binders(WhereClause::AliasEq(alias_eq))); } for bound in binding.bounds.iter() { - preds.extend(self.lower_type_bound( + predicates.extend(self.lower_type_bound( bound, TyKind::Alias(AliasTy::Projection(projection_ty.clone())).intern(Interner), false, )); } - preds + predicates }) } @@ -1145,7 +1175,7 @@ impl<'a> TyLoweringContext<'a> { return None; } - // As multiple occurrences of the same auto traits *are* permitted, we dedulicate the + // As multiple occurrences of the same auto traits *are* permitted, we deduplicate the // bounds. We shouldn't have repeated elements besides auto traits at this point. bounds.dedup(); @@ -1381,9 +1411,7 @@ pub(crate) fn generic_predicates_for_param_query( Some(it) => it, None => return true, }; - let tr = match resolver - .resolve_path_in_type_ns_fully(db.upcast(), path.mod_path()) - { + let tr = match resolver.resolve_path_in_type_ns_fully(db.upcast(), path) { Some(TypeNs::TraitId(tr)) => tr, _ => return false, }; @@ -1420,7 +1448,19 @@ pub(crate) fn generic_predicates_for_param_recover( _param_id: &TypeOrConstParamId, _assoc_name: &Option, ) -> Arc<[Binders]> { - Arc::new([]) + // FIXME: use `Arc::from_iter` when it becomes available + Arc::from(vec![]) +} + +pub(crate) fn trait_environment_for_body_query( + db: &dyn HirDatabase, + def: DefWithBodyId, +) -> Arc { + let Some(def) = def.as_generic_def_id() else { + let krate = def.module(db.upcast()).krate(); + return Arc::new(TraitEnvironment::empty(krate)); + }; + db.trait_environment(def) } pub(crate) fn trait_environment_query( @@ -1478,7 +1518,7 @@ pub(crate) fn trait_environment_query( let env = chalk_ir::Environment::new(Interner).add_clauses(Interner, clauses); - Arc::new(TraitEnvironment { krate, traits_from_clauses: traits_in_scope, env }) + Arc::new(TraitEnvironment { krate, block: None, traits_from_clauses: traits_in_scope, env }) } /// Resolve the where clause(s) of an item with generics. @@ -1547,30 +1587,33 @@ pub(crate) fn generic_defaults_query( let generic_params = generics(db.upcast(), def); let parent_start_idx = generic_params.len_self(); - let defaults = generic_params - .iter() - .enumerate() - .map(|(idx, (id, p))| { - let p = match p { - TypeOrConstParamData::TypeParamData(p) => p, - TypeOrConstParamData::ConstParamData(_) => { - // FIXME: implement const generic defaults - let val = unknown_const_as_generic( - db.const_param_ty(ConstParamId::from_unchecked(id)), - ); - return make_binders(db, &generic_params, val); - } - }; - let mut ty = - p.default.as_ref().map_or(TyKind::Error.intern(Interner), |t| ctx.lower_ty(t)); - - // Each default can only refer to previous parameters. - // Type variable default referring to parameter coming - // after it is forbidden (FIXME: report diagnostic) - ty = fallback_bound_vars(ty, idx, parent_start_idx); - crate::make_binders(db, &generic_params, ty.cast(Interner)) - }) - .collect(); + let defaults = Arc::from( + generic_params + .iter() + .enumerate() + .map(|(idx, (id, p))| { + let p = match p { + TypeOrConstParamData::TypeParamData(p) => p, + TypeOrConstParamData::ConstParamData(_) => { + // FIXME: implement const generic defaults + let val = unknown_const_as_generic( + db.const_param_ty(ConstParamId::from_unchecked(id)), + ); + return make_binders(db, &generic_params, val); + } + }; + let mut ty = + p.default.as_ref().map_or(TyKind::Error.intern(Interner), |t| ctx.lower_ty(t)); + + // Each default can only refer to previous parameters. + // Type variable default referring to parameter coming + // after it is forbidden (FIXME: report diagnostic) + ty = fallback_bound_vars(ty, idx, parent_start_idx); + crate::make_binders(db, &generic_params, ty.cast(Interner)) + }) + // FIXME: use `Arc::from_iter` when it becomes available + .collect::>(), + ); defaults } @@ -1583,18 +1626,21 @@ pub(crate) fn generic_defaults_recover( let generic_params = generics(db.upcast(), *def); // FIXME: this code is not covered in tests. // we still need one default per parameter - let defaults = generic_params - .iter_id() - .map(|id| { - let val = match id { - Either::Left(_) => { - GenericArgData::Ty(TyKind::Error.intern(Interner)).intern(Interner) - } - Either::Right(id) => unknown_const_as_generic(db.const_param_ty(id)), - }; - crate::make_binders(db, &generic_params, val) - }) - .collect(); + let defaults = Arc::from( + generic_params + .iter_id() + .map(|id| { + let val = match id { + Either::Left(_) => { + GenericArgData::Ty(TyKind::Error.intern(Interner)).intern(Interner) + } + Either::Right(id) => unknown_const_as_generic(db.const_param_ty(id)), + }; + crate::make_binders(db, &generic_params, val) + }) + // FIXME: use `Arc::from_iter` when it becomes available + .collect::>(), + ); defaults } @@ -1605,7 +1651,7 @@ fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig { let ctx_params = TyLoweringContext::new(db, &resolver) .with_impl_trait_mode(ImplTraitLoweringMode::Variable) .with_type_param_mode(ParamLoweringMode::Variable); - let params = data.params.iter().map(|(_, tr)| ctx_params.lower_ty(tr)).collect::>(); + let params = data.params.iter().map(|tr| ctx_params.lower_ty(tr)).collect::>(); let ctx_ret = TyLoweringContext::new(db, &resolver) .with_impl_trait_mode(ImplTraitLoweringMode::Opaque) .with_type_param_mode(ParamLoweringMode::Variable); @@ -1948,7 +1994,7 @@ pub(crate) fn generic_arg_to_chalk<'a, T>( // as types. Maybe here is not the best place to do it, but // it works. if let TypeRef::Path(p) = t { - let p = p.mod_path(); + let p = p.mod_path()?; if p.kind == PathKind::Plain { if let [n] = p.segments() { let c = ConstRefOrPath::Path(n.clone()); @@ -1977,8 +2023,16 @@ pub(crate) fn const_or_path_to_chalk( ConstRefOrPath::Scalar(s) => intern_const_ref(db, s, expected_ty, resolver.krate()), ConstRefOrPath::Path(n) => { let path = ModPath::from_segments(PathKind::Plain, Some(n.clone())); - path_to_const(db, resolver, &path, mode, args, debruijn) - .unwrap_or_else(|| unknown_const(expected_ty)) + path_to_const( + db, + resolver, + &Path::from_known_path_with_no_generic(path), + mode, + args, + debruijn, + expected_ty.clone(), + ) + .unwrap_or_else(|| unknown_const(expected_ty)) } } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index f3a27632bf545..6fa3d1351a916 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -2,25 +2,28 @@ //! For details about how this works in rustc, see the method lookup page in the //! [rustc guide](https://rust-lang.github.io/rustc-guide/method-lookup.html) //! and the corresponding code mostly in rustc_hir_analysis/check/method/probe.rs. -use std::{ops::ControlFlow, sync::Arc}; +use std::ops::ControlFlow; use base_db::{CrateId, Edition}; -use chalk_ir::{cast::Cast, Mutability, TyKind, UniverseIndex}; +use chalk_ir::{cast::Cast, Mutability, TyKind, UniverseIndex, WhereClause}; use hir_def::{ - data::ImplData, item_scope::ItemScope, lang_item::LangItem, nameres::DefMap, AssocItemId, - BlockId, ConstId, FunctionId, HasModule, ImplId, ItemContainerId, Lookup, ModuleDefId, - ModuleId, TraitId, + data::{adt::StructFlags, ImplData}, + item_scope::ItemScope, + nameres::DefMap, + AssocItemId, BlockId, ConstId, FunctionId, HasModule, ImplId, ItemContainerId, Lookup, + ModuleDefId, ModuleId, TraitId, }; use hir_expand::name::Name; use rustc_hash::{FxHashMap, FxHashSet}; use smallvec::{smallvec, SmallVec}; use stdx::never; +use triomphe::Arc; use crate::{ autoderef::{self, AutoderefKind}, db::HirDatabase, from_chalk_trait_id, from_foreign_def_id, - infer::{unify::InferenceTable, Adjust, Adjustment, AutoBorrow, OverloadedDeref, PointerCast}, + infer::{unify::InferenceTable, Adjust, Adjustment, OverloadedDeref, PointerCast}, primitive::{FloatTy, IntTy, UintTy}, static_lifetime, to_chalk_trait_id, utils::all_super_traits, @@ -147,31 +150,30 @@ impl TraitImpls { Arc::new(impls) } - pub(crate) fn trait_impls_in_block_query( - db: &dyn HirDatabase, - block: BlockId, - ) -> Option> { + pub(crate) fn trait_impls_in_block_query(db: &dyn HirDatabase, block: BlockId) -> Arc { let _p = profile::span("trait_impls_in_block_query"); let mut impls = Self { map: FxHashMap::default() }; - let block_def_map = db.block_def_map(block)?; + let block_def_map = db.block_def_map(block); impls.collect_def_map(db, &block_def_map); impls.shrink_to_fit(); - Some(Arc::new(impls)) + Arc::new(impls) } - pub(crate) fn trait_impls_in_deps_query(db: &dyn HirDatabase, krate: CrateId) -> Arc { + pub(crate) fn trait_impls_in_deps_query( + db: &dyn HirDatabase, + krate: CrateId, + ) -> Arc<[Arc]> { let _p = profile::span("trait_impls_in_deps_query").detail(|| format!("{krate:?}")); let crate_graph = db.crate_graph(); - let mut res = Self { map: FxHashMap::default() }; - - for krate in crate_graph.transitive_deps(krate) { - res.merge(&db.trait_impls_in_crate(krate)); - } - res.shrink_to_fit(); - - Arc::new(res) + // FIXME: use `Arc::from_iter` when it becomes available + Arc::from( + crate_graph + .transitive_deps(krate) + .map(|krate| db.trait_impls_in_crate(krate)) + .collect::>(), + ) } fn shrink_to_fit(&mut self) { @@ -185,6 +187,15 @@ impl TraitImpls { fn collect_def_map(&mut self, db: &dyn HirDatabase, def_map: &DefMap) { for (_module_id, module_data) in def_map.modules() { for impl_id in module_data.scope.impls() { + // Reservation impls should be ignored during trait resolution, so we never need + // them during type analysis. See rust-lang/rust#64631 for details. + // + // FIXME: Reservation impls should be considered during coherence checks. If we are + // (ever) to implement coherence checks, this filtering should be done by the trait + // solver. + if db.attrs(impl_id.into()).by_key("rustc_reservation_impl").exists() { + continue; + } let target_trait = match db.impl_trait(impl_id) { Some(tr) => tr.skip_binders().hir_trait_id(), None => continue, @@ -210,15 +221,6 @@ impl TraitImpls { } } - fn merge(&mut self, other: &Self) { - for (trait_, other_map) in &other.map { - let map = self.map.entry(*trait_).or_default(); - for (fp, impls) in other_map { - map.entry(*fp).or_default().extend(impls); - } - } - } - /// Queries all trait impls for the given type. pub fn for_self_ty_without_blanket_impls( &self, @@ -271,6 +273,7 @@ pub struct InherentImpls { impl InherentImpls { pub(crate) fn inherent_impls_in_crate_query(db: &dyn HirDatabase, krate: CrateId) -> Arc { + let _p = profile::span("inherent_impls_in_crate_query").detail(|| format!("{krate:?}")); let mut impls = Self { map: FxHashMap::default(), invalid_impls: Vec::default() }; let crate_def_map = db.crate_def_map(krate); @@ -280,17 +283,15 @@ impl InherentImpls { Arc::new(impls) } - pub(crate) fn inherent_impls_in_block_query( - db: &dyn HirDatabase, - block: BlockId, - ) -> Option> { + pub(crate) fn inherent_impls_in_block_query(db: &dyn HirDatabase, block: BlockId) -> Arc { + let _p = profile::span("inherent_impls_in_block_query"); let mut impls = Self { map: FxHashMap::default(), invalid_impls: Vec::default() }; - if let Some(block_def_map) = db.block_def_map(block) { - impls.collect_def_map(db, &block_def_map); - impls.shrink_to_fit(); - return Some(Arc::new(impls)); - } - None + + let block_def_map = db.block_def_map(block); + impls.collect_def_map(db, &block_def_map); + impls.shrink_to_fit(); + + Arc::new(impls) } fn shrink_to_fit(&mut self) { @@ -404,12 +405,14 @@ pub fn def_crates( match ty.kind(Interner) { &TyKind::Adt(AdtId(def_id), _) => { let rustc_has_incoherent_inherent_impls = match def_id { - hir_def::AdtId::StructId(id) => { - db.struct_data(id).rustc_has_incoherent_inherent_impls - } - hir_def::AdtId::UnionId(id) => { - db.union_data(id).rustc_has_incoherent_inherent_impls - } + hir_def::AdtId::StructId(id) => db + .struct_data(id) + .flags + .contains(StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL), + hir_def::AdtId::UnionId(id) => db + .union_data(id) + .flags + .contains(StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL), hir_def::AdtId::EnumId(id) => db.enum_data(id).rustc_has_incoherent_inherent_impls, }; Some(if rustc_has_incoherent_inherent_impls { @@ -449,55 +452,6 @@ pub fn def_crates( } } -pub fn lang_items_for_bin_op(op: syntax::ast::BinaryOp) -> Option<(Name, LangItem)> { - 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], LangItem::Add), - ArithOp::Mul => (name![mul], LangItem::Mul), - ArithOp::Sub => (name![sub], LangItem::Sub), - ArithOp::Div => (name![div], LangItem::Div), - ArithOp::Rem => (name![rem], LangItem::Rem), - ArithOp::Shl => (name![shl], LangItem::Shl), - ArithOp::Shr => (name![shr], LangItem::Shr), - ArithOp::BitXor => (name![bitxor], LangItem::BitXor), - ArithOp::BitOr => (name![bitor], LangItem::BitOr), - ArithOp::BitAnd => (name![bitand], LangItem::BitAnd), - }, - BinaryOp::Assignment { op: Some(aop) } => match aop { - ArithOp::Add => (name![add_assign], LangItem::AddAssign), - ArithOp::Mul => (name![mul_assign], LangItem::MulAssign), - ArithOp::Sub => (name![sub_assign], LangItem::SubAssign), - ArithOp::Div => (name![div_assign], LangItem::DivAssign), - ArithOp::Rem => (name![rem_assign], LangItem::RemAssign), - ArithOp::Shl => (name![shl_assign], LangItem::ShlAssign), - ArithOp::Shr => (name![shr_assign], LangItem::ShrAssign), - ArithOp::BitXor => (name![bitxor_assign], LangItem::BitXorAssign), - ArithOp::BitOr => (name![bitor_assign], LangItem::BitOrAssign), - ArithOp::BitAnd => (name![bitand_assign], LangItem::BitAndAssign), - }, - BinaryOp::CmpOp(cop) => match cop { - CmpOp::Eq { negated: false } => (name![eq], LangItem::PartialEq), - CmpOp::Eq { negated: true } => (name![ne], LangItem::PartialEq), - CmpOp::Ord { ordering: Ordering::Less, strict: false } => { - (name![le], LangItem::PartialOrd) - } - CmpOp::Ord { ordering: Ordering::Less, strict: true } => { - (name![lt], LangItem::PartialOrd) - } - CmpOp::Ord { ordering: Ordering::Greater, strict: false } => { - (name![ge], LangItem::PartialOrd) - } - CmpOp::Ord { ordering: Ordering::Greater, strict: true } => { - (name![gt], LangItem::PartialOrd) - } - }, - BinaryOp::Assignment { op: None } => return None, - }) -} - /// Look up the method with the given name. pub(crate) fn lookup_method( db: &dyn HirDatabase, @@ -600,9 +554,9 @@ impl ReceiverAdjustments { } } if let Some(m) = self.autoref { - ty = TyKind::Ref(m, static_lifetime(), ty).intern(Interner); - adjust - .push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(m)), target: ty.clone() }); + let a = Adjustment::borrow(m, ty); + ty = a.target.clone(); + adjust.push(a); } if self.unsize_array { ty = 'x: { @@ -692,6 +646,39 @@ pub fn lookup_impl_const( .unwrap_or((const_id, subs)) } +/// Checks if the self parameter of `Trait` method is the `dyn Trait` and we should +/// call the method using the vtable. +pub fn is_dyn_method( + db: &dyn HirDatabase, + _env: Arc, + func: FunctionId, + fn_subst: Substitution, +) -> Option { + let ItemContainerId::TraitId(trait_id) = func.lookup(db.upcast()).container else { + return None; + }; + let trait_params = db.generic_params(trait_id.into()).type_or_consts.len(); + let fn_params = fn_subst.len(Interner) - trait_params; + let trait_ref = TraitRef { + trait_id: to_chalk_trait_id(trait_id), + substitution: Substitution::from_iter(Interner, fn_subst.iter(Interner).skip(fn_params)), + }; + let self_ty = trait_ref.self_type_parameter(Interner); + if let TyKind::Dyn(d) = self_ty.kind(Interner) { + let is_my_trait_in_bounds = + d.bounds.skip_binders().as_slice(Interner).iter().any(|x| match x.skip_binders() { + // rustc doesn't accept `impl Foo<2> for dyn Foo<5>`, so if the trait id is equal, no matter + // what the generics are, we are sure that the method is come from the vtable. + WhereClause::Implemented(tr) => tr.trait_id == trait_ref.trait_id, + _ => false, + }); + if is_my_trait_in_bounds { + return Some(fn_params); + } + } + None +} + /// Looks up the impl method that actually runs for the trait method `func`. /// /// Returns `func` if it's not a method defined in a trait or the lookup failed. @@ -701,9 +688,8 @@ pub fn lookup_impl_method( func: FunctionId, fn_subst: Substitution, ) -> (FunctionId, Substitution) { - let trait_id = match func.lookup(db.upcast()).container { - ItemContainerId::TraitId(id) => id, - _ => return (func, fn_subst), + let ItemContainerId::TraitId(trait_id) = func.lookup(db.upcast()).container else { + return (func, fn_subst) }; let trait_params = db.generic_params(trait_id.into()).type_or_consts.len(); let fn_params = fn_subst.len(Interner) - trait_params; @@ -713,7 +699,7 @@ pub fn lookup_impl_method( }; let name = &db.function_data(func).name; - lookup_impl_assoc_item_for_trait_ref(trait_ref, db, env, name) + let Some((impl_fn, impl_subst)) = lookup_impl_assoc_item_for_trait_ref(trait_ref, db, env, name) .and_then(|assoc| { if let (AssocItemId::FunctionId(id), subst) = assoc { Some((id, subst)) @@ -721,7 +707,16 @@ pub fn lookup_impl_method( None } }) - .unwrap_or((func, fn_subst)) + else { + return (func, fn_subst); + }; + ( + impl_fn, + Substitution::from_iter( + Interner, + fn_subst.iter(Interner).take(fn_params).chain(impl_subst.iter(Interner)), + ), + ) } fn lookup_impl_assoc_item_for_trait_ref( @@ -730,10 +725,20 @@ fn lookup_impl_assoc_item_for_trait_ref( env: Arc, name: &Name, ) -> Option<(AssocItemId, Substitution)> { + let hir_trait_id = trait_ref.hir_trait_id(); let self_ty = trait_ref.self_type_parameter(Interner); let self_ty_fp = TyFingerprint::for_trait_impl(&self_ty)?; let impls = db.trait_impls_in_deps(env.krate); - let impls = impls.for_trait_and_self_ty(trait_ref.hir_trait_id(), self_ty_fp); + let self_impls = match self_ty.kind(Interner) { + TyKind::Adt(id, _) => { + id.0.module(db.upcast()).containing_block().map(|x| db.trait_impls_in_block(x)) + } + _ => None, + }; + let impls = impls + .iter() + .chain(self_impls.as_ref()) + .flat_map(|impls| impls.for_trait_and_self_ty(hir_trait_id, self_ty_fp)); let table = InferenceTable::new(db, env); @@ -759,9 +764,8 @@ fn find_matching_impl( actual_trait_ref: TraitRef, ) -> Option<(Arc, Substitution)> { let db = table.db; - loop { - let impl_ = impls.next()?; - let r = table.run_in_snapshot(|table| { + impls.find_map(|impl_| { + table.run_in_snapshot(|table| { let impl_data = db.impl_data(impl_); let impl_substs = TyBuilder::subst_for_def(db, impl_, None).fill_with_inference_vars(table).build(); @@ -778,12 +782,11 @@ fn find_matching_impl( .into_iter() .map(|b| b.cast(Interner)); let goal = crate::Goal::all(Interner, wcs); - table.try_obligation(goal).map(|_| (impl_data, table.resolve_completely(impl_substs))) - }); - if r.is_some() { - break r; - } - } + table.try_obligation(goal.clone())?; + table.register_obligation(goal); + Some((impl_data, table.resolve_completely(impl_substs))) + }) + }) } fn is_inherent_impl_coherent( @@ -824,12 +827,14 @@ fn is_inherent_impl_coherent( | TyKind::Scalar(_) => true, &TyKind::Adt(AdtId(adt), _) => match adt { - hir_def::AdtId::StructId(it) => { - db.struct_data(it).rustc_has_incoherent_inherent_impls - } - hir_def::AdtId::UnionId(it) => { - db.union_data(it).rustc_has_incoherent_inherent_impls - } + hir_def::AdtId::StructId(id) => db + .struct_data(id) + .flags + .contains(StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL), + hir_def::AdtId::UnionId(id) => db + .union_data(id) + .flags + .contains(StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL), hir_def::AdtId::EnumId(it) => db.enum_data(it).rustc_has_incoherent_inherent_impls, }, TyKind::Dyn(it) => it.principal().map_or(false, |trait_ref| { @@ -963,7 +968,14 @@ fn iterate_method_candidates_with_autoref( ) }; - iterate_method_candidates_by_receiver(receiver_ty, first_adjustment.clone())?; + let mut maybe_reborrowed = first_adjustment.clone(); + if let Some((_, _, m)) = receiver_ty.value.as_reference() { + // Prefer reborrow of references to move + maybe_reborrowed.autoref = Some(m); + maybe_reborrowed.autoderefs += 1; + } + + iterate_method_candidates_by_receiver(receiver_ty, maybe_reborrowed)?; let refed = Canonical { value: TyKind::Ref(Mutability::Not, static_lifetime(), receiver_ty.value.clone()) @@ -1108,7 +1120,7 @@ fn iterate_trait_method_candidates( }; if !known_implemented { let goal = generic_implements_goal(db, env.clone(), t, &canonical_self_ty); - if db.trait_solve(env.krate, goal.cast(Interner)).is_none() { + if db.trait_solve(env.krate, env.block, goal.cast(Interner)).is_none() { continue 'traits; } } @@ -1180,23 +1192,19 @@ fn iterate_inherent_methods( }; while let Some(block_id) = block { - if let Some(impls) = db.inherent_impls_in_block(block_id) { - impls_for_self_ty( - &impls, - self_ty, - table, - name, - receiver_ty, - receiver_adjustments.clone(), - module, - callback, - )?; - } + let impls = db.inherent_impls_in_block(block_id); + impls_for_self_ty( + &impls, + self_ty, + table, + name, + receiver_ty, + receiver_adjustments.clone(), + module, + callback, + )?; - block = db - .block_def_map(block_id) - .and_then(|map| map.parent()) - .and_then(|module| module.containing_block()); + block = db.block_def_map(block_id).parent().and_then(|module| module.containing_block()); } for krate in def_crates { @@ -1274,7 +1282,7 @@ fn iterate_inherent_methods( } /// Returns the receiver type for the index trait call. -pub fn resolve_indexing_op( +pub(crate) fn resolve_indexing_op( db: &dyn HirDatabase, env: Arc, ty: Canonical, @@ -1284,8 +1292,11 @@ pub fn resolve_indexing_op( let ty = table.instantiate_canonical(ty); let deref_chain = autoderef_method_receiver(&mut table, ty); for (ty, adj) in deref_chain { - let goal = generic_implements_goal(db, env.clone(), index_trait, &ty); - if db.trait_solve(env.krate, goal.cast(Interner)).is_some() { + let goal = generic_implements_goal(db, table.trait_env.clone(), index_trait, &ty); + if db + .trait_solve(table.trait_env.krate, table.trait_env.block, goal.cast(Interner)) + .is_some() + { return Some(adj); } } @@ -1310,14 +1321,12 @@ fn is_valid_candidate( ) -> IsValidCandidate { let db = table.db; match item { - AssocItemId::FunctionId(m) => { - is_valid_fn_candidate(table, m, name, receiver_ty, self_ty, visible_from_module) + AssocItemId::FunctionId(f) => { + is_valid_fn_candidate(table, f, name, receiver_ty, self_ty, visible_from_module) } AssocItemId::ConstId(c) => { - let data = db.const_data(c); check_that!(receiver_ty.is_none()); - - check_that!(name.map_or(true, |n| data.name.as_ref() == Some(n))); + check_that!(name.map_or(true, |n| db.const_data(c).name.as_ref() == Some(n))); if let Some(from_module) = visible_from_module { if !db.const_visibility(c).is_visible_from(db.upcast(), from_module) { @@ -1441,7 +1450,7 @@ pub fn implements_trait( trait_: TraitId, ) -> bool { let goal = generic_implements_goal(db, env.clone(), trait_, ty); - let solution = db.trait_solve(env.krate, goal.cast(Interner)); + let solution = db.trait_solve(env.krate, env.block, goal.cast(Interner)); solution.is_some() } @@ -1453,7 +1462,7 @@ pub fn implements_trait_unique( trait_: TraitId, ) -> bool { let goal = generic_implements_goal(db, env.clone(), trait_, ty); - let solution = db.trait_solve(env.krate, goal.cast(Interner)); + let solution = db.trait_solve(env.krate, env.block, goal.cast(Interner)); matches!(solution, Some(crate::Solution::Unique(_))) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs index 7c1cbbdf53d27..2345bab0bb4df 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs @@ -3,12 +3,15 @@ use std::{fmt::Display, iter}; use crate::{ - infer::PointerCast, Const, ConstScalar, InferenceResult, Interner, MemoryMap, Substitution, Ty, + consteval::usize_const, db::HirDatabase, display::HirDisplay, infer::PointerCast, + lang_items::is_box, mapping::ToChalk, CallableDefId, ClosureId, Const, ConstScalar, + InferenceResult, Interner, MemoryMap, Substitution, Ty, TyKind, }; +use base_db::CrateId; use chalk_ir::Mutability; use hir_def::{ - expr::{BindingId, Expr, ExprId, Ordering, PatId}, - DefWithBodyId, FieldId, UnionId, VariantId, + hir::{BindingId, Expr, ExprId, Ordering, PatId}, + DefWithBodyId, FieldId, StaticId, UnionId, VariantId, }; use la_arena::{Arena, ArenaMap, Idx, RawIdx}; @@ -16,12 +19,19 @@ mod eval; mod lower; mod borrowck; mod pretty; +mod monomorphization; pub use borrowck::{borrowck_query, BorrowckResult, MutabilityReason}; -pub use eval::{interpret_mir, pad16, Evaluator, MirEvalError}; -pub use lower::{lower_to_mir, mir_body_query, mir_body_recover, MirLowerError}; +pub use eval::{interpret_mir, pad16, Evaluator, MirEvalError, VTableMap}; +pub use lower::{ + lower_to_mir, mir_body_for_closure_query, mir_body_query, mir_body_recover, MirLowerError, +}; +pub use monomorphization::{ + monomorphize_mir_body_bad, monomorphized_mir_body_for_closure_query, + monomorphized_mir_body_query, monomorphized_mir_body_recover, +}; use smallvec::{smallvec, SmallVec}; -use stdx::impl_from; +use stdx::{impl_from, never}; use super::consteval::{intern_const_scalar, try_const_usize}; @@ -32,7 +42,7 @@ fn return_slot() -> LocalId { LocalId::from_raw(RawIdx::from(0)) } -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Local { pub ty: Ty, } @@ -52,7 +62,7 @@ pub struct Local { /// This is what is implemented in miri today. Are these the semantics we want for MIR? Is this /// something we can even decide without knowing more about Rust's memory model? /// -/// **Needs clarifiation:** Is loading a place that has its variant index set well-formed? Miri +/// **Needs clarification:** Is loading a place that has its variant index set well-formed? Miri /// currently implements it, but it seems like this may be something to check against in the /// validator. #[derive(Debug, PartialEq, Eq, Clone)] @@ -73,6 +83,9 @@ pub enum Operand { Move(Place), /// Constants are already semantically values, and remain unchanged. Constant(Const), + /// NON STANDARD: This kind of operand returns an immutable reference to that static memory. Rustc + /// handles it with the `Constant` variant somehow. + Static(StaticId), } impl Operand { @@ -87,31 +100,141 @@ impl Operand { fn const_zst(ty: Ty) -> Operand { Self::from_bytes(vec![], ty) } + + fn from_fn( + db: &dyn HirDatabase, + func_id: hir_def::FunctionId, + generic_args: Substitution, + ) -> Operand { + let ty = + chalk_ir::TyKind::FnDef(CallableDefId::FunctionId(func_id).to_chalk(db), generic_args) + .intern(Interner); + Operand::from_bytes(vec![], ty) + } } -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum ProjectionElem { Deref, Field(FieldId), - TupleField(usize), + // FIXME: get rid of this, and use FieldId for tuples and closures + TupleOrClosureField(usize), Index(V), - ConstantIndex { offset: u64, min_length: u64, from_end: bool }, - Subslice { from: u64, to: u64, from_end: bool }, + ConstantIndex { offset: u64, from_end: bool }, + Subslice { from: u64, to: u64 }, //Downcast(Option, VariantIdx), OpaqueCast(T), } +impl ProjectionElem { + pub fn projected_ty( + &self, + base: Ty, + db: &dyn HirDatabase, + closure_field: impl FnOnce(ClosureId, &Substitution, usize) -> Ty, + krate: CrateId, + ) -> Ty { + match self { + ProjectionElem::Deref => match &base.data(Interner).kind { + TyKind::Raw(_, inner) | TyKind::Ref(_, _, inner) => inner.clone(), + TyKind::Adt(adt, subst) if is_box(db, adt.0) => { + subst.at(Interner, 0).assert_ty_ref(Interner).clone() + } + _ => { + never!("Overloaded deref on type {} is not a projection", base.display(db)); + return TyKind::Error.intern(Interner); + } + }, + ProjectionElem::Field(f) => match &base.data(Interner).kind { + TyKind::Adt(_, subst) => { + db.field_types(f.parent)[f.local_id].clone().substitute(Interner, subst) + } + _ => { + never!("Only adt has field"); + return TyKind::Error.intern(Interner); + } + }, + ProjectionElem::TupleOrClosureField(f) => match &base.data(Interner).kind { + TyKind::Tuple(_, subst) => subst + .as_slice(Interner) + .get(*f) + .map(|x| x.assert_ty_ref(Interner)) + .cloned() + .unwrap_or_else(|| { + never!("Out of bound tuple field"); + TyKind::Error.intern(Interner) + }), + TyKind::Closure(id, subst) => closure_field(*id, subst, *f), + _ => { + never!("Only tuple or closure has tuple or closure field"); + return TyKind::Error.intern(Interner); + } + }, + ProjectionElem::ConstantIndex { .. } | ProjectionElem::Index(_) => { + match &base.data(Interner).kind { + TyKind::Array(inner, _) | TyKind::Slice(inner) => inner.clone(), + _ => { + never!("Overloaded index is not a projection"); + return TyKind::Error.intern(Interner); + } + } + } + &ProjectionElem::Subslice { from, to } => match &base.data(Interner).kind { + TyKind::Array(inner, c) => { + let next_c = usize_const( + db, + match try_const_usize(db, c) { + None => None, + Some(x) => x.checked_sub(u128::from(from + to)), + }, + krate, + ); + TyKind::Array(inner.clone(), next_c).intern(Interner) + } + TyKind::Slice(_) => base.clone(), + _ => { + never!("Subslice projection should only happen on slice and array"); + return TyKind::Error.intern(Interner); + } + }, + ProjectionElem::OpaqueCast(_) => { + never!("We don't emit these yet"); + return TyKind::Error.intern(Interner); + } + } + } +} + type PlaceElem = ProjectionElem; -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Place { pub local: LocalId, - pub projection: Vec, + pub projection: Box<[PlaceElem]>, +} + +impl Place { + fn is_parent(&self, child: &Place) -> bool { + self.local == child.local && child.projection.starts_with(&self.projection) + } + + fn iterate_over_parents(&self) -> impl Iterator + '_ { + (0..self.projection.len()) + .map(|x| &self.projection[0..x]) + .map(|x| Place { local: self.local, projection: x.to_vec().into() }) + } + + fn project(&self, projection: PlaceElem) -> Place { + Place { + local: self.local, + projection: self.projection.iter().cloned().chain([projection]).collect(), + } + } } impl From for Place { fn from(local: LocalId) -> Self { - Self { local, projection: vec![] } + Self { local, projection: vec![].into() } } } @@ -123,7 +246,7 @@ pub enum AggregateKind { Tuple(Ty), Adt(VariantId, Substitution), Union(UnionId, FieldId), - //Closure(LocalDefId, SubstsRef), + Closure(Ty), //Generator(LocalDefId, SubstsRef, Movability), } @@ -197,7 +320,13 @@ impl SwitchTargets { } #[derive(Debug, PartialEq, Eq, Clone)] -pub enum Terminator { +pub struct Terminator { + span: MirSpan, + kind: TerminatorKind, +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum TerminatorKind { /// Block has one successor; we continue execution there. Goto { target: BasicBlockId }, @@ -320,7 +449,7 @@ pub enum Terminator { /// These are owned by the callee, which is free to modify them. /// This allows the memory occupied by "by-value" arguments to be /// reused across function calls without duplicating the contents. - args: Vec, + args: Box<[Operand]>, /// Where the returned value will be written destination: Place, /// Where to go after this call returns. If none, the call necessarily diverges. @@ -418,7 +547,7 @@ pub enum Terminator { }, } -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)] pub enum BorrowKind { /// Data must be immutable and is aliasable. Shared, @@ -564,6 +693,20 @@ pub enum BinOp { Offset, } +impl BinOp { + fn run_compare(&self, l: T, r: T) -> bool { + match self { + BinOp::Ge => l >= r, + BinOp::Gt => l > r, + BinOp::Le => l <= r, + BinOp::Lt => l < r, + BinOp::Eq => l == r, + BinOp::Ne => l != r, + x => panic!("`run_compare` called on operator {x:?}"), + } + } +} + impl Display for BinOp { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str(match self { @@ -588,32 +731,32 @@ impl Display for BinOp { } } -impl From for BinOp { - fn from(value: hir_def::expr::ArithOp) -> Self { +impl From for BinOp { + fn from(value: hir_def::hir::ArithOp) -> Self { match value { - hir_def::expr::ArithOp::Add => BinOp::Add, - hir_def::expr::ArithOp::Mul => BinOp::Mul, - hir_def::expr::ArithOp::Sub => BinOp::Sub, - hir_def::expr::ArithOp::Div => BinOp::Div, - hir_def::expr::ArithOp::Rem => BinOp::Rem, - hir_def::expr::ArithOp::Shl => BinOp::Shl, - hir_def::expr::ArithOp::Shr => BinOp::Shr, - hir_def::expr::ArithOp::BitXor => BinOp::BitXor, - hir_def::expr::ArithOp::BitOr => BinOp::BitOr, - hir_def::expr::ArithOp::BitAnd => BinOp::BitAnd, + hir_def::hir::ArithOp::Add => BinOp::Add, + hir_def::hir::ArithOp::Mul => BinOp::Mul, + hir_def::hir::ArithOp::Sub => BinOp::Sub, + hir_def::hir::ArithOp::Div => BinOp::Div, + hir_def::hir::ArithOp::Rem => BinOp::Rem, + hir_def::hir::ArithOp::Shl => BinOp::Shl, + hir_def::hir::ArithOp::Shr => BinOp::Shr, + hir_def::hir::ArithOp::BitXor => BinOp::BitXor, + hir_def::hir::ArithOp::BitOr => BinOp::BitOr, + hir_def::hir::ArithOp::BitAnd => BinOp::BitAnd, } } } -impl From for BinOp { - fn from(value: hir_def::expr::CmpOp) -> Self { +impl From for BinOp { + fn from(value: hir_def::hir::CmpOp) -> Self { match value { - hir_def::expr::CmpOp::Eq { negated: false } => BinOp::Eq, - hir_def::expr::CmpOp::Eq { negated: true } => BinOp::Ne, - hir_def::expr::CmpOp::Ord { ordering: Ordering::Greater, strict: false } => BinOp::Ge, - hir_def::expr::CmpOp::Ord { ordering: Ordering::Greater, strict: true } => BinOp::Gt, - hir_def::expr::CmpOp::Ord { ordering: Ordering::Less, strict: false } => BinOp::Le, - hir_def::expr::CmpOp::Ord { ordering: Ordering::Less, strict: true } => BinOp::Lt, + hir_def::hir::CmpOp::Eq { negated: false } => BinOp::Eq, + hir_def::hir::CmpOp::Eq { negated: true } => BinOp::Ne, + hir_def::hir::CmpOp::Ord { ordering: Ordering::Greater, strict: false } => BinOp::Ge, + hir_def::hir::CmpOp::Ord { ordering: Ordering::Greater, strict: true } => BinOp::Gt, + hir_def::hir::CmpOp::Ord { ordering: Ordering::Less, strict: false } => BinOp::Le, + hir_def::hir::CmpOp::Ord { ordering: Ordering::Less, strict: true } => BinOp::Lt, } } } @@ -642,7 +785,6 @@ pub enum CastKind { FloatToInt, FloatToFloat, IntToFloat, - PtrToPtr, FnPtrToPtr, } @@ -653,13 +795,8 @@ pub enum Rvalue { /// Creates an array where each element is the value of the operand. /// - /// This is the cause of a bug in the case where the repetition count is zero because the value - /// is not dropped, see [#74836]. - /// /// Corresponds to source code like `[x; 32]`. - /// - /// [#74836]: https://github.com/rust-lang/rust/issues/74836 - //Repeat(Operand, ty::Const), + Repeat(Operand, Const), /// Creates a reference of the indicated kind to the place. /// @@ -768,7 +905,7 @@ pub enum Rvalue { /// /// Disallowed after deaggregation for all aggregate kinds except `Array` and `Generator`. After /// generator lowering, `Generator` aggregate kinds are disallowed too. - Aggregate(AggregateKind, Vec), + Aggregate(AggregateKind, Box<[Operand]>), /// Transmutes a `*mut u8` into shallow-initialized `Box`. /// @@ -777,6 +914,9 @@ pub enum Rvalue { /// affects alias analysis. ShallowInitBox(Operand, Ty), + /// NON STANDARD: allocates memory with the type's layout, and shallow init the box with the resulting pointer. + ShallowInitBoxWithAlloc(Ty), + /// A CopyForDeref is equivalent to a read from a place at the /// codegen level, but is treated specially by drop elaboration. When such a read happens, it /// is guaranteed (via nature of the mir_opt `Derefer` in rustc_mir_transform/src/deref_separator) @@ -816,7 +956,7 @@ pub struct Statement { pub span: MirSpan, } -#[derive(Debug, Default, PartialEq, Eq)] +#[derive(Debug, Default, Clone, PartialEq, Eq)] pub struct BasicBlock { /// List of statements in this block. pub statements: Vec, @@ -838,19 +978,118 @@ pub struct BasicBlock { pub is_cleanup: bool, } -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct MirBody { pub basic_blocks: Arena, pub locals: Arena, pub start_block: BasicBlockId, pub owner: DefWithBodyId, - pub arg_count: usize, pub binding_locals: ArenaMap, pub param_locals: Vec, + /// This field stores the closures directly owned by this body. It is used + /// in traversing every mir body. + pub closures: Vec, } -fn const_as_usize(c: &Const) -> usize { - try_const_usize(c).unwrap() as usize +impl MirBody { + fn walk_places(&mut self, mut f: impl FnMut(&mut Place)) { + fn for_operand(op: &mut Operand, f: &mut impl FnMut(&mut Place)) { + match op { + Operand::Copy(p) | Operand::Move(p) => { + f(p); + } + Operand::Constant(_) | Operand::Static(_) => (), + } + } + for (_, block) in self.basic_blocks.iter_mut() { + for statement in &mut block.statements { + match &mut statement.kind { + StatementKind::Assign(p, r) => { + f(p); + match r { + Rvalue::ShallowInitBoxWithAlloc(_) => (), + Rvalue::ShallowInitBox(o, _) + | Rvalue::UnaryOp(_, o) + | Rvalue::Cast(_, o, _) + | Rvalue::Repeat(o, _) + | Rvalue::Use(o) => for_operand(o, &mut f), + Rvalue::CopyForDeref(p) + | Rvalue::Discriminant(p) + | Rvalue::Len(p) + | Rvalue::Ref(_, p) => f(p), + Rvalue::CheckedBinaryOp(_, o1, o2) => { + for_operand(o1, &mut f); + for_operand(o2, &mut f); + } + Rvalue::Aggregate(_, ops) => { + for op in ops.iter_mut() { + for_operand(op, &mut f); + } + } + } + } + StatementKind::Deinit(p) => f(p), + StatementKind::StorageLive(_) + | StatementKind::StorageDead(_) + | StatementKind::Nop => (), + } + } + match &mut block.terminator { + Some(x) => match &mut x.kind { + TerminatorKind::SwitchInt { discr, .. } => for_operand(discr, &mut f), + TerminatorKind::FalseEdge { .. } + | TerminatorKind::FalseUnwind { .. } + | TerminatorKind::Goto { .. } + | TerminatorKind::Resume + | TerminatorKind::GeneratorDrop + | TerminatorKind::Abort + | TerminatorKind::Return + | TerminatorKind::Unreachable => (), + TerminatorKind::Drop { place, .. } => { + f(place); + } + TerminatorKind::DropAndReplace { place, value, .. } => { + f(place); + for_operand(value, &mut f); + } + TerminatorKind::Call { func, args, destination, .. } => { + for_operand(func, &mut f); + args.iter_mut().for_each(|x| for_operand(x, &mut f)); + f(destination); + } + TerminatorKind::Assert { cond, .. } => { + for_operand(cond, &mut f); + } + TerminatorKind::Yield { value, resume_arg, .. } => { + for_operand(value, &mut f); + f(resume_arg); + } + }, + None => (), + } + } + } + + fn shrink_to_fit(&mut self) { + let MirBody { + basic_blocks, + locals, + start_block: _, + owner: _, + binding_locals, + param_locals, + closures, + } = self; + basic_blocks.shrink_to_fit(); + locals.shrink_to_fit(); + binding_locals.shrink_to_fit(); + param_locals.shrink_to_fit(); + closures.shrink_to_fit(); + for (_, b) in basic_blocks.iter_mut() { + let BasicBlock { statements, terminator: _, is_cleanup: _ } = b; + statements.shrink_to_fit(); + } + } } #[derive(Debug, PartialEq, Eq, Clone, Copy)] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs index c8729af86a9ea..a0ea1cc5ef7d9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs @@ -3,17 +3,20 @@ // Currently it is an ad-hoc implementation, only useful for mutability analysis. Feel free to remove all of these // if needed for implementing a proper borrow checker. -use std::sync::Arc; +use std::iter; -use hir_def::DefWithBodyId; +use hir_def::{DefWithBodyId, HasModule}; use la_arena::ArenaMap; use stdx::never; +use triomphe::Arc; -use crate::db::HirDatabase; +use crate::{ + db::HirDatabase, mir::Operand, utils::ClosureSubst, ClosureId, Interner, Ty, TyExt, TypeFlags, +}; use super::{ BasicBlockId, BorrowKind, LocalId, MirBody, MirLowerError, MirSpan, Place, ProjectionElem, - Rvalue, StatementKind, Terminator, + Rvalue, StatementKind, TerminatorKind, }; #[derive(Debug, Clone, PartialEq, Eq)] @@ -23,26 +26,167 @@ pub enum MutabilityReason { Not, } +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct MovedOutOfRef { + pub ty: Ty, + pub span: MirSpan, +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct BorrowckResult { pub mir_body: Arc, pub mutability_of_locals: ArenaMap, + pub moved_out_of_ref: Vec, +} + +fn all_mir_bodies( + db: &dyn HirDatabase, + def: DefWithBodyId, +) -> Box, MirLowerError>> + '_> { + fn for_closure( + db: &dyn HirDatabase, + c: ClosureId, + ) -> Box, MirLowerError>> + '_> { + match db.mir_body_for_closure(c) { + Ok(body) => { + let closures = body.closures.clone(); + Box::new( + iter::once(Ok(body)) + .chain(closures.into_iter().flat_map(|x| for_closure(db, x))), + ) + } + Err(e) => Box::new(iter::once(Err(e))), + } + } + match db.mir_body(def) { + Ok(body) => { + let closures = body.closures.clone(); + Box::new( + iter::once(Ok(body)).chain(closures.into_iter().flat_map(|x| for_closure(db, x))), + ) + } + Err(e) => Box::new(iter::once(Err(e))), + } } pub fn borrowck_query( db: &dyn HirDatabase, def: DefWithBodyId, -) -> Result, MirLowerError> { +) -> Result, MirLowerError> { let _p = profile::span("borrowck_query"); - let body = db.mir_body(def)?; - let r = BorrowckResult { mutability_of_locals: mutability_of_locals(&body), mir_body: body }; - Ok(Arc::new(r)) + let r = all_mir_bodies(db, def) + .map(|body| { + let body = body?; + Ok(BorrowckResult { + mutability_of_locals: mutability_of_locals(db, &body), + moved_out_of_ref: moved_out_of_ref(db, &body), + mir_body: body, + }) + }) + .collect::, MirLowerError>>()?; + Ok(r.into()) } -fn is_place_direct(lvalue: &Place) -> bool { - !lvalue.projection.iter().any(|x| *x == ProjectionElem::Deref) +fn moved_out_of_ref(db: &dyn HirDatabase, body: &MirBody) -> Vec { + let mut result = vec![]; + let mut for_operand = |op: &Operand, span: MirSpan| match op { + Operand::Copy(p) | Operand::Move(p) => { + let mut ty: Ty = body.locals[p.local].ty.clone(); + let mut is_dereference_of_ref = false; + for proj in &*p.projection { + if *proj == ProjectionElem::Deref && ty.as_reference().is_some() { + is_dereference_of_ref = true; + } + ty = proj.projected_ty( + ty, + db, + |c, subst, f| { + let (def, _) = db.lookup_intern_closure(c.into()); + let infer = db.infer(def); + let (captures, _) = infer.closure_info(&c); + let parent_subst = ClosureSubst(subst).parent_subst(); + captures + .get(f) + .expect("broken closure field") + .ty + .clone() + .substitute(Interner, parent_subst) + }, + body.owner.module(db.upcast()).krate(), + ); + } + if is_dereference_of_ref + && !ty.clone().is_copy(db, body.owner) + && !ty.data(Interner).flags.intersects(TypeFlags::HAS_ERROR) + { + result.push(MovedOutOfRef { span, ty }); + } + } + Operand::Constant(_) | Operand::Static(_) => (), + }; + for (_, block) in body.basic_blocks.iter() { + for statement in &block.statements { + match &statement.kind { + StatementKind::Assign(_, r) => match r { + Rvalue::ShallowInitBoxWithAlloc(_) => (), + Rvalue::ShallowInitBox(o, _) + | Rvalue::UnaryOp(_, o) + | Rvalue::Cast(_, o, _) + | Rvalue::Repeat(o, _) + | Rvalue::Use(o) => for_operand(o, statement.span), + Rvalue::CopyForDeref(_) + | Rvalue::Discriminant(_) + | Rvalue::Len(_) + | Rvalue::Ref(_, _) => (), + Rvalue::CheckedBinaryOp(_, o1, o2) => { + for_operand(o1, statement.span); + for_operand(o2, statement.span); + } + Rvalue::Aggregate(_, ops) => { + for op in ops.iter() { + for_operand(op, statement.span); + } + } + }, + StatementKind::Deinit(_) + | StatementKind::StorageLive(_) + | StatementKind::StorageDead(_) + | StatementKind::Nop => (), + } + } + match &block.terminator { + Some(terminator) => match &terminator.kind { + TerminatorKind::SwitchInt { discr, .. } => for_operand(discr, terminator.span), + TerminatorKind::FalseEdge { .. } + | TerminatorKind::FalseUnwind { .. } + | TerminatorKind::Goto { .. } + | TerminatorKind::Resume + | TerminatorKind::GeneratorDrop + | TerminatorKind::Abort + | TerminatorKind::Return + | TerminatorKind::Unreachable + | TerminatorKind::Drop { .. } => (), + TerminatorKind::DropAndReplace { value, .. } => { + for_operand(value, terminator.span); + } + TerminatorKind::Call { func, args, .. } => { + for_operand(func, terminator.span); + args.iter().for_each(|x| for_operand(x, terminator.span)); + } + TerminatorKind::Assert { cond, .. } => { + for_operand(cond, terminator.span); + } + TerminatorKind::Yield { value, .. } => { + for_operand(value, terminator.span); + } + }, + None => (), + } + } + result } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] enum ProjectionCase { /// Projection is a local Direct, @@ -52,20 +196,39 @@ enum ProjectionCase { Indirect, } -fn place_case(lvalue: &Place) -> ProjectionCase { +fn place_case(db: &dyn HirDatabase, body: &MirBody, lvalue: &Place) -> ProjectionCase { let mut is_part_of = false; - for proj in lvalue.projection.iter().rev() { + let mut ty = body.locals[lvalue.local].ty.clone(); + for proj in lvalue.projection.iter() { match proj { - ProjectionElem::Deref => return ProjectionCase::Indirect, // It's indirect - ProjectionElem::ConstantIndex { .. } + ProjectionElem::Deref if ty.as_adt().is_none() => return ProjectionCase::Indirect, // It's indirect in case of reference and raw + ProjectionElem::Deref // It's direct in case of `Box` + | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } | ProjectionElem::Field(_) - | ProjectionElem::TupleField(_) + | ProjectionElem::TupleOrClosureField(_) | ProjectionElem::Index(_) => { is_part_of = true; } ProjectionElem::OpaqueCast(_) => (), } + ty = proj.projected_ty( + ty, + db, + |c, subst, f| { + let (def, _) = db.lookup_intern_closure(c.into()); + let infer = db.infer(def); + let (captures, _) = infer.closure_info(&c); + let parent_subst = ClosureSubst(subst).parent_subst(); + captures + .get(f) + .expect("broken closure field") + .ty + .clone() + .substitute(Interner, parent_subst) + }, + body.owner.module(db.upcast()).krate(), + ); } if is_part_of { ProjectionCase::DirectPart @@ -76,7 +239,7 @@ fn place_case(lvalue: &Place) -> ProjectionCase { /// Returns a map from basic blocks to the set of locals that might be ever initialized before /// the start of the block. Only `StorageDead` can remove something from this map, and we ignore -/// `Uninit` and `drop` and similars after initialization. +/// `Uninit` and `drop` and similar after initialization. fn ever_initialized_map(body: &MirBody) -> ArenaMap> { let mut result: ArenaMap> = body.basic_blocks.iter().map(|x| (x.0, ArenaMap::default())).collect(); @@ -107,26 +270,28 @@ fn ever_initialized_map(body: &MirBody) -> ArenaMap vec![*target], - Terminator::SwitchInt { targets, .. } => targets.all_targets().to_vec(), - Terminator::Resume - | Terminator::Abort - | Terminator::Return - | Terminator::Unreachable => vec![], - Terminator::Call { target, cleanup, destination, .. } => { + let targets = match &terminator.kind { + TerminatorKind::Goto { target } => vec![*target], + TerminatorKind::SwitchInt { targets, .. } => targets.all_targets().to_vec(), + TerminatorKind::Resume + | TerminatorKind::Abort + | TerminatorKind::Return + | TerminatorKind::Unreachable => vec![], + TerminatorKind::Call { target, cleanup, destination, .. } => { if destination.projection.len() == 0 && destination.local == l { is_ever_initialized = true; } target.into_iter().chain(cleanup.into_iter()).copied().collect() } - Terminator::Drop { .. } - | Terminator::DropAndReplace { .. } - | Terminator::Assert { .. } - | Terminator::Yield { .. } - | Terminator::GeneratorDrop - | Terminator::FalseEdge { .. } - | Terminator::FalseUnwind { .. } => { + TerminatorKind::Drop { target, unwind, place: _ } => { + Some(target).into_iter().chain(unwind.into_iter()).copied().collect() + } + TerminatorKind::DropAndReplace { .. } + | TerminatorKind::Assert { .. } + | TerminatorKind::Yield { .. } + | TerminatorKind::GeneratorDrop + | TerminatorKind::FalseEdge { .. } + | TerminatorKind::FalseUnwind { .. } => { never!("We don't emit these MIR terminators yet"); vec![] } @@ -151,7 +316,10 @@ fn ever_initialized_map(body: &MirBody) -> ArenaMap ArenaMap { +fn mutability_of_locals( + db: &dyn HirDatabase, + body: &MirBody, +) -> ArenaMap { let mut result: ArenaMap = body.locals.iter().map(|x| (x.0, MutabilityReason::Not)).collect(); let mut push_mut_span = |local, span| match &mut result[local] { @@ -164,7 +332,7 @@ fn mutability_of_locals(body: &MirBody) -> ArenaMap { for statement in &block.statements { match &statement.kind { StatementKind::Assign(place, value) => { - match place_case(place) { + match place_case(db, body, place) { ProjectionCase::Direct => { if ever_init_map.get(place.local).copied().unwrap_or_default() { push_mut_span(place.local, statement.span); @@ -179,7 +347,7 @@ fn mutability_of_locals(body: &MirBody) -> ArenaMap { ProjectionCase::Indirect => (), } if let Rvalue::Ref(BorrowKind::Mut { .. }, p) = value { - if is_place_direct(p) { + if place_case(db, body, p) != ProjectionCase::Indirect { push_mut_span(p.local, statement.span); } } @@ -194,21 +362,21 @@ fn mutability_of_locals(body: &MirBody) -> ArenaMap { never!("Terminator should be none only in construction"); continue; }; - match terminator { - Terminator::Goto { .. } - | Terminator::Resume - | Terminator::Abort - | Terminator::Return - | Terminator::Unreachable - | Terminator::FalseEdge { .. } - | Terminator::FalseUnwind { .. } - | Terminator::GeneratorDrop - | Terminator::SwitchInt { .. } - | Terminator::Drop { .. } - | Terminator::DropAndReplace { .. } - | Terminator::Assert { .. } - | Terminator::Yield { .. } => (), - Terminator::Call { destination, .. } => { + match &terminator.kind { + TerminatorKind::Goto { .. } + | TerminatorKind::Resume + | TerminatorKind::Abort + | TerminatorKind::Return + | TerminatorKind::Unreachable + | TerminatorKind::FalseEdge { .. } + | TerminatorKind::FalseUnwind { .. } + | TerminatorKind::GeneratorDrop + | TerminatorKind::SwitchInt { .. } + | TerminatorKind::Drop { .. } + | TerminatorKind::DropAndReplace { .. } + | TerminatorKind::Assert { .. } + | TerminatorKind::Yield { .. } => (), + TerminatorKind::Call { destination, .. } => { if destination.projection.len() == 0 { if ever_init_map.get(destination.local).copied().unwrap_or_default() { push_mut_span(destination.local, MirSpan::Unknown); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index c5d843d9ebd89..ce14f6dbad536 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -1,41 +1,134 @@ //! This module provides a MIR interpreter, which is used in const eval. -use std::{borrow::Cow, collections::HashMap, iter}; +use std::{borrow::Cow, collections::HashMap, fmt::Write, iter, ops::Range}; -use base_db::CrateId; -use chalk_ir::{ - fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable}, - DebruijnIndex, TyKind, -}; +use base_db::{CrateId, FileId}; +use chalk_ir::Mutability; +use either::Either; use hir_def::{ builtin_type::BuiltinType, + data::adt::{StructFlags, VariantData}, lang_item::{lang_attr, LangItem}, - layout::{Layout, LayoutError, RustcEnumVariantIdx, TagEncoding, Variants}, - AdtId, DefWithBodyId, EnumVariantId, FunctionId, HasModule, Lookup, VariantId, + layout::{TagEncoding, Variants}, + AdtId, DefWithBodyId, EnumVariantId, FunctionId, HasModule, ItemContainerId, Lookup, StaticId, + VariantId, }; +use hir_expand::InFile; use intern::Interned; use la_arena::ArenaMap; +use rustc_hash::{FxHashMap, FxHashSet}; +use stdx::never; +use syntax::{SyntaxNodePtr, TextRange}; +use triomphe::Arc; use crate::{ - consteval::{intern_const_scalar, ConstEvalError}, + consteval::{intern_const_scalar, try_const_usize, ConstEvalError}, db::HirDatabase, - from_placeholder_idx, - infer::{normalize, PointerCast}, - layout::layout_of_ty, + display::{ClosureStyle, HirDisplay}, + infer::PointerCast, + layout::{Layout, LayoutError, RustcEnumVariantIdx}, mapping::from_chalk, - method_resolution::lookup_impl_method, - CallableDefId, Const, ConstScalar, Interner, MemoryMap, Substitution, Ty, TyBuilder, TyExt, + method_resolution::{is_dyn_method, lookup_impl_method}, + name, static_lifetime, + traits::FnTrait, + utils::{detect_variant_from_bytes, ClosureSubst}, + CallableDefId, ClosureId, Const, ConstScalar, FnDefId, GenericArgData, Interner, MemoryMap, + Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, }; use super::{ - const_as_usize, return_slot, AggregateKind, BinOp, CastKind, LocalId, MirBody, MirLowerError, - Operand, Place, ProjectionElem, Rvalue, StatementKind, Terminator, UnOp, + return_slot, AggregateKind, BinOp, CastKind, LocalId, MirBody, MirLowerError, MirSpan, Operand, + Place, ProjectionElem, Rvalue, StatementKind, TerminatorKind, UnOp, }; +mod shim; +#[cfg(test)] +mod tests; + +macro_rules! from_bytes { + ($ty:tt, $value:expr) => { + ($ty::from_le_bytes(match ($value).try_into() { + Ok(x) => x, + Err(_) => return Err(MirEvalError::TypeError(stringify!(mismatched size in constructing $ty))), + })) + }; +} + +macro_rules! not_supported { + ($x: expr) => { + return Err(MirEvalError::NotSupported(format!($x))) + }; +} + +#[derive(Debug, Default, Clone, PartialEq, Eq)] +pub struct VTableMap { + ty_to_id: FxHashMap, + id_to_ty: Vec, +} + +impl VTableMap { + fn id(&mut self, ty: Ty) -> usize { + if let Some(x) = self.ty_to_id.get(&ty) { + return *x; + } + let id = self.id_to_ty.len(); + self.id_to_ty.push(ty.clone()); + self.ty_to_id.insert(ty, id); + id + } + + fn ty(&self, id: usize) -> Result<&Ty> { + self.id_to_ty.get(id).ok_or(MirEvalError::InvalidVTableId(id)) + } + + fn ty_of_bytes(&self, bytes: &[u8]) -> Result<&Ty> { + let id = from_bytes!(usize, bytes); + self.ty(id) + } +} + +#[derive(Debug, Default, Clone, PartialEq, Eq)] +struct TlsData { + keys: Vec, +} + +impl TlsData { + fn create_key(&mut self) -> usize { + self.keys.push(0); + self.keys.len() - 1 + } + + fn get_key(&mut self, key: usize) -> Result { + let r = self.keys.get(key).ok_or_else(|| { + MirEvalError::UndefinedBehavior(format!("Getting invalid tls key {key}")) + })?; + Ok(*r) + } + + fn set_key(&mut self, key: usize, value: u128) -> Result<()> { + let r = self.keys.get_mut(key).ok_or_else(|| { + MirEvalError::UndefinedBehavior(format!("Setting invalid tls key {key}")) + })?; + *r = value; + Ok(()) + } +} + pub struct Evaluator<'a> { db: &'a dyn HirDatabase, + trait_env: Arc, stack: Vec, heap: Vec, + /// Stores the global location of the statics. We const evaluate every static first time we need it + /// and see it's missing, then we add it to this to reuse. + static_locations: FxHashMap, + /// We don't really have function pointers, i.e. pointers to some assembly instructions that we can run. Instead, we + /// store the type as an interned id in place of function and vtable pointers, and we recover back the type at the + /// time of use. + vtable_map: VTableMap, + thread_local_storage: TlsData, + stdout: Vec, + stderr: Vec, crate_id: CrateId, // FIXME: This is a workaround, see the comment on `interpret_mir` assert_placeholder_ty_is_unused: bool, @@ -45,19 +138,27 @@ pub struct Evaluator<'a> { stack_depth_limit: usize, } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] enum Address { Stack(usize), Heap(usize), + Invalid(usize), } use Address::*; +#[derive(Debug, Clone, Copy)] struct Interval { addr: Address, size: usize, } +#[derive(Debug, Clone)] +struct IntervalAndTy { + interval: Interval, + ty: Ty, +} + impl Interval { fn new(addr: Address, size: usize) -> Self { Self { addr, size } @@ -66,12 +167,49 @@ impl Interval { fn get<'a>(&self, memory: &'a Evaluator<'a>) -> Result<&'a [u8]> { memory.read_memory(self.addr, self.size) } + + fn write_from_bytes(&self, memory: &mut Evaluator<'_>, bytes: &[u8]) -> Result<()> { + memory.write_memory(self.addr, bytes) + } + + fn write_from_interval(&self, memory: &mut Evaluator<'_>, interval: Interval) -> Result<()> { + // FIXME: this could be more efficient + let bytes = &interval.get(memory)?.to_vec(); + memory.write_memory(self.addr, bytes) + } + + fn slice(self, range: Range) -> Interval { + Interval { addr: self.addr.offset(range.start), size: range.len() } + } +} + +impl IntervalAndTy { + fn get<'a>(&self, memory: &'a Evaluator<'a>) -> Result<&'a [u8]> { + memory.read_memory(self.interval.addr, self.interval.size) + } + + fn new( + addr: Address, + ty: Ty, + evaluator: &Evaluator<'_>, + locals: &Locals<'_>, + ) -> Result { + let size = evaluator.size_of_sized(&ty, locals, "type of interval")?; + Ok(IntervalAndTy { interval: Interval { addr, size }, ty }) + } } enum IntervalOrOwned { Owned(Vec), Borrowed(Interval), } + +impl From for IntervalOrOwned { + fn from(it: Interval) -> IntervalOrOwned { + IntervalOrOwned::Borrowed(it) + } +} + impl IntervalOrOwned { pub(crate) fn to_vec(self, memory: &Evaluator<'_>) -> Result> { Ok(match self { @@ -79,15 +217,13 @@ impl IntervalOrOwned { IntervalOrOwned::Borrowed(b) => b.get(memory)?.to_vec(), }) } -} -macro_rules! from_bytes { - ($ty:tt, $value:expr) => { - ($ty::from_le_bytes(match ($value).try_into() { - Ok(x) => x, - Err(_) => return Err(MirEvalError::TypeError("mismatched size")), - })) - }; + fn get<'a>(&'a self, memory: &'a Evaluator<'a>) -> Result<&'a [u8]> { + Ok(match self { + IntervalOrOwned::Owned(o) => o, + IntervalOrOwned::Borrowed(b) => b.get(memory)?, + }) + } } impl Address { @@ -97,9 +233,11 @@ impl Address { fn from_usize(x: usize) -> Self { if x > usize::MAX / 2 { - Stack(usize::MAX - x) + Stack(x - usize::MAX / 2) + } else if x > usize::MAX / 4 { + Heap(x - usize::MAX / 4) } else { - Heap(x) + Invalid(x) } } @@ -109,8 +247,9 @@ impl Address { fn to_usize(&self) -> usize { let as_num = match self { - Stack(x) => usize::MAX - *x, - Heap(x) => *x, + Stack(x) => *x + usize::MAX / 2, + Heap(x) => *x + usize::MAX / 4, + Invalid(x) => *x, }; as_num } @@ -119,6 +258,7 @@ impl Address { match self { Stack(x) => Stack(f(*x)), Heap(x) => Heap(f(*x)), + Invalid(x) => Invalid(f(*x)), } } @@ -129,28 +269,123 @@ impl Address { #[derive(Clone, PartialEq, Eq)] pub enum MirEvalError { - ConstEvalError(Box), + ConstEvalError(String, Box), LayoutError(LayoutError, Ty), /// Means that code had type errors (or mismatched args) and we shouldn't generate mir in first place. TypeError(&'static str), /// Means that code had undefined behavior. We don't try to actively detect UB, but if it was detected /// then use this type of error. - UndefinedBehavior(&'static str), - Panic, + UndefinedBehavior(String), + Panic(String), MirLowerError(FunctionId, MirLowerError), + MirLowerErrorForClosure(ClosureId, MirLowerError), TypeIsUnsized(Ty, &'static str), NotSupported(String), InvalidConst(Const), - InFunction(FunctionId, Box), + InFunction(Either, Box, MirSpan, DefWithBodyId), ExecutionLimitExceeded, StackOverflow, TargetDataLayoutNotAvailable, + InvalidVTableId(usize), + CoerceUnsizedError(Ty), + LangItemNotFound(LangItem), +} + +impl MirEvalError { + pub fn pretty_print( + &self, + f: &mut String, + db: &dyn HirDatabase, + span_formatter: impl Fn(FileId, TextRange) -> String, + ) -> std::result::Result<(), std::fmt::Error> { + writeln!(f, "Mir eval error:")?; + let mut err = self; + while let MirEvalError::InFunction(func, e, span, def) = err { + err = e; + match func { + Either::Left(func) => { + let function_name = db.function_data(*func); + writeln!( + f, + "In function {} ({:?})", + function_name.name.display(db.upcast()), + func + )?; + } + Either::Right(clos) => { + writeln!(f, "In {:?}", clos)?; + } + } + let source_map = db.body_with_source_map(*def).1; + let span: InFile = match span { + MirSpan::ExprId(e) => match source_map.expr_syntax(*e) { + Ok(s) => s.map(|x| x.into()), + Err(_) => continue, + }, + MirSpan::PatId(p) => match source_map.pat_syntax(*p) { + Ok(s) => s.map(|x| match x { + Either::Left(e) => e.into(), + Either::Right(e) => e.into(), + }), + Err(_) => continue, + }, + MirSpan::Unknown => continue, + }; + let file_id = span.file_id.original_file(db.upcast()); + let text_range = span.value.text_range(); + writeln!(f, "{}", span_formatter(file_id, text_range))?; + } + match err { + MirEvalError::InFunction(..) => unreachable!(), + MirEvalError::LayoutError(err, ty) => { + write!( + f, + "Layout for type `{}` is not available due {err:?}", + ty.display(db).with_closure_style(ClosureStyle::ClosureWithId).to_string() + )?; + } + MirEvalError::MirLowerError(func, err) => { + let function_name = db.function_data(*func); + writeln!( + f, + "MIR lowering for function `{}` ({:?}) failed due:", + function_name.name.display(db.upcast()), + func + )?; + err.pretty_print(f, db, span_formatter)?; + } + MirEvalError::ConstEvalError(name, err) => { + MirLowerError::ConstEvalError(name.clone(), err.clone()).pretty_print( + f, + db, + span_formatter, + )?; + } + MirEvalError::TypeError(_) + | MirEvalError::UndefinedBehavior(_) + | MirEvalError::Panic(_) + | MirEvalError::MirLowerErrorForClosure(_, _) + | MirEvalError::TypeIsUnsized(_, _) + | MirEvalError::NotSupported(_) + | MirEvalError::InvalidConst(_) + | MirEvalError::ExecutionLimitExceeded + | MirEvalError::StackOverflow + | MirEvalError::TargetDataLayoutNotAvailable + | MirEvalError::CoerceUnsizedError(_) + | MirEvalError::LangItemNotFound(_) + | MirEvalError::InvalidVTableId(_) => writeln!(f, "{:?}", err)?, + } + Ok(()) + } } impl std::fmt::Debug for MirEvalError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Self::ConstEvalError(arg0) => f.debug_tuple("ConstEvalError").field(arg0).finish(), + Self::ConstEvalError(arg0, arg1) => { + f.debug_tuple("ConstEvalError").field(arg0).field(arg1).finish() + } + Self::LangItemNotFound(arg0) => f.debug_tuple("LangItemNotFound").field(arg0).finish(), Self::LayoutError(arg0, arg1) => { f.debug_tuple("LayoutError").field(arg0).field(arg1).finish() } @@ -158,7 +393,7 @@ impl std::fmt::Debug for MirEvalError { Self::UndefinedBehavior(arg0) => { f.debug_tuple("UndefinedBehavior").field(arg0).finish() } - Self::Panic => write!(f, "Panic"), + Self::Panic(msg) => write!(f, "Panic with message:\n{msg:?}"), Self::TargetDataLayoutNotAvailable => write!(f, "TargetDataLayoutNotAvailable"), Self::TypeIsUnsized(ty, it) => write!(f, "{ty:?} is unsized. {it} should be sized."), Self::ExecutionLimitExceeded => write!(f, "execution limit exceeded"), @@ -166,17 +401,24 @@ impl std::fmt::Debug for MirEvalError { Self::MirLowerError(arg0, arg1) => { f.debug_tuple("MirLowerError").field(arg0).field(arg1).finish() } + Self::MirLowerErrorForClosure(arg0, arg1) => { + f.debug_tuple("MirLowerError").field(arg0).field(arg1).finish() + } + Self::CoerceUnsizedError(arg0) => { + f.debug_tuple("CoerceUnsizedError").field(arg0).finish() + } + Self::InvalidVTableId(arg0) => f.debug_tuple("InvalidVTableId").field(arg0).finish(), Self::NotSupported(arg0) => f.debug_tuple("NotSupported").field(arg0).finish(), Self::InvalidConst(arg0) => { let data = &arg0.data(Interner); f.debug_struct("InvalidConst").field("ty", &data.ty).field("value", &arg0).finish() } - Self::InFunction(func, e) => { + Self::InFunction(func, e, span, _) => { let mut e = &**e; - let mut stack = vec![*func]; - while let Self::InFunction(f, next_e) = e { + let mut stack = vec![(*func, *span)]; + while let Self::InFunction(f, next_e, span, _) = e { e = &next_e; - stack.push(*f); + stack.push((*f, *span)); } f.debug_struct("WithStack").field("error", e).field("stack", &stack).finish() } @@ -184,26 +426,33 @@ impl std::fmt::Debug for MirEvalError { } } -macro_rules! not_supported { - ($x: expr) => { - return Err(MirEvalError::NotSupported(format!($x))) - }; +type Result = std::result::Result; + +#[derive(Debug, Default)] +struct DropFlags { + need_drop: FxHashSet, } -impl From for MirEvalError { - fn from(value: ConstEvalError) -> Self { - match value { - _ => MirEvalError::ConstEvalError(Box::new(value)), +impl DropFlags { + fn add_place(&mut self, p: Place) { + if p.iterate_over_parents().any(|x| self.need_drop.contains(&x)) { + return; } + self.need_drop.retain(|x| !p.is_parent(x)); + self.need_drop.insert(p); } -} -type Result = std::result::Result; + fn remove_place(&mut self, p: &Place) -> bool { + // FIXME: replace parents with parts + self.need_drop.remove(p) + } +} +#[derive(Debug)] struct Locals<'a> { - ptr: &'a ArenaMap, + ptr: &'a ArenaMap, body: &'a MirBody, - subst: &'a Substitution, + drop_flags: DropFlags, } pub fn interpret_mir( @@ -215,38 +464,65 @@ pub fn interpret_mir( // a zero size, hoping that they are all outside of our current body. Even without a fix for #7434, we can // (and probably should) do better here, for example by excluding bindings outside of the target expression. assert_placeholder_ty_is_unused: bool, -) -> Result { +) -> (Result, String, String) { let ty = body.locals[return_slot()].ty.clone(); - let mut evaluator = - Evaluator::new(db, body.owner.module(db.upcast()).krate(), assert_placeholder_ty_is_unused); - let bytes = evaluator.interpret_mir_with_no_arg(&body)?; - let memory_map = evaluator.create_memory_map( - &bytes, - &ty, - &Locals { ptr: &ArenaMap::new(), body: &body, subst: &Substitution::empty(Interner) }, - )?; - return Ok(intern_const_scalar(ConstScalar::Bytes(bytes, memory_map), ty)); + let mut evaluator = Evaluator::new(db, body, assert_placeholder_ty_is_unused); + let x: Result = (|| { + let bytes = evaluator.interpret_mir(&body, None.into_iter())?; + let mut memory_map = evaluator.create_memory_map( + &bytes, + &ty, + &Locals { ptr: &ArenaMap::new(), body: &body, drop_flags: DropFlags::default() }, + )?; + memory_map.vtable = evaluator.vtable_map.clone(); + return Ok(intern_const_scalar(ConstScalar::Bytes(bytes, memory_map), ty)); + })(); + ( + x, + String::from_utf8_lossy(&evaluator.stdout).into_owned(), + String::from_utf8_lossy(&evaluator.stderr).into_owned(), + ) } impl Evaluator<'_> { pub fn new<'a>( db: &'a dyn HirDatabase, - crate_id: CrateId, + body: &MirBody, assert_placeholder_ty_is_unused: bool, ) -> Evaluator<'a> { + let crate_id = body.owner.module(db.upcast()).krate(); + let trait_env = db.trait_environment_for_body(body.owner); Evaluator { stack: vec![0], heap: vec![0], + vtable_map: VTableMap::default(), + thread_local_storage: TlsData::default(), + static_locations: HashMap::default(), db, + trait_env, crate_id, + stdout: vec![], + stderr: vec![], assert_placeholder_ty_is_unused, stack_depth_limit: 100, - execution_limit: 100_000, + execution_limit: 1000_000, } } fn place_addr(&self, p: &Place, locals: &Locals<'_>) -> Result
{ - Ok(self.place_addr_and_ty(p, locals)?.0) + Ok(self.place_addr_and_ty_and_metadata(p, locals)?.0) + } + + fn place_interval(&self, p: &Place, locals: &Locals<'_>) -> Result { + let place_addr_and_ty = self.place_addr_and_ty_and_metadata(p, locals)?; + Ok(Interval { + addr: place_addr_and_ty.0, + size: self.size_of_sized( + &place_addr_and_ty.1, + locals, + "Type of place that we need its interval", + )?, + }) } fn ptr_size(&self) -> usize { @@ -256,125 +532,170 @@ impl Evaluator<'_> { } } - fn place_addr_and_ty<'a>(&'a self, p: &Place, locals: &'a Locals<'a>) -> Result<(Address, Ty)> { - let mut addr = locals.ptr[p.local]; - let mut ty: Ty = - self.ty_filler(&locals.body.locals[p.local].ty, locals.subst, locals.body.owner)?; - for proj in &p.projection { + fn place_addr_and_ty_and_metadata<'a>( + &'a self, + p: &Place, + locals: &'a Locals<'a>, + ) -> Result<(Address, Ty, Option)> { + let mut addr = locals.ptr[p.local].addr; + let mut ty: Ty = locals.body.locals[p.local].ty.clone(); + let mut metadata: Option = None; // locals are always sized + for proj in &*p.projection { + let prev_ty = ty.clone(); + ty = proj.projected_ty( + ty, + self.db, + |c, subst, f| { + let (def, _) = self.db.lookup_intern_closure(c.into()); + let infer = self.db.infer(def); + let (captures, _) = infer.closure_info(&c); + let parent_subst = ClosureSubst(subst).parent_subst(); + captures + .get(f) + .expect("broken closure field") + .ty + .clone() + .substitute(Interner, parent_subst) + }, + self.crate_id, + ); match proj { ProjectionElem::Deref => { - ty = match &ty.data(Interner).kind { - TyKind::Raw(_, inner) | TyKind::Ref(_, _, inner) => inner.clone(), - _ => { - return Err(MirEvalError::TypeError( - "Overloaded deref in MIR is disallowed", - )) - } + metadata = if self.size_align_of(&ty, locals)?.is_none() { + Some( + Interval { addr: addr.offset(self.ptr_size()), size: self.ptr_size() } + .into(), + ) + } else { + None }; let x = from_bytes!(usize, self.read_memory(addr, self.ptr_size())?); addr = Address::from_usize(x); } ProjectionElem::Index(op) => { - let offset = - from_bytes!(usize, self.read_memory(locals.ptr[*op], self.ptr_size())?); - match &ty.data(Interner).kind { - TyKind::Ref(_, _, inner) => match &inner.data(Interner).kind { - TyKind::Slice(inner) => { - ty = inner.clone(); - let ty_size = self.size_of_sized( - &ty, - locals, - "slice inner type should be sized", - )?; - let value = self.read_memory(addr, self.ptr_size() * 2)?; - addr = Address::from_bytes(&value[0..8])?.offset(ty_size * offset); - } - x => not_supported!("MIR index for ref type {x:?}"), - }, - TyKind::Array(inner, _) | TyKind::Slice(inner) => { - ty = inner.clone(); - let ty_size = self.size_of_sized( - &ty, - locals, - "array inner type should be sized", - )?; - addr = addr.offset(ty_size * offset); - } - x => not_supported!("MIR index for type {x:?}"), - } + let offset = from_bytes!( + usize, + self.read_memory(locals.ptr[*op].addr, self.ptr_size())? + ); + metadata = None; // Result of index is always sized + let ty_size = + self.size_of_sized(&ty, locals, "array inner type should be sized")?; + addr = addr.offset(ty_size * offset); } - &ProjectionElem::TupleField(f) => match &ty.data(Interner).kind { - TyKind::Tuple(_, subst) => { - let layout = self.layout(&ty)?; - ty = subst - .as_slice(Interner) - .get(f) - .ok_or(MirEvalError::TypeError("not enough tuple fields"))? - .assert_ty_ref(Interner) - .clone(); - let offset = layout.fields.offset(f).bytes_usize(); - addr = addr.offset(offset); - } - _ => return Err(MirEvalError::TypeError("Only tuple has tuple fields")), - }, - ProjectionElem::Field(f) => match &ty.data(Interner).kind { - TyKind::Adt(adt, subst) => { - let layout = self.layout_adt(adt.0, subst.clone())?; - let variant_layout = match &layout.variants { - Variants::Single { .. } => &layout, - Variants::Multiple { variants, .. } => { - &variants[match f.parent { - hir_def::VariantId::EnumVariantId(x) => { - RustcEnumVariantIdx(x.local_id) - } - _ => { - return Err(MirEvalError::TypeError( - "Multivariant layout only happens for enums", - )) - } - }] - } + &ProjectionElem::ConstantIndex { from_end, offset } => { + let offset = if from_end { + let len = match prev_ty.kind(Interner) { + TyKind::Array(_, c) => match try_const_usize(self.db, c) { + Some(x) => x as u64, + None => { + not_supported!("indexing array with unknown const from end") + } + }, + TyKind::Slice(_) => match metadata { + Some(x) => from_bytes!(u64, x.get(self)?), + None => not_supported!("slice place without metadata"), + }, + _ => not_supported!("bad type for const index"), }; - ty = self.db.field_types(f.parent)[f.local_id] - .clone() - .substitute(Interner, subst); - let offset = variant_layout - .fields - .offset(u32::from(f.local_id.into_raw()) as usize) - .bytes_usize(); - addr = addr.offset(offset); - } - _ => return Err(MirEvalError::TypeError("Only adt has fields")), - }, - ProjectionElem::ConstantIndex { .. } => { - not_supported!("constant index") + (len - offset - 1) as usize + } else { + offset as usize + }; + metadata = None; // Result of index is always sized + let ty_size = + self.size_of_sized(&ty, locals, "array inner type should be sized")?; + addr = addr.offset(ty_size * offset); + } + &ProjectionElem::Subslice { from, to } => { + let inner_ty = match &ty.data(Interner).kind { + TyKind::Array(inner, _) | TyKind::Slice(inner) => inner.clone(), + _ => TyKind::Error.intern(Interner), + }; + metadata = match metadata { + Some(x) => { + let prev_len = from_bytes!(u64, x.get(self)?); + Some(IntervalOrOwned::Owned( + (prev_len - from - to).to_le_bytes().to_vec(), + )) + } + None => None, + }; + let ty_size = + self.size_of_sized(&inner_ty, locals, "array inner type should be sized")?; + addr = addr.offset(ty_size * (from as usize)); + } + &ProjectionElem::TupleOrClosureField(f) => { + let layout = self.layout(&prev_ty)?; + let offset = layout.fields.offset(f).bytes_usize(); + addr = addr.offset(offset); + metadata = None; // tuple field is always sized + } + ProjectionElem::Field(f) => { + let layout = self.layout(&prev_ty)?; + let variant_layout = match &layout.variants { + Variants::Single { .. } => &layout, + Variants::Multiple { variants, .. } => { + &variants[match f.parent { + hir_def::VariantId::EnumVariantId(x) => { + RustcEnumVariantIdx(x.local_id) + } + _ => { + return Err(MirEvalError::TypeError( + "Multivariant layout only happens for enums", + )) + } + }] + } + }; + let offset = variant_layout + .fields + .offset(u32::from(f.local_id.into_raw()) as usize) + .bytes_usize(); + addr = addr.offset(offset); + // FIXME: support structs with unsized fields + metadata = None; } - ProjectionElem::Subslice { .. } => not_supported!("subslice"), ProjectionElem::OpaqueCast(_) => not_supported!("opaque cast"), } } - Ok((addr, ty)) + Ok((addr, ty, metadata)) } - fn layout(&self, ty: &Ty) -> Result { - layout_of_ty(self.db, ty, self.crate_id) + fn layout(&self, ty: &Ty) -> Result> { + self.db + .layout_of_ty(ty.clone(), self.crate_id) .map_err(|e| MirEvalError::LayoutError(e, ty.clone())) } - fn layout_adt(&self, adt: AdtId, subst: Substitution) -> Result { - self.db.layout_of_adt(adt, subst.clone()).map_err(|e| { + fn layout_adt(&self, adt: AdtId, subst: Substitution) -> Result> { + self.db.layout_of_adt(adt, subst.clone(), self.crate_id).map_err(|e| { MirEvalError::LayoutError(e, TyKind::Adt(chalk_ir::AdtId(adt), subst).intern(Interner)) }) } fn place_ty<'a>(&'a self, p: &Place, locals: &'a Locals<'a>) -> Result { - Ok(self.place_addr_and_ty(p, locals)?.1) + Ok(self.place_addr_and_ty_and_metadata(p, locals)?.1) } - fn operand_ty<'a>(&'a self, o: &'a Operand, locals: &'a Locals<'a>) -> Result { + fn operand_ty(&self, o: &Operand, locals: &Locals<'_>) -> Result { Ok(match o { Operand::Copy(p) | Operand::Move(p) => self.place_ty(p, locals)?, Operand::Constant(c) => c.data(Interner).ty.clone(), + &Operand::Static(s) => { + let ty = self.db.infer(s.into())[self.db.body(s.into()).body_expr].clone(); + TyKind::Ref(Mutability::Not, static_lifetime(), ty).intern(Interner) + } + }) + } + + fn operand_ty_and_eval( + &mut self, + o: &Operand, + locals: &mut Locals<'_>, + ) -> Result { + Ok(IntervalAndTy { + interval: self.eval_operand(o, locals)?, + ty: self.operand_ty(o, locals)?, }) } @@ -382,7 +703,6 @@ impl Evaluator<'_> { &mut self, body: &MirBody, args: impl Iterator>, - subst: Substitution, ) -> Result> { if let Some(x) = self.stack_depth_limit.checked_sub(1) { self.stack_depth_limit = x; @@ -390,7 +710,8 @@ impl Evaluator<'_> { return Err(MirEvalError::StackOverflow); } let mut current_block_idx = body.start_block; - let mut locals = Locals { ptr: &ArenaMap::new(), body: &body, subst: &subst }; + let mut locals = + Locals { ptr: &ArenaMap::new(), body: &body, drop_flags: DropFlags::default() }; let (locals_ptr, stack_size) = { let mut stack_ptr = self.stack.len(); let addr = body @@ -401,7 +722,7 @@ impl Evaluator<'_> { self.size_of_sized(&x.ty, &locals, "no unsized local in extending stack")?; let my_ptr = stack_ptr; stack_ptr += size; - Ok((id, Stack(my_ptr))) + Ok((id, Interval { addr: Stack(my_ptr), size })) }) .collect::>>()?; let stack_size = stack_ptr - self.stack.len(); @@ -409,9 +730,10 @@ impl Evaluator<'_> { }; locals.ptr = &locals_ptr; self.stack.extend(iter::repeat(0).take(stack_size)); - let mut remain_args = body.arg_count; - for ((_, addr), value) in locals_ptr.iter().skip(1).zip(args) { - self.write_memory(*addr, &value)?; + let mut remain_args = body.param_locals.len(); + for ((l, interval), value) in locals_ptr.iter().skip(1).zip(args) { + locals.drop_flags.add_place(l.into()); + interval.write_from_bytes(self, &value)?; if remain_args == 0 { return Err(MirEvalError::TypeError("more arguments provided")); } @@ -431,8 +753,9 @@ impl Evaluator<'_> { match &statement.kind { StatementKind::Assign(l, r) => { let addr = self.place_addr(l, &locals)?; - let result = self.eval_rvalue(r, &locals)?.to_vec(&self)?; + let result = self.eval_rvalue(r, &mut locals)?.to_vec(&self)?; self.write_memory(addr, &result)?; + locals.drop_flags.add_place(l.clone()); } StatementKind::Deinit(_) => not_supported!("de-init statement"), StatementKind::StorageLive(_) @@ -443,11 +766,11 @@ impl Evaluator<'_> { let Some(terminator) = current_block.terminator.as_ref() else { not_supported!("block without terminator"); }; - match terminator { - Terminator::Goto { target } => { + match &terminator.kind { + TerminatorKind::Goto { target } => { current_block_idx = *target; } - Terminator::Call { + TerminatorKind::Call { func, args, destination, @@ -455,155 +778,84 @@ impl Evaluator<'_> { cleanup: _, from_hir_call: _, } => { + let destination_interval = self.place_interval(destination, &locals)?; let fn_ty = self.operand_ty(func, &locals)?; + let args = args + .iter() + .map(|x| self.operand_ty_and_eval(x, &mut locals)) + .collect::>>()?; match &fn_ty.data(Interner).kind { + TyKind::Function(_) => { + let bytes = self.eval_operand(func, &mut locals)?; + self.exec_fn_pointer( + bytes, + destination_interval, + &args, + &locals, + terminator.span, + )?; + } TyKind::FnDef(def, generic_args) => { - let def: CallableDefId = from_chalk(self.db, *def); - let generic_args = self.subst_filler(generic_args, &locals); - match def { - CallableDefId::FunctionId(def) => { - let arg_bytes = args - .iter() - .map(|x| { - Ok(self - .eval_operand(x, &locals)? - .get(&self)? - .to_owned()) - }) - .collect::>>()? - .into_iter(); - let function_data = self.db.function_data(def); - let is_intrinsic = match &function_data.abi { - Some(abi) => *abi == Interned::new_str("rust-intrinsic"), - None => match def.lookup(self.db.upcast()).container { - hir_def::ItemContainerId::ExternBlockId(block) => { - let id = block.lookup(self.db.upcast()).id; - id.item_tree(self.db.upcast())[id.value] - .abi - .as_deref() - == Some("rust-intrinsic") - } - _ => false, - }, - }; - let result = if is_intrinsic { - self.exec_intrinsic( - function_data - .name - .as_text() - .unwrap_or_default() - .as_str(), - arg_bytes, - generic_args, - &locals, - )? - } else if let Some(x) = self.detect_lang_function(def) { - self.exec_lang_item(x, arg_bytes)? - } else { - let trait_env = { - let Some(d) = body.owner.as_generic_def_id() else { - not_supported!("trait resolving in non generic def id"); - }; - self.db.trait_environment(d) - }; - let (imp, generic_args) = lookup_impl_method( - self.db, - trait_env, - def, - generic_args.clone(), - ); - let generic_args = - self.subst_filler(&generic_args, &locals); - let def = imp.into(); - let mir_body = self - .db - .mir_body(def) - .map_err(|e| MirEvalError::MirLowerError(imp, e))?; - self.interpret_mir(&mir_body, arg_bytes, generic_args) - .map_err(|e| { - MirEvalError::InFunction(imp, Box::new(e)) - })? - }; - let dest_addr = self.place_addr(destination, &locals)?; - self.write_memory(dest_addr, &result)?; - } - CallableDefId::StructId(id) => { - let (size, variant_layout, tag) = self.layout_of_variant( - id.into(), - generic_args.clone(), - &locals, - )?; - let result = self.make_by_layout( - size, - &variant_layout, - tag, - args, - &locals, - )?; - let dest_addr = self.place_addr(destination, &locals)?; - self.write_memory(dest_addr, &result)?; - } - CallableDefId::EnumVariantId(id) => { - let (size, variant_layout, tag) = self.layout_of_variant( - id.into(), - generic_args.clone(), - &locals, - )?; - let result = self.make_by_layout( - size, - &variant_layout, - tag, - args, - &locals, - )?; - let dest_addr = self.place_addr(destination, &locals)?; - self.write_memory(dest_addr, &result)?; - } - } - current_block_idx = - target.expect("broken mir, function without target"); + self.exec_fn_def( + *def, + generic_args, + destination_interval, + &args, + &locals, + terminator.span, + )?; } - _ => not_supported!("unknown function type"), + x => not_supported!("unknown function type {x:?}"), } + locals.drop_flags.add_place(destination.clone()); + current_block_idx = target.expect("broken mir, function without target"); } - Terminator::SwitchInt { discr, targets } => { + TerminatorKind::SwitchInt { discr, targets } => { let val = u128::from_le_bytes(pad16( - self.eval_operand(discr, &locals)?.get(&self)?, + self.eval_operand(discr, &mut locals)?.get(&self)?, false, )); current_block_idx = targets.target_for_value(val); } - Terminator::Return => { - let ty = body.locals[return_slot()].ty.clone(); + TerminatorKind::Return => { self.stack_depth_limit += 1; - return Ok(self - .read_memory( - locals.ptr[return_slot()], - self.size_of_sized(&ty, &locals, "return type")?, - )? - .to_owned()); + return Ok(locals.ptr[return_slot()].get(self)?.to_vec()); + } + TerminatorKind::Unreachable => { + return Err(MirEvalError::UndefinedBehavior("unreachable executed".to_owned())); } - Terminator::Unreachable => { - return Err(MirEvalError::UndefinedBehavior("unreachable executed")) + TerminatorKind::Drop { place, target, unwind: _ } => { + self.drop_place(place, &mut locals, terminator.span)?; + current_block_idx = *target; } _ => not_supported!("unknown terminator"), } } } - fn eval_rvalue<'a>( - &'a mut self, - r: &'a Rvalue, - locals: &'a Locals<'a>, - ) -> Result { + fn eval_rvalue(&mut self, r: &Rvalue, locals: &mut Locals<'_>) -> Result { use IntervalOrOwned::*; Ok(match r { Rvalue::Use(x) => Borrowed(self.eval_operand(x, locals)?), Rvalue::Ref(_, p) => { - let addr = self.place_addr(p, locals)?; - Owned(addr.to_bytes()) + let (addr, _, metadata) = self.place_addr_and_ty_and_metadata(p, locals)?; + let mut r = addr.to_bytes(); + if let Some(metadata) = metadata { + r.extend(metadata.get(self)?); + } + Owned(r) + } + Rvalue::Len(p) => { + let (_, _, metadata) = self.place_addr_and_ty_and_metadata(p, locals)?; + match metadata { + Some(m) => m, + None => { + return Err(MirEvalError::TypeError( + "type without metadata is used for Rvalue::Len", + )); + } + } } - Rvalue::Len(_) => not_supported!("rvalue len"), Rvalue::UnaryOp(op, val) => { let mut c = self.eval_operand(val, locals)?.get(&self)?; let mut ty = self.operand_ty(val, locals)?; @@ -612,27 +864,40 @@ impl Evaluator<'_> { let size = self.size_of_sized(&ty, locals, "operand of unary op")?; c = self.read_memory(Address::from_bytes(c)?, size)?; } - let mut c = c.to_vec(); - if ty.as_builtin() == Some(BuiltinType::Bool) { - c[0] = 1 - c[0]; + if let TyKind::Scalar(chalk_ir::Scalar::Float(f)) = ty.kind(Interner) { + match f { + chalk_ir::FloatTy::F32 => { + let c = -from_bytes!(f32, c); + Owned(c.to_le_bytes().into()) + } + chalk_ir::FloatTy::F64 => { + let c = -from_bytes!(f64, c); + Owned(c.to_le_bytes().into()) + } + } } else { - match op { - UnOp::Not => c.iter_mut().for_each(|x| *x = !*x), - UnOp::Neg => { - c.iter_mut().for_each(|x| *x = !*x); - for k in c.iter_mut() { - let o; - (*k, o) = k.overflowing_add(1); - if !o { - break; + let mut c = c.to_vec(); + if ty.as_builtin() == Some(BuiltinType::Bool) { + c[0] = 1 - c[0]; + } else { + match op { + UnOp::Not => c.iter_mut().for_each(|x| *x = !*x), + UnOp::Neg => { + c.iter_mut().for_each(|x| *x = !*x); + for k in c.iter_mut() { + let o; + (*k, o) = k.overflowing_add(1); + if !o { + break; + } } } } } + Owned(c) } - Owned(c) } - Rvalue::CheckedBinaryOp(op, lhs, rhs) => { + Rvalue::CheckedBinaryOp(op, lhs, rhs) => 'binary_op: { let lc = self.eval_operand(lhs, locals)?; let rc = self.eval_operand(rhs, locals)?; let mut lc = lc.get(&self)?; @@ -640,79 +905,170 @@ impl Evaluator<'_> { let mut ty = self.operand_ty(lhs, locals)?; while let TyKind::Ref(_, _, z) = ty.kind(Interner) { ty = z.clone(); - let size = self.size_of_sized(&ty, locals, "operand of binary op")?; + let size = if ty.kind(Interner) == &TyKind::Str { + if *op != BinOp::Eq { + never!("Only eq is builtin for `str`"); + } + let ls = from_bytes!(usize, &lc[self.ptr_size()..self.ptr_size() * 2]); + let rs = from_bytes!(usize, &rc[self.ptr_size()..self.ptr_size() * 2]); + if ls != rs { + break 'binary_op Owned(vec![0]); + } + lc = &lc[..self.ptr_size()]; + rc = &rc[..self.ptr_size()]; + ls + } else { + self.size_of_sized(&ty, locals, "operand of binary op")? + }; lc = self.read_memory(Address::from_bytes(lc)?, size)?; rc = self.read_memory(Address::from_bytes(rc)?, size)?; } - let is_signed = matches!(ty.as_builtin(), Some(BuiltinType::Int(_))); - let l128 = i128::from_le_bytes(pad16(lc, is_signed)); - let r128 = i128::from_le_bytes(pad16(rc, is_signed)); - match op { - BinOp::Ge | BinOp::Gt | BinOp::Le | BinOp::Lt | BinOp::Eq | BinOp::Ne => { - let r = match op { - BinOp::Ge => l128 >= r128, - BinOp::Gt => l128 > r128, - BinOp::Le => l128 <= r128, - BinOp::Lt => l128 < r128, - BinOp::Eq => l128 == r128, - BinOp::Ne => l128 != r128, - _ => unreachable!(), - }; - let r = r as u8; - Owned(vec![r]) + if let TyKind::Scalar(chalk_ir::Scalar::Float(f)) = ty.kind(Interner) { + match f { + chalk_ir::FloatTy::F32 => { + let l = from_bytes!(f32, lc); + let r = from_bytes!(f32, rc); + match op { + BinOp::Ge + | BinOp::Gt + | BinOp::Le + | BinOp::Lt + | BinOp::Eq + | BinOp::Ne => { + let r = op.run_compare(l, r) as u8; + Owned(vec![r]) + } + BinOp::Add | BinOp::Sub | BinOp::Mul | BinOp::Div => { + let r = match op { + BinOp::Add => l + r, + BinOp::Sub => l - r, + BinOp::Mul => l * r, + BinOp::Div => l / r, + _ => unreachable!(), + }; + Owned(r.to_le_bytes().into()) + } + x => not_supported!( + "invalid binop {x:?} on floating point operators" + ), + } + } + chalk_ir::FloatTy::F64 => { + let l = from_bytes!(f64, lc); + let r = from_bytes!(f64, rc); + match op { + BinOp::Ge + | BinOp::Gt + | BinOp::Le + | BinOp::Lt + | BinOp::Eq + | BinOp::Ne => { + let r = op.run_compare(l, r) as u8; + Owned(vec![r]) + } + BinOp::Add | BinOp::Sub | BinOp::Mul | BinOp::Div => { + let r = match op { + BinOp::Add => l + r, + BinOp::Sub => l - r, + BinOp::Mul => l * r, + BinOp::Div => l / r, + _ => unreachable!(), + }; + Owned(r.to_le_bytes().into()) + } + x => not_supported!( + "invalid binop {x:?} on floating point operators" + ), + } + } } - BinOp::BitAnd - | BinOp::BitOr - | BinOp::BitXor - | BinOp::Add - | BinOp::Mul - | BinOp::Div - | BinOp::Rem - | BinOp::Sub => { - let r = match op { - BinOp::Add => l128.overflowing_add(r128).0, - BinOp::Mul => l128.overflowing_mul(r128).0, - BinOp::Div => l128.checked_div(r128).ok_or(MirEvalError::Panic)?, - BinOp::Rem => l128.checked_rem(r128).ok_or(MirEvalError::Panic)?, - BinOp::Sub => l128.overflowing_sub(r128).0, - BinOp::BitAnd => l128 & r128, - BinOp::BitOr => l128 | r128, - BinOp::BitXor => l128 ^ r128, - _ => unreachable!(), - }; + } else { + let is_signed = matches!(ty.as_builtin(), Some(BuiltinType::Int(_))); + let l128 = i128::from_le_bytes(pad16(lc, is_signed)); + let r128 = i128::from_le_bytes(pad16(rc, is_signed)); + let check_overflow = |r: i128| { + // FIXME: this is not very correct, and only catches the basic cases. let r = r.to_le_bytes(); for &k in &r[lc.len()..] { if k != 0 && (k != 255 || !is_signed) { - return Err(MirEvalError::Panic); + return Err(MirEvalError::Panic(format!("Overflow in {op:?}"))); } } - Owned(r[0..lc.len()].into()) - } - BinOp::Shl | BinOp::Shr => { - let shift_amout = if r128 < 0 { - return Err(MirEvalError::Panic); - } else if r128 > 128 { - return Err(MirEvalError::Panic); - } else { - r128 as u8 - }; - let r = match op { - BinOp::Shl => l128 << shift_amout, - BinOp::Shr => l128 >> shift_amout, - _ => unreachable!(), - }; - Owned(r.to_le_bytes()[0..lc.len()].into()) + Ok(Owned(r[0..lc.len()].into())) + }; + match op { + BinOp::Ge | BinOp::Gt | BinOp::Le | BinOp::Lt | BinOp::Eq | BinOp::Ne => { + let r = op.run_compare(l128, r128) as u8; + Owned(vec![r]) + } + BinOp::BitAnd + | BinOp::BitOr + | BinOp::BitXor + | BinOp::Add + | BinOp::Mul + | BinOp::Div + | BinOp::Rem + | BinOp::Sub => { + let r = match op { + BinOp::Add => l128.overflowing_add(r128).0, + BinOp::Mul => l128.overflowing_mul(r128).0, + BinOp::Div => l128.checked_div(r128).ok_or_else(|| { + MirEvalError::Panic(format!("Overflow in {op:?}")) + })?, + BinOp::Rem => l128.checked_rem(r128).ok_or_else(|| { + MirEvalError::Panic(format!("Overflow in {op:?}")) + })?, + BinOp::Sub => l128.overflowing_sub(r128).0, + BinOp::BitAnd => l128 & r128, + BinOp::BitOr => l128 | r128, + BinOp::BitXor => l128 ^ r128, + _ => unreachable!(), + }; + check_overflow(r)? + } + BinOp::Shl | BinOp::Shr => { + let r = 'b: { + if let Ok(shift_amount) = u32::try_from(r128) { + let r = match op { + BinOp::Shl => l128.checked_shl(shift_amount), + BinOp::Shr => l128.checked_shr(shift_amount), + _ => unreachable!(), + }; + if let Some(r) = r { + break 'b r; + } + }; + return Err(MirEvalError::Panic(format!("Overflow in {op:?}"))); + }; + check_overflow(r)? + } + BinOp::Offset => not_supported!("offset binop"), } - BinOp::Offset => not_supported!("offset binop"), } } Rvalue::Discriminant(p) => { let ty = self.place_ty(p, locals)?; let bytes = self.eval_place(p, locals)?.get(&self)?; let layout = self.layout(&ty)?; - match layout.variants { - Variants::Single { .. } => Owned(0u128.to_le_bytes().to_vec()), - Variants::Multiple { tag, tag_encoding, .. } => { + let enum_id = 'b: { + match ty.kind(Interner) { + TyKind::Adt(e, _) => match e.0 { + AdtId::EnumId(e) => break 'b e, + _ => (), + }, + _ => (), + } + return Ok(Owned(0u128.to_le_bytes().to_vec())); + }; + match &layout.variants { + Variants::Single { index } => { + let r = self.const_eval_discriminant(EnumVariantId { + parent: enum_id, + local_id: index.0, + })?; + Owned(r.to_le_bytes().to_vec()) + } + Variants::Multiple { tag, tag_encoding, variants, .. } => { let Some(target_data_layout) = self.db.target_data_layout(self.crate_id) else { not_supported!("missing target data layout"); }; @@ -725,120 +1081,142 @@ impl Evaluator<'_> { } TagEncoding::Niche { untagged_variant, niche_start, .. } => { let tag = &bytes[offset..offset + size]; - let candidate_discriminant = i128::from_le_bytes(pad16(tag, false)) - .wrapping_sub(niche_start as i128); - let enum_id = match ty.kind(Interner) { - TyKind::Adt(e, _) => match e.0 { - AdtId::EnumId(e) => e, - _ => not_supported!("Non enum with multi variant layout"), - }, - _ => not_supported!("Non adt with multi variant layout"), - }; - let enum_data = self.db.enum_data(enum_id); - let result = 'b: { - for (local_id, _) in enum_data.variants.iter() { - if candidate_discriminant - == self.db.const_eval_discriminant(EnumVariantId { - parent: enum_id, - local_id, - })? - { - break 'b candidate_discriminant; - } - } - self.db.const_eval_discriminant(EnumVariantId { - parent: enum_id, - local_id: untagged_variant.0, - })? - }; + let candidate_tag = i128::from_le_bytes(pad16(tag, false)) + .wrapping_sub(*niche_start as i128) + as usize; + let variant = variants + .iter_enumerated() + .map(|(x, _)| x) + .filter(|x| x != untagged_variant) + .nth(candidate_tag) + .unwrap_or(*untagged_variant) + .0; + let result = self.const_eval_discriminant(EnumVariantId { + parent: enum_id, + local_id: variant, + })?; Owned(result.to_le_bytes().to_vec()) } } } } } + Rvalue::Repeat(x, len) => { + let len = match try_const_usize(self.db, &len) { + Some(x) => x as usize, + None => not_supported!("non evaluatable array len in repeat Rvalue"), + }; + let val = self.eval_operand(x, locals)?.get(self)?; + let size = len * val.len(); + Owned(val.iter().copied().cycle().take(size).collect()) + } Rvalue::ShallowInitBox(_, _) => not_supported!("shallow init box"), + Rvalue::ShallowInitBoxWithAlloc(ty) => { + let Some((size, align)) = self.size_align_of(ty, locals)? else { + not_supported!("unsized box initialization"); + }; + let addr = self.heap_allocate(size, align); + Owned(addr.to_bytes()) + } Rvalue::CopyForDeref(_) => not_supported!("copy for deref"), - Rvalue::Aggregate(kind, values) => match kind { - AggregateKind::Array(_) => { - let mut r = vec![]; - for x in values { - let value = self.eval_operand(x, locals)?.get(&self)?; - r.extend(value); + Rvalue::Aggregate(kind, values) => { + let values = values + .iter() + .map(|x| self.eval_operand(x, locals)) + .collect::>>()?; + match kind { + AggregateKind::Array(_) => { + let mut r = vec![]; + for x in values { + let value = x.get(&self)?; + r.extend(value); + } + Owned(r) + } + AggregateKind::Tuple(ty) => { + let layout = self.layout(&ty)?; + Owned(self.make_by_layout( + layout.size.bytes_usize(), + &layout, + None, + values.iter().map(|&x| x.into()), + )?) + } + AggregateKind::Union(x, f) => { + let layout = self.layout_adt((*x).into(), Substitution::empty(Interner))?; + let offset = layout + .fields + .offset(u32::from(f.local_id.into_raw()) as usize) + .bytes_usize(); + let op = values[0].get(&self)?; + let mut result = vec![0; layout.size.bytes_usize()]; + result[offset..offset + op.len()].copy_from_slice(op); + Owned(result) + } + AggregateKind::Adt(x, subst) => { + let (size, variant_layout, tag) = + self.layout_of_variant(*x, subst.clone(), locals)?; + Owned(self.make_by_layout( + size, + &variant_layout, + tag, + values.iter().map(|&x| x.into()), + )?) + } + AggregateKind::Closure(ty) => { + let layout = self.layout(&ty)?; + Owned(self.make_by_layout( + layout.size.bytes_usize(), + &layout, + None, + values.iter().map(|&x| x.into()), + )?) } - Owned(r) - } - AggregateKind::Tuple(ty) => { - let layout = self.layout(&ty)?; - Owned(self.make_by_layout( - layout.size.bytes_usize(), - &layout, - None, - values, - locals, - )?) - } - AggregateKind::Union(x, f) => { - let layout = self.layout_adt((*x).into(), Substitution::empty(Interner))?; - let offset = layout - .fields - .offset(u32::from(f.local_id.into_raw()) as usize) - .bytes_usize(); - let op = self.eval_operand(&values[0], locals)?.get(&self)?; - let mut result = vec![0; layout.size.bytes_usize()]; - result[offset..offset + op.len()].copy_from_slice(op); - Owned(result) - } - AggregateKind::Adt(x, subst) => { - let (size, variant_layout, tag) = - self.layout_of_variant(*x, subst.clone(), locals)?; - Owned(self.make_by_layout(size, &variant_layout, tag, values, locals)?) } - }, + } Rvalue::Cast(kind, operand, target_ty) => match kind { - CastKind::PointerExposeAddress => not_supported!("exposing pointer address"), - CastKind::PointerFromExposedAddress => { - not_supported!("creating pointer from exposed address") - } CastKind::Pointer(cast) => match cast { - PointerCast::Unsize => { + PointerCast::ReifyFnPointer | PointerCast::ClosureFnPointer(_) => { let current_ty = self.operand_ty(operand, locals)?; - match &target_ty.data(Interner).kind { - TyKind::Raw(_, ty) | TyKind::Ref(_, _, ty) => { - match &ty.data(Interner).kind { - TyKind::Slice(_) => match ¤t_ty.data(Interner).kind { - TyKind::Raw(_, ty) | TyKind::Ref(_, _, ty) => { - match &ty.data(Interner).kind { - TyKind::Array(_, size) => { - let addr = self - .eval_operand(operand, locals)? - .get(&self)?; - let len = const_as_usize(size); - let mut r = Vec::with_capacity(16); - r.extend(addr.iter().copied()); - r.extend(len.to_le_bytes().into_iter()); - Owned(r) - } - _ => { - not_supported!("slice unsizing from non arrays") - } - } - } - _ => not_supported!("slice unsizing from non pointers"), - }, - TyKind::Dyn(_) => not_supported!("dyn pointer unsize cast"), - _ => not_supported!("unknown unsized cast"), - } - } - _ => not_supported!("unsized cast on unknown pointer type"), + if let TyKind::FnDef(_, _) | TyKind::Closure(_, _) = + ¤t_ty.data(Interner).kind + { + let id = self.vtable_map.id(current_ty); + let ptr_size = self.ptr_size(); + Owned(id.to_le_bytes()[0..ptr_size].to_vec()) + } else { + not_supported!( + "creating a fn pointer from a non FnDef or Closure type" + ); } } - x => not_supported!("pointer cast {x:?}"), + PointerCast::Unsize => { + let current_ty = self.operand_ty(operand, locals)?; + let addr = self.eval_operand(operand, locals)?; + self.coerce_unsized(addr, ¤t_ty, target_ty)? + } + PointerCast::MutToConstPointer | PointerCast::UnsafeFnPointer => { + // This is no-op + Borrowed(self.eval_operand(operand, locals)?) + } + PointerCast::ArrayToPointer => { + // We should remove the metadata part if the current type is slice + Borrowed(self.eval_operand(operand, locals)?.slice(0..self.ptr_size())) + } }, CastKind::DynStar => not_supported!("dyn star cast"), - CastKind::IntToInt => { - // FIXME: handle signed cast - let current = pad16(self.eval_operand(operand, locals)?.get(&self)?, false); + CastKind::IntToInt + | CastKind::PointerExposeAddress + | CastKind::PointerFromExposedAddress => { + let current_ty = self.operand_ty(operand, locals)?; + let is_signed = match current_ty.kind(Interner) { + TyKind::Scalar(s) => match s { + chalk_ir::Scalar::Int(_) => true, + _ => false, + }, + _ => false, + }; + let current = pad16(self.eval_operand(operand, locals)?.get(&self)?, is_signed); let dest_size = self.size_of_sized(target_ty, locals, "destination of int to int cast")?; Owned(current[0..dest_size].to_vec()) @@ -846,31 +1224,106 @@ impl Evaluator<'_> { CastKind::FloatToInt => not_supported!("float to int cast"), CastKind::FloatToFloat => not_supported!("float to float cast"), CastKind::IntToFloat => not_supported!("float to int cast"), - CastKind::PtrToPtr => not_supported!("ptr to ptr cast"), CastKind::FnPtrToPtr => not_supported!("fn ptr to ptr cast"), }, }) } + fn coerce_unsized_look_through_fields( + &self, + ty: &Ty, + goal: impl Fn(&TyKind) -> Option, + ) -> Result { + let kind = ty.kind(Interner); + if let Some(x) = goal(kind) { + return Ok(x); + } + if let TyKind::Adt(id, subst) = kind { + if let AdtId::StructId(struct_id) = id.0 { + let field_types = self.db.field_types(struct_id.into()); + let mut field_types = field_types.iter(); + if let Some(ty) = + field_types.next().map(|x| x.1.clone().substitute(Interner, subst)) + { + return self.coerce_unsized_look_through_fields(&ty, goal); + } + } + } + Err(MirEvalError::CoerceUnsizedError(ty.clone())) + } + + fn coerce_unsized( + &mut self, + addr: Interval, + current_ty: &Ty, + target_ty: &Ty, + ) -> Result { + use IntervalOrOwned::*; + fn for_ptr(x: &TyKind) -> Option { + match x { + TyKind::Raw(_, ty) | TyKind::Ref(_, _, ty) => Some(ty.clone()), + _ => None, + } + } + Ok(match self.coerce_unsized_look_through_fields(target_ty, for_ptr)? { + ty => match &ty.data(Interner).kind { + TyKind::Slice(_) => { + match self.coerce_unsized_look_through_fields(current_ty, for_ptr)? { + ty => match &ty.data(Interner).kind { + TyKind::Array(_, size) => { + let len = match try_const_usize(self.db, size) { + None => not_supported!( + "unevaluatble len of array in coerce unsized" + ), + Some(x) => x as usize, + }; + let mut r = Vec::with_capacity(16); + let addr = addr.get(self)?; + r.extend(addr.iter().copied()); + r.extend(len.to_le_bytes().into_iter()); + Owned(r) + } + t => { + not_supported!("slice unsizing from non array type {t:?}") + } + }, + } + } + TyKind::Dyn(_) => match ¤t_ty.data(Interner).kind { + TyKind::Raw(_, ty) | TyKind::Ref(_, _, ty) => { + let vtable = self.vtable_map.id(ty.clone()); + let mut r = Vec::with_capacity(16); + let addr = addr.get(self)?; + r.extend(addr.iter().copied()); + r.extend(vtable.to_le_bytes().into_iter()); + Owned(r) + } + _ => not_supported!("dyn unsizing from non pointers"), + }, + _ => not_supported!("unknown unsized cast"), + }, + }) + } + fn layout_of_variant( &mut self, x: VariantId, subst: Substitution, locals: &Locals<'_>, - ) -> Result<(usize, Layout, Option<(usize, usize, i128)>)> { + ) -> Result<(usize, Arc, Option<(usize, usize, i128)>)> { let adt = x.adt_id(); if let DefWithBodyId::VariantId(f) = locals.body.owner { if let VariantId::EnumVariantId(x) = x { if AdtId::from(f.parent) == adt { // Computing the exact size of enums require resolving the enum discriminants. In order to prevent loops (and // infinite sized type errors) we use a dummy layout - let i = self.db.const_eval_discriminant(x)?; + let i = self.const_eval_discriminant(x)?; return Ok((16, self.layout(&TyBuilder::unit())?, Some((0, 16, i)))); } } } let layout = self.layout_adt(adt, subst)?; - Ok(match layout.variants { + Ok(match &layout.variants { Variants::Single { .. } => (layout.size.bytes_usize(), layout, None), Variants::Multiple { variants, tag, tag_encoding, .. } => { let cx = self @@ -882,18 +1335,27 @@ impl Evaluator<'_> { _ => not_supported!("multi variant layout for non-enums"), }; let rustc_enum_variant_idx = RustcEnumVariantIdx(enum_variant_id.local_id); - let mut discriminant = self.db.const_eval_discriminant(enum_variant_id)?; + let mut discriminant = self.const_eval_discriminant(enum_variant_id)?; let variant_layout = variants[rustc_enum_variant_idx].clone(); let have_tag = match tag_encoding { TagEncoding::Direct => true, TagEncoding::Niche { untagged_variant, niche_variants: _, niche_start } => { - discriminant = discriminant.wrapping_add(niche_start as i128); - untagged_variant != rustc_enum_variant_idx + if *untagged_variant == rustc_enum_variant_idx { + false + } else { + discriminant = (variants + .iter_enumerated() + .filter(|(x, _)| x != untagged_variant) + .position(|(x, _)| x == rustc_enum_variant_idx) + .unwrap() as i128) + .wrapping_add(*niche_start as i128); + true + } } }; ( layout.size.bytes_usize(), - variant_layout, + Arc::new(variant_layout), if have_tag { Some(( layout.fields.offset(0).bytes_usize(), @@ -910,71 +1372,84 @@ impl Evaluator<'_> { fn make_by_layout( &mut self, - size: usize, // Not neccessarily equal to variant_layout.size + size: usize, // Not necessarily equal to variant_layout.size variant_layout: &Layout, tag: Option<(usize, usize, i128)>, - values: &Vec, - locals: &Locals<'_>, + values: impl Iterator, ) -> Result> { let mut result = vec![0; size]; if let Some((offset, size, value)) = tag { result[offset..offset + size].copy_from_slice(&value.to_le_bytes()[0..size]); } - for (i, op) in values.iter().enumerate() { + for (i, op) in values.enumerate() { let offset = variant_layout.fields.offset(i).bytes_usize(); - let op = self.eval_operand(op, locals)?.get(&self)?; + let op = op.get(&self)?; result[offset..offset + op.len()].copy_from_slice(op); } Ok(result) } - fn eval_operand(&mut self, x: &Operand, locals: &Locals<'_>) -> Result { + fn eval_operand(&mut self, x: &Operand, locals: &mut Locals<'_>) -> Result { Ok(match x { - Operand::Copy(p) | Operand::Move(p) => self.eval_place(p, locals)?, + Operand::Copy(p) | Operand::Move(p) => { + locals.drop_flags.remove_place(p); + self.eval_place(p, locals)? + } + Operand::Static(st) => { + let addr = self.eval_static(*st, locals)?; + Interval::new(addr, self.ptr_size()) + } Operand::Constant(konst) => { let data = &konst.data(Interner); match &data.value { - chalk_ir::ConstValue::BoundVar(b) => { - let c = locals - .subst - .as_slice(Interner) - .get(b.index) - .ok_or(MirEvalError::TypeError("missing generic arg"))? - .assert_const_ref(Interner); - self.eval_operand(&Operand::Constant(c.clone()), locals)? - } + chalk_ir::ConstValue::BoundVar(_) => not_supported!("bound var constant"), chalk_ir::ConstValue::InferenceVar(_) => { not_supported!("inference var constant") } chalk_ir::ConstValue::Placeholder(_) => not_supported!("placeholder constant"), - chalk_ir::ConstValue::Concrete(c) => match &c.interned { - ConstScalar::Bytes(v, memory_map) => { - let mut v: Cow<'_, [u8]> = Cow::Borrowed(v); - let patch_map = memory_map.transform_addresses(|b| { - let addr = self.heap_allocate(b.len()); - self.write_memory(addr, b)?; - Ok(addr.to_usize()) - })?; - let size = self.size_of(&data.ty, locals)?.unwrap_or(v.len()); - if size != v.len() { - // Handle self enum - if size == 16 && v.len() < 16 { - v = Cow::Owned(pad16(&v, false).to_vec()); - } else if size < 16 && v.len() == 16 { - v = Cow::Owned(v[0..size].to_vec()); - } else { - return Err(MirEvalError::InvalidConst(konst.clone())); - } - } - let addr = self.heap_allocate(size); - self.write_memory(addr, &v)?; - self.patch_addresses(&patch_map, addr, &data.ty, locals)?; - Interval::new(addr, size) - } - ConstScalar::Unknown => not_supported!("evaluating unknown const"), - }, + chalk_ir::ConstValue::Concrete(c) => { + self.allocate_const_in_heap(c, &data.ty, locals, konst)? + } + } + } + }) + } + + fn allocate_const_in_heap( + &mut self, + c: &chalk_ir::ConcreteConst, + ty: &Ty, + locals: &Locals<'_>, + konst: &chalk_ir::Const, + ) -> Result { + Ok(match &c.interned { + ConstScalar::Bytes(v, memory_map) => { + let mut v: Cow<'_, [u8]> = Cow::Borrowed(v); + let patch_map = memory_map.transform_addresses(|b| { + let addr = self.heap_allocate(b.len(), 1); // FIXME: align is wrong + self.write_memory(addr, b)?; + Ok(addr.to_usize()) + })?; + let (size, align) = self.size_align_of(ty, locals)?.unwrap_or((v.len(), 1)); + if size != v.len() { + // Handle self enum + if size == 16 && v.len() < 16 { + v = Cow::Owned(pad16(&v, false).to_vec()); + } else if size < 16 && v.len() == 16 { + v = Cow::Owned(v[0..size].to_vec()); + } else { + return Err(MirEvalError::InvalidConst(konst.clone())); + } } + let addr = self.heap_allocate(size, align); + self.write_memory(addr, &v)?; + self.patch_addresses(&patch_map, &memory_map.vtable, addr, ty, locals)?; + Interval::new(addr, size) + } + ConstScalar::UnevaluatedConst(..) => { + not_supported!("unevaluated const present in monomorphized mir"); } + ConstScalar::Unknown => not_supported!("evaluating unknown const"), }) } @@ -987,197 +1462,205 @@ impl Evaluator<'_> { } fn read_memory(&self, addr: Address, size: usize) -> Result<&[u8]> { + if size == 0 { + return Ok(&[]); + } let (mem, pos) = match addr { Stack(x) => (&self.stack, x), Heap(x) => (&self.heap, x), + Invalid(x) => { + return Err(MirEvalError::UndefinedBehavior(format!( + "read invalid memory address {x} with size {size}" + ))); + } }; - mem.get(pos..pos + size).ok_or(MirEvalError::UndefinedBehavior("out of bound memory read")) + mem.get(pos..pos + size) + .ok_or_else(|| MirEvalError::UndefinedBehavior("out of bound memory read".to_string())) } fn write_memory(&mut self, addr: Address, r: &[u8]) -> Result<()> { + if r.is_empty() { + return Ok(()); + } let (mem, pos) = match addr { Stack(x) => (&mut self.stack, x), Heap(x) => (&mut self.heap, x), + Invalid(x) => { + return Err(MirEvalError::UndefinedBehavior(format!( + "write invalid memory address {x} with content {r:?}" + ))); + } }; mem.get_mut(pos..pos + r.len()) - .ok_or(MirEvalError::UndefinedBehavior("out of bound memory write"))? + .ok_or_else(|| { + MirEvalError::UndefinedBehavior("out of bound memory write".to_string()) + })? .copy_from_slice(r); Ok(()) } - fn size_of(&self, ty: &Ty, locals: &Locals<'_>) -> Result> { + fn size_align_of(&self, ty: &Ty, locals: &Locals<'_>) -> Result> { if let DefWithBodyId::VariantId(f) = locals.body.owner { if let Some((adt, _)) = ty.as_adt() { if AdtId::from(f.parent) == adt { // Computing the exact size of enums require resolving the enum discriminants. In order to prevent loops (and // infinite sized type errors) we use a dummy size - return Ok(Some(16)); + return Ok(Some((16, 16))); } } } - let ty = &self.ty_filler(ty, locals.subst, locals.body.owner)?; let layout = self.layout(ty); if self.assert_placeholder_ty_is_unused { if matches!(layout, Err(MirEvalError::LayoutError(LayoutError::HasPlaceholder, _))) { - return Ok(Some(0)); + return Ok(Some((0, 1))); } } let layout = layout?; - Ok(layout.is_sized().then(|| layout.size.bytes_usize())) + Ok(layout + .is_sized() + .then(|| (layout.size.bytes_usize(), layout.align.abi.bytes() as usize))) } /// A version of `self.size_of` which returns error if the type is unsized. `what` argument should /// be something that complete this: `error: type {ty} was unsized. {what} should be sized` fn size_of_sized(&self, ty: &Ty, locals: &Locals<'_>, what: &'static str) -> Result { - match self.size_of(ty, locals)? { - Some(x) => Ok(x), + match self.size_align_of(ty, locals)? { + Some(x) => Ok(x.0), None => Err(MirEvalError::TypeIsUnsized(ty.clone(), what)), } } - /// Uses `ty_filler` to fill an entire subst - fn subst_filler(&self, subst: &Substitution, locals: &Locals<'_>) -> Substitution { - Substitution::from_iter( - Interner, - subst.iter(Interner).map(|x| match x.data(Interner) { - chalk_ir::GenericArgData::Ty(ty) => { - let Ok(ty) = self.ty_filler(ty, locals.subst, locals.body.owner) else { - return x.clone(); - }; - chalk_ir::GenericArgData::Ty(ty).intern(Interner) - } - _ => x.clone(), - }), - ) - } - - /// This function substitutes placeholders of the body with the provided subst, effectively plays - /// the rule of monomorphization. In addition to placeholders, it substitutes opaque types (return - /// position impl traits) with their underlying type. - fn ty_filler(&self, ty: &Ty, subst: &Substitution, owner: DefWithBodyId) -> Result { - struct Filler<'a> { - db: &'a dyn HirDatabase, - subst: &'a Substitution, - skip_params: usize, - } - impl FallibleTypeFolder for Filler<'_> { - type Error = MirEvalError; - - fn as_dyn(&mut self) -> &mut dyn FallibleTypeFolder { - self - } - - fn interner(&self) -> Interner { - Interner - } - - fn try_fold_ty( - &mut self, - ty: Ty, - outer_binder: DebruijnIndex, - ) -> std::result::Result { - match ty.kind(Interner) { - TyKind::OpaqueType(id, subst) => { - let impl_trait_id = self.db.lookup_intern_impl_trait_id((*id).into()); - match impl_trait_id { - crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => { - let infer = self.db.infer(func.into()); - let filler = &mut Filler { db: self.db, subst, skip_params: 0 }; - filler.try_fold_ty(infer.type_of_rpit[idx].clone(), outer_binder) - } - crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => { - not_supported!("async block impl trait"); - } - } - } - _ => ty.try_super_fold_with(self.as_dyn(), outer_binder), - } - } - - fn try_fold_free_placeholder_ty( - &mut self, - idx: chalk_ir::PlaceholderIndex, - _outer_binder: DebruijnIndex, - ) -> std::result::Result { - let x = from_placeholder_idx(self.db, idx); - Ok(self - .subst - .as_slice(Interner) - .get((u32::from(x.local_id.into_raw()) as usize) + self.skip_params) - .and_then(|x| x.ty(Interner)) - .ok_or(MirEvalError::TypeError("Generic arg not provided"))? - .clone()) - } - } - let filler = &mut Filler { db: self.db, subst, skip_params: 0 }; - Ok(normalize(self.db, owner, ty.clone().try_fold_with(filler, DebruijnIndex::INNERMOST)?)) - } - - fn heap_allocate(&mut self, s: usize) -> Address { + fn heap_allocate(&mut self, size: usize, _align: usize) -> Address { let pos = self.heap.len(); - self.heap.extend(iter::repeat(0).take(s)); + self.heap.extend(iter::repeat(0).take(size)); Address::Heap(pos) } - pub fn interpret_mir_with_no_arg(&mut self, body: &MirBody) -> Result> { - self.interpret_mir(&body, vec![].into_iter(), Substitution::empty(Interner)) - } - - fn detect_lang_function(&self, def: FunctionId) -> Option { - let candidate = lang_attr(self.db.upcast(), def)?; - // filter normal lang functions out - if [LangItem::IntoIterIntoIter, LangItem::IteratorNext].contains(&candidate) { + fn detect_fn_trait(&self, def: FunctionId) -> Option { + use LangItem::*; + let ItemContainerId::TraitId(parent) = self.db.lookup_intern_function(def).container else { return None; + }; + let l = lang_attr(self.db.upcast(), parent)?; + match l { + FnOnce => Some(FnTrait::FnOnce), + FnMut => Some(FnTrait::FnMut), + Fn => Some(FnTrait::Fn), + _ => None, } - Some(candidate) } fn create_memory_map(&self, bytes: &[u8], ty: &Ty, locals: &Locals<'_>) -> Result { - // FIXME: support indirect references - let mut mm = MemoryMap::default(); - match ty.kind(Interner) { - TyKind::Ref(_, _, t) => { - let size = self.size_of(t, locals)?; - match size { - Some(size) => { - let addr_usize = from_bytes!(usize, bytes); - mm.insert( - addr_usize, - self.read_memory(Address::from_usize(addr_usize), size)?.to_vec(), - ) - } - None => { - let element_size = match t.kind(Interner) { - TyKind::Str => 1, - TyKind::Slice(t) => { - self.size_of_sized(t, locals, "slice inner type")? + fn rec( + this: &Evaluator<'_>, + bytes: &[u8], + ty: &Ty, + locals: &Locals<'_>, + mm: &mut MemoryMap, + ) -> Result<()> { + match ty.kind(Interner) { + TyKind::Ref(_, _, t) => { + let size = this.size_align_of(t, locals)?; + match size { + Some((size, _)) => { + let addr_usize = from_bytes!(usize, bytes); + mm.insert( + addr_usize, + this.read_memory(Address::from_usize(addr_usize), size)?.to_vec(), + ) + } + None => { + let mut check_inner = None; + let element_size = match t.kind(Interner) { + TyKind::Str => 1, + TyKind::Slice(t) => { + check_inner = Some(t); + this.size_of_sized(t, locals, "slice inner type")? + } + _ => return Ok(()), // FIXME: support other kind of unsized types + }; + let (addr, meta) = bytes.split_at(bytes.len() / 2); + let count = from_bytes!(usize, meta); + let size = element_size * count; + let addr = Address::from_bytes(addr)?; + let b = this.read_memory(addr, size)?; + mm.insert(addr.to_usize(), b.to_vec()); + if let Some(ty) = check_inner { + for i in 0..count { + let offset = element_size * i; + rec(this, &b[offset..offset + element_size], ty, locals, mm)?; + } } - _ => return Ok(mm), // FIXME: support other kind of unsized types - }; - let (addr, meta) = bytes.split_at(bytes.len() / 2); - let size = element_size * from_bytes!(usize, meta); - let addr = Address::from_bytes(addr)?; - mm.insert(addr.to_usize(), self.read_memory(addr, size)?.to_vec()); + } } } + chalk_ir::TyKind::Tuple(_, subst) => { + let layout = this.layout(ty)?; + for (id, ty) in subst.iter(Interner).enumerate() { + let ty = ty.assert_ty_ref(Interner); // Tuple only has type argument + let offset = layout.fields.offset(id).bytes_usize(); + let size = this.layout(ty)?.size.bytes_usize(); + rec(this, &bytes[offset..offset + size], ty, locals, mm)?; + } + } + chalk_ir::TyKind::Adt(adt, subst) => match adt.0 { + AdtId::StructId(s) => { + let data = this.db.struct_data(s); + let layout = this.layout(ty)?; + let field_types = this.db.field_types(s.into()); + for (f, _) in data.variant_data.fields().iter() { + let offset = layout + .fields + .offset(u32::from(f.into_raw()) as usize) + .bytes_usize(); + let ty = &field_types[f].clone().substitute(Interner, subst); + let size = this.layout(ty)?.size.bytes_usize(); + rec(this, &bytes[offset..offset + size], ty, locals, mm)?; + } + } + AdtId::EnumId(e) => { + let layout = this.layout(ty)?; + if let Some((v, l)) = + detect_variant_from_bytes(&layout, this.db, this.crate_id, bytes, e) + { + let data = &this.db.enum_data(e).variants[v].variant_data; + let field_types = this + .db + .field_types(EnumVariantId { parent: e, local_id: v }.into()); + for (f, _) in data.fields().iter() { + let offset = + l.fields.offset(u32::from(f.into_raw()) as usize).bytes_usize(); + let ty = &field_types[f].clone().substitute(Interner, subst); + let size = this.layout(ty)?.size.bytes_usize(); + rec(this, &bytes[offset..offset + size], ty, locals, mm)?; + } + } + } + AdtId::UnionId(_) => (), + }, + _ => (), } - _ => (), + Ok(()) } + let mut mm = MemoryMap::default(); + rec(self, bytes, ty, locals, &mut mm)?; Ok(mm) } fn patch_addresses( &mut self, patch_map: &HashMap, + old_vtable: &VTableMap, addr: Address, ty: &Ty, locals: &Locals<'_>, ) -> Result<()> { // FIXME: support indirect references + let layout = self.layout(ty)?; let my_size = self.size_of_sized(ty, locals, "value to patch address")?; match ty.kind(Interner) { TyKind::Ref(_, _, t) => { - let size = self.size_of(t, locals)?; + let size = self.size_align_of(t, locals)?; match size { Some(_) => { let current = from_bytes!(usize, self.read_memory(addr, my_size)?); @@ -1193,51 +1676,441 @@ impl Evaluator<'_> { } } } - _ => (), + TyKind::Function(_) => { + let ty = old_vtable.ty_of_bytes(self.read_memory(addr, my_size)?)?.clone(); + let new_id = self.vtable_map.id(ty); + self.write_memory(addr, &new_id.to_le_bytes())?; + } + TyKind::Adt(id, subst) => match id.0 { + AdtId::StructId(s) => { + for (i, (_, ty)) in self.db.field_types(s.into()).iter().enumerate() { + let offset = layout.fields.offset(i).bytes_usize(); + let ty = ty.clone().substitute(Interner, subst); + self.patch_addresses( + patch_map, + old_vtable, + addr.offset(offset), + &ty, + locals, + )?; + } + } + AdtId::UnionId(_) => (), + AdtId::EnumId(_) => (), + }, + TyKind::AssociatedType(_, _) + | TyKind::Scalar(_) + | TyKind::Tuple(_, _) + | TyKind::Array(_, _) + | TyKind::Slice(_) + | TyKind::Raw(_, _) + | TyKind::OpaqueType(_, _) + | TyKind::FnDef(_, _) + | TyKind::Str + | TyKind::Never + | TyKind::Closure(_, _) + | TyKind::Generator(_, _) + | TyKind::GeneratorWitness(_, _) + | TyKind::Foreign(_) + | TyKind::Error + | TyKind::Placeholder(_) + | TyKind::Dyn(_) + | TyKind::Alias(_) + | TyKind::BoundVar(_) + | TyKind::InferenceVar(_, _) => (), } Ok(()) } - fn exec_intrinsic( - &self, - as_str: &str, - _arg_bytes: impl Iterator>, - generic_args: Substitution, + fn exec_fn_pointer( + &mut self, + bytes: Interval, + destination: Interval, + args: &[IntervalAndTy], locals: &Locals<'_>, - ) -> Result> { - match as_str { - "size_of" => { - let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else { - return Err(MirEvalError::TypeError("size_of generic arg is not provided")); - }; - let size = self.size_of(ty, locals)?; - match size { - Some(x) => Ok(x.to_le_bytes().to_vec()), - None => return Err(MirEvalError::TypeError("size_of arg is unsized")), + span: MirSpan, + ) -> Result<()> { + let id = from_bytes!(usize, bytes.get(self)?); + let next_ty = self.vtable_map.ty(id)?.clone(); + match &next_ty.data(Interner).kind { + TyKind::FnDef(def, generic_args) => { + self.exec_fn_def(*def, generic_args, destination, args, &locals, span)?; + } + TyKind::Closure(id, subst) => { + self.exec_closure(*id, bytes.slice(0..0), subst, destination, args, locals, span)?; + } + _ => return Err(MirEvalError::TypeError("function pointer to non function")), + } + Ok(()) + } + + fn exec_closure( + &mut self, + closure: ClosureId, + closure_data: Interval, + generic_args: &Substitution, + destination: Interval, + args: &[IntervalAndTy], + locals: &Locals<'_>, + span: MirSpan, + ) -> Result<()> { + let mir_body = self + .db + .monomorphized_mir_body_for_closure( + closure, + generic_args.clone(), + self.trait_env.clone(), + ) + .map_err(|x| MirEvalError::MirLowerErrorForClosure(closure, x))?; + let closure_data = if mir_body.locals[mir_body.param_locals[0]].ty.as_reference().is_some() + { + closure_data.addr.to_bytes() + } else { + closure_data.get(self)?.to_owned() + }; + let arg_bytes = iter::once(Ok(closure_data)) + .chain(args.iter().map(|x| Ok(x.get(&self)?.to_owned()))) + .collect::>>()?; + let bytes = self.interpret_mir(&mir_body, arg_bytes.into_iter()).map_err(|e| { + MirEvalError::InFunction(Either::Right(closure), Box::new(e), span, locals.body.owner) + })?; + destination.write_from_bytes(self, &bytes) + } + + fn exec_fn_def( + &mut self, + def: FnDefId, + generic_args: &Substitution, + destination: Interval, + args: &[IntervalAndTy], + locals: &Locals<'_>, + span: MirSpan, + ) -> Result<()> { + let def: CallableDefId = from_chalk(self.db, def); + let generic_args = generic_args.clone(); + match def { + CallableDefId::FunctionId(def) => { + if let Some(_) = self.detect_fn_trait(def) { + self.exec_fn_trait(&args, destination, locals, span)?; + return Ok(()); } + self.exec_fn_with_args(def, args, generic_args, locals, destination, span)?; + } + CallableDefId::StructId(id) => { + let (size, variant_layout, tag) = + self.layout_of_variant(id.into(), generic_args, &locals)?; + let result = self.make_by_layout( + size, + &variant_layout, + tag, + args.iter().map(|x| x.interval.into()), + )?; + destination.write_from_bytes(self, &result)?; + } + CallableDefId::EnumVariantId(id) => { + let (size, variant_layout, tag) = + self.layout_of_variant(id.into(), generic_args, &locals)?; + let result = self.make_by_layout( + size, + &variant_layout, + tag, + args.iter().map(|x| x.interval.into()), + )?; + destination.write_from_bytes(self, &result)?; } - _ => not_supported!("unknown intrinsic {as_str}"), } + Ok(()) } - pub(crate) fn exec_lang_item( - &self, - x: LangItem, - mut args: std::vec::IntoIter>, - ) -> Result> { - use LangItem::*; - match x { - PanicFmt | BeginPanic => Err(MirEvalError::Panic), - SliceLen => { - let arg = args - .next() - .ok_or(MirEvalError::TypeError("argument of <[T]>::len() is not provided"))?; - let ptr_size = arg.len() / 2; - Ok(arg[ptr_size..].into()) + fn exec_fn_with_args( + &mut self, + def: FunctionId, + args: &[IntervalAndTy], + generic_args: Substitution, + locals: &Locals<'_>, + destination: Interval, + span: MirSpan, + ) -> Result<()> { + if self.detect_and_exec_special_function( + def, + args, + &generic_args, + locals, + destination, + span, + )? { + return Ok(()); + } + let arg_bytes = + args.iter().map(|x| Ok(x.get(&self)?.to_owned())).collect::>>()?; + if let Some(self_ty_idx) = + is_dyn_method(self.db, self.trait_env.clone(), def, generic_args.clone()) + { + // In the layout of current possible receiver, which at the moment of writing this code is one of + // `&T`, `&mut T`, `Box`, `Rc`, `Arc`, and `Pin

` where `P` is one of possible recievers, + // the vtable is exactly in the `[ptr_size..2*ptr_size]` bytes. So we can use it without branching on + // the type. + let ty = + self.vtable_map.ty_of_bytes(&arg_bytes[0][self.ptr_size()..self.ptr_size() * 2])?; + let mut args_for_target = args.to_vec(); + args_for_target[0] = IntervalAndTy { + interval: args_for_target[0].interval.slice(0..self.ptr_size()), + ty: ty.clone(), + }; + let ty = GenericArgData::Ty(ty.clone()).intern(Interner); + let generics_for_target = + Substitution::from_iter( + Interner, + generic_args.iter(Interner).enumerate().map(|(i, x)| { + if i == self_ty_idx { + &ty + } else { + x + } + }), + ); + return self.exec_fn_with_args( + def, + &args_for_target, + generics_for_target, + locals, + destination, + span, + ); + } + let (imp, generic_args) = + lookup_impl_method(self.db, self.trait_env.clone(), def, generic_args); + self.exec_looked_up_function(generic_args, locals, imp, arg_bytes, span, destination) + } + + fn exec_looked_up_function( + &mut self, + generic_args: Substitution, + locals: &Locals<'_>, + imp: FunctionId, + arg_bytes: Vec>, + span: MirSpan, + destination: Interval, + ) -> Result<()> { + let def = imp.into(); + let mir_body = self + .db + .monomorphized_mir_body(def, generic_args, self.trait_env.clone()) + .map_err(|e| { + MirEvalError::InFunction( + Either::Left(imp), + Box::new(MirEvalError::MirLowerError(imp, e)), + span, + locals.body.owner, + ) + })?; + let result = self.interpret_mir(&mir_body, arg_bytes.iter().cloned()).map_err(|e| { + MirEvalError::InFunction(Either::Left(imp), Box::new(e), span, locals.body.owner) + })?; + destination.write_from_bytes(self, &result)?; + Ok(()) + } + + fn exec_fn_trait( + &mut self, + args: &[IntervalAndTy], + destination: Interval, + locals: &Locals<'_>, + span: MirSpan, + ) -> Result<()> { + let func = args.get(0).ok_or(MirEvalError::TypeError("fn trait with no arg"))?; + let mut func_ty = func.ty.clone(); + let mut func_data = func.interval; + while let TyKind::Ref(_, _, z) = func_ty.kind(Interner) { + func_ty = z.clone(); + if matches!(func_ty.kind(Interner), TyKind::Dyn(_)) { + let id = + from_bytes!(usize, &func_data.get(self)?[self.ptr_size()..self.ptr_size() * 2]); + func_data = func_data.slice(0..self.ptr_size()); + func_ty = self.vtable_map.ty(id)?.clone(); + } + let size = self.size_of_sized(&func_ty, locals, "self type of fn trait")?; + func_data = Interval { addr: Address::from_bytes(func_data.get(self)?)?, size }; + } + match &func_ty.data(Interner).kind { + TyKind::FnDef(def, subst) => { + self.exec_fn_def(*def, subst, destination, &args[1..], locals, span)?; + } + TyKind::Function(_) => { + self.exec_fn_pointer(func_data, destination, &args[1..], locals, span)?; + } + TyKind::Closure(closure, subst) => { + self.exec_closure( + *closure, + func_data, + &Substitution::from_iter(Interner, ClosureSubst(subst).parent_subst()), + destination, + &args[1..], + locals, + span, + )?; + } + x => not_supported!("Call FnTrait methods with type {x:?}"), + } + Ok(()) + } + + fn eval_static(&mut self, st: StaticId, locals: &Locals<'_>) -> Result

{ + if let Some(o) = self.static_locations.get(&st) { + return Ok(*o); + }; + let static_data = self.db.static_data(st); + let result = if !static_data.is_extern { + let konst = self.db.const_eval_static(st).map_err(|e| { + MirEvalError::ConstEvalError( + static_data.name.as_str().unwrap_or("_").to_owned(), + Box::new(e), + ) + })?; + let data = &konst.data(Interner); + if let chalk_ir::ConstValue::Concrete(c) = &data.value { + self.allocate_const_in_heap(&c, &data.ty, locals, &konst)? + } else { + not_supported!("unevaluatable static"); + } + } else { + let ty = &self.db.infer(st.into())[self.db.body(st.into()).body_expr]; + let Some((size, align)) = self.size_align_of(&ty, locals)? else { + not_supported!("unsized extern static"); + }; + let addr = self.heap_allocate(size, align); + Interval::new(addr, size) + }; + let addr = self.heap_allocate(self.ptr_size(), self.ptr_size()); + self.write_memory(addr, &result.addr.to_bytes())?; + self.static_locations.insert(st, addr); + Ok(addr) + } + + fn const_eval_discriminant(&self, variant: EnumVariantId) -> Result { + let r = self.db.const_eval_discriminant(variant); + match r { + Ok(r) => Ok(r), + Err(e) => { + let data = self.db.enum_data(variant.parent); + let name = format!( + "{}::{}", + data.name.display(self.db.upcast()), + data.variants[variant.local_id].name.display(self.db.upcast()) + ); + Err(MirEvalError::ConstEvalError(name, Box::new(e))) } - x => not_supported!("Executing lang item {x:?}"), } } + + fn drop_place(&mut self, place: &Place, locals: &mut Locals<'_>, span: MirSpan) -> Result<()> { + let (addr, ty, metadata) = self.place_addr_and_ty_and_metadata(place, locals)?; + if !locals.drop_flags.remove_place(place) { + return Ok(()); + } + let metadata = match metadata { + Some(x) => x.get(self)?.to_vec(), + None => vec![], + }; + self.run_drop_glue_deep(ty, locals, addr, &metadata, span) + } + + fn run_drop_glue_deep( + &mut self, + ty: Ty, + locals: &Locals<'_>, + addr: Address, + _metadata: &[u8], + span: MirSpan, + ) -> Result<()> { + let Some(drop_fn) = (|| { + let drop_trait = self.db.lang_item(self.crate_id, LangItem::Drop)?.as_trait()?; + self.db.trait_data(drop_trait).method_by_name(&name![drop]) + })() else { + // in some tests we don't have drop trait in minicore, and + // we can ignore drop in them. + return Ok(()); + }; + let (impl_drop_candidate, subst) = lookup_impl_method( + self.db, + self.trait_env.clone(), + drop_fn, + Substitution::from1(Interner, ty.clone()), + ); + if impl_drop_candidate != drop_fn { + self.exec_looked_up_function( + subst, + locals, + impl_drop_candidate, + vec![addr.to_bytes()], + span, + Interval { addr: Address::Invalid(0), size: 0 }, + )?; + } + match ty.kind(Interner) { + TyKind::Adt(id, subst) => { + match id.0 { + AdtId::StructId(s) => { + let data = self.db.struct_data(s); + if data.flags.contains(StructFlags::IS_MANUALLY_DROP) { + return Ok(()); + } + let layout = self.layout_adt(id.0, subst.clone())?; + match data.variant_data.as_ref() { + VariantData::Record(fields) | VariantData::Tuple(fields) => { + let field_types = self.db.field_types(s.into()); + for (field, _) in fields.iter() { + let offset = layout + .fields + .offset(u32::from(field.into_raw()) as usize) + .bytes_usize(); + let addr = addr.offset(offset); + let ty = field_types[field].clone().substitute(Interner, subst); + self.run_drop_glue_deep(ty, locals, addr, &[], span)?; + } + } + VariantData::Unit => (), + } + } + AdtId::UnionId(_) => (), // union fields don't need drop + AdtId::EnumId(_) => (), + } + } + TyKind::AssociatedType(_, _) + | TyKind::Scalar(_) + | TyKind::Tuple(_, _) + | TyKind::Array(_, _) + | TyKind::Slice(_) + | TyKind::Raw(_, _) + | TyKind::Ref(_, _, _) + | TyKind::OpaqueType(_, _) + | TyKind::FnDef(_, _) + | TyKind::Str + | TyKind::Never + | TyKind::Closure(_, _) + | TyKind::Generator(_, _) + | TyKind::GeneratorWitness(_, _) + | TyKind::Foreign(_) + | TyKind::Error + | TyKind::Placeholder(_) + | TyKind::Dyn(_) + | TyKind::Alias(_) + | TyKind::Function(_) + | TyKind::BoundVar(_) + | TyKind::InferenceVar(_, _) => (), + }; + Ok(()) + } + + fn write_to_stdout(&mut self, interval: Interval) -> Result<()> { + self.stdout.extend(interval.get(self)?.to_vec()); + Ok(()) + } + + fn write_to_stderr(&mut self, interval: Interval) -> Result<()> { + self.stderr.extend(interval.get(self)?.to_vec()); + Ok(()) + } } pub fn pad16(x: &[u8], is_signed: bool) -> [u8; 16] { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs new file mode 100644 index 0000000000000..3b9ef03c369f4 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs @@ -0,0 +1,792 @@ +//! Interpret intrinsics, lang items and `extern "C"` wellknown functions which their implementation +//! is not available. + +use std::cmp; + +use super::*; + +macro_rules! from_bytes { + ($ty:tt, $value:expr) => { + ($ty::from_le_bytes(match ($value).try_into() { + Ok(x) => x, + Err(_) => return Err(MirEvalError::TypeError("mismatched size")), + })) + }; +} + +macro_rules! not_supported { + ($x: expr) => { + return Err(MirEvalError::NotSupported(format!($x))) + }; +} + +impl Evaluator<'_> { + pub(super) fn detect_and_exec_special_function( + &mut self, + def: FunctionId, + args: &[IntervalAndTy], + generic_args: &Substitution, + locals: &Locals<'_>, + destination: Interval, + span: MirSpan, + ) -> Result { + let function_data = self.db.function_data(def); + let is_intrinsic = match &function_data.abi { + Some(abi) => *abi == Interned::new_str("rust-intrinsic"), + None => match def.lookup(self.db.upcast()).container { + hir_def::ItemContainerId::ExternBlockId(block) => { + let id = block.lookup(self.db.upcast()).id; + id.item_tree(self.db.upcast())[id.value].abi.as_deref() + == Some("rust-intrinsic") + } + _ => false, + }, + }; + if is_intrinsic { + self.exec_intrinsic( + function_data.name.as_text().unwrap_or_default().as_str(), + args, + generic_args, + destination, + &locals, + span, + )?; + return Ok(true); + } + let is_extern_c = match def.lookup(self.db.upcast()).container { + hir_def::ItemContainerId::ExternBlockId(block) => { + let id = block.lookup(self.db.upcast()).id; + id.item_tree(self.db.upcast())[id.value].abi.as_deref() == Some("C") + } + _ => false, + }; + if is_extern_c { + self.exec_extern_c( + function_data.name.as_text().unwrap_or_default().as_str(), + args, + generic_args, + destination, + &locals, + span, + )?; + return Ok(true); + } + let alloc_fn = function_data + .attrs + .iter() + .filter_map(|x| x.path().as_ident()) + .filter_map(|x| x.as_str()) + .find(|x| { + [ + "rustc_allocator", + "rustc_deallocator", + "rustc_reallocator", + "rustc_allocator_zeroed", + ] + .contains(x) + }); + if let Some(alloc_fn) = alloc_fn { + self.exec_alloc_fn(alloc_fn, args, destination)?; + return Ok(true); + } + if let Some(x) = self.detect_lang_function(def) { + let arg_bytes = + args.iter().map(|x| Ok(x.get(&self)?.to_owned())).collect::>>()?; + let result = self.exec_lang_item(x, generic_args, &arg_bytes, locals, span)?; + destination.write_from_bytes(self, &result)?; + return Ok(true); + } + Ok(false) + } + + fn exec_alloc_fn( + &mut self, + alloc_fn: &str, + args: &[IntervalAndTy], + destination: Interval, + ) -> Result<()> { + match alloc_fn { + "rustc_allocator_zeroed" | "rustc_allocator" => { + let [size, align] = args else { + return Err(MirEvalError::TypeError("rustc_allocator args are not provided")); + }; + let size = from_bytes!(usize, size.get(self)?); + let align = from_bytes!(usize, align.get(self)?); + let result = self.heap_allocate(size, align); + destination.write_from_bytes(self, &result.to_bytes())?; + } + "rustc_deallocator" => { /* no-op for now */ } + "rustc_reallocator" => { + let [ptr, old_size, align, new_size] = args else { + return Err(MirEvalError::TypeError("rustc_allocator args are not provided")); + }; + let ptr = Address::from_bytes(ptr.get(self)?)?; + let old_size = from_bytes!(usize, old_size.get(self)?); + let new_size = from_bytes!(usize, new_size.get(self)?); + let align = from_bytes!(usize, align.get(self)?); + let result = self.heap_allocate(new_size, align); + Interval { addr: result, size: old_size } + .write_from_interval(self, Interval { addr: ptr, size: old_size })?; + destination.write_from_bytes(self, &result.to_bytes())?; + } + _ => not_supported!("unknown alloc function"), + } + Ok(()) + } + + fn detect_lang_function(&self, def: FunctionId) -> Option { + use LangItem::*; + let candidate = lang_attr(self.db.upcast(), def)?; + // We want to execute these functions with special logic + if [PanicFmt, BeginPanic, SliceLen, DropInPlace].contains(&candidate) { + return Some(candidate); + } + None + } + + fn exec_lang_item( + &mut self, + x: LangItem, + generic_args: &Substitution, + args: &[Vec], + locals: &Locals<'_>, + span: MirSpan, + ) -> Result> { + use LangItem::*; + let mut args = args.iter(); + match x { + BeginPanic => Err(MirEvalError::Panic("".to_string())), + PanicFmt => { + let message = (|| { + let arguments_struct = + self.db.lang_item(self.crate_id, LangItem::FormatArguments)?.as_struct()?; + let arguments_layout = self + .layout_adt(arguments_struct.into(), Substitution::empty(Interner)) + .ok()?; + let arguments_field_pieces = + self.db.struct_data(arguments_struct).variant_data.field(&name![pieces])?; + let pieces_offset = arguments_layout + .fields + .offset(u32::from(arguments_field_pieces.into_raw()) as usize) + .bytes_usize(); + let ptr_size = self.ptr_size(); + let arg = args.next()?; + let pieces_array_addr = + Address::from_bytes(&arg[pieces_offset..pieces_offset + ptr_size]).ok()?; + let pieces_array_len = usize::from_le_bytes( + (&arg[pieces_offset + ptr_size..pieces_offset + 2 * ptr_size]) + .try_into() + .ok()?, + ); + let mut message = "".to_string(); + for i in 0..pieces_array_len { + let piece_ptr_addr = pieces_array_addr.offset(2 * i * ptr_size); + let piece_addr = + Address::from_bytes(self.read_memory(piece_ptr_addr, ptr_size).ok()?) + .ok()?; + let piece_len = usize::from_le_bytes( + self.read_memory(piece_ptr_addr.offset(ptr_size), ptr_size) + .ok()? + .try_into() + .ok()?, + ); + let piece_data = self.read_memory(piece_addr, piece_len).ok()?; + message += &std::string::String::from_utf8_lossy(piece_data); + } + Some(message) + })() + .unwrap_or_else(|| "".to_string()); + Err(MirEvalError::Panic(message)) + } + SliceLen => { + let arg = args + .next() + .ok_or(MirEvalError::TypeError("argument of <[T]>::len() is not provided"))?; + let ptr_size = arg.len() / 2; + Ok(arg[ptr_size..].into()) + } + DropInPlace => { + let ty = + generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)).ok_or( + MirEvalError::TypeError( + "generic argument of drop_in_place is not provided", + ), + )?; + let arg = args + .next() + .ok_or(MirEvalError::TypeError("argument of drop_in_place is not provided"))?; + self.run_drop_glue_deep( + ty.clone(), + locals, + Address::from_bytes(&arg[0..self.ptr_size()])?, + &arg[self.ptr_size()..], + span, + )?; + Ok(vec![]) + } + x => not_supported!("Executing lang item {x:?}"), + } + } + + fn exec_extern_c( + &mut self, + as_str: &str, + args: &[IntervalAndTy], + _generic_args: &Substitution, + destination: Interval, + locals: &Locals<'_>, + _span: MirSpan, + ) -> Result<()> { + match as_str { + "memcmp" => { + let [ptr1, ptr2, size] = args else { + return Err(MirEvalError::TypeError("memcmp args are not provided")); + }; + let addr1 = Address::from_bytes(ptr1.get(self)?)?; + let addr2 = Address::from_bytes(ptr2.get(self)?)?; + let size = from_bytes!(usize, size.get(self)?); + let slice1 = self.read_memory(addr1, size)?; + let slice2 = self.read_memory(addr2, size)?; + let r: i128 = match slice1.cmp(slice2) { + cmp::Ordering::Less => -1, + cmp::Ordering::Equal => 0, + cmp::Ordering::Greater => 1, + }; + destination.write_from_bytes(self, &r.to_le_bytes()[..destination.size]) + } + "write" => { + let [fd, ptr, len] = args else { + return Err(MirEvalError::TypeError("libc::write args are not provided")); + }; + let fd = u128::from_le_bytes(pad16(fd.get(self)?, false)); + let interval = Interval { + addr: Address::from_bytes(ptr.get(self)?)?, + size: from_bytes!(usize, len.get(self)?), + }; + match fd { + 1 => { + self.write_to_stdout(interval)?; + } + 2 => { + self.write_to_stderr(interval)?; + } + _ => not_supported!("write to arbitrary file descriptor"), + } + destination.write_from_interval(self, len.interval)?; + Ok(()) + } + "pthread_key_create" => { + let key = self.thread_local_storage.create_key(); + let Some(arg0) = args.get(0) else { + return Err(MirEvalError::TypeError("pthread_key_create arg0 is not provided")); + }; + let arg0_addr = Address::from_bytes(arg0.get(self)?)?; + let key_ty = if let Some((ty, ..)) = arg0.ty.as_reference_or_ptr() { + ty + } else { + return Err(MirEvalError::TypeError( + "pthread_key_create arg0 is not a pointer", + )); + }; + let arg0_interval = Interval::new( + arg0_addr, + self.size_of_sized(key_ty, locals, "pthread_key_create key arg")?, + ); + arg0_interval.write_from_bytes(self, &key.to_le_bytes()[0..arg0_interval.size])?; + // return 0 as success + destination.write_from_bytes(self, &0u64.to_le_bytes()[0..destination.size])?; + Ok(()) + } + "pthread_getspecific" => { + let Some(arg0) = args.get(0) else { + return Err(MirEvalError::TypeError("pthread_getspecific arg0 is not provided")); + }; + let key = from_bytes!(usize, &pad16(arg0.get(self)?, false)[0..8]); + let value = self.thread_local_storage.get_key(key)?; + destination.write_from_bytes(self, &value.to_le_bytes()[0..destination.size])?; + Ok(()) + } + "pthread_setspecific" => { + let Some(arg0) = args.get(0) else { + return Err(MirEvalError::TypeError("pthread_setspecific arg0 is not provided")); + }; + let key = from_bytes!(usize, &pad16(arg0.get(self)?, false)[0..8]); + let Some(arg1) = args.get(1) else { + return Err(MirEvalError::TypeError("pthread_setspecific arg1 is not provided")); + }; + let value = from_bytes!(u128, pad16(arg1.get(self)?, false)); + self.thread_local_storage.set_key(key, value)?; + // return 0 as success + destination.write_from_bytes(self, &0u64.to_le_bytes()[0..destination.size])?; + Ok(()) + } + "pthread_key_delete" => { + // we ignore this currently + // return 0 as success + destination.write_from_bytes(self, &0u64.to_le_bytes()[0..destination.size])?; + Ok(()) + } + _ => not_supported!("unknown external function {as_str}"), + } + } + + fn exec_intrinsic( + &mut self, + name: &str, + args: &[IntervalAndTy], + generic_args: &Substitution, + destination: Interval, + locals: &Locals<'_>, + span: MirSpan, + ) -> Result<()> { + if let Some(name) = name.strip_prefix("atomic_") { + return self.exec_atomic_intrinsic(name, args, generic_args, destination, locals, span); + } + if let Some(name) = name.strip_suffix("f64") { + let result = match name { + "sqrt" | "sin" | "cos" | "exp" | "exp2" | "log" | "log10" | "log2" | "fabs" + | "floor" | "ceil" | "trunc" | "rint" | "nearbyint" | "round" | "roundeven" => { + let [arg] = args else { + return Err(MirEvalError::TypeError("f64 intrinsic signature doesn't match fn (f64) -> f64")); + }; + let arg = from_bytes!(f64, arg.get(self)?); + match name { + "sqrt" => arg.sqrt(), + "sin" => arg.sin(), + "cos" => arg.cos(), + "exp" => arg.exp(), + "exp2" => arg.exp2(), + "log" => arg.ln(), + "log10" => arg.log10(), + "log2" => arg.log2(), + "fabs" => arg.abs(), + "floor" => arg.floor(), + "ceil" => arg.ceil(), + "trunc" => arg.trunc(), + // FIXME: these rounds should be different, but only `.round()` is stable now. + "rint" => arg.round(), + "nearbyint" => arg.round(), + "round" => arg.round(), + "roundeven" => arg.round(), + _ => unreachable!(), + } + } + "pow" | "minnum" | "maxnum" | "copysign" => { + let [arg1, arg2] = args else { + return Err(MirEvalError::TypeError("f64 intrinsic signature doesn't match fn (f64, f64) -> f64")); + }; + let arg1 = from_bytes!(f64, arg1.get(self)?); + let arg2 = from_bytes!(f64, arg2.get(self)?); + match name { + "pow" => arg1.powf(arg2), + "minnum" => arg1.min(arg2), + "maxnum" => arg1.max(arg2), + "copysign" => arg1.copysign(arg2), + _ => unreachable!(), + } + } + "powi" => { + let [arg1, arg2] = args else { + return Err(MirEvalError::TypeError("powif64 signature doesn't match fn (f64, i32) -> f64")); + }; + let arg1 = from_bytes!(f64, arg1.get(self)?); + let arg2 = from_bytes!(i32, arg2.get(self)?); + arg1.powi(arg2) + } + "fma" => { + let [arg1, arg2, arg3] = args else { + return Err(MirEvalError::TypeError("fmaf64 signature doesn't match fn (f64, f64, f64) -> f64")); + }; + let arg1 = from_bytes!(f64, arg1.get(self)?); + let arg2 = from_bytes!(f64, arg2.get(self)?); + let arg3 = from_bytes!(f64, arg3.get(self)?); + arg1.mul_add(arg2, arg3) + } + _ => not_supported!("unknown f64 intrinsic {name}"), + }; + return destination.write_from_bytes(self, &result.to_le_bytes()); + } + if let Some(name) = name.strip_suffix("f32") { + let result = match name { + "sqrt" | "sin" | "cos" | "exp" | "exp2" | "log" | "log10" | "log2" | "fabs" + | "floor" | "ceil" | "trunc" | "rint" | "nearbyint" | "round" | "roundeven" => { + let [arg] = args else { + return Err(MirEvalError::TypeError("f32 intrinsic signature doesn't match fn (f32) -> f32")); + }; + let arg = from_bytes!(f32, arg.get(self)?); + match name { + "sqrt" => arg.sqrt(), + "sin" => arg.sin(), + "cos" => arg.cos(), + "exp" => arg.exp(), + "exp2" => arg.exp2(), + "log" => arg.ln(), + "log10" => arg.log10(), + "log2" => arg.log2(), + "fabs" => arg.abs(), + "floor" => arg.floor(), + "ceil" => arg.ceil(), + "trunc" => arg.trunc(), + // FIXME: these rounds should be different, but only `.round()` is stable now. + "rint" => arg.round(), + "nearbyint" => arg.round(), + "round" => arg.round(), + "roundeven" => arg.round(), + _ => unreachable!(), + } + } + "pow" | "minnum" | "maxnum" | "copysign" => { + let [arg1, arg2] = args else { + return Err(MirEvalError::TypeError("f32 intrinsic signature doesn't match fn (f32, f32) -> f32")); + }; + let arg1 = from_bytes!(f32, arg1.get(self)?); + let arg2 = from_bytes!(f32, arg2.get(self)?); + match name { + "pow" => arg1.powf(arg2), + "minnum" => arg1.min(arg2), + "maxnum" => arg1.max(arg2), + "copysign" => arg1.copysign(arg2), + _ => unreachable!(), + } + } + "powi" => { + let [arg1, arg2] = args else { + return Err(MirEvalError::TypeError("powif32 signature doesn't match fn (f32, i32) -> f32")); + }; + let arg1 = from_bytes!(f32, arg1.get(self)?); + let arg2 = from_bytes!(i32, arg2.get(self)?); + arg1.powi(arg2) + } + "fma" => { + let [arg1, arg2, arg3] = args else { + return Err(MirEvalError::TypeError("fmaf32 signature doesn't match fn (f32, f32, f32) -> f32")); + }; + let arg1 = from_bytes!(f32, arg1.get(self)?); + let arg2 = from_bytes!(f32, arg2.get(self)?); + let arg3 = from_bytes!(f32, arg3.get(self)?); + arg1.mul_add(arg2, arg3) + } + _ => not_supported!("unknown f32 intrinsic {name}"), + }; + return destination.write_from_bytes(self, &result.to_le_bytes()); + } + match name { + "size_of" => { + let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else { + return Err(MirEvalError::TypeError("size_of generic arg is not provided")); + }; + let size = self.size_of_sized(ty, locals, "size_of arg")?; + destination.write_from_bytes(self, &size.to_le_bytes()[0..destination.size]) + } + "min_align_of" | "pref_align_of" => { + let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else { + return Err(MirEvalError::TypeError("align_of generic arg is not provided")); + }; + let align = self.layout(ty)?.align.abi.bytes(); + destination.write_from_bytes(self, &align.to_le_bytes()[0..destination.size]) + } + "needs_drop" => { + let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else { + return Err(MirEvalError::TypeError("size_of generic arg is not provided")); + }; + let result = !ty.clone().is_copy(self.db, locals.body.owner); + destination.write_from_bytes(self, &[u8::from(result)]) + } + "ptr_guaranteed_cmp" => { + // FIXME: this is wrong for const eval, it should return 2 in some + // cases. + let [lhs, rhs] = args else { + return Err(MirEvalError::TypeError("wrapping_add args are not provided")); + }; + let ans = lhs.get(self)? == rhs.get(self)?; + destination.write_from_bytes(self, &[u8::from(ans)]) + } + "saturating_add" => { + let [lhs, rhs] = args else { + return Err(MirEvalError::TypeError("saturating_add args are not provided")); + }; + let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false)); + let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false)); + let ans = lhs.saturating_add(rhs); + let bits = destination.size * 8; + // FIXME: signed + let is_signed = false; + let mx: u128 = if is_signed { (1 << (bits - 1)) - 1 } else { (1 << bits) - 1 }; + // FIXME: signed + let mn: u128 = 0; + let ans = cmp::min(mx, cmp::max(mn, ans)); + destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]) + } + "wrapping_add" | "unchecked_add" => { + let [lhs, rhs] = args else { + return Err(MirEvalError::TypeError("wrapping_add args are not provided")); + }; + let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false)); + let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false)); + let ans = lhs.wrapping_add(rhs); + destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]) + } + "wrapping_sub" | "unchecked_sub" | "ptr_offset_from_unsigned" | "ptr_offset_from" => { + let [lhs, rhs] = args else { + return Err(MirEvalError::TypeError("wrapping_sub args are not provided")); + }; + let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false)); + let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false)); + let ans = lhs.wrapping_sub(rhs); + destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]) + } + "wrapping_mul" | "unchecked_mul" => { + let [lhs, rhs] = args else { + return Err(MirEvalError::TypeError("wrapping_mul args are not provided")); + }; + let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false)); + let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false)); + let ans = lhs.wrapping_mul(rhs); + destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]) + } + "unchecked_rem" => { + // FIXME: signed + let [lhs, rhs] = args else { + return Err(MirEvalError::TypeError("unchecked_rem args are not provided")); + }; + let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false)); + let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false)); + let ans = lhs.checked_rem(rhs).ok_or_else(|| { + MirEvalError::UndefinedBehavior("unchecked_rem with bad inputs".to_owned()) + })?; + destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]) + } + "unchecked_div" | "exact_div" => { + // FIXME: signed + let [lhs, rhs] = args else { + return Err(MirEvalError::TypeError("unchecked_div args are not provided")); + }; + let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false)); + let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false)); + let ans = lhs.checked_div(rhs).ok_or_else(|| { + MirEvalError::UndefinedBehavior("unchecked_rem with bad inputs".to_owned()) + })?; + destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]) + } + "add_with_overflow" | "sub_with_overflow" | "mul_with_overflow" => { + let [lhs, rhs] = args else { + return Err(MirEvalError::TypeError("const_eval_select args are not provided")); + }; + let result_ty = TyKind::Tuple( + 2, + Substitution::from_iter(Interner, [lhs.ty.clone(), TyBuilder::bool()]), + ) + .intern(Interner); + let op_size = + self.size_of_sized(&lhs.ty, locals, "operand of add_with_overflow")?; + let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false)); + let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false)); + let (ans, u128overflow) = match name { + "add_with_overflow" => lhs.overflowing_add(rhs), + "sub_with_overflow" => lhs.overflowing_sub(rhs), + "mul_with_overflow" => lhs.overflowing_mul(rhs), + _ => unreachable!(), + }; + let is_overflow = u128overflow + || ans.to_le_bytes()[op_size..].iter().any(|&x| x != 0 && x != 255); + let is_overflow = vec![u8::from(is_overflow)]; + let layout = self.layout(&result_ty)?; + let result = self.make_by_layout( + layout.size.bytes_usize(), + &layout, + None, + [ans.to_le_bytes()[0..op_size].to_vec(), is_overflow] + .into_iter() + .map(IntervalOrOwned::Owned), + )?; + destination.write_from_bytes(self, &result) + } + "copy" | "copy_nonoverlapping" => { + let [src, dst, offset] = args else { + return Err(MirEvalError::TypeError("copy_nonoverlapping args are not provided")); + }; + let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else { + return Err(MirEvalError::TypeError("copy_nonoverlapping generic arg is not provided")); + }; + let src = Address::from_bytes(src.get(self)?)?; + let dst = Address::from_bytes(dst.get(self)?)?; + let offset = from_bytes!(usize, offset.get(self)?); + let size = self.size_of_sized(ty, locals, "copy_nonoverlapping ptr type")?; + let size = offset * size; + let src = Interval { addr: src, size }; + let dst = Interval { addr: dst, size }; + dst.write_from_interval(self, src) + } + "offset" | "arith_offset" => { + let [ptr, offset] = args else { + return Err(MirEvalError::TypeError("offset args are not provided")); + }; + let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else { + return Err(MirEvalError::TypeError("offset generic arg is not provided")); + }; + let ptr = u128::from_le_bytes(pad16(ptr.get(self)?, false)); + let offset = u128::from_le_bytes(pad16(offset.get(self)?, false)); + let size = self.size_of_sized(ty, locals, "offset ptr type")? as u128; + let ans = ptr + offset * size; + destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]) + } + "assert_inhabited" | "assert_zero_valid" | "assert_uninit_valid" | "assume" => { + // FIXME: We should actually implement these checks + Ok(()) + } + "forget" => { + // We don't call any drop glue yet, so there is nothing here + Ok(()) + } + "transmute" => { + let [arg] = args else { + return Err(MirEvalError::TypeError("trasmute arg is not provided")); + }; + destination.write_from_interval(self, arg.interval) + } + "likely" | "unlikely" => { + let [arg] = args else { + return Err(MirEvalError::TypeError("likely arg is not provided")); + }; + destination.write_from_interval(self, arg.interval) + } + "ctpop" => { + let [arg] = args else { + return Err(MirEvalError::TypeError("likely arg is not provided")); + }; + let result = u128::from_le_bytes(pad16(arg.get(self)?, false)).count_ones(); + destination + .write_from_bytes(self, &(result as u128).to_le_bytes()[0..destination.size]) + } + "cttz" | "cttz_nonzero" => { + let [arg] = args else { + return Err(MirEvalError::TypeError("likely arg is not provided")); + }; + let result = u128::from_le_bytes(pad16(arg.get(self)?, false)).trailing_zeros(); + destination + .write_from_bytes(self, &(result as u128).to_le_bytes()[0..destination.size]) + } + "const_eval_select" => { + let [tuple, const_fn, _] = args else { + return Err(MirEvalError::TypeError("const_eval_select args are not provided")); + }; + let mut args = vec![const_fn.clone()]; + let TyKind::Tuple(_, fields) = tuple.ty.kind(Interner) else { + return Err(MirEvalError::TypeError("const_eval_select arg[0] is not a tuple")); + }; + let layout = self.layout(&tuple.ty)?; + for (i, field) in fields.iter(Interner).enumerate() { + let field = field.assert_ty_ref(Interner).clone(); + let offset = layout.fields.offset(i).bytes_usize(); + let addr = tuple.interval.addr.offset(offset); + args.push(IntervalAndTy::new(addr, field, self, locals)?); + } + self.exec_fn_trait(&args, destination, locals, span) + } + _ => not_supported!("unknown intrinsic {name}"), + } + } + + fn exec_atomic_intrinsic( + &mut self, + name: &str, + args: &[IntervalAndTy], + generic_args: &Substitution, + destination: Interval, + locals: &Locals<'_>, + _span: MirSpan, + ) -> Result<()> { + // We are a single threaded runtime with no UB checking and no optimization, so + // we can implement these as normal functions. + let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else { + return Err(MirEvalError::TypeError("atomic intrinsic generic arg is not provided")); + }; + let Some(arg0) = args.get(0) else { + return Err(MirEvalError::TypeError("atomic intrinsic arg0 is not provided")); + }; + let arg0_addr = Address::from_bytes(arg0.get(self)?)?; + let arg0_interval = + Interval::new(arg0_addr, self.size_of_sized(ty, locals, "atomic intrinsic type arg")?); + if name.starts_with("load_") { + return destination.write_from_interval(self, arg0_interval); + } + let Some(arg1) = args.get(1) else { + return Err(MirEvalError::TypeError("atomic intrinsic arg1 is not provided")); + }; + if name.starts_with("store_") { + return arg0_interval.write_from_interval(self, arg1.interval); + } + if name.starts_with("xchg_") { + destination.write_from_interval(self, arg0_interval)?; + return arg0_interval.write_from_interval(self, arg1.interval); + } + if name.starts_with("xadd_") { + destination.write_from_interval(self, arg0_interval)?; + let lhs = u128::from_le_bytes(pad16(arg0_interval.get(self)?, false)); + let rhs = u128::from_le_bytes(pad16(arg1.get(self)?, false)); + let ans = lhs.wrapping_add(rhs); + return arg0_interval.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]); + } + if name.starts_with("xsub_") { + destination.write_from_interval(self, arg0_interval)?; + let lhs = u128::from_le_bytes(pad16(arg0_interval.get(self)?, false)); + let rhs = u128::from_le_bytes(pad16(arg1.get(self)?, false)); + let ans = lhs.wrapping_sub(rhs); + return arg0_interval.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]); + } + if name.starts_with("and_") { + destination.write_from_interval(self, arg0_interval)?; + let lhs = u128::from_le_bytes(pad16(arg0_interval.get(self)?, false)); + let rhs = u128::from_le_bytes(pad16(arg1.get(self)?, false)); + let ans = lhs & rhs; + return arg0_interval.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]); + } + if name.starts_with("or_") { + destination.write_from_interval(self, arg0_interval)?; + let lhs = u128::from_le_bytes(pad16(arg0_interval.get(self)?, false)); + let rhs = u128::from_le_bytes(pad16(arg1.get(self)?, false)); + let ans = lhs | rhs; + return arg0_interval.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]); + } + if name.starts_with("xor_") { + destination.write_from_interval(self, arg0_interval)?; + let lhs = u128::from_le_bytes(pad16(arg0_interval.get(self)?, false)); + let rhs = u128::from_le_bytes(pad16(arg1.get(self)?, false)); + let ans = lhs ^ rhs; + return arg0_interval.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]); + } + if name.starts_with("nand_") { + destination.write_from_interval(self, arg0_interval)?; + let lhs = u128::from_le_bytes(pad16(arg0_interval.get(self)?, false)); + let rhs = u128::from_le_bytes(pad16(arg1.get(self)?, false)); + let ans = !(lhs & rhs); + return arg0_interval.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]); + } + let Some(arg2) = args.get(2) else { + return Err(MirEvalError::TypeError("atomic intrinsic arg2 is not provided")); + }; + if name.starts_with("cxchg_") || name.starts_with("cxchgweak_") { + let dest = if arg1.get(self)? == arg0_interval.get(self)? { + arg0_interval.write_from_interval(self, arg2.interval)?; + (arg1.interval, true) + } else { + (arg0_interval, false) + }; + let result_ty = TyKind::Tuple( + 2, + Substitution::from_iter(Interner, [ty.clone(), TyBuilder::bool()]), + ) + .intern(Interner); + let layout = self.layout(&result_ty)?; + let result = self.make_by_layout( + layout.size.bytes_usize(), + &layout, + None, + [IntervalOrOwned::Borrowed(dest.0), IntervalOrOwned::Owned(vec![u8::from(dest.1)])] + .into_iter(), + )?; + return destination.write_from_bytes(self, &result); + } + not_supported!("unknown atomic intrinsic {name}"); + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs new file mode 100644 index 0000000000000..ca4268b8fb00c --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs @@ -0,0 +1,676 @@ +use base_db::{fixture::WithFixture, FileId}; +use hir_def::db::DefDatabase; +use syntax::{TextRange, TextSize}; + +use crate::{db::HirDatabase, test_db::TestDB, Interner, Substitution}; + +use super::{interpret_mir, MirEvalError}; + +fn eval_main(db: &TestDB, file_id: FileId) -> Result<(String, String), MirEvalError> { + let module_id = db.module_for_file(file_id); + let def_map = module_id.def_map(db); + let scope = &def_map[module_id.local_id].scope; + let func_id = scope + .declarations() + .find_map(|x| match x { + hir_def::ModuleDefId::FunctionId(x) => { + if db.function_data(x).name.display(db).to_string() == "main" { + Some(x) + } else { + None + } + } + _ => None, + }) + .expect("no main function found"); + let body = db + .monomorphized_mir_body( + func_id.into(), + Substitution::empty(Interner), + db.trait_environment(func_id.into()), + ) + .map_err(|e| MirEvalError::MirLowerError(func_id.into(), e))?; + let (result, stdout, stderr) = interpret_mir(db, &body, false); + result?; + Ok((stdout, stderr)) +} + +fn check_pass(ra_fixture: &str) { + check_pass_and_stdio(ra_fixture, "", ""); +} + +fn check_pass_and_stdio(ra_fixture: &str, expected_stdout: &str, expected_stderr: &str) { + let (db, file_ids) = TestDB::with_many_files(ra_fixture); + let file_id = *file_ids.last().unwrap(); + let x = eval_main(&db, file_id); + match x { + Err(e) => { + let mut err = String::new(); + let line_index = |size: TextSize| { + let mut size = u32::from(size) as usize; + let mut lines = ra_fixture.lines().enumerate(); + while let Some((i, l)) = lines.next() { + if let Some(x) = size.checked_sub(l.len()) { + size = x; + } else { + return (i, size); + } + } + (usize::MAX, size) + }; + let span_formatter = |file, range: TextRange| { + format!("{:?} {:?}..{:?}", file, line_index(range.start()), line_index(range.end())) + }; + e.pretty_print(&mut err, &db, span_formatter).unwrap(); + panic!("Error in interpreting: {err}"); + } + Ok((stdout, stderr)) => { + assert_eq!(stdout, expected_stdout); + assert_eq!(stderr, expected_stderr); + } + } +} + +#[test] +fn function_with_extern_c_abi() { + check_pass( + r#" +extern "C" fn foo(a: i32, b: i32) -> i32 { + a + b +} + +fn main() { + let x = foo(2, 3); +} + "#, + ); +} + +#[test] +fn drop_basic() { + check_pass( + r#" +//- minicore: drop, add + +struct X<'a>(&'a mut i32); +impl<'a> Drop for X<'a> { + fn drop(&mut self) { + *self.0 += 1; + } +} + +struct NestedX<'a> { f1: X<'a>, f2: X<'a> } + +fn should_not_reach() { + _ // FIXME: replace this function with panic when that works +} + +fn my_drop2(x: X<'_>) { + return; +} + +fn my_drop(x: X<'_>) { + drop(x); +} + +fn main() { + let mut s = 10; + let mut x = X(&mut s); + my_drop(x); + x = X(&mut s); + my_drop2(x); + X(&mut s); // dropped immediately + let x = X(&mut s); + NestedX { f1: x, f2: X(&mut s) }; + if s != 15 { + should_not_reach(); + } +} + "#, + ); +} + +#[test] +fn drop_if_let() { + check_pass( + r#" +//- minicore: drop, add, option, cell, builtin_impls + +use core::cell::Cell; + +struct X<'a>(&'a Cell); +impl<'a> Drop for X<'a> { + fn drop(&mut self) { + self.0.set(self.0.get() + 1) + } +} + +fn should_not_reach() { + _ // FIXME: replace this function with panic when that works +} + +#[test] +fn main() { + let s = Cell::new(0); + let x = Some(X(&s)); + if let Some(y) = x { + if s.get() != 0 { + should_not_reach(); + } + if s.get() != 0 { + should_not_reach(); + } + } else { + should_not_reach(); + } + if s.get() != 1 { + should_not_reach(); + } + let x = Some(X(&s)); + if let None = x { + should_not_reach(); + } else { + if s.get() != 1 { + should_not_reach(); + } + } + if s.get() != 1 { + should_not_reach(); + } +} + "#, + ); +} + +#[test] +fn drop_in_place() { + check_pass( + r#" +//- minicore: drop, add, coerce_unsized +use core::ptr::drop_in_place; + +struct X<'a>(&'a mut i32); +impl<'a> Drop for X<'a> { + fn drop(&mut self) { + *self.0 += 1; + } +} + +fn should_not_reach() { + _ // FIXME: replace this function with panic when that works +} + +fn main() { + let mut s = 2; + let x = X(&mut s); + drop_in_place(&mut x); + drop(x); + if s != 4 { + should_not_reach(); + } + let p: &mut [X] = &mut [X(&mut 2)]; + drop_in_place(p); +} + "#, + ); +} + +#[test] +fn manually_drop() { + check_pass( + r#" +//- minicore: manually_drop +use core::mem::ManuallyDrop; + +struct X; +impl Drop for X { + fn drop(&mut self) { + should_not_reach(); + } +} + +fn should_not_reach() { + _ // FIXME: replace this function with panic when that works +} + +fn main() { + let x = ManuallyDrop::new(X); +} + "#, + ); +} + +#[test] +fn generic_impl_for_trait_with_generic_method() { + check_pass( + r#" +//- minicore: drop +struct S(T); + +trait Tr { + fn f(&self, x: F); +} + +impl Tr for S { + fn f(&self, x: F) { + } +} + +fn main() { + let s = S(1u8); + s.f(5i64); +} + "#, + ); +} + +#[test] +fn index_of_slice_should_preserve_len() { + check_pass( + r#" +//- minicore: index, slice, coerce_unsized + +struct X; + +impl core::ops::Index for [i32] { + type Output = i32; + + fn index(&self, _: X) -> &i32 { + if self.len() != 3 { + should_not_reach(); + } + &self[0] + } +} + +fn should_not_reach() { + _ // FIXME: replace this function with panic when that works +} + +fn main() { + let x: &[i32] = &[1, 2, 3]; + &x[X]; +} + "#, + ); +} + +#[test] +fn memcmp() { + check_pass( + r#" +//- minicore: slice, coerce_unsized, index + +fn should_not_reach() -> bool { + _ // FIXME: replace this function with panic when that works +} + +extern "C" { + fn memcmp(s1: *const u8, s2: *const u8, n: usize) -> i32; +} + +fn my_cmp(x: &[u8], y: &[u8]) -> i32 { + memcmp(x as *const u8, y as *const u8, x.len()) +} + +fn main() { + if my_cmp(&[1, 2, 3], &[1, 2, 3]) != 0 { + should_not_reach(); + } + if my_cmp(&[1, 20, 3], &[1, 2, 3]) <= 0 { + should_not_reach(); + } + if my_cmp(&[1, 2, 3], &[1, 20, 3]) >= 0 { + should_not_reach(); + } +} + "#, + ); +} + +#[test] +fn unix_write_stdout() { + check_pass_and_stdio( + r#" +//- minicore: slice, index, coerce_unsized + +type pthread_key_t = u32; +type c_void = u8; +type c_int = i32; + +extern "C" { + pub fn write(fd: i32, buf: *const u8, count: usize) -> usize; +} + +fn main() { + let stdout = b"stdout"; + let stderr = b"stderr"; + write(1, &stdout[0], 6); + write(2, &stderr[0], 6); +} + "#, + "stdout", + "stderr", + ); +} + +#[test] +fn closure_layout_in_rpit() { + check_pass( + r#" +//- minicore: fn + +fn f(x: F) { + fn g(x: impl Fn()) -> impl FnOnce() { + move || { + x(); + } + } + g(x)(); +} + +fn main() { + f(|| {}); +} + "#, + ); +} + +#[test] +fn from_fn() { + check_pass( + r#" +//- minicore: fn, iterator +struct FromFn(F); + +impl Option> Iterator for FromFn { + type Item = T; + + fn next(&mut self) -> Option { + (self.0)() + } +} + +fn main() { + let mut tokenize = { + FromFn(move || Some(2)) + }; + let s = tokenize.next(); +} + "#, + ); +} + +#[test] +fn for_loop() { + check_pass( + r#" +//- minicore: iterator, add +fn should_not_reach() { + _ // FIXME: replace this function with panic when that works +} + +struct X; +struct XIter(i32); + +impl IntoIterator for X { + type Item = i32; + + type IntoIter = XIter; + + fn into_iter(self) -> Self::IntoIter { + XIter(0) + } +} + +impl Iterator for XIter { + type Item = i32; + + fn next(&mut self) -> Option { + if self.0 == 5 { + None + } else { + self.0 += 1; + Some(self.0) + } + } +} + +fn main() { + let mut s = 0; + for x in X { + s += x; + } + if s != 15 { + should_not_reach(); + } +} + "#, + ); +} + +#[test] +fn field_with_associated_type() { + check_pass( + r#" +//- /b/mod.rs crate:b +pub trait Tr { + fn f(self); +} + +pub trait Tr2 { + type Ty: Tr; +} + +pub struct S { + pub t: T::Ty, +} + +impl S { + pub fn g(&self) { + let k = (self.t, self.t); + self.t.f(); + } +} + +//- /a/mod.rs crate:a deps:b +use b::{Tr, Tr2, S}; + +struct A(i32); +struct B(u8); + +impl Tr for A { + fn f(&self) { + } +} + +impl Tr2 for B { + type Ty = A; +} + +#[test] +fn main() { + let s: S = S { t: A(2) }; + s.g(); +} + "#, + ); +} + +#[test] +fn specialization_array_clone() { + check_pass( + r#" +//- minicore: copy, derive, slice, index, coerce_unsized +impl Clone for [T; N] { + #[inline] + fn clone(&self) -> Self { + SpecArrayClone::clone(self) + } +} + +trait SpecArrayClone: Clone { + fn clone(array: &[Self; N]) -> [Self; N]; +} + +impl SpecArrayClone for T { + #[inline] + default fn clone(array: &[T; N]) -> [T; N] { + // FIXME: panic here when we actually implement specialization. + from_slice(array) + } +} + +fn from_slice(s: &[T]) -> [T; N] { + [s[0]; N] +} + +impl SpecArrayClone for T { + #[inline] + fn clone(array: &[T; N]) -> [T; N] { + *array + } +} + +#[derive(Clone, Copy)] +struct X(i32); + +fn main() { + let ar = [X(1), X(2)]; + ar.clone(); +} + "#, + ); +} + +#[test] +fn short_circuit_operator() { + check_pass( + r#" +fn should_not_reach() -> bool { + _ // FIXME: replace this function with panic when that works +} + +fn main() { + if false && should_not_reach() { + should_not_reach(); + } + true || should_not_reach(); + +} + "#, + ); +} + +#[test] +fn closure_state() { + check_pass( + r#" +//- minicore: fn, add, copy +fn should_not_reach() { + _ // FIXME: replace this function with panic when that works +} + +fn main() { + let mut x = 2; + let mut c = move || { + x += 1; + x + }; + c(); + c(); + c(); + if x != 2 { + should_not_reach(); + } + if c() != 6 { + should_not_reach(); + } +} + "#, + ); +} + +#[test] +fn closure_capture_array_const_generic() { + check_pass( + r#" +//- minicore: fn, add, copy +struct X(i32); + +fn f(mut x: [X; N]) { // -> impl FnOnce() { + let c = || { + x; + }; + c(); +} + +fn main() { + let s = f([X(1)]); + //s(); +} + "#, + ); +} + +#[test] +fn posix_tls() { + check_pass( + r#" +//- minicore: option + +type pthread_key_t = u32; +type c_void = u8; +type c_int = i32; + +extern "C" { + pub fn pthread_key_create( + key: *mut pthread_key_t, + dtor: Option, + ) -> c_int; + pub fn pthread_key_delete(key: pthread_key_t) -> c_int; + pub fn pthread_getspecific(key: pthread_key_t) -> *mut c_void; + pub fn pthread_setspecific(key: pthread_key_t, value: *const c_void) -> c_int; +} + +fn main() { + let mut key = 2; + pthread_key_create(&mut key, None); +} + "#, + ); +} + +#[test] +fn regression_14966() { + check_pass( + r#" +//- minicore: fn, copy, coerce_unsized +trait A { + fn a(&self) {} +} +impl A<()> for () {} + +struct B; +impl B { + pub fn b(s: &dyn A) -> Self { + B + } +} +struct C; +impl C { + fn c(a: &dyn A) -> Self { + let mut c = C; + let b = B::b(a); + c.d(|| a.a()); + c + } + fn d(&mut self, f: impl FnOnce()) {} +} + +fn main() { + C::c(&()); +} +"#, + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index c4dd7c0ace46c..aad1a82f2981c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -1,60 +1,88 @@ //! This module generates a polymorphic MIR from a hir body -use std::{iter, mem, sync::Arc}; +use std::{fmt::Write, iter, mem}; +use base_db::FileId; use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind}; use hir_def::{ body::Body, - expr::{ - Array, BindingAnnotation, BindingId, ExprId, LabelId, Literal, MatchArm, Pat, PatId, - RecordLitField, + data::adt::{StructKind, VariantData}, + hir::{ + ArithOp, Array, BinaryOp, BindingAnnotation, BindingId, ExprId, LabelId, Literal, + LiteralOrConst, MatchArm, Pat, PatId, RecordFieldPat, RecordLitField, }, lang_item::{LangItem, LangItemTarget}, - layout::LayoutError, path::Path, - resolver::{resolver_for_expr, ResolveValueResult, ValueNs}, - DefWithBodyId, EnumVariantId, HasModule, + resolver::{resolver_for_expr, HasResolver, ResolveValueResult, ValueNs}, + AdtId, DefWithBodyId, EnumVariantId, GeneralConstId, HasModule, ItemContainerId, LocalFieldId, + TraitId, TypeOrConstParamId, }; use hir_expand::name::Name; use la_arena::ArenaMap; +use rustc_hash::FxHashMap; +use syntax::TextRange; +use triomphe::Arc; use crate::{ - consteval::ConstEvalError, db::HirDatabase, display::HirDisplay, infer::TypeMismatch, - inhabitedness::is_ty_uninhabited_from, layout::layout_of_ty, mapping::ToChalk, static_lifetime, - utils::generics, Adjust, Adjustment, AutoBorrow, CallableDefId, TyBuilder, TyExt, + consteval::ConstEvalError, + db::HirDatabase, + display::HirDisplay, + infer::{CaptureKind, CapturedItem, TypeMismatch}, + inhabitedness::is_ty_uninhabited_from, + layout::LayoutError, + mapping::ToChalk, + static_lifetime, + traits::FnTrait, + utils::{generics, ClosureSubst}, + Adjust, Adjustment, AutoBorrow, CallableDefId, TyBuilder, TyExt, }; use super::*; mod as_place; +mod pattern_matching; -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone)] struct LoopBlocks { begin: BasicBlockId, /// `None` for loops that are not terminating end: Option, + place: Place, + drop_scope_index: usize, +} + +#[derive(Debug, Clone, Default)] +struct DropScope { + /// locals, in order of definition (so we should run drop glues in reverse order) + locals: Vec, } struct MirLowerCtx<'a> { result: MirBody, owner: DefWithBodyId, current_loop_blocks: Option, + labeled_loop_blocks: FxHashMap, discr_temp: Option, db: &'a dyn HirDatabase, body: &'a Body, infer: &'a InferenceResult, + drop_scopes: Vec, } #[derive(Debug, Clone, PartialEq, Eq)] pub enum MirLowerError { - ConstEvalError(Box), + ConstEvalError(String, Box), LayoutError(LayoutError), IncompleteExpr, + IncompletePattern, + /// Trying to lower a trait function, instead of an implementation + TraitFunctionDefinition(TraitId, Name), UnresolvedName(String), RecordLiteralWithoutPath, - UnresolvedMethod, + UnresolvedMethod(String), UnresolvedField, - MissingFunctionDefinition, + UnsizedTemporary(Ty), + MissingFunctionDefinition(DefWithBodyId, ExprId), TypeMismatch(TypeMismatch), /// This should be never happen. Type mismatch should catch everything. TypeError(&'static str), @@ -63,9 +91,114 @@ pub enum MirLowerError { BreakWithoutLoop, Loop, /// Something that should never happen and is definitely a bug, but we don't want to panic if it happened - ImplementationError(&'static str), + ImplementationError(String), LangItemNotFound(LangItem), MutatingRvalue, + UnresolvedLabel, + UnresolvedUpvar(Place), + UnaccessableLocal, + + // monomorphization errors: + GenericArgNotProvided(TypeOrConstParamId, Substitution), +} + +/// A token to ensuring that each drop scope is popped at most once, thanks to the compiler that checks moves. +struct DropScopeToken; +impl DropScopeToken { + fn pop_and_drop(self, ctx: &mut MirLowerCtx<'_>, current: BasicBlockId) -> BasicBlockId { + std::mem::forget(self); + ctx.pop_drop_scope_internal(current) + } + + /// It is useful when we want a drop scope is syntaxically closed, but we don't want to execute any drop + /// code. Either when the control flow is diverging (so drop code doesn't reached) or when drop is handled + /// for us (for example a block that ended with a return statement. Return will drop everything, so the block shouldn't + /// do anything) + fn pop_assume_dropped(self, ctx: &mut MirLowerCtx<'_>) { + std::mem::forget(self); + ctx.pop_drop_scope_assume_dropped_internal(); + } +} + +// Uncomment this to make `DropScopeToken` a drop bomb. Unfortunately we can't do this in release, since +// in cases that mir lowering fails, we don't handle (and don't need to handle) drop scopes so it will be +// actually reached. `pop_drop_scope_assert_finished` will also detect this case, but doesn't show useful +// stack trace. +// +// impl Drop for DropScopeToken { +// fn drop(&mut self) { +// never!("Drop scope doesn't popped"); +// } +// } + +impl MirLowerError { + pub fn pretty_print( + &self, + f: &mut String, + db: &dyn HirDatabase, + span_formatter: impl Fn(FileId, TextRange) -> String, + ) -> std::result::Result<(), std::fmt::Error> { + match self { + MirLowerError::ConstEvalError(name, e) => { + writeln!(f, "In evaluating constant {name}")?; + match &**e { + ConstEvalError::MirLowerError(e) => e.pretty_print(f, db, span_formatter)?, + ConstEvalError::MirEvalError(e) => e.pretty_print(f, db, span_formatter)?, + } + } + MirLowerError::MissingFunctionDefinition(owner, x) => { + let body = db.body(*owner); + writeln!( + f, + "Missing function definition for {}", + body.pretty_print_expr(db.upcast(), *owner, *x) + )?; + } + MirLowerError::TypeMismatch(e) => { + writeln!( + f, + "Type mismatch: Expected {}, found {}", + e.expected.display(db), + e.actual.display(db), + )?; + } + MirLowerError::GenericArgNotProvided(id, subst) => { + let parent = id.parent; + let param = &db.generic_params(parent).type_or_consts[id.local_id]; + writeln!( + f, + "Generic arg not provided for {}", + param.name().unwrap_or(&Name::missing()).display(db.upcast()) + )?; + writeln!(f, "Provided args: [")?; + for g in subst.iter(Interner) { + write!(f, " {},", g.display(db).to_string())?; + } + writeln!(f, "]")?; + } + MirLowerError::LayoutError(_) + | MirLowerError::UnsizedTemporary(_) + | MirLowerError::IncompleteExpr + | MirLowerError::IncompletePattern + | MirLowerError::UnaccessableLocal + | MirLowerError::TraitFunctionDefinition(_, _) + | MirLowerError::UnresolvedName(_) + | MirLowerError::RecordLiteralWithoutPath + | MirLowerError::UnresolvedMethod(_) + | MirLowerError::UnresolvedField + | MirLowerError::TypeError(_) + | MirLowerError::NotSupported(_) + | MirLowerError::ContinueWithoutLoop + | MirLowerError::BreakWithoutLoop + | MirLowerError::Loop + | MirLowerError::ImplementationError(_) + | MirLowerError::LangItemNotFound(_) + | MirLowerError::MutatingRvalue + | MirLowerError::UnresolvedLabel + | MirLowerError::UnresolvedUpvar(_) => writeln!(f, "{:?}", self)?, + } + Ok(()) + } } macro_rules! not_supported { @@ -76,20 +209,11 @@ macro_rules! not_supported { macro_rules! implementation_error { ($x: expr) => {{ - ::stdx::never!("MIR lower implementation bug: {}", $x); - return Err(MirLowerError::ImplementationError($x)); + ::stdx::never!("MIR lower implementation bug: {}", format!($x)); + return Err(MirLowerError::ImplementationError(format!($x))); }}; } -impl From for MirLowerError { - fn from(value: ConstEvalError) -> Self { - match value { - ConstEvalError::MirLowerError(e) => e, - _ => MirLowerError::ConstEvalError(Box::new(value)), - } - } -} - impl From for MirLowerError { fn from(value: LayoutError) -> Self { MirLowerError::LayoutError(value) @@ -104,12 +228,51 @@ impl MirLowerError { type Result = std::result::Result; -impl MirLowerCtx<'_> { - fn temp(&mut self, ty: Ty) -> Result { +impl<'ctx> MirLowerCtx<'ctx> { + fn new( + db: &'ctx dyn HirDatabase, + owner: DefWithBodyId, + body: &'ctx Body, + infer: &'ctx InferenceResult, + ) -> Self { + let mut basic_blocks = Arena::new(); + let start_block = basic_blocks.alloc(BasicBlock { + statements: vec![], + terminator: None, + is_cleanup: false, + }); + let locals = Arena::new(); + let binding_locals: ArenaMap = ArenaMap::new(); + let mir = MirBody { + basic_blocks, + locals, + start_block, + binding_locals, + param_locals: vec![], + owner, + closures: vec![], + }; + let ctx = MirLowerCtx { + result: mir, + db, + infer, + body, + owner, + current_loop_blocks: None, + labeled_loop_blocks: Default::default(), + discr_temp: None, + drop_scopes: vec![DropScope::default()], + }; + ctx + } + + fn temp(&mut self, ty: Ty, current: BasicBlockId, span: MirSpan) -> Result { if matches!(ty.kind(Interner), TyKind::Slice(_) | TyKind::Dyn(_)) { - implementation_error!("unsized temporaries"); + return Err(MirLowerError::UnsizedTemporary(ty)); } - Ok(self.result.locals.alloc(Local { ty })) + let l = self.result.locals.alloc(Local { ty }); + self.push_storage_live_for_local(l, current, span)?; + Ok(l) } fn lower_expr_to_some_operand( @@ -120,7 +283,7 @@ impl MirLowerCtx<'_> { if !self.has_adjustments(expr_id) { match &self.body.exprs[expr_id] { Expr::Literal(l) => { - let ty = self.expr_ty(expr_id); + let ty = self.expr_ty_without_adjust(expr_id); return Ok(Some((self.lower_literal_to_operand(ty, l)?, current))); } _ => (), @@ -142,7 +305,8 @@ impl MirLowerCtx<'_> { match adjustments.split_last() { Some((last, rest)) => match &last.kind { Adjust::NeverToAny => { - let temp = self.temp(TyKind::Never.intern(Interner))?; + let temp = + self.temp(TyKind::Never.intern(Interner), current, MirSpan::Unknown)?; self.lower_expr_to_place_with_adjust(expr_id, temp.into(), current, rest) } Adjust::Deref(_) => { @@ -200,65 +364,82 @@ impl MirLowerCtx<'_> { mut current: BasicBlockId, ) -> Result> { match &self.body.exprs[expr_id] { - Expr::Missing => Err(MirLowerError::IncompleteExpr), + Expr::Missing => { + if let DefWithBodyId::FunctionId(f) = self.owner { + let assoc = self.db.lookup_intern_function(f); + if let ItemContainerId::TraitId(t) = assoc.container { + let name = &self.db.function_data(f).name; + return Err(MirLowerError::TraitFunctionDefinition(t, name.clone())); + } + } + Err(MirLowerError::IncompleteExpr) + }, Expr::Path(p) => { - let unresolved_name = || MirLowerError::unresolved_path(self.db, p); - let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id); - let pr = resolver - .resolve_path_in_value_ns(self.db.upcast(), p.mod_path()) - .ok_or_else(unresolved_name)?; - let pr = match pr { - ResolveValueResult::ValueNs(v) => v, - ResolveValueResult::Partial(..) => { - if let Some(assoc) = self - .infer - .assoc_resolutions_for_expr(expr_id) - { - match assoc.0 { - hir_def::AssocItemId::ConstId(c) => { - self.lower_const(c, current, place, expr_id.into())?; - return Ok(Some(current)) - }, - _ => not_supported!("associated functions and types"), - } - } else if let Some(variant) = self - .infer - .variant_resolution_for_expr(expr_id) - { - match variant { - VariantId::EnumVariantId(e) => ValueNs::EnumVariantId(e), - VariantId::StructId(s) => ValueNs::StructId(s), - VariantId::UnionId(_) => implementation_error!("Union variant as path"), - } - } else { - return Err(unresolved_name()); + let pr = if let Some((assoc, subst)) = self + .infer + .assoc_resolutions_for_expr(expr_id) + { + match assoc { + hir_def::AssocItemId::ConstId(c) => { + self.lower_const(c.into(), current, place, subst, expr_id.into(), self.expr_ty_without_adjust(expr_id))?; + return Ok(Some(current)) + }, + hir_def::AssocItemId::FunctionId(_) => { + // FnDefs are zero sized, no action is needed. + return Ok(Some(current)) } + hir_def::AssocItemId::TypeAliasId(_) => { + // FIXME: If it is unreachable, use proper error instead of `not_supported`. + not_supported!("associated functions and types") + }, } + } else if let Some(variant) = self + .infer + .variant_resolution_for_expr(expr_id) + { + match variant { + VariantId::EnumVariantId(e) => ValueNs::EnumVariantId(e), + VariantId::StructId(s) => ValueNs::StructId(s), + VariantId::UnionId(_) => implementation_error!("Union variant as path"), + } + } else { + let unresolved_name = || MirLowerError::unresolved_path(self.db, p); + let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id); + resolver + .resolve_path_in_value_ns_fully(self.db.upcast(), p) + .ok_or_else(unresolved_name)? }; match pr { - ValueNs::LocalBinding(pat_id) => { + ValueNs::LocalBinding(_) | ValueNs::StaticId(_) => { + let Some((temp, current)) = self.lower_expr_as_place_without_adjust(current, expr_id, false)? else { + return Ok(None); + }; self.push_assignment( current, place, - Operand::Copy(self.result.binding_locals[pat_id].into()).into(), + Operand::Copy(temp).into(), expr_id.into(), ); Ok(Some(current)) } ValueNs::ConstId(const_id) => { - self.lower_const(const_id, current, place, expr_id.into())?; + self.lower_const(const_id.into(), current, place, Substitution::empty(Interner), expr_id.into(), self.expr_ty_without_adjust(expr_id))?; Ok(Some(current)) } ValueNs::EnumVariantId(variant_id) => { - let ty = self.infer.type_of_expr[expr_id].clone(); - let current = self.lower_enum_variant( - variant_id, - current, - place, - ty, - vec![], - expr_id.into(), - )?; + let variant_data = &self.db.enum_data(variant_id.parent).variants[variant_id.local_id]; + if variant_data.variant_data.kind() == StructKind::Unit { + let ty = self.infer.type_of_expr[expr_id].clone(); + current = self.lower_enum_variant( + variant_id, + current, + place, + ty, + Box::new([]), + expr_id.into(), + )?; + } + // Otherwise its a tuple like enum, treated like a zero sized function, so no action is needed Ok(Some(current)) } ValueNs::GenericParam(p) => { @@ -266,7 +447,7 @@ impl MirLowerCtx<'_> { not_supported!("owner without generic def id"); }; let gen = generics(self.db.upcast(), def); - let ty = self.expr_ty(expr_id); + let ty = self.expr_ty_without_adjust(expr_id); self.push_assignment( current, place, @@ -287,7 +468,7 @@ impl MirLowerCtx<'_> { ); Ok(Some(current)) } - ValueNs::StructId(_) => { + ValueNs::FunctionId(_) | ValueNs::StructId(_) => { // It's probably a unit struct or a zero sized function, so no action is needed. Ok(Some(current)) } @@ -311,12 +492,13 @@ impl MirLowerCtx<'_> { }; self.set_terminator( current, - Terminator::SwitchInt { + TerminatorKind::SwitchInt { discr, targets: SwitchTargets::static_if(1, start_of_then, start_of_else), }, + expr_id.into(), ); - Ok(self.merge_blocks(end_of_then, end_of_else)) + Ok(self.merge_blocks(end_of_then, end_of_else, expr_id.into())) } Expr::Let { pat, expr } => { let Some((cond_place, current)) = self.lower_expr_as_place(current, *expr, true)? else { @@ -326,9 +508,7 @@ impl MirLowerCtx<'_> { current, None, cond_place, - self.expr_ty_after_adjustments(*expr), *pat, - BindingAnnotation::Unannotated, )?; self.write_bytes_to_place( then_target, @@ -346,141 +526,107 @@ impl MirLowerCtx<'_> { MirSpan::Unknown, )?; } - Ok(self.merge_blocks(Some(then_target), else_target)) + Ok(self.merge_blocks(Some(then_target), else_target, expr_id.into())) } Expr::Unsafe { id: _, statements, tail } => { - self.lower_block_to_place(None, statements, current, *tail, place) + self.lower_block_to_place(statements, current, *tail, place, expr_id.into()) } Expr::Block { id: _, statements, tail, label } => { - self.lower_block_to_place(*label, statements, current, *tail, place) + if let Some(label) = label { + self.lower_loop(current, place.clone(), Some(*label), expr_id.into(), |this, begin| { + if let Some(current) = this.lower_block_to_place(statements, begin, *tail, place, expr_id.into())? { + let end = this.current_loop_end()?; + this.set_goto(current, end, expr_id.into()); + } + Ok(()) + }) + } else { + self.lower_block_to_place(statements, current, *tail, place, expr_id.into()) + } } - Expr::Loop { body, label } => self.lower_loop(current, *label, |this, begin| { - if let Some((_, block)) = this.lower_expr_as_place(begin, *body, true)? { - this.set_goto(block, begin); + Expr::Loop { body, label } => self.lower_loop(current, place, *label, expr_id.into(), |this, begin| { + let scope = this.push_drop_scope(); + if let Some((_, mut current)) = this.lower_expr_as_place(begin, *body, true)? { + current = scope.pop_and_drop(this, current); + this.set_goto(current, begin, expr_id.into()); + } else { + scope.pop_assume_dropped(this); } Ok(()) }), Expr::While { condition, body, label } => { - self.lower_loop(current, *label, |this, begin| { + self.lower_loop(current, place, *label, expr_id.into(),|this, begin| { + let scope = this.push_drop_scope(); let Some((discr, to_switch)) = this.lower_expr_to_some_operand(*condition, begin)? else { return Ok(()); }; - let end = this.current_loop_end()?; + let fail_cond = this.new_basic_block(); let after_cond = this.new_basic_block(); this.set_terminator( to_switch, - Terminator::SwitchInt { + TerminatorKind::SwitchInt { discr, - targets: SwitchTargets::static_if(1, after_cond, end), + targets: SwitchTargets::static_if(1, after_cond, fail_cond), }, + expr_id.into(), ); + let fail_cond = this.drop_until_scope(this.drop_scopes.len() - 1, fail_cond); + let end = this.current_loop_end()?; + this.set_goto(fail_cond, end, expr_id.into()); if let Some((_, block)) = this.lower_expr_as_place(after_cond, *body, true)? { - this.set_goto(block, begin); + let block = scope.pop_and_drop(this, block); + this.set_goto(block, begin, expr_id.into()); + } else { + scope.pop_assume_dropped(this); } Ok(()) }) } - &Expr::For { iterable, pat, body, label } => { - let into_iter_fn = self.resolve_lang_item(LangItem::IntoIterIntoIter)? - .as_function().ok_or(MirLowerError::LangItemNotFound(LangItem::IntoIterIntoIter))?; - let iter_next_fn = self.resolve_lang_item(LangItem::IteratorNext)? - .as_function().ok_or(MirLowerError::LangItemNotFound(LangItem::IteratorNext))?; - let option_some = self.resolve_lang_item(LangItem::OptionSome)? - .as_enum_variant().ok_or(MirLowerError::LangItemNotFound(LangItem::OptionSome))?; - let option = option_some.parent; - let into_iter_fn_op = Operand::const_zst( - TyKind::FnDef( - self.db.intern_callable_def(CallableDefId::FunctionId(into_iter_fn)).into(), - Substitution::from1(Interner, self.expr_ty(iterable)) - ).intern(Interner)); - let iter_next_fn_op = Operand::const_zst( - TyKind::FnDef( - self.db.intern_callable_def(CallableDefId::FunctionId(iter_next_fn)).into(), - Substitution::from1(Interner, self.expr_ty(iterable)) - ).intern(Interner)); - let &Some(iterator_ty) = &self.infer.type_of_for_iterator.get(&expr_id) else { - return Err(MirLowerError::TypeError("unknown for loop iterator type")); - }; - let ref_mut_iterator_ty = TyKind::Ref(Mutability::Mut, static_lifetime(), iterator_ty.clone()).intern(Interner); - let item_ty = &self.infer.type_of_pat[pat]; - let option_item_ty = TyKind::Adt(chalk_ir::AdtId(option.into()), Substitution::from1(Interner, item_ty.clone())).intern(Interner); - let iterator_place: Place = self.temp(iterator_ty.clone())?.into(); - let option_item_place: Place = self.temp(option_item_ty.clone())?.into(); - let ref_mut_iterator_place: Place = self.temp(ref_mut_iterator_ty)?.into(); - let Some(current) = self.lower_call_and_args(into_iter_fn_op, Some(iterable).into_iter(), iterator_place.clone(), current, false)? - else { - return Ok(None); - }; - self.push_assignment(current, ref_mut_iterator_place.clone(), Rvalue::Ref(BorrowKind::Mut { allow_two_phase_borrow: false }, iterator_place), expr_id.into()); - self.lower_loop(current, label, |this, begin| { - let Some(current) = this.lower_call(iter_next_fn_op, vec![Operand::Copy(ref_mut_iterator_place)], option_item_place.clone(), begin, false)? - else { - return Ok(()); - }; - let end = this.current_loop_end()?; - let (current, _) = this.pattern_matching_variant( - option_item_ty.clone(), - BindingAnnotation::Unannotated, - option_item_place.into(), - option_some.into(), - current, - pat.into(), - Some(end), - &[pat], &None)?; - if let Some((_, block)) = this.lower_expr_as_place(current, body, true)? { - this.set_goto(block, begin); - } - Ok(()) - }) - }, Expr::Call { callee, args, .. } => { + if let Some((func_id, generic_args)) = + self.infer.method_resolution(expr_id) { + let ty = chalk_ir::TyKind::FnDef( + CallableDefId::FunctionId(func_id).to_chalk(self.db), + generic_args, + ) + .intern(Interner); + let func = Operand::from_bytes(vec![], ty); + return self.lower_call_and_args( + func, + iter::once(*callee).chain(args.iter().copied()), + place, + current, + self.is_uninhabited(expr_id), + expr_id.into(), + ); + } let callee_ty = self.expr_ty_after_adjustments(*callee); match &callee_ty.data(Interner).kind { chalk_ir::TyKind::FnDef(..) => { let func = Operand::from_bytes(vec![], callee_ty.clone()); - self.lower_call_and_args(func, args.iter().copied(), place, current, self.is_uninhabited(expr_id)) + self.lower_call_and_args(func, args.iter().copied(), place, current, self.is_uninhabited(expr_id), expr_id.into()) } - TyKind::Scalar(_) - | TyKind::Tuple(_, _) - | TyKind::Array(_, _) - | TyKind::Adt(_, _) - | TyKind::Str - | TyKind::Foreign(_) - | TyKind::Slice(_) => { - return Err(MirLowerError::TypeError("function call on data type")) + chalk_ir::TyKind::Function(_) => { + let Some((func, current)) = self.lower_expr_to_some_operand(*callee, current)? else { + return Ok(None); + }; + self.lower_call_and_args(func, args.iter().copied(), place, current, self.is_uninhabited(expr_id), expr_id.into()) } - TyKind::Error => return Err(MirLowerError::MissingFunctionDefinition), - TyKind::AssociatedType(_, _) - | TyKind::Raw(_, _) - | TyKind::Ref(_, _, _) - | TyKind::OpaqueType(_, _) - | TyKind::Never - | TyKind::Closure(_, _) - | TyKind::Generator(_, _) - | TyKind::GeneratorWitness(_, _) - | TyKind::Placeholder(_) - | TyKind::Dyn(_) - | TyKind::Alias(_) - | TyKind::Function(_) - | TyKind::BoundVar(_) - | TyKind::InferenceVar(_, _) => not_supported!("dynamic function call"), + TyKind::Error => return Err(MirLowerError::MissingFunctionDefinition(self.owner, expr_id)), + _ => return Err(MirLowerError::TypeError("function call on bad type")), } } - Expr::MethodCall { receiver, args, .. } => { + Expr::MethodCall { receiver, args, method_name, .. } => { let (func_id, generic_args) = - self.infer.method_resolution(expr_id).ok_or(MirLowerError::UnresolvedMethod)?; - let ty = chalk_ir::TyKind::FnDef( - CallableDefId::FunctionId(func_id).to_chalk(self.db), - generic_args, - ) - .intern(Interner); - let func = Operand::from_bytes(vec![], ty); + self.infer.method_resolution(expr_id).ok_or_else(|| MirLowerError::UnresolvedMethod(method_name.display(self.db.upcast()).to_string()))?; + let func = Operand::from_fn(self.db, func_id, generic_args); self.lower_call_and_args( func, iter::once(*receiver).chain(args.iter().copied()), place, current, self.is_uninhabited(expr_id), + expr_id.into(), ) } Expr::Match { expr, arms } => { @@ -488,23 +634,27 @@ impl MirLowerCtx<'_> { else { return Ok(None); }; - let cond_ty = self.expr_ty_after_adjustments(*expr); let mut end = None; for MatchArm { pat, guard, expr } in arms.iter() { - if guard.is_some() { - not_supported!("pattern matching with guard"); - } - let (then, otherwise) = self.pattern_match( + let (then, mut otherwise) = self.pattern_match( current, None, cond_place.clone(), - cond_ty.clone(), *pat, - BindingAnnotation::Unannotated, )?; + let then = if let &Some(guard) = guard { + let next = self.new_basic_block(); + let o = otherwise.get_or_insert_with(|| self.new_basic_block()); + if let Some((discr, c)) = self.lower_expr_to_some_operand(guard, then)? { + self.set_terminator(c, TerminatorKind::SwitchInt { discr, targets: SwitchTargets::static_if(1, next, *o) }, expr_id.into()); + } + next + } else { + then + }; if let Some(block) = self.lower_expr_to_place(*expr, place.clone(), then)? { let r = end.get_or_insert_with(|| self.new_basic_block()); - self.set_goto(block, *r); + self.set_goto(block, *r, expr_id.into()); } match otherwise { Some(o) => current = o, @@ -516,32 +666,43 @@ impl MirLowerCtx<'_> { } } if self.is_unterminated(current) { - self.set_terminator(current, Terminator::Unreachable); + self.set_terminator(current, TerminatorKind::Unreachable, expr_id.into()); } Ok(end) } - Expr::Continue { label } => match label { - Some(_) => not_supported!("continue with label"), - None => { - let loop_data = - self.current_loop_blocks.ok_or(MirLowerError::ContinueWithoutLoop)?; - self.set_goto(current, loop_data.begin); - Ok(None) - } + Expr::Continue { label } => { + let loop_data = match label { + Some(l) => self.labeled_loop_blocks.get(l).ok_or(MirLowerError::UnresolvedLabel)?, + None => self.current_loop_blocks.as_ref().ok_or(MirLowerError::ContinueWithoutLoop)?, + }; + let begin = loop_data.begin; + current = self.drop_until_scope(loop_data.drop_scope_index, current); + self.set_goto(current, begin, expr_id.into()); + Ok(None) }, - Expr::Break { expr, label } => { - if expr.is_some() { - not_supported!("break with value"); + &Expr::Break { expr, label } => { + if let Some(expr) = expr { + let loop_data = match label { + Some(l) => self.labeled_loop_blocks.get(&l).ok_or(MirLowerError::UnresolvedLabel)?, + None => self.current_loop_blocks.as_ref().ok_or(MirLowerError::BreakWithoutLoop)?, + }; + let Some(c) = self.lower_expr_to_place(expr, loop_data.place.clone(), current)? else { + return Ok(None); + }; + current = c; } - match label { - Some(_) => not_supported!("break with label"), + let (end, drop_scope) = match label { + Some(l) => { + let loop_blocks = self.labeled_loop_blocks.get(&l).ok_or(MirLowerError::UnresolvedLabel)?; + (loop_blocks.end.expect("We always generate end for labeled loops"), loop_blocks.drop_scope_index) + }, None => { - let end = - self.current_loop_end()?; - self.set_goto(current, end); - Ok(None) - } - } + (self.current_loop_end()?, self.current_loop_blocks.as_ref().unwrap().drop_scope_index) + }, + }; + current = self.drop_until_scope(drop_scope, current); + self.set_goto(current, end, expr_id.into()); + Ok(None) } Expr::Return { expr } => { if let Some(expr) = expr { @@ -551,11 +712,22 @@ impl MirLowerCtx<'_> { return Ok(None); } } - self.set_terminator(current, Terminator::Return); + current = self.drop_until_scope(0, current); + self.set_terminator(current, TerminatorKind::Return, expr_id.into()); Ok(None) } Expr::Yield { .. } => not_supported!("yield"), - Expr::RecordLit { fields, path, .. } => { + Expr::RecordLit { fields, path, spread, ellipsis: _, is_assignee_expr: _ } => { + let spread_place = match spread { + &Some(x) => { + let Some((p, c)) = self.lower_expr_as_place(current, x, true)? else { + return Ok(None); + }; + current = c; + Some(p) + }, + None => None, + }; let variant_id = self .infer .variant_resolution_for_expr(expr_id) @@ -563,7 +735,7 @@ impl MirLowerCtx<'_> { Some(p) => MirLowerError::UnresolvedName(p.display(self.db).to_string()), None => MirLowerError::RecordLiteralWithoutPath, })?; - let subst = match self.expr_ty(expr_id).kind(Interner) { + let subst = match self.expr_ty_without_adjust(expr_id).kind(Interner) { TyKind::Adt(_, s) => s.clone(), _ => not_supported!("Non ADT record literal"), }; @@ -585,9 +757,23 @@ impl MirLowerCtx<'_> { place, Rvalue::Aggregate( AggregateKind::Adt(variant_id, subst), - operands.into_iter().map(|x| x).collect::>().ok_or( - MirLowerError::TypeError("missing field in record literal"), - )?, + match spread_place { + Some(sp) => operands.into_iter().enumerate().map(|(i, x)| { + match x { + Some(x) => x, + None => { + let p = sp.project(ProjectionElem::Field(FieldId { + parent: variant_id, + local_id: LocalFieldId::from_raw(RawIdx::from(i as u32)), + })); + Operand::Copy(p) + }, + } + }).collect(), + None => operands.into_iter().collect::>().ok_or( + MirLowerError::TypeError("missing field in record literal"), + )?, + }, ), expr_id.into(), ); @@ -599,20 +785,19 @@ impl MirLowerCtx<'_> { }; let local_id = variant_data.field(name).ok_or(MirLowerError::UnresolvedField)?; - let mut place = place; - place - .projection - .push(PlaceElem::Field(FieldId { parent: union_id.into(), local_id })); + let place = place.project(PlaceElem::Field(FieldId { parent: union_id.into(), local_id })); self.lower_expr_to_place(*expr, place, current) } } } Expr::Await { .. } => not_supported!("await"), - Expr::Try { .. } => not_supported!("? operator"), Expr::Yeet { .. } => not_supported!("yeet"), - Expr::TryBlock { .. } => not_supported!("try block"), Expr::Async { .. } => not_supported!("async block"), - Expr::Const { .. } => not_supported!("anonymous const block"), + &Expr::Const(id) => { + let subst = self.placeholder_subst(); + self.lower_const(id.into(), current, place, subst, expr_id.into(), self.expr_ty_without_adjust(expr_id))?; + Ok(Some(current)) + }, Expr::Cast { expr, type_ref: _ } => { let Some((x, current)) = self.lower_expr_to_some_operand(*expr, current)? else { return Ok(None); @@ -635,21 +820,30 @@ impl MirLowerCtx<'_> { self.push_assignment(current, place, Rvalue::Ref(bk, p), expr_id.into()); Ok(Some(current)) } - Expr::Box { .. } => not_supported!("box expression"), - Expr::Field { .. } | Expr::Index { .. } | Expr::UnaryOp { op: hir_def::expr::UnaryOp::Deref, .. } => { + Expr::Box { expr } => { + let ty = self.expr_ty_after_adjustments(*expr); + self.push_assignment(current, place.clone(), Rvalue::ShallowInitBoxWithAlloc(ty), expr_id.into()); + let Some((operand, current)) = self.lower_expr_to_some_operand(*expr, current)? else { + return Ok(None); + }; + let p = place.project(ProjectionElem::Deref); + self.push_assignment(current, p, operand.into(), expr_id.into()); + Ok(Some(current)) + }, + Expr::Field { .. } | Expr::Index { .. } | Expr::UnaryOp { op: hir_def::hir::UnaryOp::Deref, .. } => { let Some((p, current)) = self.lower_expr_as_place_without_adjust(current, expr_id, true)? else { return Ok(None); }; self.push_assignment(current, place, Operand::Copy(p).into(), expr_id.into()); Ok(Some(current)) } - Expr::UnaryOp { expr, op: op @ (hir_def::expr::UnaryOp::Not | hir_def::expr::UnaryOp::Neg) } => { + Expr::UnaryOp { expr, op: op @ (hir_def::hir::UnaryOp::Not | hir_def::hir::UnaryOp::Neg) } => { let Some((operand, current)) = self.lower_expr_to_some_operand(*expr, current)? else { return Ok(None); }; let operation = match op { - hir_def::expr::UnaryOp::Not => UnOp::Not, - hir_def::expr::UnaryOp::Neg => UnOp::Neg, + hir_def::hir::UnaryOp::Not => UnOp::Not, + hir_def::hir::UnaryOp::Neg => UnOp::Neg, _ => unreachable!(), }; self.push_assignment( @@ -662,24 +856,93 @@ impl MirLowerCtx<'_> { }, Expr::BinaryOp { lhs, rhs, op } => { let op = op.ok_or(MirLowerError::IncompleteExpr)?; - if let hir_def::expr::BinaryOp::Assignment { op } = op { - if op.is_some() { - not_supported!("assignment with arith op (like +=)"); + let is_builtin = 'b: { + // Without adjust here is a hack. We assume that we know every possible adjustment + // for binary operator, and use without adjust to simplify our conditions. + let lhs_ty = self.expr_ty_without_adjust(*lhs); + let rhs_ty = self.expr_ty_without_adjust(*rhs); + if matches!(op ,BinaryOp::CmpOp(syntax::ast::CmpOp::Eq { .. })) { + if lhs_ty.as_raw_ptr().is_some() && rhs_ty.as_raw_ptr().is_some() { + break 'b true; + } } - let Some((lhs_place, current)) = + let builtin_inequal_impls = matches!( + op, + BinaryOp::ArithOp(ArithOp::Shl | ArithOp::Shr) | BinaryOp::Assignment { op: Some(ArithOp::Shl | ArithOp::Shr) } + ); + lhs_ty.is_scalar() && rhs_ty.is_scalar() && (lhs_ty == rhs_ty || builtin_inequal_impls) + }; + if !is_builtin { + if let Some((func_id, generic_args)) = self.infer.method_resolution(expr_id) { + let func = Operand::from_fn(self.db, func_id, generic_args); + return self.lower_call_and_args( + func, + [*lhs, *rhs].into_iter(), + place, + current, + self.is_uninhabited(expr_id), + expr_id.into(), + ); + } + } + if let hir_def::hir::BinaryOp::Assignment { op } = op { + if let Some(op) = op { + // last adjustment is `&mut` which we don't want it. + let adjusts = self + .infer + .expr_adjustments + .get(lhs) + .and_then(|x| x.split_last()) + .map(|x| x.1) + .ok_or(MirLowerError::TypeError("adjustment of binary op was missing"))?; + let Some((lhs_place, current)) = + self.lower_expr_as_place_with_adjust(current, *lhs, false, adjusts)? + else { + return Ok(None); + }; + let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)? else { + return Ok(None); + }; + let r_value = Rvalue::CheckedBinaryOp(op.into(), Operand::Copy(lhs_place.clone()), rhs_op); + self.push_assignment(current, lhs_place, r_value, expr_id.into()); + return Ok(Some(current)); + } else { + let Some((lhs_place, current)) = self.lower_expr_as_place(current, *lhs, false)? - else { - return Ok(None); - }; - let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)? else { - return Ok(None); - }; - self.push_assignment(current, lhs_place, rhs_op.into(), expr_id.into()); - return Ok(Some(current)); + else { + return Ok(None); + }; + let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)? else { + return Ok(None); + }; + self.push_assignment(current, lhs_place, rhs_op.into(), expr_id.into()); + return Ok(Some(current)); + } } let Some((lhs_op, current)) = self.lower_expr_to_some_operand(*lhs, current)? else { return Ok(None); }; + if let hir_def::hir::BinaryOp::LogicOp(op) = op { + let value_to_short = match op { + syntax::ast::LogicOp::And => 0, + syntax::ast::LogicOp::Or => 1, + }; + let start_of_then = self.new_basic_block(); + self.push_assignment(start_of_then, place.clone(), lhs_op.clone().into(), expr_id.into()); + let end_of_then = Some(start_of_then); + let start_of_else = self.new_basic_block(); + let end_of_else = + self.lower_expr_to_place(*rhs, place, start_of_else)?; + self.set_terminator( + current, + TerminatorKind::SwitchInt { + discr: lhs_op, + targets: SwitchTargets::static_if(value_to_short, start_of_then, start_of_else), + }, + expr_id.into(), + ); + return Ok(self.merge_blocks(end_of_then, end_of_else, expr_id.into())); + } let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)? else { return Ok(None); }; @@ -688,13 +951,13 @@ impl MirLowerCtx<'_> { place, Rvalue::CheckedBinaryOp( match op { - hir_def::expr::BinaryOp::LogicOp(op) => match op { - hir_def::expr::LogicOp::And => BinOp::BitAnd, // FIXME: make these short circuit - hir_def::expr::LogicOp::Or => BinOp::BitOr, + hir_def::hir::BinaryOp::LogicOp(op) => match op { + hir_def::hir::LogicOp::And => BinOp::BitAnd, // FIXME: make these short circuit + hir_def::hir::LogicOp::Or => BinOp::BitOr, }, - hir_def::expr::BinaryOp::ArithOp(op) => BinOp::from(op), - hir_def::expr::BinaryOp::CmpOp(op) => BinOp::from(op), - hir_def::expr::BinaryOp::Assignment { .. } => unreachable!(), // handled above + hir_def::hir::BinaryOp::ArithOp(op) => BinOp::from(op), + hir_def::hir::BinaryOp::CmpOp(op) => BinOp::from(op), + hir_def::hir::BinaryOp::Assignment { .. } => unreachable!(), // handled above }, lhs_op, rhs_op, @@ -703,8 +966,96 @@ impl MirLowerCtx<'_> { ); Ok(Some(current)) } - Expr::Range { .. } => not_supported!("range"), - Expr::Closure { .. } => not_supported!("closure"), + &Expr::Range { lhs, rhs, range_type: _ } => { + let ty = self.expr_ty_without_adjust(expr_id); + let Some((adt, subst)) = ty.as_adt() else { + return Err(MirLowerError::TypeError("Range type is not adt")); + }; + let AdtId::StructId(st) = adt else { + return Err(MirLowerError::TypeError("Range type is not struct")); + }; + let mut lp = None; + let mut rp = None; + if let Some(x) = lhs { + let Some((o, c)) = self.lower_expr_to_some_operand(x, current)? else { + return Ok(None); + }; + lp = Some(o); + current = c; + } + if let Some(x) = rhs { + let Some((o, c)) = self.lower_expr_to_some_operand(x, current)? else { + return Ok(None); + }; + rp = Some(o); + current = c; + } + self.push_assignment( + current, + place, + Rvalue::Aggregate( + AggregateKind::Adt(st.into(), subst.clone()), + self.db.struct_data(st).variant_data.fields().iter().map(|x| { + let o = match x.1.name.as_str() { + Some("start") => lp.take(), + Some("end") => rp.take(), + Some("exhausted") => Some(Operand::from_bytes(vec![0], TyBuilder::bool())), + _ => None, + }; + o.ok_or(MirLowerError::UnresolvedField) + }).collect::>()?, + ), + expr_id.into(), + ); + Ok(Some(current)) + }, + Expr::Closure { .. } => { + let ty = self.expr_ty_without_adjust(expr_id); + let TyKind::Closure(id, _) = ty.kind(Interner) else { + not_supported!("closure with non closure type"); + }; + self.result.closures.push(*id); + let (captures, _) = self.infer.closure_info(id); + let mut operands = vec![]; + for capture in captures.iter() { + let p = Place { + local: self.binding_local(capture.place.local)?, + projection: capture.place.projections.clone().into_iter().map(|x| { + match x { + ProjectionElem::Deref => ProjectionElem::Deref, + ProjectionElem::Field(x) => ProjectionElem::Field(x), + ProjectionElem::TupleOrClosureField(x) => ProjectionElem::TupleOrClosureField(x), + ProjectionElem::ConstantIndex { offset, from_end } => ProjectionElem::ConstantIndex { offset, from_end }, + ProjectionElem::Subslice { from, to } => ProjectionElem::Subslice { from, to }, + ProjectionElem::OpaqueCast(x) => ProjectionElem::OpaqueCast(x), + ProjectionElem::Index(x) => match x { }, + } + }).collect(), + }; + match &capture.kind { + CaptureKind::ByRef(bk) => { + let placeholder_subst = self.placeholder_subst(); + let tmp_ty = capture.ty.clone().substitute(Interner, &placeholder_subst); + let tmp: Place = self.temp(tmp_ty, current, capture.span)?.into(); + self.push_assignment( + current, + tmp.clone(), + Rvalue::Ref(bk.clone(), p), + capture.span, + ); + operands.push(Operand::Move(tmp)); + }, + CaptureKind::ByValue => operands.push(Operand::Move(p)), + } + } + self.push_assignment( + current, + place, + Rvalue::Aggregate(AggregateKind::Closure(ty), operands.into()), + expr_id.into(), + ); + Ok(Some(current)) + }, Expr::Tuple { exprs, is_assignee_expr: _ } => { let Some(values) = exprs .iter() @@ -720,7 +1071,7 @@ impl MirLowerCtx<'_> { return Ok(None); }; let r = Rvalue::Aggregate( - AggregateKind::Tuple(self.expr_ty(expr_id)), + AggregateKind::Tuple(self.expr_ty_without_adjust(expr_id)), values, ); self.push_assignment(current, place, r, expr_id.into()); @@ -728,7 +1079,7 @@ impl MirLowerCtx<'_> { } Expr::Array(l) => match l { Array::ElementList { elements, .. } => { - let elem_ty = match &self.expr_ty(expr_id).data(Interner).kind { + let elem_ty = match &self.expr_ty_without_adjust(expr_id).data(Interner).kind { TyKind::Array(ty, _) => ty.clone(), _ => { return Err(MirLowerError::TypeError( @@ -756,10 +1107,25 @@ impl MirLowerCtx<'_> { self.push_assignment(current, place, r, expr_id.into()); Ok(Some(current)) } - Array::Repeat { .. } => not_supported!("array repeat"), + Array::Repeat { initializer, .. } => { + let Some((init, current)) = self.lower_expr_to_some_operand(*initializer, current)? else { + return Ok(None); + }; + let len = match &self.expr_ty_without_adjust(expr_id).data(Interner).kind { + TyKind::Array(_, len) => len.clone(), + _ => { + return Err(MirLowerError::TypeError( + "Array repeat expression with non array type", + )) + } + }; + let r = Rvalue::Repeat(init, len); + self.push_assignment(current, place, r, expr_id.into()); + Ok(Some(current)) + }, }, Expr::Literal(l) => { - let ty = self.expr_ty(expr_id); + let ty = self.expr_ty_without_adjust(expr_id); let op = self.lower_literal_to_operand(ty, l)?; self.push_assignment(current, place, op.into(), expr_id.into()); Ok(Some(current)) @@ -768,17 +1134,25 @@ impl MirLowerCtx<'_> { } } + fn placeholder_subst(&mut self) -> Substitution { + let placeholder_subst = match self.owner.as_generic_def_id() { + Some(x) => TyBuilder::placeholder_subst(self.db, x), + None => Substitution::empty(Interner), + }; + placeholder_subst + } + fn push_field_projection(&self, place: &mut Place, expr_id: ExprId) -> Result<()> { if let Expr::Field { expr, name } = &self.body[expr_id] { if let TyKind::Tuple(..) = self.expr_ty_after_adjustments(*expr).kind(Interner) { let index = name .as_tuple_index() .ok_or(MirLowerError::TypeError("named field on tuple"))?; - place.projection.push(ProjectionElem::TupleField(index)) + *place = place.project(ProjectionElem::TupleOrClosureField(index)) } else { let field = self.infer.field_resolution(expr_id).ok_or(MirLowerError::UnresolvedField)?; - place.projection.push(ProjectionElem::Field(field)); + *place = place.project(ProjectionElem::Field(field)); } } else { not_supported!("") @@ -786,33 +1160,75 @@ impl MirLowerCtx<'_> { Ok(()) } + fn lower_literal_or_const_to_operand( + &mut self, + ty: Ty, + loc: &LiteralOrConst, + ) -> Result { + match loc { + LiteralOrConst::Literal(l) => self.lower_literal_to_operand(ty, l), + LiteralOrConst::Const(c) => { + let unresolved_name = || MirLowerError::unresolved_path(self.db, c); + let resolver = self.owner.resolver(self.db.upcast()); + let pr = resolver + .resolve_path_in_value_ns(self.db.upcast(), c) + .ok_or_else(unresolved_name)?; + match pr { + ResolveValueResult::ValueNs(v) => { + if let ValueNs::ConstId(c) = v { + self.lower_const_to_operand(Substitution::empty(Interner), c.into(), ty) + } else { + not_supported!("bad path in range pattern"); + } + } + ResolveValueResult::Partial(_, _) => { + not_supported!("associated constants in range pattern") + } + } + } + } + } + fn lower_literal_to_operand(&mut self, ty: Ty, l: &Literal) -> Result { - let size = layout_of_ty(self.db, &ty, self.owner.module(self.db.upcast()).krate())? + let size = self + .db + .layout_of_ty(ty.clone(), self.owner.module(self.db.upcast()).krate())? .size .bytes_usize(); let bytes = match l { - hir_def::expr::Literal::String(b) => { + hir_def::hir::Literal::String(b) => { let b = b.as_bytes(); - let mut data = vec![]; + let mut data = Vec::with_capacity(mem::size_of::() * 2); data.extend(0usize.to_le_bytes()); data.extend(b.len().to_le_bytes()); let mut mm = MemoryMap::default(); mm.insert(0, b.to_vec()); return Ok(Operand::from_concrete_const(data, mm, ty)); } - hir_def::expr::Literal::ByteString(b) => { - let mut data = vec![]; + hir_def::hir::Literal::CString(b) => { + let b = b.as_bytes(); + let bytes = b.iter().copied().chain(iter::once(0)).collect::>(); + + let mut data = Vec::with_capacity(mem::size_of::() * 2); + data.extend(0usize.to_le_bytes()); + data.extend(bytes.len().to_le_bytes()); + let mut mm = MemoryMap::default(); + mm.insert(0, bytes); + return Ok(Operand::from_concrete_const(data, mm, ty)); + } + hir_def::hir::Literal::ByteString(b) => { + let mut data = Vec::with_capacity(mem::size_of::() * 2); data.extend(0usize.to_le_bytes()); data.extend(b.len().to_le_bytes()); let mut mm = MemoryMap::default(); mm.insert(0, b.to_vec()); return Ok(Operand::from_concrete_const(data, mm, ty)); } - hir_def::expr::Literal::Char(c) => u32::from(*c).to_le_bytes().into(), - hir_def::expr::Literal::Bool(b) => vec![*b as u8], - hir_def::expr::Literal::Int(x, _) => x.to_le_bytes()[0..size].into(), - hir_def::expr::Literal::Uint(x, _) => x.to_le_bytes()[0..size].into(), - hir_def::expr::Literal::Float(f, _) => match size { + hir_def::hir::Literal::Char(c) => u32::from(*c).to_le_bytes().into(), + hir_def::hir::Literal::Bool(b) => vec![*b as u8], + hir_def::hir::Literal::Int(x, _) => x.to_le_bytes()[0..size].into(), + hir_def::hir::Literal::Uint(x, _) => x.to_le_bytes()[0..size].into(), + hir_def::hir::Literal::Float(f, _) => match size { 8 => f.into_f64().to_le_bytes().into(), 4 => f.into_f32().to_le_bytes().into(), _ => { @@ -829,24 +1245,34 @@ impl MirLowerCtx<'_> { fn lower_const( &mut self, - const_id: hir_def::ConstId, + const_id: GeneralConstId, prev_block: BasicBlockId, place: Place, + subst: Substitution, span: MirSpan, + ty: Ty, ) -> Result<()> { - let c = self.db.const_eval(const_id)?; - self.write_const_to_place(c, prev_block, place, span) + let c = self.lower_const_to_operand(subst, const_id, ty)?; + self.push_assignment(prev_block, place, c.into(), span); + Ok(()) } - fn write_const_to_place( + fn lower_const_to_operand( &mut self, - c: Const, - prev_block: BasicBlockId, - place: Place, - span: MirSpan, - ) -> Result<()> { - self.push_assignment(prev_block, place, Operand::Constant(c).into(), span); - Ok(()) + subst: Substitution, + const_id: GeneralConstId, + ty: Ty, + ) -> Result { + let c = if subst.len(Interner) != 0 { + // We can't evaluate constant with substitution now, as generics are not monomorphized in lowering. + intern_const_scalar(ConstScalar::UnevaluatedConst(const_id, subst), ty) + } else { + let name = const_id.name(self.db.upcast()); + self.db + .const_eval(const_id.into(), subst) + .map_err(|e| MirLowerError::ConstEvalError(name, Box::new(e)))? + }; + Ok(Operand::Constant(c)) } fn write_bytes_to_place( @@ -867,12 +1293,12 @@ impl MirLowerCtx<'_> { prev_block: BasicBlockId, place: Place, ty: Ty, - fields: Vec, + fields: Box<[Operand]>, span: MirSpan, ) -> Result { let subst = match ty.kind(Interner) { TyKind::Adt(_, subst) => subst.clone(), - _ => not_supported!("Non ADT enum"), + _ => implementation_error!("Non ADT enum"), }; self.push_assignment( prev_block, @@ -890,6 +1316,7 @@ impl MirLowerCtx<'_> { place: Place, mut current: BasicBlockId, is_uninhabited: bool, + span: MirSpan, ) -> Result> { let Some(args) = args .map(|arg| { @@ -904,21 +1331,22 @@ impl MirLowerCtx<'_> { else { return Ok(None); }; - self.lower_call(func, args, place, current, is_uninhabited) + self.lower_call(func, args.into(), place, current, is_uninhabited, span) } fn lower_call( &mut self, func: Operand, - args: Vec, + args: Box<[Operand]>, place: Place, current: BasicBlockId, is_uninhabited: bool, + span: MirSpan, ) -> Result> { let b = if is_uninhabited { None } else { Some(self.new_basic_block()) }; self.set_terminator( current, - Terminator::Call { + TerminatorKind::Call { func, args, destination: place, @@ -926,6 +1354,7 @@ impl MirLowerCtx<'_> { cleanup: None, from_hir_call: true, }, + span, ); Ok(b) } @@ -934,15 +1363,15 @@ impl MirLowerCtx<'_> { self.result.basic_blocks[source].terminator.is_none() } - fn set_terminator(&mut self, source: BasicBlockId, terminator: Terminator) { - self.result.basic_blocks[source].terminator = Some(terminator); + fn set_terminator(&mut self, source: BasicBlockId, terminator: TerminatorKind, span: MirSpan) { + self.result.basic_blocks[source].terminator = Some(Terminator { span, kind: terminator }); } - fn set_goto(&mut self, source: BasicBlockId, target: BasicBlockId) { - self.set_terminator(source, Terminator::Goto { target }); + fn set_goto(&mut self, source: BasicBlockId, target: BasicBlockId, span: MirSpan) { + self.set_terminator(source, TerminatorKind::Goto { target }, span); } - fn expr_ty(&self, e: ExprId) -> Ty { + fn expr_ty_without_adjust(&self, e: ExprId) -> Ty { self.infer[e].clone() } @@ -953,7 +1382,7 @@ impl MirLowerCtx<'_> { ty = Some(x.target.clone()); } } - ty.unwrap_or_else(|| self.expr_ty(e)) + ty.unwrap_or_else(|| self.expr_ty_without_adjust(e)) } fn push_statement(&mut self, block: BasicBlockId, statement: Statement) { @@ -970,293 +1399,14 @@ impl MirLowerCtx<'_> { self.push_statement(block, StatementKind::Assign(place, rvalue).with_span(span)); } - /// It gets a `current` unterminated block, appends some statements and possibly a terminator to it to check if - /// the pattern matches and write bindings, and returns two unterminated blocks, one for the matched path (which - /// can be the `current` block) and one for the mismatched path. If the input pattern is irrefutable, the - /// mismatched path block is `None`. - /// - /// By default, it will create a new block for mismatched path. If you already have one, you can provide it with - /// `current_else` argument to save an unneccessary jump. If `current_else` isn't `None`, the result mismatched path - /// wouldn't be `None` as well. Note that this function will add jumps to the beginning of the `current_else` block, - /// so it should be an empty block. - fn pattern_match( - &mut self, - mut current: BasicBlockId, - mut current_else: Option, - mut cond_place: Place, - mut cond_ty: Ty, - pattern: PatId, - mut binding_mode: BindingAnnotation, - ) -> Result<(BasicBlockId, Option)> { - Ok(match &self.body.pats[pattern] { - Pat::Missing => return Err(MirLowerError::IncompleteExpr), - Pat::Wild => (current, current_else), - Pat::Tuple { args, ellipsis } => { - pattern_matching_dereference(&mut cond_ty, &mut binding_mode, &mut cond_place); - let subst = match cond_ty.kind(Interner) { - TyKind::Tuple(_, s) => s, - _ => { - return Err(MirLowerError::TypeError( - "non tuple type matched with tuple pattern", - )) - } - }; - self.pattern_match_tuple_like( - current, - current_else, - args.iter().enumerate().map(|(i, x)| { - ( - PlaceElem::TupleField(i), - *x, - subst.at(Interner, i).assert_ty_ref(Interner).clone(), - ) - }), - *ellipsis, - &cond_place, - binding_mode, - )? - } - Pat::Or(pats) => { - let then_target = self.new_basic_block(); - let mut finished = false; - for pat in &**pats { - let (next, next_else) = self.pattern_match( - current, - None, - cond_place.clone(), - cond_ty.clone(), - *pat, - binding_mode, - )?; - self.set_goto(next, then_target); - match next_else { - Some(t) => { - current = t; - } - None => { - finished = true; - break; - } - } - } - if !finished { - let ce = *current_else.get_or_insert_with(|| self.new_basic_block()); - self.set_goto(current, ce); - } - (then_target, current_else) - } - Pat::Record { .. } => not_supported!("record pattern"), - Pat::Range { .. } => not_supported!("range pattern"), - Pat::Slice { .. } => not_supported!("slice pattern"), - Pat::Path(_) => { - let Some(variant) = self.infer.variant_resolution_for_pat(pattern) else { - not_supported!("unresolved variant"); - }; - self.pattern_matching_variant( - cond_ty, - binding_mode, - cond_place, - variant, - current, - pattern.into(), - current_else, - &[], - &None, - )? - } - Pat::Lit(l) => { - let then_target = self.new_basic_block(); - let else_target = current_else.unwrap_or_else(|| self.new_basic_block()); - match &self.body.exprs[*l] { - Expr::Literal(l) => match l { - hir_def::expr::Literal::Int(x, _) => { - self.set_terminator( - current, - Terminator::SwitchInt { - discr: Operand::Copy(cond_place), - targets: SwitchTargets::static_if( - *x as u128, - then_target, - else_target, - ), - }, - ); - } - hir_def::expr::Literal::Uint(x, _) => { - self.set_terminator( - current, - Terminator::SwitchInt { - discr: Operand::Copy(cond_place), - targets: SwitchTargets::static_if(*x, then_target, else_target), - }, - ); - } - _ => not_supported!("non int path literal"), - }, - _ => not_supported!("expression path literal"), - } - (then_target, Some(else_target)) - } - Pat::Bind { id, subpat } => { - let target_place = self.result.binding_locals[*id]; - let mode = self.body.bindings[*id].mode; - if let Some(subpat) = subpat { - (current, current_else) = self.pattern_match( - current, - current_else, - cond_place.clone(), - cond_ty, - *subpat, - binding_mode, - )? - } - if matches!(mode, BindingAnnotation::Ref | BindingAnnotation::RefMut) { - binding_mode = mode; - } - self.push_storage_live(*id, current); - self.push_assignment( - current, - target_place.into(), - match binding_mode { - BindingAnnotation::Unannotated | BindingAnnotation::Mutable => { - Operand::Copy(cond_place).into() - } - BindingAnnotation::Ref => Rvalue::Ref(BorrowKind::Shared, cond_place), - BindingAnnotation::RefMut => Rvalue::Ref( - BorrowKind::Mut { allow_two_phase_borrow: false }, - cond_place, - ), - }, - pattern.into(), - ); - (current, current_else) - } - Pat::TupleStruct { path: _, args, ellipsis } => { - let Some(variant) = self.infer.variant_resolution_for_pat(pattern) else { - not_supported!("unresolved variant"); - }; - self.pattern_matching_variant( - cond_ty, - binding_mode, - cond_place, - variant, - current, - pattern.into(), - current_else, - args, - ellipsis, - )? - } - Pat::Ref { .. } => not_supported!("& pattern"), - Pat::Box { .. } => not_supported!("box pattern"), - Pat::ConstBlock(_) => not_supported!("const block pattern"), - }) - } - - fn pattern_matching_variant( - &mut self, - mut cond_ty: Ty, - mut binding_mode: BindingAnnotation, - mut cond_place: Place, - variant: VariantId, - current: BasicBlockId, - span: MirSpan, - current_else: Option, - args: &[PatId], - ellipsis: &Option, - ) -> Result<(BasicBlockId, Option)> { - pattern_matching_dereference(&mut cond_ty, &mut binding_mode, &mut cond_place); - let subst = match cond_ty.kind(Interner) { - TyKind::Adt(_, s) => s, - _ => return Err(MirLowerError::TypeError("non adt type matched with tuple struct")), - }; - let fields_type = self.db.field_types(variant); - Ok(match variant { - VariantId::EnumVariantId(v) => { - let e = self.db.const_eval_discriminant(v)? as u128; - let next = self.new_basic_block(); - let tmp = self.discr_temp_place(); - self.push_assignment( - current, - tmp.clone(), - Rvalue::Discriminant(cond_place.clone()), - span, - ); - let else_target = current_else.unwrap_or_else(|| self.new_basic_block()); - self.set_terminator( - current, - Terminator::SwitchInt { - discr: Operand::Copy(tmp), - targets: SwitchTargets::static_if(e, next, else_target), - }, - ); - let enum_data = self.db.enum_data(v.parent); - let fields = - enum_data.variants[v.local_id].variant_data.fields().iter().map(|(x, _)| { - ( - PlaceElem::Field(FieldId { parent: v.into(), local_id: x }), - fields_type[x].clone().substitute(Interner, subst), - ) - }); - self.pattern_match_tuple_like( - next, - Some(else_target), - args.iter().zip(fields).map(|(x, y)| (y.0, *x, y.1)), - *ellipsis, - &cond_place, - binding_mode, - )? - } - VariantId::StructId(s) => { - let struct_data = self.db.struct_data(s); - let fields = struct_data.variant_data.fields().iter().map(|(x, _)| { - ( - PlaceElem::Field(FieldId { parent: s.into(), local_id: x }), - fields_type[x].clone().substitute(Interner, subst), - ) - }); - self.pattern_match_tuple_like( - current, - current_else, - args.iter().zip(fields).map(|(x, y)| (y.0, *x, y.1)), - *ellipsis, - &cond_place, - binding_mode, - )? - } - VariantId::UnionId(_) => { - return Err(MirLowerError::TypeError("pattern matching on union")) - } - }) - } - - fn pattern_match_tuple_like( - &mut self, - mut current: BasicBlockId, - mut current_else: Option, - args: impl Iterator, - ellipsis: Option, - cond_place: &Place, - binding_mode: BindingAnnotation, - ) -> Result<(BasicBlockId, Option)> { - if ellipsis.is_some() { - not_supported!("tuple like pattern with ellipsis"); - } - for (proj, arg, ty) in args { - let mut cond_place = cond_place.clone(); - cond_place.projection.push(proj); - (current, current_else) = - self.pattern_match(current, current_else, cond_place, ty, arg, binding_mode)?; - } - Ok((current, current_else)) - } - - fn discr_temp_place(&mut self) -> Place { + fn discr_temp_place(&mut self, current: BasicBlockId) -> Place { match &self.discr_temp { Some(x) => x.clone(), None => { - let tmp: Place = - self.temp(TyBuilder::discr_ty()).expect("discr_ty is never unsized").into(); + let tmp: Place = self + .temp(TyBuilder::discr_ty(), current, MirSpan::Unknown) + .expect("discr_ty is never unsized") + .into(); self.discr_temp = Some(tmp.clone()); tmp } @@ -1266,19 +1416,34 @@ impl MirLowerCtx<'_> { fn lower_loop( &mut self, prev_block: BasicBlockId, + place: Place, label: Option, + span: MirSpan, f: impl FnOnce(&mut MirLowerCtx<'_>, BasicBlockId) -> Result<()>, ) -> Result> { - if label.is_some() { - not_supported!("loop with label"); - } let begin = self.new_basic_block(); - let prev = - mem::replace(&mut self.current_loop_blocks, Some(LoopBlocks { begin, end: None })); - self.set_goto(prev_block, begin); + let prev = mem::replace( + &mut self.current_loop_blocks, + Some(LoopBlocks { begin, end: None, place, drop_scope_index: self.drop_scopes.len() }), + ); + let prev_label = if let Some(label) = label { + // We should generate the end now, to make sure that it wouldn't change later. It is + // bad as we may emit end (unnecessary unreachable block) for unterminating loop, but + // it should not affect correctness. + self.current_loop_end()?; + self.labeled_loop_blocks + .insert(label, self.current_loop_blocks.as_ref().unwrap().clone()) + } else { + None + }; + self.set_goto(prev_block, begin, span); f(self, begin)?; - let my = mem::replace(&mut self.current_loop_blocks, prev) - .ok_or(MirLowerError::ImplementationError("current_loop_blocks is corrupt"))?; + let my = mem::replace(&mut self.current_loop_blocks, prev).ok_or( + MirLowerError::ImplementationError("current_loop_blocks is corrupt".to_string()), + )?; + if let Some(prev) = prev_label { + self.labeled_loop_blocks.insert(label.unwrap(), prev); + } Ok(my.end) } @@ -1290,14 +1455,15 @@ impl MirLowerCtx<'_> { &mut self, b1: Option, b2: Option, + span: MirSpan, ) -> Option { match (b1, b2) { (None, None) => None, (None, Some(b)) | (Some(b), None) => Some(b), (Some(b1), Some(b2)) => { let bm = self.new_basic_block(); - self.set_goto(b1, bm); - self.set_goto(b2, bm); + self.set_goto(b1, bm, span); + self.set_goto(b2, bm, span); Some(bm) } } @@ -1307,7 +1473,9 @@ impl MirLowerCtx<'_> { let r = match self .current_loop_blocks .as_mut() - .ok_or(MirLowerError::ImplementationError("Current loop access out of loop"))? + .ok_or(MirLowerError::ImplementationError( + "Current loop access out of loop".to_string(), + ))? .end { Some(x) => x, @@ -1315,7 +1483,9 @@ impl MirLowerCtx<'_> { let s = self.new_basic_block(); self.current_loop_blocks .as_mut() - .ok_or(MirLowerError::ImplementationError("Current loop access out of loop"))? + .ok_or(MirLowerError::ImplementationError( + "Current loop access out of loop".to_string(), + ))? .end = Some(s); s } @@ -1327,36 +1497,28 @@ impl MirLowerCtx<'_> { is_ty_uninhabited_from(&self.infer[expr_id], self.owner.module(self.db.upcast()), self.db) } - /// This function push `StorageLive` statement for the binding, and applies changes to add `StorageDead` in - /// the appropriated places. - fn push_storage_live(&mut self, b: BindingId, current: BasicBlockId) { - // Current implementation is wrong. It adds no `StorageDead` at the end of scope, and before each break - // and continue. It just add a `StorageDead` before the `StorageLive`, which is not wrong, but unneeeded in - // the proper implementation. Due this limitation, implementing a borrow checker on top of this mir will falsely - // allow this: - // - // ``` - // let x; - // loop { - // let y = 2; - // x = &y; - // if some_condition { - // break; // we need to add a StorageDead(y) above this to kill the x borrow - // } - // } - // use(x) - // ``` - // But I think this approach work for mutability analysis, as user can't write code which mutates a binding - // after StorageDead, except loops, which are handled by this hack. + /// This function push `StorageLive` statement for the binding, and applies changes to add `StorageDead` and + /// `Drop` in the appropriated places. + fn push_storage_live(&mut self, b: BindingId, current: BasicBlockId) -> Result<()> { let span = self.body.bindings[b] .definitions .first() .copied() .map(MirSpan::PatId) .unwrap_or(MirSpan::Unknown); - let l = self.result.binding_locals[b]; - self.push_statement(current, StatementKind::StorageDead(l).with_span(span)); + let l = self.binding_local(b)?; + self.push_storage_live_for_local(l, current, span) + } + + fn push_storage_live_for_local( + &mut self, + l: LocalId, + current: BasicBlockId, + span: MirSpan, + ) -> Result<()> { + self.drop_scopes.last_mut().unwrap().locals.push(l); self.push_statement(current, StatementKind::StorageLive(l).with_span(span)); + Ok(()) } fn resolve_lang_item(&self, item: LangItem) -> Result { @@ -1366,81 +1528,204 @@ impl MirLowerCtx<'_> { fn lower_block_to_place( &mut self, - label: Option, - statements: &[hir_def::expr::Statement], + statements: &[hir_def::hir::Statement], mut current: BasicBlockId, tail: Option, place: Place, + span: MirSpan, ) -> Result>> { - if label.is_some() { - not_supported!("block with label"); - } + let scope = self.push_drop_scope(); for statement in statements.iter() { match statement { - hir_def::expr::Statement::Let { pat, initializer, else_branch, type_ref: _ } => { + hir_def::hir::Statement::Let { pat, initializer, else_branch, type_ref: _ } => { if let Some(expr_id) = initializer { let else_block; let Some((init_place, c)) = self.lower_expr_as_place(current, *expr_id, true)? else { + scope.pop_assume_dropped(self); return Ok(None); }; current = c; - (current, else_block) = self.pattern_match( - current, - None, - init_place, - self.expr_ty_after_adjustments(*expr_id), - *pat, - BindingAnnotation::Unannotated, - )?; + (current, else_block) = + self.pattern_match(current, None, init_place, *pat)?; match (else_block, else_branch) { (None, _) => (), (Some(else_block), None) => { - self.set_terminator(else_block, Terminator::Unreachable); + self.set_terminator(else_block, TerminatorKind::Unreachable, span); } (Some(else_block), Some(else_branch)) => { if let Some((_, b)) = self.lower_expr_as_place(else_block, *else_branch, true)? { - self.set_terminator(b, Terminator::Unreachable); + self.set_terminator(b, TerminatorKind::Unreachable, span); } } } } else { + let mut err = None; self.body.walk_bindings_in_pat(*pat, |b| { - self.push_storage_live(b, current); + if let Err(e) = self.push_storage_live(b, current) { + err = Some(e); + } }); + if let Some(e) = err { + return Err(e); + } } } - hir_def::expr::Statement::Expr { expr, has_semi: _ } => { + hir_def::hir::Statement::Expr { expr, has_semi: _ } => { + let scope2 = self.push_drop_scope(); let Some((_, c)) = self.lower_expr_as_place(current, *expr, true)? else { + scope2.pop_assume_dropped(self); + scope.pop_assume_dropped(self); return Ok(None); }; - current = c; + current = scope2.pop_and_drop(self, c); } } } - match tail { - Some(tail) => self.lower_expr_to_place(tail, place, current), - None => Ok(Some(current)), + if let Some(tail) = tail { + let Some(c) = self.lower_expr_to_place(tail, place, current)? else { + scope.pop_assume_dropped(self); + return Ok(None); + }; + current = c; } + current = scope.pop_and_drop(self, current); + Ok(Some(current)) } -} -fn pattern_matching_dereference( - cond_ty: &mut Ty, - binding_mode: &mut BindingAnnotation, - cond_place: &mut Place, -) { - while let Some((ty, _, mu)) = cond_ty.as_reference() { - if mu == Mutability::Mut && *binding_mode != BindingAnnotation::Ref { - *binding_mode = BindingAnnotation::RefMut; - } else { - *binding_mode = BindingAnnotation::Ref; + fn lower_params_and_bindings( + &mut self, + params: impl Iterator + Clone, + pick_binding: impl Fn(BindingId) -> bool, + ) -> Result { + let base_param_count = self.result.param_locals.len(); + self.result.param_locals.extend(params.clone().map(|(x, ty)| { + let local_id = self.result.locals.alloc(Local { ty }); + self.drop_scopes.last_mut().unwrap().locals.push(local_id); + if let Pat::Bind { id, subpat: None } = self.body[x] { + if matches!( + self.body.bindings[id].mode, + BindingAnnotation::Unannotated | BindingAnnotation::Mutable + ) { + self.result.binding_locals.insert(id, local_id); + } + } + local_id + })); + // and then rest of bindings + for (id, _) in self.body.bindings.iter() { + if !pick_binding(id) { + continue; + } + if !self.result.binding_locals.contains_idx(id) { + self.result + .binding_locals + .insert(id, self.result.locals.alloc(Local { ty: self.infer[id].clone() })); + } + } + let mut current = self.result.start_block; + for ((param, _), local) in + params.zip(self.result.param_locals.clone().into_iter().skip(base_param_count)) + { + if let Pat::Bind { id, .. } = self.body[param] { + if local == self.binding_local(id)? { + continue; + } + } + let r = self.pattern_match(current, None, local.into(), param)?; + if let Some(b) = r.1 { + self.set_terminator(b, TerminatorKind::Unreachable, param.into()); + } + current = r.0; + } + Ok(current) + } + + fn binding_local(&self, b: BindingId) -> Result { + match self.result.binding_locals.get(b) { + Some(x) => Ok(*x), + None => { + // FIXME: It should never happens, but currently it will happen in `const_dependent_on_local` test, which + // is a hir lowering problem IMO. + // never!("Using unaccessable local for binding is always a bug"); + Err(MirLowerError::UnaccessableLocal) + } + } + } + + fn const_eval_discriminant(&self, variant: EnumVariantId) -> Result { + let r = self.db.const_eval_discriminant(variant); + match r { + Ok(r) => Ok(r), + Err(e) => { + let data = self.db.enum_data(variant.parent); + let name = format!( + "{}::{}", + data.name.display(self.db.upcast()), + data.variants[variant.local_id].name.display(self.db.upcast()) + ); + Err(MirLowerError::ConstEvalError(name, Box::new(e))) + } + } + } + + fn drop_until_scope(&mut self, scope_index: usize, mut current: BasicBlockId) -> BasicBlockId { + for scope in self.drop_scopes[scope_index..].to_vec().iter().rev() { + self.emit_drop_and_storage_dead_for_scope(scope, &mut current); + } + current + } + + fn push_drop_scope(&mut self) -> DropScopeToken { + self.drop_scopes.push(DropScope::default()); + DropScopeToken + } + + /// Don't call directly + fn pop_drop_scope_assume_dropped_internal(&mut self) { + self.drop_scopes.pop(); + } + + /// Don't call directly + fn pop_drop_scope_internal(&mut self, mut current: BasicBlockId) -> BasicBlockId { + let scope = self.drop_scopes.pop().unwrap(); + self.emit_drop_and_storage_dead_for_scope(&scope, &mut current); + current + } + + fn pop_drop_scope_assert_finished( + &mut self, + mut current: BasicBlockId, + ) -> Result { + current = self.pop_drop_scope_internal(current); + if !self.drop_scopes.is_empty() { + implementation_error!("Mismatched count between drop scope push and pops"); + } + Ok(current) + } + + fn emit_drop_and_storage_dead_for_scope( + &mut self, + scope: &DropScope, + current: &mut Idx, + ) { + for &l in scope.locals.iter().rev() { + if !self.result.locals[l].ty.clone().is_copy(self.db, self.owner) { + let prev = std::mem::replace(current, self.new_basic_block()); + self.set_terminator( + prev, + TerminatorKind::Drop { place: l.into(), target: *current, unwind: None }, + MirSpan::Unknown, + ); + } + self.push_statement( + *current, + StatementKind::StorageDead(l).with_span(MirSpan::Unknown), + ); } - *cond_ty = ty.clone(); - cond_place.projection.push(ProjectionElem::Deref); } } @@ -1452,6 +1737,26 @@ fn cast_kind(source_ty: &Ty, target_ty: &Ty) -> Result { (_, chalk_ir::Scalar::Float(_)) => CastKind::IntToFloat, (_, _) => CastKind::IntToInt, }, + (TyKind::Scalar(_), TyKind::Raw(..)) => CastKind::PointerFromExposedAddress, + (TyKind::Raw(..), TyKind::Scalar(_)) => CastKind::PointerExposeAddress, + (TyKind::Raw(_, a) | TyKind::Ref(_, _, a), TyKind::Raw(_, b) | TyKind::Ref(_, _, b)) => { + CastKind::Pointer(if a == b { + PointerCast::MutToConstPointer + } else if matches!(a.kind(Interner), TyKind::Slice(_) | TyKind::Str) + && matches!(b.kind(Interner), TyKind::Slice(_) | TyKind::Str) + { + // slice to slice cast is no-op (metadata is not touched), so we use this + PointerCast::MutToConstPointer + } else if matches!(b.kind(Interner), TyKind::Slice(_) | TyKind::Dyn(_)) { + PointerCast::Unsize + } else if matches!(a.kind(Interner), TyKind::Slice(s) if s == b) { + PointerCast::ArrayToPointer + } else { + // cast between two sized pointer, like *const i32 to *const i8. There is no specific variant + // for it in `PointerCast` so we use `MutToConstPointer` + PointerCast::MutToConstPointer + }) + } // Enum to int casts (TyKind::Scalar(_), TyKind::Adt(..)) | (TyKind::Adt(..), TyKind::Scalar(_)) => { CastKind::IntToInt @@ -1460,20 +1765,122 @@ fn cast_kind(source_ty: &Ty, target_ty: &Ty) -> Result { }) } +pub fn mir_body_for_closure_query( + db: &dyn HirDatabase, + closure: ClosureId, +) -> Result> { + let (owner, expr) = db.lookup_intern_closure(closure.into()); + let body = db.body(owner); + let infer = db.infer(owner); + let Expr::Closure { args, body: root, .. } = &body[expr] else { + implementation_error!("closure expression is not closure"); + }; + let TyKind::Closure(_, substs) = &infer[expr].kind(Interner) else { + implementation_error!("closure expression is not closure"); + }; + let (captures, kind) = infer.closure_info(&closure); + let mut ctx = MirLowerCtx::new(db, owner, &body, &infer); + // 0 is return local + ctx.result.locals.alloc(Local { ty: infer[*root].clone() }); + let closure_local = ctx.result.locals.alloc(Local { + ty: match kind { + FnTrait::FnOnce => infer[expr].clone(), + FnTrait::FnMut => TyKind::Ref(Mutability::Mut, static_lifetime(), infer[expr].clone()) + .intern(Interner), + FnTrait::Fn => TyKind::Ref(Mutability::Not, static_lifetime(), infer[expr].clone()) + .intern(Interner), + }, + }); + ctx.result.param_locals.push(closure_local); + let Some(sig) = ClosureSubst(substs).sig_ty().callable_sig(db) else { + implementation_error!("closure has not callable sig"); + }; + let current = ctx.lower_params_and_bindings( + args.iter().zip(sig.params().iter()).map(|(x, y)| (*x, y.clone())), + |_| true, + )?; + if let Some(current) = ctx.lower_expr_to_place(*root, return_slot().into(), current)? { + let current = ctx.pop_drop_scope_assert_finished(current)?; + ctx.set_terminator(current, TerminatorKind::Return, (*root).into()); + } + let mut upvar_map: FxHashMap> = FxHashMap::default(); + for (i, capture) in captures.iter().enumerate() { + let local = ctx.binding_local(capture.place.local)?; + upvar_map.entry(local).or_default().push((capture, i)); + } + let mut err = None; + let closure_local = ctx.result.locals.iter().nth(1).unwrap().0; + let closure_projection = match kind { + FnTrait::FnOnce => vec![], + FnTrait::FnMut | FnTrait::Fn => vec![ProjectionElem::Deref], + }; + ctx.result.walk_places(|p| { + if let Some(x) = upvar_map.get(&p.local) { + let r = x.iter().find(|x| { + if p.projection.len() < x.0.place.projections.len() { + return false; + } + for (x, y) in p.projection.iter().zip(x.0.place.projections.iter()) { + match (x, y) { + (ProjectionElem::Deref, ProjectionElem::Deref) => (), + (ProjectionElem::Field(x), ProjectionElem::Field(y)) if x == y => (), + ( + ProjectionElem::TupleOrClosureField(x), + ProjectionElem::TupleOrClosureField(y), + ) if x == y => (), + _ => return false, + } + } + true + }); + match r { + Some(x) => { + p.local = closure_local; + let mut next_projs = closure_projection.clone(); + next_projs.push(PlaceElem::TupleOrClosureField(x.1)); + let prev_projs = mem::take(&mut p.projection); + if x.0.kind != CaptureKind::ByValue { + next_projs.push(ProjectionElem::Deref); + } + next_projs.extend(prev_projs.iter().cloned().skip(x.0.place.projections.len())); + p.projection = next_projs.into(); + } + None => err = Some(p.clone()), + } + } + }); + ctx.result.binding_locals = ctx + .result + .binding_locals + .into_iter() + .filter(|x| ctx.body[x.0].owner == Some(expr)) + .collect(); + if let Some(err) = err { + return Err(MirLowerError::UnresolvedUpvar(err)); + } + ctx.result.shrink_to_fit(); + Ok(Arc::new(ctx.result)) +} + pub fn mir_body_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Result> { let _p = profile::span("mir_body_query").detail(|| match def { - DefWithBodyId::FunctionId(it) => db.function_data(it).name.to_string(), - DefWithBodyId::StaticId(it) => db.static_data(it).name.clone().to_string(), - DefWithBodyId::ConstId(it) => { - db.const_data(it).name.clone().unwrap_or_else(Name::missing).to_string() - } + DefWithBodyId::FunctionId(it) => db.function_data(it).name.display(db.upcast()).to_string(), + DefWithBodyId::StaticId(it) => db.static_data(it).name.display(db.upcast()).to_string(), + DefWithBodyId::ConstId(it) => db + .const_data(it) + .name + .clone() + .unwrap_or_else(Name::missing) + .display(db.upcast()) + .to_string(), DefWithBodyId::VariantId(it) => { - db.enum_data(it.parent).variants[it.local_id].name.to_string() + db.enum_data(it.parent).variants[it.local_id].name.display(db.upcast()).to_string() } }); let body = db.body(def); let infer = db.infer(def); - let result = lower_to_mir(db, def, &body, &infer, body.body_expr)?; + let mut result = lower_to_mir(db, def, &body, &infer, body.body_expr)?; + result.shrink_to_fit(); Ok(Arc::new(result)) } @@ -1497,85 +1904,39 @@ pub fn lower_to_mir( if let Some((_, x)) = infer.type_mismatches().next() { return Err(MirLowerError::TypeMismatch(x.clone())); } - let mut basic_blocks = Arena::new(); - let start_block = - basic_blocks.alloc(BasicBlock { statements: vec![], terminator: None, is_cleanup: false }); - let mut locals = Arena::new(); + let mut ctx = MirLowerCtx::new(db, owner, body, infer); // 0 is return local - locals.alloc(Local { ty: infer[root_expr].clone() }); - let mut binding_locals: ArenaMap = ArenaMap::new(); - // 1 to param_len is for params - let param_locals: Vec = if let DefWithBodyId::FunctionId(fid) = owner { - let substs = TyBuilder::placeholder_subst(db, fid); - let callable_sig = db.callable_item_signature(fid.into()).substitute(Interner, &substs); - body.params - .iter() - .zip(callable_sig.params().iter()) - .map(|(&x, ty)| { - let local_id = locals.alloc(Local { ty: ty.clone() }); - if let Pat::Bind { id, subpat: None } = body[x] { - if matches!( - body.bindings[id].mode, - BindingAnnotation::Unannotated | BindingAnnotation::Mutable - ) { - binding_locals.insert(id, local_id); - } - } - local_id - }) - .collect() - } else { - if !body.params.is_empty() { - return Err(MirLowerError::TypeError("Unexpected parameter for non function body")); - } - vec![] - }; - // and then rest of bindings - for (id, _) in body.bindings.iter() { - if !binding_locals.contains_idx(id) { - binding_locals.insert(id, locals.alloc(Local { ty: infer[id].clone() })); + ctx.result.locals.alloc(Local { ty: ctx.expr_ty_after_adjustments(root_expr) }); + let binding_picker = |b: BindingId| { + if root_expr == body.body_expr { + body[b].owner.is_none() + } else { + body[b].owner == Some(root_expr) } - } - let mir = MirBody { - basic_blocks, - locals, - start_block, - binding_locals, - param_locals, - owner, - arg_count: body.params.len(), - }; - let mut ctx = MirLowerCtx { - result: mir, - db, - infer, - body, - owner, - current_loop_blocks: None, - discr_temp: None, }; - let mut current = start_block; - for (¶m, local) in body.params.iter().zip(ctx.result.param_locals.clone().into_iter()) { - if let Pat::Bind { id, .. } = body[param] { - if local == ctx.result.binding_locals[id] { - continue; + // 1 to param_len is for params + // FIXME: replace with let chain once it becomes stable + let current = 'b: { + if body.body_expr == root_expr { + // otherwise it's an inline const, and has no parameter + if let DefWithBodyId::FunctionId(fid) = owner { + let substs = TyBuilder::placeholder_subst(db, fid); + let callable_sig = + db.callable_item_signature(fid.into()).substitute(Interner, &substs); + break 'b ctx.lower_params_and_bindings( + body.params + .iter() + .zip(callable_sig.params().iter()) + .map(|(x, y)| (*x, y.clone())), + binding_picker, + )?; } } - let r = ctx.pattern_match( - current, - None, - local.into(), - ctx.result.locals[local].ty.clone(), - param, - BindingAnnotation::Unannotated, - )?; - if let Some(b) = r.1 { - ctx.set_terminator(b, Terminator::Unreachable); - } - current = r.0; - } - if let Some(b) = ctx.lower_expr_to_place(root_expr, return_slot().into(), current)? { - ctx.result.basic_blocks[b].terminator = Some(Terminator::Return); + ctx.lower_params_and_bindings([].into_iter(), binding_picker)? + }; + if let Some(current) = ctx.lower_expr_to_place(root_expr, return_slot().into(), current)? { + let current = ctx.pop_drop_scope_assert_finished(current)?; + ctx.set_terminator(current, TerminatorKind::Return, root_expr.into()); } Ok(ctx.result) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs index fe8147dcd3e7a..d2c8d9a089e00 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs @@ -1,6 +1,7 @@ //! MIR lowering for places use super::*; +use hir_def::{lang_item::lang_attr, FunctionId}; use hir_expand::name; macro_rules! not_supported { @@ -15,8 +16,8 @@ impl MirLowerCtx<'_> { expr_id: ExprId, prev_block: BasicBlockId, ) -> Result> { - let ty = self.expr_ty(expr_id); - let place = self.temp(ty)?; + let ty = self.expr_ty_without_adjust(expr_id); + let place = self.temp(ty, prev_block, expr_id.into())?; let Some(current) = self.lower_expr_to_place_without_adjust(expr_id, place.into(), prev_block)? else { return Ok(None); }; @@ -29,9 +30,11 @@ impl MirLowerCtx<'_> { prev_block: BasicBlockId, adjustments: &[Adjustment], ) -> Result> { - let ty = - adjustments.last().map(|x| x.target.clone()).unwrap_or_else(|| self.expr_ty(expr_id)); - let place = self.temp(ty)?; + let ty = adjustments + .last() + .map(|x| x.target.clone()) + .unwrap_or_else(|| self.expr_ty_without_adjust(expr_id)); + let place = self.temp(ty, prev_block, expr_id.into())?; let Some(current) = self.lower_expr_to_place_with_adjust(expr_id, place.into(), prev_block, adjustments)? else { return Ok(None); }; @@ -62,7 +65,7 @@ impl MirLowerCtx<'_> { )? else { return Ok(None); }; - x.0.projection.push(ProjectionElem::Deref); + x.0 = x.0.project(ProjectionElem::Deref); Ok(Some(x)) } Adjust::Deref(Some(od)) => { @@ -79,7 +82,7 @@ impl MirLowerCtx<'_> { r, rest.last() .map(|x| x.target.clone()) - .unwrap_or_else(|| self.expr_ty(expr_id)), + .unwrap_or_else(|| self.expr_ty_without_adjust(expr_id)), last.target.clone(), expr_id.into(), match od.0 { @@ -125,35 +128,74 @@ impl MirLowerCtx<'_> { match &self.body.exprs[expr_id] { Expr::Path(p) => { let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id); - let Some(pr) = resolver.resolve_path_in_value_ns(self.db.upcast(), p.mod_path()) else { - return Err(MirLowerError::unresolved_path(self.db, p)); - }; - let pr = match pr { - ResolveValueResult::ValueNs(v) => v, - ResolveValueResult::Partial(..) => return try_rvalue(self), + let Some(pr) = resolver.resolve_path_in_value_ns_fully(self.db.upcast(), p) else { + return try_rvalue(self); }; match pr { ValueNs::LocalBinding(pat_id) => { - Ok(Some((self.result.binding_locals[pat_id].into(), current))) + Ok(Some((self.binding_local(pat_id)?.into(), current))) + } + ValueNs::StaticId(s) => { + let ty = self.expr_ty_without_adjust(expr_id); + let ref_ty = + TyKind::Ref(Mutability::Not, static_lifetime(), ty).intern(Interner); + let temp: Place = self.temp(ref_ty, current, expr_id.into())?.into(); + self.push_assignment( + current, + temp.clone(), + Operand::Static(s).into(), + expr_id.into(), + ); + Ok(Some((temp.project(ProjectionElem::Deref), current))) } _ => try_rvalue(self), } } Expr::UnaryOp { expr, op } => match op { - hir_def::expr::UnaryOp::Deref => { - if !matches!( - self.expr_ty(*expr).kind(Interner), - TyKind::Ref(..) | TyKind::Raw(..) - ) { - let Some(_) = self.lower_expr_as_place(current, *expr, true)? else { + hir_def::hir::UnaryOp::Deref => { + let is_builtin = match self.expr_ty_without_adjust(*expr).kind(Interner) { + TyKind::Ref(..) | TyKind::Raw(..) => true, + TyKind::Adt(id, _) => { + if let Some(lang_item) = lang_attr(self.db.upcast(), id.0) { + lang_item == LangItem::OwnedBox + } else { + false + } + } + _ => false, + }; + if !is_builtin { + let Some((p, current)) = self.lower_expr_as_place(current, *expr, true)? else { return Ok(None); }; - not_supported!("explicit overloaded deref"); + return self.lower_overloaded_deref( + current, + p, + self.expr_ty_after_adjustments(*expr), + self.expr_ty_without_adjust(expr_id), + expr_id.into(), + 'b: { + if let Some((f, _)) = self.infer.method_resolution(expr_id) { + if let Some(deref_trait) = + self.resolve_lang_item(LangItem::DerefMut)?.as_trait() + { + if let Some(deref_fn) = self + .db + .trait_data(deref_trait) + .method_by_name(&name![deref_mut]) + { + break 'b deref_fn == f; + } + } + } + false + }, + ); } let Some((mut r, current)) = self.lower_expr_as_place(current, *expr, true)? else { return Ok(None); }; - r.projection.push(ProjectionElem::Deref); + r = r.project(ProjectionElem::Deref); Ok(Some((r, current))) } _ => try_rvalue(self), @@ -169,25 +211,84 @@ impl MirLowerCtx<'_> { let base_ty = self.expr_ty_after_adjustments(*base); let index_ty = self.expr_ty_after_adjustments(*index); if index_ty != TyBuilder::usize() - || !matches!(base_ty.kind(Interner), TyKind::Array(..) | TyKind::Slice(..)) + || !matches!( + base_ty.strip_reference().kind(Interner), + TyKind::Array(..) | TyKind::Slice(..) + ) { - not_supported!("overloaded index"); + let Some(index_fn) = self.infer.method_resolution(expr_id) else { + return Err(MirLowerError::UnresolvedMethod("[overloaded index]".to_string())); + }; + let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true)? else { + return Ok(None); + }; + let Some((index_operand, current)) = self.lower_expr_to_some_operand(*index, current)? else { + return Ok(None); + }; + return self.lower_overloaded_index( + current, + base_place, + base_ty, + self.expr_ty_without_adjust(expr_id), + index_operand, + expr_id.into(), + index_fn, + ); } + let adjusts = self + .infer + .expr_adjustments + .get(base) + .and_then(|x| x.split_last()) + .map(|x| x.1) + .unwrap_or(&[]); let Some((mut p_base, current)) = - self.lower_expr_as_place(current, *base, true)? else { + self.lower_expr_as_place_with_adjust(current, *base, true, adjusts)? + else { return Ok(None); }; - let l_index = self.temp(self.expr_ty_after_adjustments(*index))?; + let l_index = + self.temp(self.expr_ty_after_adjustments(*index), current, expr_id.into())?; let Some(current) = self.lower_expr_to_place(*index, l_index.into(), current)? else { return Ok(None); }; - p_base.projection.push(ProjectionElem::Index(l_index)); + p_base = p_base.project(ProjectionElem::Index(l_index)); Ok(Some((p_base, current))) } _ => try_rvalue(self), } } + fn lower_overloaded_index( + &mut self, + current: BasicBlockId, + place: Place, + base_ty: Ty, + result_ty: Ty, + index_operand: Operand, + span: MirSpan, + index_fn: (FunctionId, Substitution), + ) -> Result> { + let mutability = match base_ty.as_reference() { + Some((_, _, mutability)) => mutability, + None => Mutability::Not, + }; + let result_ref = TyKind::Ref(mutability, static_lifetime(), result_ty).intern(Interner); + let mut result: Place = self.temp(result_ref, current, span)?.into(); + let index_fn_op = Operand::const_zst( + TyKind::FnDef( + self.db.intern_callable_def(CallableDefId::FunctionId(index_fn.0)).into(), + index_fn.1, + ) + .intern(Interner), + ); + let Some(current) = self.lower_call(index_fn_op, Box::new([Operand::Copy(place), index_operand]), result.clone(), current, false, span)? else { + return Ok(None); + }; + result = result.project(ProjectionElem::Deref); + Ok(Some((result, current))) + } + fn lower_overloaded_deref( &mut self, current: BasicBlockId, @@ -209,7 +310,7 @@ impl MirLowerCtx<'_> { }; let ty_ref = TyKind::Ref(chalk_mut, static_lifetime(), source_ty.clone()).intern(Interner); let target_ty_ref = TyKind::Ref(chalk_mut, static_lifetime(), target_ty).intern(Interner); - let ref_place: Place = self.temp(ty_ref)?.into(); + let ref_place: Place = self.temp(ty_ref, current, span)?.into(); self.push_assignment(current, ref_place.clone(), Rvalue::Ref(borrow_kind, place), span); let deref_trait = self .resolve_lang_item(trait_lang_item)? @@ -227,11 +328,11 @@ impl MirLowerCtx<'_> { ) .intern(Interner), ); - let mut result: Place = self.temp(target_ty_ref)?.into(); - let Some(current) = self.lower_call(deref_fn_op, vec![Operand::Copy(ref_place)], result.clone(), current, false)? else { + let mut result: Place = self.temp(target_ty_ref, current, span)?.into(); + let Some(current) = self.lower_call(deref_fn_op, Box::new([Operand::Copy(ref_place)]), result.clone(), current, false, span)? else { return Ok(None); }; - result.projection.push(ProjectionElem::Deref); + result = result.project(ProjectionElem::Deref); Ok(Some((result, current))) } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs new file mode 100644 index 0000000000000..ff43c64a9e60f --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs @@ -0,0 +1,617 @@ +//! MIR lowering for patterns + +use hir_def::{hir::LiteralOrConst, resolver::HasResolver, AssocItemId}; + +use crate::BindingMode; + +use super::*; + +macro_rules! not_supported { + ($x: expr) => { + return Err(MirLowerError::NotSupported(format!($x))) + }; +} + +pub(super) enum AdtPatternShape<'a> { + Tuple { args: &'a [PatId], ellipsis: Option }, + Record { args: &'a [RecordFieldPat] }, + Unit, +} + +/// We need to do pattern matching in two phases: One to check if the pattern matches, and one to fill the bindings +/// of patterns. This is necessary to prevent double moves and similar problems. For example: +/// ```ignore +/// struct X; +/// match (X, 3) { +/// (b, 2) | (b, 3) => {}, +/// _ => {} +/// } +/// ``` +/// If we do everything in one pass, we will move `X` to the first `b`, then we see that the second field of tuple +/// doesn't match and we should move the `X` to the second `b` (which here is the same thing, but doesn't need to be) and +/// it might even doesn't match the second pattern and we may want to not move `X` at all. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum MatchingMode { + /// Check that if this pattern matches + Check, + /// Assume that this pattern matches, fill bindings + Bind, +} + +impl MirLowerCtx<'_> { + /// It gets a `current` unterminated block, appends some statements and possibly a terminator to it to check if + /// the pattern matches and write bindings, and returns two unterminated blocks, one for the matched path (which + /// can be the `current` block) and one for the mismatched path. If the input pattern is irrefutable, the + /// mismatched path block is `None`. + /// + /// By default, it will create a new block for mismatched path. If you already have one, you can provide it with + /// `current_else` argument to save an unnecessary jump. If `current_else` isn't `None`, the result mismatched path + /// wouldn't be `None` as well. Note that this function will add jumps to the beginning of the `current_else` block, + /// so it should be an empty block. + pub(super) fn pattern_match( + &mut self, + current: BasicBlockId, + current_else: Option, + cond_place: Place, + pattern: PatId, + ) -> Result<(BasicBlockId, Option)> { + let (current, current_else) = self.pattern_match_inner( + current, + current_else, + cond_place.clone(), + pattern, + MatchingMode::Check, + )?; + let (current, current_else) = self.pattern_match_inner( + current, + current_else, + cond_place, + pattern, + MatchingMode::Bind, + )?; + Ok((current, current_else)) + } + + fn pattern_match_inner( + &mut self, + mut current: BasicBlockId, + mut current_else: Option, + mut cond_place: Place, + pattern: PatId, + mode: MatchingMode, + ) -> Result<(BasicBlockId, Option)> { + let cnt = self.infer.pat_adjustments.get(&pattern).map(|x| x.len()).unwrap_or_default(); + cond_place.projection = cond_place + .projection + .iter() + .cloned() + .chain((0..cnt).map(|_| ProjectionElem::Deref)) + .collect::>() + .into(); + Ok(match &self.body.pats[pattern] { + Pat::Missing => return Err(MirLowerError::IncompletePattern), + Pat::Wild => (current, current_else), + Pat::Tuple { args, ellipsis } => { + let subst = match self.infer[pattern].kind(Interner) { + TyKind::Tuple(_, s) => s, + _ => { + return Err(MirLowerError::TypeError( + "non tuple type matched with tuple pattern", + )) + } + }; + self.pattern_match_tuple_like( + current, + current_else, + args, + *ellipsis, + (0..subst.len(Interner)).map(|i| PlaceElem::TupleOrClosureField(i)), + &(&mut cond_place), + mode, + )? + } + Pat::Or(pats) => { + let then_target = self.new_basic_block(); + let mut finished = false; + for pat in &**pats { + let (mut next, next_else) = self.pattern_match_inner( + current, + None, + (&mut cond_place).clone(), + *pat, + MatchingMode::Check, + )?; + if mode == MatchingMode::Bind { + (next, _) = self.pattern_match_inner( + next, + None, + (&mut cond_place).clone(), + *pat, + MatchingMode::Bind, + )?; + } + self.set_goto(next, then_target, pattern.into()); + match next_else { + Some(t) => { + current = t; + } + None => { + finished = true; + break; + } + } + } + if !finished { + if mode == MatchingMode::Bind { + self.set_terminator(current, TerminatorKind::Unreachable, pattern.into()); + } else { + let ce = *current_else.get_or_insert_with(|| self.new_basic_block()); + self.set_goto(current, ce, pattern.into()); + } + } + (then_target, current_else) + } + Pat::Record { args, .. } => { + let Some(variant) = self.infer.variant_resolution_for_pat(pattern) else { + not_supported!("unresolved variant for record"); + }; + self.pattern_matching_variant( + cond_place, + variant, + current, + pattern.into(), + current_else, + AdtPatternShape::Record { args: &*args }, + mode, + )? + } + Pat::Range { start, end } => { + let mut add_check = |l: &LiteralOrConst, binop| -> Result<()> { + let lv = + self.lower_literal_or_const_to_operand(self.infer[pattern].clone(), l)?; + let else_target = *current_else.get_or_insert_with(|| self.new_basic_block()); + let next = self.new_basic_block(); + let discr: Place = + self.temp(TyBuilder::bool(), current, pattern.into())?.into(); + self.push_assignment( + current, + discr.clone(), + Rvalue::CheckedBinaryOp( + binop, + lv, + Operand::Copy((&mut cond_place).clone()), + ), + pattern.into(), + ); + let discr = Operand::Copy(discr); + self.set_terminator( + current, + TerminatorKind::SwitchInt { + discr, + targets: SwitchTargets::static_if(1, next, else_target), + }, + pattern.into(), + ); + current = next; + Ok(()) + }; + if mode == MatchingMode::Check { + if let Some(start) = start { + add_check(start, BinOp::Le)?; + } + if let Some(end) = end { + add_check(end, BinOp::Ge)?; + } + } + (current, current_else) + } + Pat::Slice { prefix, slice, suffix } => { + if mode == MatchingMode::Check { + // emit runtime length check for slice + if let TyKind::Slice(_) = self.infer[pattern].kind(Interner) { + let pattern_len = prefix.len() + suffix.len(); + let place_len: Place = + self.temp(TyBuilder::usize(), current, pattern.into())?.into(); + self.push_assignment( + current, + place_len.clone(), + Rvalue::Len((&mut cond_place).clone()), + pattern.into(), + ); + let else_target = + *current_else.get_or_insert_with(|| self.new_basic_block()); + let next = self.new_basic_block(); + if slice.is_none() { + self.set_terminator( + current, + TerminatorKind::SwitchInt { + discr: Operand::Copy(place_len), + targets: SwitchTargets::static_if( + pattern_len as u128, + next, + else_target, + ), + }, + pattern.into(), + ); + } else { + let c = Operand::from_concrete_const( + pattern_len.to_le_bytes().to_vec(), + MemoryMap::default(), + TyBuilder::usize(), + ); + let discr: Place = + self.temp(TyBuilder::bool(), current, pattern.into())?.into(); + self.push_assignment( + current, + discr.clone(), + Rvalue::CheckedBinaryOp(BinOp::Le, c, Operand::Copy(place_len)), + pattern.into(), + ); + let discr = Operand::Copy(discr); + self.set_terminator( + current, + TerminatorKind::SwitchInt { + discr, + targets: SwitchTargets::static_if(1, next, else_target), + }, + pattern.into(), + ); + } + current = next; + } + } + for (i, &pat) in prefix.iter().enumerate() { + let next_place = (&mut cond_place).project(ProjectionElem::ConstantIndex { + offset: i as u64, + from_end: false, + }); + (current, current_else) = + self.pattern_match_inner(current, current_else, next_place, pat, mode)?; + } + if let Some(slice) = slice { + if mode == MatchingMode::Bind { + if let Pat::Bind { id, subpat: _ } = self.body[*slice] { + let next_place = (&mut cond_place).project(ProjectionElem::Subslice { + from: prefix.len() as u64, + to: suffix.len() as u64, + }); + (current, current_else) = self.pattern_match_binding( + id, + next_place, + (*slice).into(), + current, + current_else, + )?; + } + } + } + for (i, &pat) in suffix.iter().enumerate() { + let next_place = (&mut cond_place).project(ProjectionElem::ConstantIndex { + offset: i as u64, + from_end: true, + }); + (current, current_else) = + self.pattern_match_inner(current, current_else, next_place, pat, mode)?; + } + (current, current_else) + } + Pat::Path(p) => match self.infer.variant_resolution_for_pat(pattern) { + Some(variant) => self.pattern_matching_variant( + cond_place, + variant, + current, + pattern.into(), + current_else, + AdtPatternShape::Unit, + mode, + )?, + None => { + let unresolved_name = || MirLowerError::unresolved_path(self.db, p); + let resolver = self.owner.resolver(self.db.upcast()); + let pr = resolver + .resolve_path_in_value_ns(self.db.upcast(), p) + .ok_or_else(unresolved_name)?; + let (c, subst) = 'b: { + if let Some(x) = self.infer.assoc_resolutions_for_pat(pattern) { + if let AssocItemId::ConstId(c) = x.0 { + break 'b (c, x.1); + } + } + if let ResolveValueResult::ValueNs(v) = pr { + if let ValueNs::ConstId(c) = v { + break 'b (c, Substitution::empty(Interner)); + } + } + not_supported!("path in pattern position that is not const or variant") + }; + let tmp: Place = + self.temp(self.infer[pattern].clone(), current, pattern.into())?.into(); + let span = pattern.into(); + self.lower_const( + c.into(), + current, + tmp.clone(), + subst, + span, + self.infer[pattern].clone(), + )?; + let tmp2: Place = self.temp(TyBuilder::bool(), current, pattern.into())?.into(); + self.push_assignment( + current, + tmp2.clone(), + Rvalue::CheckedBinaryOp( + BinOp::Eq, + Operand::Copy(tmp), + Operand::Copy(cond_place), + ), + span, + ); + let next = self.new_basic_block(); + let else_target = current_else.unwrap_or_else(|| self.new_basic_block()); + self.set_terminator( + current, + TerminatorKind::SwitchInt { + discr: Operand::Copy(tmp2), + targets: SwitchTargets::static_if(1, next, else_target), + }, + span, + ); + (next, Some(else_target)) + } + }, + Pat::Lit(l) => match &self.body.exprs[*l] { + Expr::Literal(l) => { + let c = self.lower_literal_to_operand(self.infer[pattern].clone(), l)?; + if mode == MatchingMode::Check { + self.pattern_match_const(current_else, current, c, cond_place, pattern)? + } else { + (current, current_else) + } + } + _ => not_supported!("expression path literal"), + }, + Pat::Bind { id, subpat } => { + if let Some(subpat) = subpat { + (current, current_else) = self.pattern_match_inner( + current, + current_else, + (&mut cond_place).clone(), + *subpat, + mode, + )? + } + if mode == MatchingMode::Bind { + self.pattern_match_binding( + *id, + cond_place, + pattern.into(), + current, + current_else, + )? + } else { + (current, current_else) + } + } + Pat::TupleStruct { path: _, args, ellipsis } => { + let Some(variant) = self.infer.variant_resolution_for_pat(pattern) else { + not_supported!("unresolved variant"); + }; + self.pattern_matching_variant( + cond_place, + variant, + current, + pattern.into(), + current_else, + AdtPatternShape::Tuple { args, ellipsis: *ellipsis }, + mode, + )? + } + Pat::Ref { pat, mutability: _ } => self.pattern_match_inner( + current, + current_else, + cond_place.project(ProjectionElem::Deref), + *pat, + mode, + )?, + Pat::Box { .. } => not_supported!("box pattern"), + Pat::ConstBlock(_) => not_supported!("const block pattern"), + }) + } + + fn pattern_match_binding( + &mut self, + id: BindingId, + cond_place: Place, + span: MirSpan, + current: BasicBlockId, + current_else: Option, + ) -> Result<(BasicBlockId, Option)> { + let target_place = self.binding_local(id)?; + let mode = self.infer.binding_modes[id]; + self.push_storage_live(id, current)?; + self.push_assignment( + current, + target_place.into(), + match mode { + BindingMode::Move => Operand::Copy(cond_place).into(), + BindingMode::Ref(Mutability::Not) => Rvalue::Ref(BorrowKind::Shared, cond_place), + BindingMode::Ref(Mutability::Mut) => { + Rvalue::Ref(BorrowKind::Mut { allow_two_phase_borrow: false }, cond_place) + } + }, + span, + ); + Ok((current, current_else)) + } + + fn pattern_match_const( + &mut self, + current_else: Option, + current: BasicBlockId, + c: Operand, + cond_place: Place, + pattern: Idx, + ) -> Result<(BasicBlockId, Option)> { + let then_target = self.new_basic_block(); + let else_target = current_else.unwrap_or_else(|| self.new_basic_block()); + let discr: Place = self.temp(TyBuilder::bool(), current, pattern.into())?.into(); + self.push_assignment( + current, + discr.clone(), + Rvalue::CheckedBinaryOp(BinOp::Eq, c, Operand::Copy(cond_place)), + pattern.into(), + ); + let discr = Operand::Copy(discr); + self.set_terminator( + current, + TerminatorKind::SwitchInt { + discr, + targets: SwitchTargets::static_if(1, then_target, else_target), + }, + pattern.into(), + ); + Ok((then_target, Some(else_target))) + } + + fn pattern_matching_variant( + &mut self, + cond_place: Place, + variant: VariantId, + mut current: BasicBlockId, + span: MirSpan, + mut current_else: Option, + shape: AdtPatternShape<'_>, + mode: MatchingMode, + ) -> Result<(BasicBlockId, Option)> { + Ok(match variant { + VariantId::EnumVariantId(v) => { + if mode == MatchingMode::Check { + let e = self.const_eval_discriminant(v)? as u128; + let tmp = self.discr_temp_place(current); + self.push_assignment( + current, + tmp.clone(), + Rvalue::Discriminant(cond_place.clone()), + span, + ); + let next = self.new_basic_block(); + let else_target = current_else.get_or_insert_with(|| self.new_basic_block()); + self.set_terminator( + current, + TerminatorKind::SwitchInt { + discr: Operand::Copy(tmp), + targets: SwitchTargets::static_if(e, next, *else_target), + }, + span, + ); + current = next; + } + let enum_data = self.db.enum_data(v.parent); + self.pattern_matching_variant_fields( + shape, + &enum_data.variants[v.local_id].variant_data, + variant, + current, + current_else, + &cond_place, + mode, + )? + } + VariantId::StructId(s) => { + let struct_data = self.db.struct_data(s); + self.pattern_matching_variant_fields( + shape, + &struct_data.variant_data, + variant, + current, + current_else, + &cond_place, + mode, + )? + } + VariantId::UnionId(_) => { + return Err(MirLowerError::TypeError("pattern matching on union")) + } + }) + } + + fn pattern_matching_variant_fields( + &mut self, + shape: AdtPatternShape<'_>, + variant_data: &VariantData, + v: VariantId, + current: BasicBlockId, + current_else: Option, + cond_place: &Place, + mode: MatchingMode, + ) -> Result<(BasicBlockId, Option)> { + Ok(match shape { + AdtPatternShape::Record { args } => { + let it = args + .iter() + .map(|x| { + let field_id = + variant_data.field(&x.name).ok_or(MirLowerError::UnresolvedField)?; + Ok(( + PlaceElem::Field(FieldId { parent: v.into(), local_id: field_id }), + x.pat, + )) + }) + .collect::>>()?; + self.pattern_match_adt(current, current_else, it.into_iter(), cond_place, mode)? + } + AdtPatternShape::Tuple { args, ellipsis } => { + let fields = variant_data + .fields() + .iter() + .map(|(x, _)| PlaceElem::Field(FieldId { parent: v.into(), local_id: x })); + self.pattern_match_tuple_like( + current, + current_else, + args, + ellipsis, + fields, + cond_place, + mode, + )? + } + AdtPatternShape::Unit => (current, current_else), + }) + } + + fn pattern_match_adt( + &mut self, + mut current: BasicBlockId, + mut current_else: Option, + args: impl Iterator, + cond_place: &Place, + mode: MatchingMode, + ) -> Result<(BasicBlockId, Option)> { + for (proj, arg) in args { + let cond_place = cond_place.project(proj); + (current, current_else) = + self.pattern_match_inner(current, current_else, cond_place, arg, mode)?; + } + Ok((current, current_else)) + } + + fn pattern_match_tuple_like( + &mut self, + current: BasicBlockId, + current_else: Option, + args: &[PatId], + ellipsis: Option, + fields: impl DoubleEndedIterator + Clone, + cond_place: &Place, + mode: MatchingMode, + ) -> Result<(BasicBlockId, Option)> { + let (al, ar) = args.split_at(ellipsis.unwrap_or(args.len())); + let it = al + .iter() + .zip(fields.clone()) + .chain(ar.iter().rev().zip(fields.rev())) + .map(|(x, y)| (y, *x)); + self.pattern_match_adt(current, current_else, it, cond_place, mode) + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs new file mode 100644 index 0000000000000..ce3f7a8e51026 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs @@ -0,0 +1,351 @@ +//! Monomorphization of mir, which is used in mir interpreter and const eval. +//! +//! The job of monomorphization is: +//! * Monomorphization. That is, replacing `Option` with `Option` where `T:=i32` substitution +//! is provided +//! * Normalizing types, for example replacing RPIT of other functions called in this body. +//! +//! So the monomorphization should be called even if the substitution is empty. + +use std::mem; + +use chalk_ir::{ + fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable}, + ConstData, DebruijnIndex, +}; +use hir_def::{DefWithBodyId, GeneralConstId}; +use triomphe::Arc; + +use crate::{ + consteval::unknown_const, + db::HirDatabase, + from_placeholder_idx, + infer::normalize, + method_resolution::lookup_impl_const, + utils::{generics, Generics}, + ClosureId, Const, Interner, ProjectionTy, Substitution, TraitEnvironment, Ty, TyKind, +}; + +use super::{MirBody, MirLowerError, Operand, Rvalue, StatementKind, TerminatorKind}; + +macro_rules! not_supported { + ($x: expr) => { + return Err(MirLowerError::NotSupported(format!($x))) + }; +} + +struct Filler<'a> { + db: &'a dyn HirDatabase, + trait_env: Arc, + subst: &'a Substitution, + generics: Option, + owner: DefWithBodyId, +} +impl FallibleTypeFolder for Filler<'_> { + type Error = MirLowerError; + + fn as_dyn(&mut self) -> &mut dyn FallibleTypeFolder { + self + } + + fn interner(&self) -> Interner { + Interner + } + + fn try_fold_ty( + &mut self, + ty: Ty, + outer_binder: DebruijnIndex, + ) -> std::result::Result { + match ty.kind(Interner) { + TyKind::AssociatedType(id, subst) => { + // I don't know exactly if and why this is needed, but it looks like `normalize_ty` likes + // this kind of associated types. + Ok(TyKind::Alias(chalk_ir::AliasTy::Projection(ProjectionTy { + associated_ty_id: *id, + substitution: subst.clone().try_fold_with(self, outer_binder)?, + })) + .intern(Interner)) + } + TyKind::OpaqueType(id, subst) => { + let impl_trait_id = self.db.lookup_intern_impl_trait_id((*id).into()); + let subst = subst.clone().try_fold_with(self.as_dyn(), outer_binder)?; + match impl_trait_id { + crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => { + let infer = self.db.infer(func.into()); + let filler = &mut Filler { + db: self.db, + owner: self.owner, + trait_env: self.trait_env.clone(), + subst: &subst, + generics: Some(generics(self.db.upcast(), func.into())), + }; + filler.try_fold_ty(infer.type_of_rpit[idx].clone(), outer_binder) + } + crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => { + not_supported!("async block impl trait"); + } + } + } + _ => ty.try_super_fold_with(self.as_dyn(), outer_binder), + } + } + + fn try_fold_free_placeholder_const( + &mut self, + _ty: chalk_ir::Ty, + idx: chalk_ir::PlaceholderIndex, + _outer_binder: DebruijnIndex, + ) -> std::result::Result, Self::Error> { + let x = from_placeholder_idx(self.db, idx); + let Some(idx) = self.generics.as_ref().and_then(|g| g.param_idx(x)) else { + not_supported!("missing idx in generics"); + }; + Ok(self + .subst + .as_slice(Interner) + .get(idx) + .and_then(|x| x.constant(Interner)) + .ok_or_else(|| MirLowerError::GenericArgNotProvided(x, self.subst.clone()))? + .clone()) + } + + fn try_fold_free_placeholder_ty( + &mut self, + idx: chalk_ir::PlaceholderIndex, + _outer_binder: DebruijnIndex, + ) -> std::result::Result { + let x = from_placeholder_idx(self.db, idx); + let Some(idx) = self.generics.as_ref().and_then(|g| g.param_idx(x)) else { + not_supported!("missing idx in generics"); + }; + Ok(self + .subst + .as_slice(Interner) + .get(idx) + .and_then(|x| x.ty(Interner)) + .ok_or_else(|| MirLowerError::GenericArgNotProvided(x, self.subst.clone()))? + .clone()) + } + + fn try_fold_const( + &mut self, + constant: chalk_ir::Const, + outer_binder: DebruijnIndex, + ) -> Result, Self::Error> { + let next_ty = normalize( + self.db, + self.trait_env.clone(), + constant.data(Interner).ty.clone().try_fold_with(self, outer_binder)?, + ); + ConstData { ty: next_ty, value: constant.data(Interner).value.clone() } + .intern(Interner) + .try_super_fold_with(self, outer_binder) + } +} + +impl Filler<'_> { + fn fill_ty(&mut self, ty: &mut Ty) -> Result<(), MirLowerError> { + let tmp = mem::replace(ty, TyKind::Error.intern(Interner)); + *ty = normalize( + self.db, + self.trait_env.clone(), + tmp.try_fold_with(self, DebruijnIndex::INNERMOST)?, + ); + Ok(()) + } + + fn fill_const(&mut self, c: &mut Const) -> Result<(), MirLowerError> { + let tmp = mem::replace(c, unknown_const(c.data(Interner).ty.clone())); + *c = tmp.try_fold_with(self, DebruijnIndex::INNERMOST)?; + Ok(()) + } + + fn fill_subst(&mut self, ty: &mut Substitution) -> Result<(), MirLowerError> { + let tmp = mem::replace(ty, Substitution::empty(Interner)); + *ty = tmp.try_fold_with(self, DebruijnIndex::INNERMOST)?; + Ok(()) + } + + fn fill_operand(&mut self, op: &mut Operand) -> Result<(), MirLowerError> { + match op { + Operand::Constant(c) => { + match &c.data(Interner).value { + chalk_ir::ConstValue::BoundVar(b) => { + let resolved = self + .subst + .as_slice(Interner) + .get(b.index) + .ok_or_else(|| { + MirLowerError::GenericArgNotProvided( + self.generics + .as_ref() + .and_then(|x| x.iter().nth(b.index)) + .unwrap() + .0, + self.subst.clone(), + ) + })? + .assert_const_ref(Interner); + *c = resolved.clone(); + } + chalk_ir::ConstValue::InferenceVar(_) + | chalk_ir::ConstValue::Placeholder(_) => {} + chalk_ir::ConstValue::Concrete(cc) => match &cc.interned { + crate::ConstScalar::UnevaluatedConst(const_id, subst) => { + let mut const_id = *const_id; + let mut subst = subst.clone(); + self.fill_subst(&mut subst)?; + if let GeneralConstId::ConstId(c) = const_id { + let (c, s) = lookup_impl_const( + self.db, + self.db.trait_environment_for_body(self.owner), + c, + subst, + ); + const_id = GeneralConstId::ConstId(c); + subst = s; + } + let result = + self.db.const_eval(const_id.into(), subst).map_err(|e| { + let name = const_id.name(self.db.upcast()); + MirLowerError::ConstEvalError(name, Box::new(e)) + })?; + *c = result; + } + crate::ConstScalar::Bytes(_, _) | crate::ConstScalar::Unknown => (), + }, + } + self.fill_const(c)?; + } + Operand::Copy(_) | Operand::Move(_) | Operand::Static(_) => (), + } + Ok(()) + } + + fn fill_body(&mut self, body: &mut MirBody) -> Result<(), MirLowerError> { + for (_, l) in body.locals.iter_mut() { + self.fill_ty(&mut l.ty)?; + } + for (_, bb) in body.basic_blocks.iter_mut() { + for statement in &mut bb.statements { + match &mut statement.kind { + StatementKind::Assign(_, r) => match r { + Rvalue::Aggregate(ak, ops) => { + for op in &mut **ops { + self.fill_operand(op)?; + } + match ak { + super::AggregateKind::Array(ty) + | super::AggregateKind::Tuple(ty) + | super::AggregateKind::Closure(ty) => self.fill_ty(ty)?, + super::AggregateKind::Adt(_, subst) => self.fill_subst(subst)?, + super::AggregateKind::Union(_, _) => (), + } + } + Rvalue::ShallowInitBox(_, ty) | Rvalue::ShallowInitBoxWithAlloc(ty) => { + self.fill_ty(ty)?; + } + Rvalue::Use(op) => { + self.fill_operand(op)?; + } + Rvalue::Repeat(op, len) => { + self.fill_operand(op)?; + self.fill_const(len)?; + } + Rvalue::Ref(_, _) + | Rvalue::Len(_) + | Rvalue::Cast(_, _, _) + | Rvalue::CheckedBinaryOp(_, _, _) + | Rvalue::UnaryOp(_, _) + | Rvalue::Discriminant(_) + | Rvalue::CopyForDeref(_) => (), + }, + StatementKind::Deinit(_) + | StatementKind::StorageLive(_) + | StatementKind::StorageDead(_) + | StatementKind::Nop => (), + } + } + if let Some(terminator) = &mut bb.terminator { + match &mut terminator.kind { + TerminatorKind::Call { func, args, .. } => { + self.fill_operand(func)?; + for op in &mut **args { + self.fill_operand(op)?; + } + } + TerminatorKind::SwitchInt { discr, .. } => { + self.fill_operand(discr)?; + } + TerminatorKind::Goto { .. } + | TerminatorKind::Resume + | TerminatorKind::Abort + | TerminatorKind::Return + | TerminatorKind::Unreachable + | TerminatorKind::Drop { .. } + | TerminatorKind::DropAndReplace { .. } + | TerminatorKind::Assert { .. } + | TerminatorKind::Yield { .. } + | TerminatorKind::GeneratorDrop + | TerminatorKind::FalseEdge { .. } + | TerminatorKind::FalseUnwind { .. } => (), + } + } + } + Ok(()) + } +} + +pub fn monomorphized_mir_body_query( + db: &dyn HirDatabase, + owner: DefWithBodyId, + subst: Substitution, + trait_env: Arc, +) -> Result, MirLowerError> { + let generics = owner.as_generic_def_id().map(|g_def| generics(db.upcast(), g_def)); + let filler = &mut Filler { db, subst: &subst, trait_env, generics, owner }; + let body = db.mir_body(owner)?; + let mut body = (*body).clone(); + filler.fill_body(&mut body)?; + Ok(Arc::new(body)) +} + +pub fn monomorphized_mir_body_recover( + _: &dyn HirDatabase, + _: &[String], + _: &DefWithBodyId, + _: &Substitution, + _: &Arc, +) -> Result, MirLowerError> { + return Err(MirLowerError::Loop); +} + +pub fn monomorphized_mir_body_for_closure_query( + db: &dyn HirDatabase, + closure: ClosureId, + subst: Substitution, + trait_env: Arc, +) -> Result, MirLowerError> { + let (owner, _) = db.lookup_intern_closure(closure.into()); + let generics = owner.as_generic_def_id().map(|g_def| generics(db.upcast(), g_def)); + let filler = &mut Filler { db, subst: &subst, trait_env, generics, owner }; + let body = db.mir_body_for_closure(closure)?; + let mut body = (*body).clone(); + filler.fill_body(&mut body)?; + Ok(Arc::new(body)) +} + +// FIXME: remove this function. Monomorphization is a time consuming job and should always be a query. +pub fn monomorphize_mir_body_bad( + db: &dyn HirDatabase, + mut body: MirBody, + subst: Substitution, + trait_env: Arc, +) -> Result { + let owner = body.owner; + let generics = owner.as_generic_def_id().map(|g_def| generics(db.upcast(), g_def)); + let filler = &mut Filler { db, subst: &subst, trait_env, generics, owner }; + filler.fill_body(&mut body)?; + Ok(body) +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs index ffc08b7e346c7..58662b01b99b1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs @@ -1,28 +1,80 @@ //! A pretty-printer for MIR. -use std::fmt::{Display, Write}; +use std::{ + fmt::{Debug, Display, Write}, + mem, +}; -use hir_def::{body::Body, expr::BindingId}; +use hir_def::{body::Body, hir::BindingId}; use hir_expand::name::Name; use la_arena::ArenaMap; use crate::{ db::HirDatabase, - display::HirDisplay, - mir::{PlaceElem, ProjectionElem, StatementKind, Terminator}, + display::{ClosureStyle, HirDisplay}, + mir::{PlaceElem, ProjectionElem, StatementKind, TerminatorKind}, + ClosureId, }; use super::{ AggregateKind, BasicBlockId, BorrowKind, LocalId, MirBody, Operand, Place, Rvalue, UnOp, }; +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)*); } + }; +} + impl MirBody { pub fn pretty_print(&self, db: &dyn HirDatabase) -> String { let hir_body = db.body(self.owner); let mut ctx = MirPrettyCtx::new(self, &hir_body, db); - ctx.for_body(); + ctx.for_body(|this| match ctx.body.owner { + hir_def::DefWithBodyId::FunctionId(id) => { + let data = db.function_data(id); + w!(this, "fn {}() ", data.name.display(db.upcast())); + } + hir_def::DefWithBodyId::StaticId(id) => { + let data = db.static_data(id); + w!(this, "static {}: _ = ", data.name.display(db.upcast())); + } + hir_def::DefWithBodyId::ConstId(id) => { + let data = db.const_data(id); + w!( + this, + "const {}: _ = ", + data.name.as_ref().unwrap_or(&Name::missing()).display(db.upcast()) + ); + } + hir_def::DefWithBodyId::VariantId(id) => { + let data = db.enum_data(id.parent); + w!(this, "enum {} = ", data.name.display(db.upcast())); + } + }); ctx.result } + + // String with lines is rendered poorly in `dbg` macros, which I use very much, so this + // function exists to solve that. + pub fn dbg(&self, db: &dyn HirDatabase) -> impl Debug { + struct StringDbg(String); + impl Debug for StringDbg { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&self.0) + } + } + StringDbg(self.pretty_print(db)) + } } struct MirPrettyCtx<'a> { @@ -30,25 +82,10 @@ struct MirPrettyCtx<'a> { hir_body: &'a Body, db: &'a dyn HirDatabase, result: String, - ident: String, + indent: String, local_to_binding: ArenaMap, } -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)*); } - }; -} - impl Write for MirPrettyCtx<'_> { fn write_str(&mut self, s: &str) -> std::fmt::Result { let mut it = s.split('\n'); // note: `.lines()` is wrong here @@ -66,31 +103,62 @@ enum LocalName { Binding(Name, LocalId), } -impl Display for LocalName { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl HirDisplay for LocalName { + fn hir_fmt( + &self, + f: &mut crate::display::HirFormatter<'_>, + ) -> Result<(), crate::display::HirDisplayError> { match self { LocalName::Unknown(l) => write!(f, "_{}", u32::from(l.into_raw())), - LocalName::Binding(n, l) => write!(f, "{n}_{}", u32::from(l.into_raw())), + LocalName::Binding(n, l) => { + write!(f, "{}_{}", n.display(f.db.upcast()), u32::from(l.into_raw())) + } } } } impl<'a> MirPrettyCtx<'a> { - fn for_body(&mut self) { + fn for_body(&mut self, name: impl FnOnce(&mut MirPrettyCtx<'_>)) { + name(self); self.with_block(|this| { this.locals(); wln!(this); this.blocks(); }); + for &closure in &self.body.closures { + self.for_closure(closure); + } + } + + fn for_closure(&mut self, closure: ClosureId) { + let body = match self.db.mir_body_for_closure(closure) { + Ok(x) => x, + Err(e) => { + wln!(self, "// error in {closure:?}: {e:?}"); + return; + } + }; + let result = mem::take(&mut self.result); + let indent = mem::take(&mut self.indent); + let mut ctx = MirPrettyCtx { + body: &body, + local_to_binding: body.binding_locals.iter().map(|(x, y)| (*y, x)).collect(), + result, + indent, + ..*self + }; + ctx.for_body(|this| wln!(this, "// Closure: {:?}", closure)); + self.result = ctx.result; + self.indent = ctx.indent; } fn with_block(&mut self, f: impl FnOnce(&mut MirPrettyCtx<'_>)) { - self.ident += " "; + self.indent += " "; wln!(self, "{{"); f(self); for _ in 0..4 { self.result.pop(); - self.ident.pop(); + self.indent.pop(); } wln!(self, "}}"); } @@ -101,7 +169,7 @@ impl<'a> MirPrettyCtx<'a> { body, db, result: String::new(), - ident: String::new(), + indent: String::new(), local_to_binding, hir_body, } @@ -109,7 +177,7 @@ impl<'a> MirPrettyCtx<'a> { fn write_line(&mut self) { self.result.push('\n'); - self.result += &self.ident; + self.result += &self.indent; } fn write(&mut self, line: &str) { @@ -118,7 +186,12 @@ impl<'a> MirPrettyCtx<'a> { fn locals(&mut self) { for (id, local) in self.body.locals.iter() { - wln!(self, "let {}: {};", self.local_name(id), local.ty.display(self.db)); + wln!( + self, + "let {}: {};", + self.local_name(id).display(self.db), + self.hir_display(&local.ty) + ); } } @@ -147,10 +220,10 @@ impl<'a> MirPrettyCtx<'a> { wln!(this, ";"); } StatementKind::StorageDead(p) => { - wln!(this, "StorageDead({})", this.local_name(*p)); + wln!(this, "StorageDead({})", this.local_name(*p).display(self.db)); } StatementKind::StorageLive(p) => { - wln!(this, "StorageLive({})", this.local_name(*p)); + wln!(this, "StorageLive({})", this.local_name(*p).display(self.db)); } StatementKind::Deinit(p) => { w!(this, "Deinit("); @@ -161,11 +234,11 @@ impl<'a> MirPrettyCtx<'a> { } } match &block.terminator { - Some(terminator) => match terminator { - Terminator::Goto { target } => { + Some(terminator) => match &terminator.kind { + TerminatorKind::Goto { target } => { wln!(this, "goto 'bb{};", u32::from(target.into_raw())) } - Terminator::SwitchInt { discr, targets } => { + TerminatorKind::SwitchInt { discr, targets } => { w!(this, "switch "); this.operand(discr); w!(this, " "); @@ -176,7 +249,7 @@ impl<'a> MirPrettyCtx<'a> { wln!(this, "_ => {},", this.basic_block_id(targets.otherwise())); }); } - Terminator::Call { func, args, destination, target, .. } => { + TerminatorKind::Call { func, args, destination, target, .. } => { w!(this, "Call "); this.with_block(|this| { w!(this, "func: "); @@ -208,7 +281,7 @@ impl<'a> MirPrettyCtx<'a> { fn f(this: &mut MirPrettyCtx<'_>, local: LocalId, projections: &[PlaceElem]) { let Some((last, head)) = projections.split_last() else { // no projection - w!(this, "{}", this.local_name(local)); + w!(this, "{}", this.local_name(local).display(this.db)); return; }; match last { @@ -226,21 +299,26 @@ impl<'a> MirPrettyCtx<'a> { f(this, local, head); let variant_name = &this.db.enum_data(e.parent).variants[e.local_id].name; - w!(this, " as {}).{}", variant_name, name); + w!( + this, + " as {}).{}", + variant_name.display(this.db.upcast()), + name.display(this.db.upcast()) + ); } hir_def::VariantId::StructId(_) | hir_def::VariantId::UnionId(_) => { f(this, local, head); - w!(this, ".{name}"); + w!(this, ".{}", name.display(this.db.upcast())); } } } - ProjectionElem::TupleField(x) => { + ProjectionElem::TupleOrClosureField(x) => { f(this, local, head); w!(this, ".{}", x); } ProjectionElem::Index(l) => { f(this, local, head); - w!(this, "[{}]", this.local_name(*l)); + w!(this, "[{}]", this.local_name(*l).display(this.db)); } x => { f(this, local, head); @@ -258,7 +336,8 @@ impl<'a> MirPrettyCtx<'a> { // equally. Feel free to change it. self.place(p); } - Operand::Constant(c) => w!(self, "Const({})", c.display(self.db)), + Operand::Constant(c) => w!(self, "Const({})", self.hir_display(c)), + Operand::Static(s) => w!(self, "Static({:?})", s), } } @@ -284,11 +363,21 @@ impl<'a> MirPrettyCtx<'a> { self.operand_list(x); w!(self, "]"); } + Rvalue::Repeat(op, len) => { + w!(self, "["); + self.operand(op); + w!(self, "; {}]", len.display(self.db)); + } Rvalue::Aggregate(AggregateKind::Adt(_, _), x) => { w!(self, "Adt("); self.operand_list(x); w!(self, ")"); } + Rvalue::Aggregate(AggregateKind::Closure(_), x) => { + w!(self, "Closure("); + self.operand_list(x); + w!(self, ")"); + } Rvalue::Aggregate(AggregateKind::Union(_, _), x) => { w!(self, "Union("); self.operand_list(x); @@ -300,9 +389,9 @@ impl<'a> MirPrettyCtx<'a> { w!(self, ")"); } Rvalue::Cast(ck, op, ty) => { - w!(self, "Discriminant({ck:?}"); + w!(self, "Cast({ck:?}, "); self.operand(op); - w!(self, "{})", ty.display(self.db)); + w!(self, ", {})", self.hir_display(ty)); } Rvalue::CheckedBinaryOp(b, o1, o2) => { self.operand(o1); @@ -322,6 +411,7 @@ impl<'a> MirPrettyCtx<'a> { self.place(p); w!(self, ")"); } + Rvalue::ShallowInitBoxWithAlloc(_) => w!(self, "ShallowInitBoxWithAlloc"), Rvalue::ShallowInitBox(op, _) => { w!(self, "ShallowInitBox("); self.operand(op); @@ -345,4 +435,8 @@ impl<'a> MirPrettyCtx<'a> { } } } + + fn hir_display(&self, ty: &'a T) -> impl Display + 'a { + ty.display(self.db).with_closure_style(ClosureStyle::ClosureWithSubst) + } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs index 8c48331b94b53..7d19e0a19169a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs @@ -1,18 +1,18 @@ //! Database used for testing `hir`. -use std::{ - fmt, panic, - sync::{Arc, Mutex}, -}; +use std::{fmt, panic, sync::Mutex}; use base_db::{ - salsa, AnchoredPath, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast, + salsa::{self, Durability}, + AnchoredPath, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast, }; use hir_def::{db::DefDatabase, ModuleId}; use hir_expand::db::ExpandDatabase; -use stdx::hash::{NoHashHashMap, NoHashHashSet}; +use nohash_hasher::IntMap; +use rustc_hash::FxHashSet; use syntax::TextRange; use test_utils::extract_annotations; +use triomphe::Arc; #[salsa::database( base_db::SourceDatabaseExtStorage, @@ -30,7 +30,7 @@ pub(crate) struct TestDB { impl Default for TestDB { fn default() -> Self { let mut this = Self { storage: Default::default(), events: Default::default() }; - this.set_enable_proc_attr_macros(true); + this.set_expand_proc_attr_macros_with_durability(true, Durability::HIGH); this } } @@ -74,13 +74,13 @@ impl salsa::ParallelDatabase for TestDB { impl panic::RefUnwindSafe for TestDB {} impl FileLoader for TestDB { - fn file_text(&self, file_id: FileId) -> Arc { + fn file_text(&self, file_id: FileId) -> Arc { FileLoaderDelegate(self).file_text(file_id) } fn resolve_path(&self, path: AnchoredPath<'_>) -> Option { FileLoaderDelegate(self).resolve_path(path) } - fn relevant_crates(&self, file_id: FileId) -> Arc> { + fn relevant_crates(&self, file_id: FileId) -> Arc> { FileLoaderDelegate(self).relevant_crates(file_id) } } @@ -102,7 +102,7 @@ impl TestDB { self.module_for_file_opt(file_id).unwrap() } - pub(crate) fn extract_annotations(&self) -> NoHashHashMap> { + pub(crate) fn extract_annotations(&self) -> IntMap> { let mut files = Vec::new(); let crate_graph = self.crate_graph(); for krate in crate_graph.iter() { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs index 83d31f002a1dc..2db04024b7b64 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs @@ -10,14 +10,14 @@ mod display_source_code; mod incremental; mod diagnostics; -use std::{collections::HashMap, env, sync::Arc}; +use std::{collections::HashMap, env}; use base_db::{fixture::WithFixture, FileRange, SourceDatabaseExt}; use expect_test::Expect; use hir_def::{ body::{Body, BodySourceMap, SyntheticSyntax}, db::{DefDatabase, InternDatabase}, - expr::{ExprId, PatId}, + hir::{ExprId, Pat, PatId}, item_scope::ItemScope, nameres::DefMap, src::HasSource, @@ -32,6 +32,7 @@ use syntax::{ }; use tracing_subscriber::{layer::SubscriberExt, EnvFilter, Registry}; use tracing_tree::HierarchicalLayer; +use triomphe::Arc; use crate::{ db::HirDatabase, @@ -148,10 +149,13 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour }); let mut unexpected_type_mismatches = String::new(); for def in defs { - let (_body, body_source_map) = db.body_with_source_map(def); + let (body, body_source_map) = db.body_with_source_map(def); let inference_result = db.infer(def); - for (pat, ty) in inference_result.type_of_pat.iter() { + for (pat, mut ty) in inference_result.type_of_pat.iter() { + if let Pat::Bind { id, .. } = body.pats[pat] { + ty = &inference_result.type_of_binding[id]; + } let node = match pat_node(&body_source_map, pat, &db) { Some(value) => value, None => continue, @@ -159,7 +163,7 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour let range = node.as_ref().original_file_range(&db); if let Some(expected) = types.remove(&range) { let actual = if display_source { - ty.display_source_code(&db, def.module(&db)).unwrap() + ty.display_source_code(&db, def.module(&db), true).unwrap() } else { ty.display_test(&db).to_string() }; @@ -175,7 +179,7 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour let range = node.as_ref().original_file_range(&db); if let Some(expected) = types.remove(&range) { let actual = if display_source { - ty.display_source_code(&db, def.module(&db)).unwrap() + ty.display_source_code(&db, def.module(&db), true).unwrap() } else { ty.display_test(&db).to_string() }; @@ -198,8 +202,8 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour for (expr_or_pat, mismatch) in inference_result.type_mismatches() { let Some(node) = (match expr_or_pat { - hir_def::expr::ExprOrPatId::ExprId(expr) => expr_node(&body_source_map, expr, &db), - hir_def::expr::ExprOrPatId::PatId(pat) => pat_node(&body_source_map, pat, &db), + hir_def::hir::ExprOrPatId::ExprId(expr) => expr_node(&body_source_map, expr, &db), + hir_def::hir::ExprOrPatId::PatId(pat) => pat_node(&body_source_map, pat, &db), }) else { continue; }; let range = node.as_ref().original_file_range(&db); let actual = format!( @@ -246,7 +250,7 @@ fn expr_node( ) -> Option> { Some(match body_source_map.expr_syntax(expr) { Ok(sp) => { - let root = db.parse_or_expand(sp.file_id).unwrap(); + let root = db.parse_or_expand(sp.file_id); sp.map(|ptr| ptr.to_node(&root).syntax().clone()) } Err(SyntheticSyntax) => return None, @@ -260,7 +264,7 @@ fn pat_node( ) -> Option> { Some(match body_source_map.pat_syntax(pat) { Ok(sp) => { - let root = db.parse_or_expand(sp.file_id).unwrap(); + let root = db.parse_or_expand(sp.file_id); sp.map(|ptr| { ptr.either( |it| it.to_node(&root).syntax().clone(), @@ -283,14 +287,18 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { let mut buf = String::new(); let mut infer_def = |inference_result: Arc, + body: Arc, body_source_map: Arc| { let mut types: Vec<(InFile, &Ty)> = Vec::new(); let mut mismatches: Vec<(InFile, &TypeMismatch)> = Vec::new(); - for (pat, ty) in inference_result.type_of_pat.iter() { + for (pat, mut ty) in inference_result.type_of_pat.iter() { + if let Pat::Bind { id, .. } = body.pats[pat] { + ty = &inference_result.type_of_binding[id]; + } let syntax_ptr = match body_source_map.pat_syntax(pat) { Ok(sp) => { - let root = db.parse_or_expand(sp.file_id).unwrap(); + let root = db.parse_or_expand(sp.file_id); sp.map(|ptr| { ptr.either( |it| it.to_node(&root).syntax().clone(), @@ -309,7 +317,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { for (expr, ty) in inference_result.type_of_expr.iter() { let node = match body_source_map.expr_syntax(expr) { Ok(sp) => { - let root = db.parse_or_expand(sp.file_id).unwrap(); + let root = db.parse_or_expand(sp.file_id); sp.map(|ptr| ptr.to_node(&root).syntax().clone()) } Err(SyntheticSyntax) => continue, @@ -385,9 +393,9 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { } }); for def in defs { - let (_body, source_map) = db.body_with_source_map(def); + let (body, source_map) = db.body_with_source_map(def); let infer = db.infer(def); - infer_def(infer, source_map); + infer_def(infer, body, source_map); } buf.truncate(buf.trim_end().len()); @@ -572,10 +580,9 @@ fn salsa_bug() { let x = 1; x.push(1); } - " - .to_string(); + "; - db.set_file_text(pos.file_id, Arc::new(new_text)); + db.set_file_text(pos.file_id, Arc::from(new_text)); let module = db.module_for_file(pos.file_id); let crate_def_map = module.def_map(&db); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs index b524922b6cf40..16e5ef85d09d5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs @@ -258,7 +258,6 @@ fn test() { #[test] fn coerce_autoderef_block() { - // FIXME: We should know mutability in overloaded deref check_no_mismatches( r#" //- minicore: deref @@ -268,7 +267,7 @@ fn takes_ref_str(x: &str) {} fn returns_string() -> String { loop {} } fn test() { takes_ref_str(&{ returns_string() }); - // ^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(None), Deref(Some(OverloadedDeref(None))), Borrow(Ref(Not)) + // ^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(None), Deref(Some(OverloadedDeref(Some(Not)))), Borrow(Ref(Not)) } "#, ); @@ -396,10 +395,40 @@ fn test() { ); } +#[test] +fn coerce_fn_item_to_fn_ptr_in_array() { + check_no_mismatches( + r" +fn foo(x: u32) -> isize { 1 } +fn bar(x: u32) -> isize { 1 } +fn test() { + let f = [foo, bar]; + // ^^^ adjustments: Pointer(ReifyFnPointer) +}", + ); +} + #[test] fn coerce_fn_items_in_match_arms() { cov_mark::check!(coerce_fn_reification); + check_no_mismatches( + r" +fn foo1(x: u32) -> isize { 1 } +fn foo2(x: u32) -> isize { 2 } +fn foo3(x: u32) -> isize { 3 } +fn test() { + let x = match 1 { + 1 => foo1, + // ^^^^ adjustments: Pointer(ReifyFnPointer) + 2 => foo2, + // ^^^^ adjustments: Pointer(ReifyFnPointer) + _ => foo3, + // ^^^^ adjustments: Pointer(ReifyFnPointer) + }; + x; +}", + ); check_types( r" fn foo1(x: u32) -> isize { 1 } @@ -507,7 +536,6 @@ fn test() { #[test] fn coerce_unsize_generic() { - // FIXME: fix the type mismatches here check( r#" //- minicore: coerce_unsized @@ -516,9 +544,9 @@ struct Bar(Foo); fn test() { let _: &Foo<[usize]> = &Foo { t: [1, 2, 3] }; - //^^^^^^^^^ expected [usize], got [usize; 3] + //^^^^^^^^^^^^^^^^^^^^^ expected &Foo<[usize]>, got &Foo<[i32; 3]> let _: &Bar<[usize]> = &Bar(Foo { t: [1, 2, 3] }); - //^^^^^^^^^ expected [usize], got [usize; 3] + //^^^^^^^^^^^^^^^^^^^^^^^^^^ expected &Bar<[usize]>, got &Bar<[i32; 3]> } "#, ); @@ -547,7 +575,7 @@ fn two_closures_lub() { fn foo(c: i32) { let add = |a: i32, b: i32| a + b; let sub = |a, b| a - b; - //^^^^^^^^^^^^ |i32, i32| -> i32 + //^^^^^^^^^^^^ impl Fn(i32, i32) -> i32 if c > 42 { add } else { sub }; //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ fn(i32, i32) -> i32 } @@ -842,3 +870,74 @@ fn test() { }", ); } + +#[test] +fn adjust_index() { + check_no_mismatches( + r" +//- minicore: index, slice, coerce_unsized +fn test() { + let x = [1, 2, 3]; + x[2] = 6; + // ^ adjustments: Borrow(Ref(Mut)) +} + ", + ); + check_no_mismatches( + r" +//- minicore: index +struct Struct; +impl core::ops::Index for Struct { + type Output = (); + + fn index(&self, index: usize) -> &Self::Output { &() } +} +struct StructMut; + +impl core::ops::Index for StructMut { + type Output = (); + + fn index(&self, index: usize) -> &Self::Output { &() } +} +impl core::ops::IndexMut for StructMut { + fn index_mut(&mut self, index: usize) -> &mut Self::Output { &mut () } +} +fn test() { + Struct[0]; + // ^^^^^^ adjustments: Borrow(Ref(Not)) + StructMut[0]; + // ^^^^^^^^^ adjustments: Borrow(Ref(Not)) + &mut StructMut[0]; + // ^^^^^^^^^ adjustments: Borrow(Ref(Mut)) +}", + ); +} + +#[test] +fn regression_14443_dyn_coercion_block_impls() { + check_no_mismatches( + r#" +//- minicore: coerce_unsized +trait T {} + +fn dyn_t(d: &dyn T) {} + +fn main() { + struct A; + impl T for A {} + + let a = A; + + let b = { + struct B; + impl T for B {} + + B + }; + + dyn_t(&a); + dyn_t(&b); +} +"#, + ) +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs index 073d6d9be2b9f..bb15ca8c436a2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs @@ -1,6 +1,5 @@ -use std::sync::Arc; - use base_db::{fixture::WithFixture, SourceDatabaseExt}; +use triomphe::Arc; use crate::{db::HirDatabase, test_db::TestDB}; @@ -33,10 +32,9 @@ fn typing_whitespace_inside_a_function_should_not_invalidate_types() { + 1 } - " - .to_string(); + "; - db.set_file_text(pos.file_id, Arc::new(new_text)); + db.set_file_text(pos.file_id, Arc::from(new_text)); { let events = db.log_executed(|| { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs index 8b75ec842a4f6..111ac0b618eb2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs @@ -140,6 +140,7 @@ fn infer_path_qualified_macros_expanded() { fn expr_macro_def_expanded_in_various_places() { check_infer( r#" + //- minicore: iterator macro spam() { 1isize } @@ -195,10 +196,19 @@ fn expr_macro_def_expanded_in_various_places() { !0..6 '1isize': isize 39..442 '{ ...!(); }': () 73..94 'spam!(...am!())': {unknown} + 100..119 'for _ ...!() {}': fn into_iter(isize) -> ::IntoIter + 100..119 'for _ ...!() {}': IntoIterator::IntoIter + 100..119 'for _ ...!() {}': ! + 100..119 'for _ ...!() {}': IntoIterator::IntoIter + 100..119 'for _ ...!() {}': &mut IntoIterator::IntoIter + 100..119 'for _ ...!() {}': fn next>(&mut IntoIterator::IntoIter) -> Option< as Iterator>::Item> + 100..119 'for _ ...!() {}': Option>> 100..119 'for _ ...!() {}': () - 104..105 '_': {unknown} + 100..119 'for _ ...!() {}': () + 100..119 'for _ ...!() {}': () + 104..105 '_': Iterator::Item> 117..119 '{}': () - 124..134 '|| spam!()': || -> isize + 124..134 '|| spam!()': impl Fn() -> isize 140..156 'while ...!() {}': () 154..156 '{}': () 161..174 'break spam!()': ! @@ -221,6 +231,7 @@ fn expr_macro_def_expanded_in_various_places() { fn expr_macro_rules_expanded_in_various_places() { check_infer( r#" + //- minicore: iterator macro_rules! spam { () => (1isize); } @@ -276,10 +287,19 @@ fn expr_macro_rules_expanded_in_various_places() { !0..6 '1isize': isize 53..456 '{ ...!(); }': () 87..108 'spam!(...am!())': {unknown} + 114..133 'for _ ...!() {}': fn into_iter(isize) -> ::IntoIter + 114..133 'for _ ...!() {}': IntoIterator::IntoIter + 114..133 'for _ ...!() {}': ! + 114..133 'for _ ...!() {}': IntoIterator::IntoIter + 114..133 'for _ ...!() {}': &mut IntoIterator::IntoIter + 114..133 'for _ ...!() {}': fn next>(&mut IntoIterator::IntoIter) -> Option< as Iterator>::Item> + 114..133 'for _ ...!() {}': Option>> + 114..133 'for _ ...!() {}': () 114..133 'for _ ...!() {}': () - 118..119 '_': {unknown} + 114..133 'for _ ...!() {}': () + 118..119 '_': Iterator::Item> 131..133 '{}': () - 138..148 '|| spam!()': || -> isize + 138..148 '|| spam!()': impl Fn() -> isize 154..170 'while ...!() {}': () 168..170 '{}': () 175..188 'break spam!()': ! @@ -661,8 +681,9 @@ fn infer_builtin_macros_line() { "#, expect![[r#" !0..1 '0': i32 + !0..6 '0asu32': u32 63..87 '{ ...!(); }': () - 73..74 'x': i32 + 73..74 'x': u32 "#]], ); } @@ -699,8 +720,9 @@ fn infer_builtin_macros_column() { "#, expect![[r#" !0..1 '0': i32 + !0..6 '0asu32': u32 65..91 '{ ...!(); }': () - 75..76 'x': i32 + 75..76 'x': u32 "#]], ); } @@ -945,7 +967,7 @@ fn infer_builtin_macros_concat_with_lazy() { #[test] fn infer_builtin_macros_env() { - check_infer( + check_types( r#" //- /main.rs env:foo=bar #[rustc_builtin_macro] @@ -953,13 +975,26 @@ fn infer_builtin_macros_env() { fn main() { let x = env!("foo"); + //^ &str + } + "#, + ); +} + +#[test] +fn infer_builtin_macros_option_env() { + check_types( + r#" + //- minicore: option + //- /main.rs env:foo=bar + #[rustc_builtin_macro] + macro_rules! option_env {() => {}} + + fn main() { + let x = option_env!("foo"); + //^ Option<&str> } "#, - expect![[r#" - !0..22 '"__RA_...TED__"': &str - 62..90 '{ ...o"); }': () - 72..73 'x': &str - "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs index 378d478336102..1e57a4ae2968a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs @@ -388,6 +388,24 @@ mod bar_test { ); } +#[test] +fn infer_trait_method_multiple_mutable_reference() { + check_types( + r#" +trait Trait { + fn method(&mut self) -> i32 { 5 } +} +struct S; +impl Trait for &mut &mut S {} +fn test() { + let s = &mut &mut &mut S; + s.method(); + //^^^^^^^^^^ i32 +} + "#, + ); +} + #[test] fn infer_trait_method_generic_1() { // the trait implementation is intentionally incomplete -- it shouldn't matter @@ -1255,7 +1273,6 @@ fn foo(a: &T) { #[test] fn autoderef_visibility_field() { - // FIXME: We should know mutability in overloaded deref check( r#" //- minicore: deref @@ -1277,7 +1294,7 @@ mod a { mod b { fn foo() { let x = super::a::Bar::new().0; - // ^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(None))) + // ^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(Some(Not)))) // ^^^^^^^^^^^^^^^^^^^^^^ type: char } } @@ -1723,7 +1740,7 @@ fn test() { Foo.foo(); //^^^ adjustments: Borrow(Ref(Not)) (&Foo).foo(); - // ^^^^ adjustments: , + // ^^^^ adjustments: Deref(None), Borrow(Ref(Not)) } "#, ); @@ -1922,3 +1939,54 @@ fn foo() { "#, ); } + +#[test] +fn box_deref_is_builtin() { + check( + r#" +//- minicore: deref +use core::ops::Deref; + +#[lang = "owned_box"] +struct Box(*mut T); + +impl Box { + fn new(t: T) -> Self { + loop {} + } +} + +impl Deref for Box { + type Target = T; + fn deref(&self) -> &Self::Target; +} + +struct Foo; +impl Foo { + fn foo(&self) {} +} +fn test() { + Box::new(Foo).foo(); + //^^^^^^^^^^^^^ adjustments: Deref(None), Borrow(Ref(Not)) +} +"#, + ); +} + +#[test] +fn manually_drop_deref_is_not_builtin() { + check( + r#" +//- minicore: manually_drop, deref +struct Foo; +impl Foo { + fn foo(&self) {} +} +use core::mem::ManuallyDrop; +fn test() { + ManuallyDrop::new(Foo).foo(); + //^^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(Some(Not)))), Borrow(Ref(Not)) +} +"#, + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs index fbdc8209f8f4b..59046c0435a3a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs @@ -327,6 +327,7 @@ fn diverging_expression_2() { fn diverging_expression_3_break() { check_infer_with_mismatches( r" + //- minicore: iterator //- /main.rs fn test1() { // should give type mismatch @@ -360,6 +361,15 @@ fn diverging_expression_3_break() { 97..343 '{ ...; }; }': () 140..141 'x': u32 149..175 '{ for ...; }; }': u32 + 151..172 'for a ...eak; }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter + 151..172 'for a ...eak; }': {unknown} + 151..172 'for a ...eak; }': ! + 151..172 'for a ...eak; }': {unknown} + 151..172 'for a ...eak; }': &mut {unknown} + 151..172 'for a ...eak; }': fn next<{unknown}>(&mut {unknown}) -> Option<<{unknown} as Iterator>::Item> + 151..172 'for a ...eak; }': Option<{unknown}> + 151..172 'for a ...eak; }': () + 151..172 'for a ...eak; }': () 151..172 'for a ...eak; }': () 155..156 'a': {unknown} 160..161 'b': {unknown} @@ -367,12 +377,30 @@ fn diverging_expression_3_break() { 164..169 'break': ! 226..227 'x': u32 235..253 '{ for ... {}; }': u32 + 237..250 'for a in b {}': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter + 237..250 'for a in b {}': {unknown} + 237..250 'for a in b {}': ! + 237..250 'for a in b {}': {unknown} + 237..250 'for a in b {}': &mut {unknown} + 237..250 'for a in b {}': fn next<{unknown}>(&mut {unknown}) -> Option<<{unknown} as Iterator>::Item> + 237..250 'for a in b {}': Option<{unknown}> + 237..250 'for a in b {}': () + 237..250 'for a in b {}': () 237..250 'for a in b {}': () 241..242 'a': {unknown} 246..247 'b': {unknown} 248..250 '{}': () 304..305 'x': u32 313..340 '{ for ...; }; }': u32 + 315..337 'for a ...urn; }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter + 315..337 'for a ...urn; }': {unknown} + 315..337 'for a ...urn; }': ! + 315..337 'for a ...urn; }': {unknown} + 315..337 'for a ...urn; }': &mut {unknown} + 315..337 'for a ...urn; }': fn next<{unknown}>(&mut {unknown}) -> Option<<{unknown} as Iterator>::Item> + 315..337 'for a ...urn; }': Option<{unknown}> + 315..337 'for a ...urn; }': () + 315..337 'for a ...urn; }': () 315..337 'for a ...urn; }': () 319..320 'a': {unknown} 324..325 'b': {unknown} @@ -483,3 +511,22 @@ fn example() -> bool { "#, ); } + +#[test] +fn reservation_impl_should_be_ignored() { + // See rust-lang/rust#64631. + check_types( + r#" +//- minicore: from +struct S; +#[rustc_reservation_impl] +impl From for T {} +fn foo>(_: U) -> T { loop {} } + +fn test() { + let s = foo(S); + //^ S +} +"#, + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs index 74bcab6caa94a..0f5a3e1752cf5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs @@ -1,11 +1,12 @@ use expect_test::expect; -use super::{check, check_infer, check_infer_with_mismatches, check_types}; +use super::{check, check_infer, check_infer_with_mismatches, check_no_mismatches, check_types}; #[test] fn infer_pattern() { check_infer( r#" + //- minicore: iterator fn test(x: &i32) { let y = x; let &z = x; @@ -46,6 +47,15 @@ fn infer_pattern() { 82..94 '(1, "hello")': (i32, &str) 83..84 '1': i32 86..93 '"hello"': &str + 101..151 'for (e... }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter + 101..151 'for (e... }': {unknown} + 101..151 'for (e... }': ! + 101..151 'for (e... }': {unknown} + 101..151 'for (e... }': &mut {unknown} + 101..151 'for (e... }': fn next<{unknown}>(&mut {unknown}) -> Option<<{unknown} as Iterator>::Item> + 101..151 'for (e... }': Option<({unknown}, {unknown})> + 101..151 'for (e... }': () + 101..151 'for (e... }': () 101..151 'for (e... }': () 105..111 '(e, f)': ({unknown}, {unknown}) 106..107 'e': {unknown} @@ -70,8 +80,8 @@ fn infer_pattern() { 228..233 '&true': &bool 229..233 'true': bool 234..236 '{}': () - 246..252 'lambda': |u64, u64, i32| -> i32 - 255..287 '|a: u6...b; c }': |u64, u64, i32| -> i32 + 246..252 'lambda': impl Fn(u64, u64, i32) -> i32 + 255..287 '|a: u6...b; c }': impl Fn(u64, u64, i32) -> i32 256..257 'a': u64 264..265 'b': u64 267..268 'c': i32 @@ -240,6 +250,21 @@ fn infer_pattern_match_ergonomics_ref() { ); } +#[test] +fn ref_pat_with_inference_variable() { + check_no_mismatches( + r#" +enum E { A } +fn test() { + let f = |e| match e { + &E::A => {} + }; + f(&E::A); +} +"#, + ); +} + #[test] fn infer_pattern_match_slice() { check_infer( @@ -476,7 +501,7 @@ fn infer_adt_pattern() { 183..184 'x': usize 190..191 'x': usize 201..205 'E::B': E - 209..212 'foo': {unknown} + 209..212 'foo': bool 216..217 '1': usize 227..231 'E::B': E 235..237 '10': usize @@ -677,25 +702,25 @@ fn test() { 51..58 'loop {}': ! 56..58 '{}': () 72..171 '{ ... x); }': () - 78..81 'foo': fn foo<&(i32, &str), i32, |&(i32, &str)| -> i32>(&(i32, &str), |&(i32, &str)| -> i32) -> i32 + 78..81 'foo': fn foo<&(i32, &str), i32, impl Fn(&(i32, &str)) -> i32>(&(i32, &str), impl Fn(&(i32, &str)) -> i32) -> i32 78..105 'foo(&(...y)| x)': i32 82..91 '&(1, "a")': &(i32, &str) 83..91 '(1, "a")': (i32, &str) 84..85 '1': i32 87..90 '"a"': &str - 93..104 '|&(x, y)| x': |&(i32, &str)| -> i32 + 93..104 '|&(x, y)| x': impl Fn(&(i32, &str)) -> i32 94..101 '&(x, y)': &(i32, &str) 95..101 '(x, y)': (i32, &str) 96..97 'x': i32 99..100 'y': &str 103..104 'x': i32 - 142..145 'foo': fn foo<&(i32, &str), &i32, |&(i32, &str)| -> &i32>(&(i32, &str), |&(i32, &str)| -> &i32) -> &i32 + 142..145 'foo': fn foo<&(i32, &str), &i32, impl Fn(&(i32, &str)) -> &i32>(&(i32, &str), impl Fn(&(i32, &str)) -> &i32) -> &i32 142..168 'foo(&(...y)| x)': &i32 146..155 '&(1, "a")': &(i32, &str) 147..155 '(1, "a")': (i32, &str) 148..149 '1': i32 151..154 '"a"': &str - 157..167 '|(x, y)| x': |&(i32, &str)| -> &i32 + 157..167 '|(x, y)| x': impl Fn(&(i32, &str)) -> &i32 158..164 '(x, y)': (i32, &str) 159..160 'x': &i32 162..163 'y': &&str @@ -1084,7 +1109,7 @@ fn var_args() { #[lang = "va_list"] pub struct VaListImpl<'f>; fn my_fn(foo: ...) {} - //^^^ VaListImpl + //^^^ VaListImpl<'_> "#, ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index 689f0da44f680..f18c953a7afd2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -246,6 +246,7 @@ fn infer_std_crash_5() { // taken from rustc check_infer( r#" + //- minicore: iterator fn extra_compiler_flags() { for content in doesnt_matter { let name = if doesnt_matter { @@ -264,13 +265,22 @@ fn infer_std_crash_5() { "#, expect![[r#" 26..322 '{ ... } }': () + 32..320 'for co... }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter + 32..320 'for co... }': {unknown} + 32..320 'for co... }': ! + 32..320 'for co... }': {unknown} + 32..320 'for co... }': &mut {unknown} + 32..320 'for co... }': fn next<{unknown}>(&mut {unknown}) -> Option<<{unknown} as Iterator>::Item> + 32..320 'for co... }': Option<{unknown}> + 32..320 'for co... }': () + 32..320 'for co... }': () 32..320 'for co... }': () 36..43 'content': {unknown} 47..60 'doesnt_matter': {unknown} 61..320 '{ ... }': () 75..79 'name': &{unknown} 82..166 'if doe... }': &{unknown} - 85..98 'doesnt_matter': {unknown} + 85..98 'doesnt_matter': bool 99..128 '{ ... }': &{unknown} 113..118 'first': &{unknown} 134..166 '{ ... }': &{unknown} @@ -279,7 +289,7 @@ fn infer_std_crash_5() { 181..188 'content': &{unknown} 191..313 'if ICE... }': &{unknown} 194..231 'ICE_RE..._VALUE': {unknown} - 194..247 'ICE_RE...&name)': {unknown} + 194..247 'ICE_RE...&name)': bool 241..246 '&name': &&{unknown} 242..246 'name': &{unknown} 248..276 '{ ... }': &{unknown} @@ -805,19 +815,19 @@ fn issue_4966() { 225..229 'iter': T 244..246 '{}': Vec 258..402 '{ ...r(); }': () - 268..273 'inner': Map<|&f64| -> f64> - 276..300 'Map { ... 0.0 }': Map<|&f64| -> f64> - 285..298 '|_: &f64| 0.0': |&f64| -> f64 + 268..273 'inner': Map f64> + 276..300 'Map { ... 0.0 }': Map f64> + 285..298 '|_: &f64| 0.0': impl Fn(&f64) -> f64 286..287 '_': &f64 295..298 '0.0': f64 - 311..317 'repeat': Repeat f64>> - 320..345 'Repeat...nner }': Repeat f64>> - 338..343 'inner': Map<|&f64| -> f64> - 356..359 'vec': Vec f64>>>> - 362..371 'from_iter': fn from_iter f64>>>, Repeat f64>>>(Repeat f64>>) -> Vec f64>>>> - 362..379 'from_i...epeat)': Vec f64>>>> - 372..378 'repeat': Repeat f64>> - 386..389 'vec': Vec f64>>>> + 311..317 'repeat': Repeat f64>> + 320..345 'Repeat...nner }': Repeat f64>> + 338..343 'inner': Map f64> + 356..359 'vec': Vec f64>>>> + 362..371 'from_iter': fn from_iter f64>>>, Repeat f64>>>(Repeat f64>>) -> Vec f64>>>> + 362..379 'from_i...epeat)': Vec f64>>>> + 372..378 'repeat': Repeat f64>> + 386..389 'vec': Vec f64>>>> 386..399 'vec.foo_bar()': {unknown} "#]], ); @@ -852,7 +862,7 @@ fn main() { 123..126 'S()': S 132..133 's': S 132..144 's.g(|_x| {})': () - 136..143 '|_x| {}': |&i32| -> () + 136..143 '|_x| {}': impl Fn(&i32) 137..139 '_x': &i32 141..143 '{}': () 150..151 's': S @@ -886,13 +896,13 @@ fn flush(&self) { "#, expect![[r#" 123..127 'self': &Mutex - 150..152 '{}': MutexGuard + 150..152 '{}': MutexGuard<'_, T> 234..238 'self': &{unknown} 240..290 '{ ...()); }': () 250..251 'w': &Mutex 276..287 '*(w.lock())': BufWriter 278..279 'w': &Mutex - 278..286 'w.lock()': MutexGuard + 278..286 'w.lock()': MutexGuard<'_, BufWriter> "#]], ); } @@ -1060,13 +1070,30 @@ fn infix_parse(_state: S, _level_code: &Fn(S)) -> T { loop {} } -fn parse_arule() { +fn parse_a_rule() { infix_parse((), &(|_recurse| ())) } "#, ) } +#[test] +fn nested_closure() { + check_types( + r#" +//- minicore: fn, option + +fn map(o: Option, f: impl FnOnce(T) -> U) -> Option { loop {} } + +fn test() { + let o = Some(Some(2)); + map(o, |s| map(s, |x| x)); + // ^ i32 +} + "#, + ); +} + #[test] fn call_expected_type_closure() { check_types( @@ -1198,6 +1225,7 @@ fn mamba(a: U32!(), p: u32) -> u32 { fn for_loop_block_expr_iterable() { check_infer( r#" +//- minicore: iterator fn test() { for _ in { let x = 0; } { let y = 0; @@ -1206,8 +1234,17 @@ fn test() { "#, expect![[r#" 10..68 '{ ... } }': () + 16..66 'for _ ... }': fn into_iter<()>(()) -> <() as IntoIterator>::IntoIter + 16..66 'for _ ... }': IntoIterator::IntoIter<()> + 16..66 'for _ ... }': ! + 16..66 'for _ ... }': IntoIterator::IntoIter<()> + 16..66 'for _ ... }': &mut IntoIterator::IntoIter<()> + 16..66 'for _ ... }': fn next>(&mut IntoIterator::IntoIter<()>) -> Option< as Iterator>::Item> + 16..66 'for _ ... }': Option>> + 16..66 'for _ ... }': () 16..66 'for _ ... }': () - 20..21 '_': {unknown} + 16..66 'for _ ... }': () + 20..21 '_': Iterator::Item> 25..39 '{ let x = 0; }': () 31..32 'x': i32 35..36 '0': i32 @@ -1458,13 +1495,12 @@ fn regression_11688_3() { struct Ar(T); fn f( num_zeros: usize, - ) -> dyn Iterator; LEN]> { + ) -> &dyn Iterator; LEN]> { loop {} } fn dynamic_programming() { - for board in f::<9, u8, 7>(1) { - //^^^^^ [Ar; 9] - } + let board = f::<9, u8, 7>(1).next(); + //^^^^^ Option<[Ar; 9]> } "#, ); @@ -1757,6 +1793,21 @@ const C: usize = 2 + 2; ); } +#[test] +fn regression_14456() { + check_types( + r#" +//- minicore: future +async fn x() {} +fn f() { + let fut = x(); + let t = [0u8; { let a = 2 + 2; a }]; + //^ [u8; 4] +} +"#, + ); +} + #[test] fn regression_14164() { check_types( @@ -1788,3 +1839,119 @@ where "#, ); } + +#[test] +fn match_ergonomics_with_binding_modes_interaction() { + check_types( + r" +enum E { A } +fn foo() { + match &E::A { + b @ (x @ E::A | x) => { + b; + //^ &E + x; + //^ &E + } + } +}", + ); +} + +#[test] +fn regression_14844() { + check_no_mismatches( + r#" +pub type Ty = Unknown; + +pub struct Inner(); + +pub struct Outer { + pub inner: Inner, +} + +fn main() { + _ = Outer { + inner: Inner::(), + }; +} + "#, + ); + check_no_mismatches( + r#" +pub const ONE: usize = 1; + +pub struct Inner(); + +pub struct Outer { + pub inner: Inner, +} + +fn main() { + _ = Outer { + inner: Inner::<1>(), + }; +} + "#, + ); + check_no_mismatches( + r#" +pub const ONE: usize = unknown(); + +pub struct Inner(); + +pub struct Outer { + pub inner: Inner, +} + +fn main() { + _ = Outer { + inner: Inner::<1>(), + }; +} + "#, + ); + check_no_mismatches( + r#" +pub const N: usize = 2 + 2; + +fn f(t: [u8; N]) {} + +fn main() { + let a = [1, 2, 3, 4]; + f(a); + let b = [1; 4]; + let c: [u8; N] = b; + let d = [1; N]; + let e: [u8; N] = d; + let f = [1; N]; + let g = match f { + [a, b, c, d] => a + b + c + d, + }; +} + "#, + ); +} + +#[test] +fn regression_14844_2() { + check_no_mismatches( + r#" +//- minicore: fn +pub const ONE: usize = 1; + +pub type MyInner = Inner; + +pub struct Inner(); + +impl Inner<1> { + fn map(&self, func: F) -> bool + where + F: Fn(&MyInner) -> bool, + { + func(self) + } +} + "#, + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs index 13cc3fea52d16..3ece40486dde8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs @@ -854,9 +854,9 @@ fn test2(a1: *const A, a2: *mut A) { 237..239 'a2': *mut A 249..272 '{ ...2.b; }': () 255..257 'a1': *const A - 255..259 'a1.b': B + 255..259 'a1.b': {unknown} 265..267 'a2': *mut A - 265..269 'a2.b': B + 265..269 'a2.b': {unknown} "#]], ); } @@ -1812,6 +1812,20 @@ fn main() { //^ [(); 7] }"#, ); + check_types( + r#" +trait Foo { + fn x(self); +} + +impl Foo for u8 { + fn x(self) { + let t = [0; 4 + 2]; + //^ [i32; 6] + } +} + "#, + ); } #[test] @@ -1906,8 +1920,8 @@ fn closure_return() { "#, expect![[r#" 16..58 '{ ...; }; }': u32 - 26..27 'x': || -> usize - 30..55 '|| -> ...n 1; }': || -> usize + 26..27 'x': impl Fn() -> usize + 30..55 '|| -> ...n 1; }': impl Fn() -> usize 42..55 '{ return 1; }': usize 44..52 'return 1': ! 51..52 '1': usize @@ -1925,8 +1939,8 @@ fn closure_return_unit() { "#, expect![[r#" 16..47 '{ ...; }; }': u32 - 26..27 'x': || -> () - 30..44 '|| { return; }': || -> () + 26..27 'x': impl Fn() + 30..44 '|| { return; }': impl Fn() 33..44 '{ return; }': () 35..41 'return': ! "#]], @@ -1943,8 +1957,8 @@ fn closure_return_inferred() { "#, expect![[r#" 16..46 '{ ..." }; }': u32 - 26..27 'x': || -> &str - 30..43 '|| { "test" }': || -> &str + 26..27 'x': impl Fn() -> &str + 30..43 '|| { "test" }': impl Fn() -> &str 33..43 '{ "test" }': &str 35..41 '"test"': &str "#]], @@ -2033,6 +2047,56 @@ fn test() { ); } +#[test] +fn tuple_pattern_nested_match_ergonomics() { + check_no_mismatches( + r#" +fn f(x: (&i32, &i32)) -> i32 { + match x { + (3, 4) => 5, + _ => 12, + } +} + "#, + ); + check_types( + r#" +fn f(x: (&&&&i32, &&&i32)) { + let f = match x { + t @ (3, 4) => t, + _ => loop {}, + }; + f; + //^ (&&&&i32, &&&i32) +} + "#, + ); + check_types( + r#" +fn f() { + let x = &&&(&&&2, &&&&&3); + let (y, z) = x; + //^ &&&&i32 + let t @ (y, z) = x; + t; + //^ &&&(&&&i32, &&&&&i32) +} + "#, + ); + check_types( + r#" +fn f() { + let x = &&&(&&&2, &&&&&3); + let (y, z) = x; + //^ &&&&i32 + let t @ (y, z) = x; + t; + //^ &&&(&&&i32, &&&&&i32) +} + "#, + ); +} + #[test] fn fn_pointer_return() { check_infer( @@ -2050,7 +2114,7 @@ fn fn_pointer_return() { 47..120 '{ ...hod; }': () 57..63 'vtable': Vtable 66..90 'Vtable...| {} }': Vtable - 83..88 '|| {}': || -> () + 83..88 '|| {}': impl Fn() 86..88 '{}': () 100..101 'm': fn() 104..110 'vtable': Vtable @@ -2087,6 +2151,7 @@ async fn main() { 136..138 '()': () 150..151 'w': i32 154..166 'const { 92 }': i32 + 154..166 'const { 92 }': i32 162..164 '92': i32 176..177 't': i32 180..190 ''a: { 92 }': i32 @@ -2094,6 +2159,24 @@ async fn main() { "#]], ) } + +#[test] +fn async_fn_and_try_operator() { + check_no_mismatches( + r#" +//- minicore: future, result, fn, try, from +async fn foo() -> Result<(), ()> { + Ok(()) +} + +async fn bar() -> Result<(), ()> { + let x = foo().await?; + Ok(x) +} + "#, + ) +} + #[test] fn async_block_early_return() { check_infer( @@ -2124,9 +2207,9 @@ fn main() { 149..151 'Ok': Ok<(), ()>(()) -> Result<(), ()> 149..155 'Ok(())': Result<(), ()> 152..154 '()': () - 167..171 'test': fn test<(), (), || -> impl Future>, impl Future>>(|| -> impl Future>) + 167..171 'test': fn test<(), (), impl Fn() -> impl Future>, impl Future>>(impl Fn() -> impl Future>) 167..228 'test(|... })': () - 172..227 '|| asy... }': || -> impl Future> + 172..227 '|| asy... }': impl Fn() -> impl Future> 175..227 'async ... }': impl Future> 191..205 'return Err(())': ! 198..201 'Err': Err<(), ()>(()) -> Result<(), ()> @@ -2252,8 +2335,8 @@ fn infer_labelled_break_with_val() { "#, expect![[r#" 9..335 '{ ... }; }': () - 19..21 '_x': || -> bool - 24..332 '|| 'ou... }': || -> bool + 19..21 '_x': impl Fn() -> bool + 24..332 '|| 'ou... }': impl Fn() -> bool 27..332 ''outer... }': bool 40..332 '{ ... }': () 54..59 'inner': i8 @@ -2677,6 +2760,179 @@ impl B for Astruct {} ) } +#[test] +fn capture_kinds_simple() { + check_types( + r#" +struct S; + +impl S { + fn read(&self) -> &S { self } + fn write(&mut self) -> &mut S { self } + fn consume(self) -> S { self } +} + +fn f() { + let x = S; + let c1 = || x.read(); + //^^ impl Fn() -> &S + let c2 = || x.write(); + //^^ impl FnMut() -> &mut S + let c3 = || x.consume(); + //^^ impl FnOnce() -> S + let c3 = || x.consume().consume().consume(); + //^^ impl FnOnce() -> S + let c3 = || x.consume().write().read(); + //^^ impl FnOnce() -> &S + let x = &mut x; + let c1 = || x.write(); + //^^ impl FnMut() -> &mut S + let x = S; + let c1 = || { let ref t = x; t }; + //^^ impl Fn() -> &S + let c2 = || { let ref mut t = x; t }; + //^^ impl FnMut() -> &mut S + let c3 = || { let t = x; t }; + //^^ impl FnOnce() -> S +} + "#, + ) +} + +#[test] +fn capture_kinds_closure() { + check_types( + r#" +//- minicore: copy, fn +fn f() { + let mut x = 2; + x = 5; + let mut c1 = || { x = 3; x }; + //^^^^^^ impl FnMut() -> i32 + let mut c2 = || { c1() }; + //^^^^^^ impl FnMut() -> i32 + let mut c1 = || { x }; + //^^^^^^ impl Fn() -> i32 + let mut c2 = || { c1() }; + //^^^^^^ impl Fn() -> i32 + struct X; + let x = X; + let mut c1 = || { x }; + //^^^^^^ impl FnOnce() -> X + let mut c2 = || { c1() }; + //^^^^^^ impl FnOnce() -> X +} + "#, + ); +} + +#[test] +fn capture_kinds_overloaded_deref() { + check_types( + r#" +//- minicore: fn, deref_mut +use core::ops::{Deref, DerefMut}; + +struct Foo; +impl Deref for Foo { + type Target = (i32, u8); + fn deref(&self) -> &(i32, u8) { + &(5, 2) + } +} +impl DerefMut for Foo { + fn deref_mut(&mut self) -> &mut (i32, u8) { + &mut (5, 2) + } +} +fn test() { + let mut x = Foo; + let c1 = || *x; + //^^ impl Fn() -> (i32, u8) + let c2 = || { *x = (2, 5); }; + //^^ impl FnMut() + let c3 = || { x.1 }; + //^^ impl Fn() -> u8 + let c4 = || { x.1 = 6; }; + //^^ impl FnMut() +} + "#, + ); +} + +#[test] +fn capture_kinds_with_copy_types() { + check_types( + r#" +//- minicore: copy, clone, derive +#[derive(Clone, Copy)] +struct Copy; +struct NotCopy; +#[derive(Clone, Copy)] +struct Generic(T); + +trait Tr { + type Assoc; +} + +impl Tr for Copy { + type Assoc = NotCopy; +} + +#[derive(Clone, Copy)] +struct AssocGeneric(T::Assoc); + +fn f() { + let a = Copy; + let b = NotCopy; + let c = Generic(Copy); + let d = Generic(NotCopy); + let e: AssocGeneric = AssocGeneric(NotCopy); + let c1 = || a; + //^^ impl Fn() -> Copy + let c2 = || b; + //^^ impl FnOnce() -> NotCopy + let c3 = || c; + //^^ impl Fn() -> Generic + let c3 = || d; + //^^ impl FnOnce() -> Generic + let c3 = || e; + //^^ impl FnOnce() -> AssocGeneric +} + "#, + ) +} + +#[test] +fn derive_macro_should_work_for_associated_type() { + check_types( + r#" +//- minicore: copy, clone, derive +#[derive(Clone)] +struct X; +#[derive(Clone)] +struct Y; + +trait Tr { + type Assoc; +} + +impl Tr for X { + type Assoc = Y; +} + +#[derive(Clone)] +struct AssocGeneric(T::Assoc); + +fn f() { + let e: AssocGeneric = AssocGeneric(Y); + let e_clone = e.clone(); + //^^^^^^^ AssocGeneric +} + "#, + ) +} + #[test] fn cfgd_out_assoc_items() { check_types( @@ -2696,6 +2952,21 @@ fn f() { ) } +#[test] +fn infer_ref_to_raw_cast() { + check_types( + r#" +struct S; + +fn f() { + let s = &mut S; + let s = s as *mut _; + //^ *mut S +} + "#, + ); +} + #[test] fn infer_missing_type() { check_types( @@ -3258,35 +3529,60 @@ fn f(t: Ark) { ); } -// FIXME #[test] -fn castable_to2() { - check_infer( +fn const_dependent_on_local() { + check_types( r#" -fn func() { - let x = &0u32 as *const _; +fn main() { + let s = 5; + let t = [2; s]; + //^ [i32; _] } "#, - expect![[r#" - 10..44 '{ ...t _; }': () - 20..21 'x': *const {unknown} - 24..29 '&0u32': &u32 - 24..41 '&0u32 ...onst _': *const {unknown} - 25..29 '0u32': u32 - "#]], ); } #[test] fn issue_14275() { - // FIXME: evaluate const generic check_types( r#" struct Foo; fn main() { const B: bool = false; let foo = Foo::; - //^^^ Foo<_> + //^^^ Foo +} +"#, + ); + check_types( + r#" +struct Foo; +impl Foo { + fn foo(self) -> u8 { 2 } +} +impl Foo { + fn foo(self) -> u16 { 5 } +} +fn main() { + const B: bool = false; + let foo: Foo = Foo; + let x = foo.foo(); + //^ u16 +} +"#, + ); +} + +#[test] +fn cstring_literals() { + check_types( + r#" +#[lang = "CStr"] +pub struct CStr; + +fn main() { + c"ello"; + //^^^^^^^ &CStr } "#, ); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs index da76d7fd83f76..829a6ab189ecc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs @@ -90,7 +90,7 @@ fn infer_async_closure() { async fn test() { let f = async move |x: i32| x + 42; f; -// ^ |i32| -> impl Future +// ^ impl Fn(i32) -> impl Future let a = f(4); a; // ^ impl Future @@ -99,7 +99,7 @@ async fn test() { // ^ i32 let f = async move || 42; f; -// ^ || -> impl Future +// ^ impl Fn() -> impl Future let a = f(); a; // ^ impl Future @@ -116,7 +116,7 @@ async fn test() { }; let _: Option = c().await; c; -// ^ || -> impl Future> +// ^ impl Fn() -> impl Future> } "#, ); @@ -206,19 +206,27 @@ fn test() { fn infer_try_trait() { check_types( r#" -//- minicore: try, result +//- minicore: try, result, from fn test() { let r: Result = Result::Ok(1); let v = r?; v; } //^ i32 - -impl core::ops::Try for Result { - type Output = O; - type Error = Result; +"#, + ); } -impl> core::ops::FromResidual> for Result {} +#[test] +fn infer_try_block() { + // FIXME: We should test more cases, but it currently doesn't work, since + // our labeled block type inference is broken. + check_types( + r#" +//- minicore: try, option +fn test() { + let x: Option<_> = try { Some(2)?; }; + //^ Option<()> +} "#, ); } @@ -542,7 +550,7 @@ fn test() -> u64 { 53..54 'a': S 57..58 'S': S(fn(u32) -> u64) -> S 57..74 'S(|i| ...s u64)': S - 59..73 '|i| 2*i as u64': |u32| -> u64 + 59..73 '|i| 2*i as u64': impl Fn(u32) -> u64 60..61 'i': u32 63..64 '2': u64 63..73 '2*i as u64': u64 @@ -1325,9 +1333,9 @@ fn foo() -> (impl FnOnce(&str, T), impl Trait) { } "#, expect![[r#" - 134..165 '{ ...(C)) }': (|&str, T| -> (), Bar) - 140..163 '(|inpu...ar(C))': (|&str, T| -> (), Bar) - 141..154 '|input, t| {}': |&str, T| -> () + 134..165 '{ ...(C)) }': (impl Fn(&str, T), Bar) + 140..163 '(|inpu...ar(C))': (impl Fn(&str, T), Bar) + 141..154 '|input, t| {}': impl Fn(&str, T) 142..147 'input': &str 149..150 't': T 152..154 '{}': () @@ -1498,8 +1506,8 @@ fn main() { 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| -> () + 79..101 '&|numb....foo()': &impl Fn(S) + 80..101 '|numbe....foo()': impl Fn(S) 81..87 'number': S 89..95 'number': S 89..101 'number.foo()': () @@ -1904,13 +1912,13 @@ fn test() { 131..132 'f': F 151..153 '{}': Lazy 251..497 '{ ...o(); }': () - 261..266 'lazy1': Lazy Foo> - 283..292 'Lazy::new': fn new Foo>(|| -> Foo) -> Lazy Foo> - 283..300 'Lazy::...| Foo)': Lazy Foo> - 293..299 '|| Foo': || -> Foo + 261..266 'lazy1': Lazy Foo> + 283..292 'Lazy::new': fn new Foo>(impl Fn() -> Foo) -> Lazy Foo> + 283..300 'Lazy::...| Foo)': Lazy Foo> + 293..299 '|| Foo': impl Fn() -> Foo 296..299 'Foo': Foo 310..312 'r1': usize - 315..320 'lazy1': Lazy Foo> + 315..320 'lazy1': Lazy Foo> 315..326 'lazy1.foo()': usize 368..383 'make_foo_fn_ptr': fn() -> Foo 399..410 'make_foo_fn': fn make_foo_fn() -> Foo @@ -1955,20 +1963,20 @@ fn test() { 163..167 '1u32': u32 174..175 'x': Option 174..190 'x.map(...v + 1)': Option - 180..189 '|v| v + 1': |u32| -> u32 + 180..189 '|v| v + 1': impl Fn(u32) -> u32 181..182 'v': u32 184..185 'v': u32 184..189 'v + 1': u32 188..189 '1': u32 196..197 'x': Option 196..212 'x.map(... 1u64)': Option - 202..211 '|_v| 1u64': |u32| -> u64 + 202..211 '|_v| 1u64': impl Fn(u32) -> u64 203..205 '_v': u32 207..211 '1u64': u64 222..223 'y': Option 239..240 'x': Option 239..252 'x.map(|_v| 1)': Option - 245..251 '|_v| 1': |u32| -> i64 + 245..251 '|_v| 1': impl Fn(u32) -> i64 246..248 '_v': u32 250..251 '1': i64 "#]], @@ -1997,11 +2005,11 @@ fn test u64>(f: F) { //^^^^ u64 let g = |v| v + 1; //^^^^^ u64 - //^^^^^^^^^ |u64| -> u64 + //^^^^^^^^^ impl Fn(u64) -> u64 g(1u64); //^^^^^^^ u64 let h = |v| 1u128 + v; - //^^^^^^^^^^^^^ |u128| -> u128 + //^^^^^^^^^^^^^ impl Fn(u128) -> u128 }"#, ); } @@ -2054,17 +2062,17 @@ fn test() { 312..314 '{}': () 330..489 '{ ... S); }': () 340..342 'x1': u64 - 345..349 'foo1': fn foo1 u64>(S, |S| -> u64) -> u64 + 345..349 'foo1': fn foo1 u64>(S, impl Fn(S) -> u64) -> u64 345..368 'foo1(S...hod())': u64 350..351 'S': S - 353..367 '|s| s.method()': |S| -> u64 + 353..367 '|s| s.method()': impl Fn(S) -> u64 354..355 's': S 357..358 's': S 357..367 's.method()': u64 378..380 'x2': u64 - 383..387 'foo2': fn foo2 u64>(|S| -> u64, S) -> u64 + 383..387 'foo2': fn foo2 u64>(impl Fn(S) -> u64, S) -> u64 383..406 'foo2(|...(), S)': u64 - 388..402 '|s| s.method()': |S| -> u64 + 388..402 '|s| s.method()': impl Fn(S) -> u64 389..390 's': S 392..393 's': S 392..402 's.method()': u64 @@ -2073,14 +2081,14 @@ fn test() { 421..422 'S': S 421..446 'S.foo1...hod())': u64 428..429 'S': S - 431..445 '|s| s.method()': |S| -> u64 + 431..445 '|s| s.method()': impl Fn(S) -> u64 432..433 's': S 435..436 's': S 435..445 's.method()': u64 456..458 'x4': u64 461..462 'S': S 461..486 'S.foo2...(), S)': u64 - 468..482 '|s| s.method()': |S| -> u64 + 468..482 '|s| s.method()': impl Fn(S) -> u64 469..470 's': S 472..473 's': S 472..482 's.method()': u64 @@ -2554,9 +2562,9 @@ fn main() { 72..74 '_v': F 117..120 '{ }': () 132..163 '{ ... }); }': () - 138..148 'f::<(), _>': fn f<(), |&()| -> ()>(|&()| -> ()) + 138..148 'f::<(), _>': fn f<(), impl Fn(&())>(impl Fn(&())) 138..160 'f::<()... z; })': () - 149..159 '|z| { z; }': |&()| -> () + 149..159 '|z| { z; }': impl Fn(&()) 150..151 'z': &() 153..159 '{ z; }': () 155..156 'z': &() @@ -2713,9 +2721,9 @@ fn main() { 983..998 'Vec::::new': fn new() -> Vec 983..1000 'Vec::<...:new()': Vec 983..1012 'Vec::<...iter()': IntoIter - 983..1075 'Vec::<...one })': FilterMap, |i32| -> Option> + 983..1075 'Vec::<...one })': FilterMap, impl Fn(i32) -> Option> 983..1101 'Vec::<... y; })': () - 1029..1074 '|x| if...None }': |i32| -> Option + 1029..1074 '|x| if...None }': impl Fn(i32) -> Option 1030..1031 'x': i32 1033..1074 'if x >...None }': Option 1036..1037 'x': i32 @@ -2728,7 +2736,7 @@ fn main() { 1049..1057 'x as u32': u32 1066..1074 '{ None }': Option 1068..1072 'None': Option - 1090..1100 '|y| { y; }': |u32| -> () + 1090..1100 '|y| { y; }': impl Fn(u32) 1091..1092 'y': u32 1094..1100 '{ y; }': () 1096..1097 'y': u32 @@ -2971,13 +2979,13 @@ fn foo() { 52..126 '{ ...)(s) }': () 62..63 's': Option 66..78 'Option::None': Option - 88..89 'f': |Option| -> () - 92..111 '|x: Op...2>| {}': |Option| -> () + 88..89 'f': impl Fn(Option) + 92..111 '|x: Op...2>| {}': impl Fn(Option) 93..94 'x': Option 109..111 '{}': () 117..124 '(&f)(s)': () - 118..120 '&f': &|Option| -> () - 119..120 'f': |Option| -> () + 118..120 '&f': &impl Fn(Option) + 119..120 'f': impl Fn(Option) 122..123 's': Option "#]], ); @@ -3043,7 +3051,7 @@ impl core::ops::Deref for Box { type Target = T; fn deref(&self) -> &T { - &self.inner + unsafe { &*self.inner } } } @@ -3054,23 +3062,25 @@ fn foo() { }"#, expect![[r#" 154..158 'self': &Box - 166..193 '{ ... }': &T - 176..187 '&self.inner': &*mut T - 177..181 'self': &Box - 177..187 'self.inner': *mut T - 206..296 '{ ...&s); }': () - 216..217 's': Option - 220..224 'None': Option - 234..235 'f': Box)> - 269..282 'box (|ps| {})': Box<|&Option| -> ()> - 274..281 '|ps| {}': |&Option| -> () - 275..277 'ps': &Option - 279..281 '{}': () - 288..289 'f': Box)> - 288..293 'f(&s)': () - 290..292 '&s': &Option - 291..292 's': Option - 269..282: expected Box)>, got Box<|&Option| -> ()> + 166..205 '{ ... }': &T + 176..199 'unsafe...nner }': &T + 185..197 '&*self.inner': &T + 186..197 '*self.inner': T + 187..191 'self': &Box + 187..197 'self.inner': *mut T + 218..308 '{ ...&s); }': () + 228..229 's': Option + 232..236 'None': Option + 246..247 'f': Box)> + 281..294 'box (|ps| {})': Box)> + 286..293 '|ps| {}': impl Fn(&Option) + 287..289 'ps': &Option + 291..293 '{}': () + 300..301 'f': Box)> + 300..305 'f(&s)': () + 302..304 '&s': &Option + 303..304 's': Option + 281..294: expected Box)>, got Box)> "#]], ); } @@ -3709,7 +3719,6 @@ async fn get_accounts() -> Result { #[test] fn local_impl_1() { - check!(block_local_impls); check_types( r#" trait Trait { @@ -3731,7 +3740,6 @@ fn test() { #[test] fn local_impl_2() { - check!(block_local_impls); check_types( r#" struct S; @@ -3753,7 +3761,6 @@ fn test() { #[test] fn local_impl_3() { - check!(block_local_impls); check_types( r#" trait Trait { @@ -3777,6 +3784,62 @@ fn test() { ); } +#[test] +fn foreign_trait_with_local_trait_impl() { + check!(block_local_impls); + check( + r#" +mod module { + pub trait T { + const C: usize; + fn f(&self); + } +} + +fn f() { + use module::T; + impl T for usize { + const C: usize = 0; + fn f(&self) {} + } + 0usize.f(); + //^^^^^^^^^^ type: () + usize::C; + //^^^^^^^^type: usize +} +"#, + ); +} + +#[test] +fn regression_14443_trait_solve() { + check_no_mismatches( + r#" +trait T { + fn f(&self) {} +} + + +fn main() { + struct A; + impl T for A {} + + let a = A; + + let b = { + struct B; + impl T for B {} + + B + }; + + a.f(); + b.f(); +} +"#, + ) +} + #[test] fn associated_type_sized_bounds() { check_infer( @@ -4149,3 +4212,201 @@ fn test() { "#, ); } + +#[test] +fn associated_type_in_struct_expr_path() { + // FIXME: All annotation should be resolvable. + // For lines marked as unstable, see rust-lang/rust#86935. + // FIXME: Remove the comments once stablized. + check_types( + r#" +trait Trait { + type Assoc; + fn f(); +} + +struct S { x: u32 } + +impl Trait for () { + type Assoc = S; + + fn f() { + let x = 42; + let a = Self::Assoc { x }; + // ^ S + let a = ::Assoc { x }; // unstable + // ^ {unknown} + + // should be `Copy` but we don't track ownership anyway. + let value = S { x }; + if let Self::Assoc { x } = value {} + // ^ u32 + if let ::Assoc { x } = value {} // unstable + // ^ {unknown} + } +} + "#, + ); +} + +#[test] +fn associated_type_in_struct_expr_path_enum() { + // FIXME: All annotation should be resolvable. + // For lines marked as unstable, see rust-lang/rust#86935. + // FIXME: Remove the comments once stablized. + check_types( + r#" +trait Trait { + type Assoc; + fn f(); +} + +enum E { + Unit, + Struct { x: u32 }, +} + +impl Trait for () { + type Assoc = E; + + fn f() { + let a = Self::Assoc::Unit; + // ^ E + let a = ::Assoc::Unit; + // ^ E + let a = ::Unit; + // ^ E + let a = <::Assoc>::Unit; + // ^ E + + // should be `Copy` but we don't track ownership anyway. + let value = E::Unit; + if let Self::Assoc::Unit = value {} + // ^^^^^^^^^^^^^^^^^ E + if let ::Assoc::Unit = value {} + // ^^^^^^^^^^^^^^^^^^^ E + if let ::Unit = value {} + // ^^^^^^^^^^^^^^^^^^^ E + if let <::Assoc>::Unit = value {} + // ^^^^^^^^^^^^^^^^^^^^^ E + + let x = 42; + let a = Self::Assoc::Struct { x }; + // ^ E + let a = ::Assoc::Struct { x }; // unstable + // ^ {unknown} + let a = ::Struct { x }; // unstable + // ^ {unknown} + let a = <::Assoc>::Struct { x }; // unstable + // ^ {unknown} + + // should be `Copy` but we don't track ownership anyway. + let value = E::Struct { x: 42 }; + if let Self::Assoc::Struct { x } = value {} + // ^ u32 + if let ::Assoc::Struct { x } = value {} // unstable + // ^ {unknown} + if let ::Struct { x } = value {} // unstable + // ^ {unknown} + if let <::Assoc>::Struct { x } = value {} // unstable + // ^ {unknown} + } +} + "#, + ); +} + +#[test] +fn derive_macro_bounds() { + check_types( + r#" + //- minicore: clone, derive + #[derive(Clone)] + struct Copy; + struct NotCopy; + #[derive(Clone)] + struct Generic(T); + trait Tr { + type Assoc; + } + impl Tr for Copy { + type Assoc = NotCopy; + } + #[derive(Clone)] + struct AssocGeneric(T::Assoc); + + #[derive(Clone)] + struct AssocGeneric2(::Assoc); + + #[derive(Clone)] + struct AssocGeneric3(Generic); + + #[derive(Clone)] + struct Vec(); + + #[derive(Clone)] + struct R1(Vec); + #[derive(Clone)] + struct R2(R1); + + fn f() { + let x = (&Copy).clone(); + //^ Copy + let x = (&NotCopy).clone(); + //^ &NotCopy + let x = (&Generic(Copy)).clone(); + //^ Generic + let x = (&Generic(NotCopy)).clone(); + //^ &Generic + let x: &AssocGeneric = &AssocGeneric(NotCopy); + let x = x.clone(); + //^ &AssocGeneric + let x: &AssocGeneric2 = &AssocGeneric2(NotCopy); + let x = x.clone(); + //^ &AssocGeneric2 + let x: &AssocGeneric3 = &AssocGeneric3(Generic(NotCopy)); + let x = x.clone(); + //^ &AssocGeneric3 + let x = (&R1(Vec())).clone(); + //^ R1 + let x = (&R2(R1(Vec()))).clone(); + //^ R2 + } + "#, + ); +} + +#[test] +fn trait_obligations_should_be_registered_during_path_inference() { + check_types( + r#" +//- minicore: fn, from +struct S(T); +fn map S>(_: T, _: F) -> U { loop {} } + +fn test(v: S) { + let res = map(v, Into::into); + //^^^ i32 +} +"#, + ); +} + +#[test] +fn fn_obligation_should_be_registered_during_path_inference() { + check_types( + r#" +//- minicore: fn, from +struct S(T); +impl S { + fn foo>>(_: U) -> Self { loop {} } +} +fn map U>(_: T, _: F) -> U { loop {} } + +fn test(v: S) { + let res = map(v, S::foo); + //^^^ S +} +"#, + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tls.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tls.rs index b7e6ee6740be7..83814ed0ec1f1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tls.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tls.rs @@ -24,7 +24,8 @@ impl DebugContext<'_> { AdtId::UnionId(it) => self.0.union_data(it).name.clone(), AdtId::EnumId(it) => self.0.enum_data(it).name.clone(), }; - name.fmt(f) + name.display(self.0.upcast()).fmt(f)?; + Ok(()) } pub(crate) fn debug_trait_id( @@ -34,7 +35,8 @@ impl DebugContext<'_> { ) -> Result<(), fmt::Error> { let trait_: hir_def::TraitId = from_chalk_trait_id(id); let trait_data = self.0.trait_data(trait_); - trait_data.name.fmt(f) + trait_data.name.display(self.0.upcast()).fmt(f)?; + Ok(()) } pub(crate) fn debug_assoc_type_id( @@ -49,7 +51,13 @@ impl DebugContext<'_> { _ => panic!("associated type not in trait"), }; let trait_data = self.0.trait_data(trait_); - write!(fmt, "{}::{}", trait_data.name, type_alias_data.name) + write!( + fmt, + "{}::{}", + trait_data.name.display(self.0.upcast()), + type_alias_data.name.display(self.0.upcast()) + )?; + Ok(()) } pub(crate) fn debug_projection_ty( @@ -67,7 +75,7 @@ impl DebugContext<'_> { let trait_ref = projection_ty.trait_ref(self.0); let trait_params = trait_ref.substitution.as_slice(Interner); let self_ty = trait_ref.self_type_parameter(Interner); - write!(fmt, "<{self_ty:?} as {trait_name}")?; + write!(fmt, "<{self_ty:?} as {}", trait_name.display(self.0.upcast()))?; if trait_params.len() > 1 { write!( fmt, @@ -75,7 +83,7 @@ impl DebugContext<'_> { trait_params[1..].iter().format_with(", ", |x, f| f(&format_args!("{x:?}"))), )?; } - write!(fmt, ">::{}", type_alias_data.name)?; + write!(fmt, ">::{}", type_alias_data.name.display(self.0.upcast()))?; let proj_params_count = projection_ty.substitution.len(Interner) - trait_params.len(); let proj_params = &projection_ty.substitution.as_slice(Interner)[..proj_params_count]; @@ -105,9 +113,9 @@ impl DebugContext<'_> { } }; match def { - CallableDefId::FunctionId(_) => write!(fmt, "{{fn {name}}}"), + CallableDefId::FunctionId(_) => write!(fmt, "{{fn {}}}", name.display(self.0.upcast())), CallableDefId::StructId(_) | CallableDefId::EnumVariantId(_) => { - write!(fmt, "{{ctor {name}}}") + write!(fmt, "{{ctor {}}}", name.display(self.0.upcast())) } } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs index 3ab85c68f5b9d..f40b7db3a5512 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs @@ -1,22 +1,24 @@ //! Trait solving using Chalk. -use std::{env::var, sync::Arc}; +use std::env::var; -use chalk_ir::GoalData; +use chalk_ir::{fold::TypeFoldable, DebruijnIndex, GoalData}; use chalk_recursive::Cache; -use chalk_solve::{logging_db::LoggingRustIrDatabase, Solver}; +use chalk_solve::{logging_db::LoggingRustIrDatabase, rust_ir, Solver}; use base_db::CrateId; use hir_def::{ lang_item::{LangItem, LangItemTarget}, - TraitId, + BlockId, TraitId, }; +use hir_expand::name::{name, Name}; use stdx::panic_context; +use triomphe::Arc; use crate::{ - db::HirDatabase, infer::unify::InferenceTable, AliasEq, AliasTy, Canonical, DomainGoal, Goal, - Guidance, InEnvironment, Interner, ProjectionTy, ProjectionTyExt, Solution, TraitRefExt, Ty, - TyKind, WhereClause, + db::HirDatabase, infer::unify::InferenceTable, utils::UnevaluatedConstEvaluatorFolder, AliasEq, + AliasTy, Canonical, DomainGoal, Goal, Guidance, InEnvironment, Interner, ProjectionTy, + ProjectionTyExt, Solution, TraitRefExt, Ty, TyKind, WhereClause, }; /// This controls how much 'time' we give the Chalk solver before giving up. @@ -26,6 +28,7 @@ const CHALK_SOLVER_FUEL: i32 = 1000; pub(crate) struct ChalkContext<'a> { pub(crate) db: &'a dyn HirDatabase, pub(crate) krate: CrateId, + pub(crate) block: Option, } fn create_chalk_solver() -> chalk_recursive::RecursiveSolver { @@ -43,6 +46,7 @@ fn create_chalk_solver() -> chalk_recursive::RecursiveSolver { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct TraitEnvironment { pub krate: CrateId, + pub block: Option, // FIXME make this a BTreeMap pub(crate) traits_from_clauses: Vec<(Ty, TraitId)>, pub env: chalk_ir::Environment, @@ -52,6 +56,7 @@ impl TraitEnvironment { pub fn empty(krate: CrateId) -> Self { TraitEnvironment { krate, + block: None, traits_from_clauses: Vec::new(), env: chalk_ir::Environment::new(Interner), } @@ -78,11 +83,12 @@ pub(crate) fn normalize_projection_query( pub(crate) fn trait_solve_query( db: &dyn HirDatabase, krate: CrateId, + block: Option, goal: Canonical>, ) -> Option { let _p = profile::span("trait_solve_query").detail(|| match &goal.value.goal.data(Interner) { GoalData::DomainGoal(DomainGoal::Holds(WhereClause::Implemented(it))) => { - db.trait_data(it.hir_trait_id()).name.to_string() + db.trait_data(it.hir_trait_id()).name.display(db.upcast()).to_string() } GoalData::DomainGoal(DomainGoal::Holds(WhereClause::AliasEq(_))) => "alias_eq".to_string(), _ => "??".to_string(), @@ -100,18 +106,25 @@ pub(crate) fn trait_solve_query( } } + // Chalk see `UnevaluatedConst` as a unique concrete value, but we see it as an alias for another const. So + // we should get rid of it when talking to chalk. + let goal = goal + .try_fold_with(&mut UnevaluatedConstEvaluatorFolder { db }, DebruijnIndex::INNERMOST) + .unwrap(); + // We currently don't deal with universes (I think / hope they're not yet // relevant for our use cases?) let u_canonical = chalk_ir::UCanonical { canonical: goal, universes: 1 }; - solve(db, krate, &u_canonical) + solve(db, krate, block, &u_canonical) } fn solve( db: &dyn HirDatabase, krate: CrateId, + block: Option, goal: &chalk_ir::UCanonical>>, ) -> Option> { - let context = ChalkContext { db, krate }; + let context = ChalkContext { db, krate, block }; tracing::debug!("solve goal: {:?}", goal); let mut solver = create_chalk_solver(); @@ -171,8 +184,10 @@ fn is_chalk_print() -> bool { std::env::var("CHALK_PRINT").is_ok() } -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum FnTrait { + // Warning: Order is important. If something implements `x` it should also implement + // `y` if `y <= x`. FnOnce, FnMut, Fn, @@ -187,7 +202,23 @@ impl FnTrait { } } - pub fn get_id(&self, db: &dyn HirDatabase, krate: CrateId) -> Option { + pub const fn to_chalk_ir(self) -> rust_ir::ClosureKind { + match self { + FnTrait::FnOnce => rust_ir::ClosureKind::FnOnce, + FnTrait::FnMut => rust_ir::ClosureKind::FnMut, + FnTrait::Fn => rust_ir::ClosureKind::Fn, + } + } + + pub fn method_name(self) -> Name { + match self { + FnTrait::FnOnce => name!(call_once), + FnTrait::FnMut => name!(call_mut), + FnTrait::Fn => name!(call), + } + } + + pub fn get_id(self, db: &dyn HirDatabase, krate: CrateId) -> Option { let target = db.lang_item(krate, self.lang_item())?; match target { LangItemTarget::Trait(t) => Some(t), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs index 34d957e26ef5b..681d087ede6ea 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs @@ -4,7 +4,11 @@ use std::iter; use base_db::CrateId; -use chalk_ir::{cast::Cast, fold::Shift, BoundVar, DebruijnIndex}; +use chalk_ir::{ + cast::Cast, + fold::{FallibleTypeFolder, Shift}, + BoundVar, DebruijnIndex, +}; use either::Either; use hir_def::{ db::DefDatabase, @@ -15,16 +19,22 @@ use hir_def::{ lang_item::LangItem, resolver::{HasResolver, TypeNs}, type_ref::{TraitBoundModifier, TypeRef}, - ConstParamId, FunctionId, GenericDefId, ItemContainerId, Lookup, TraitId, TypeAliasId, - TypeOrConstParamId, TypeParamId, + ConstParamId, EnumId, EnumVariantId, FunctionId, GenericDefId, ItemContainerId, + LocalEnumVariantId, Lookup, TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, }; use hir_expand::name::Name; use intern::Interned; use rustc_hash::FxHashSet; use smallvec::{smallvec, SmallVec}; +use stdx::never; use crate::{ - db::HirDatabase, ChalkTraitId, Interner, Substitution, TraitRef, TraitRefExt, WhereClause, + consteval::unknown_const, + db::HirDatabase, + layout::{Layout, TagEncoding}, + mir::pad16, + ChalkTraitId, Const, ConstScalar, GenericArg, Interner, Substitution, TraitRef, TraitRefExt, + Ty, WhereClause, }; pub(crate) fn fn_traits( @@ -69,9 +79,7 @@ pub(super) fn all_super_trait_refs( cb: impl FnMut(TraitRef) -> Option, ) -> Option { let seen = iter::once(trait_ref.trait_id).collect(); - let mut stack = Vec::new(); - stack.push(trait_ref); - SuperTraits { db, seen, stack }.find_map(cb) + SuperTraits { db, seen, stack: vec![trait_ref] }.find_map(cb) } struct SuperTraits<'a> { @@ -130,7 +138,7 @@ fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId, cb: impl FnMut(Tra WherePredicate::Lifetime { .. } => None, }) .filter(|(_, bound_modifier)| matches!(bound_modifier, TraitBoundModifier::None)) - .filter_map(|(path, _)| match resolver.resolve_path_in_type_ns_fully(db, path.mod_path()) { + .filter_map(|(path, _)| match resolver.resolve_path_in_type_ns_fully(db, path) { Some(TypeNs::TraitId(t)) => Some(t), _ => None, }) @@ -176,6 +184,37 @@ pub(crate) fn generics(db: &dyn DefDatabase, def: GenericDefId) -> Generics { Generics { def, params: db.generic_params(def), parent_generics } } +/// It is a bit different from the rustc equivalent. Currently it stores: +/// - 0: the function signature, encoded as a function pointer type +/// - 1..n: generics of the parent +/// +/// and it doesn't store the closure types and fields. +/// +/// Codes should not assume this ordering, and should always use methods available +/// on this struct for retriving, and `TyBuilder::substs_for_closure` for creating. +pub(crate) struct ClosureSubst<'a>(pub(crate) &'a Substitution); + +impl<'a> ClosureSubst<'a> { + pub(crate) fn parent_subst(&self) -> &'a [GenericArg] { + match self.0.as_slice(Interner) { + [_, x @ ..] => x, + _ => { + never!("Closure missing parameter"); + &[] + } + } + } + + pub(crate) fn sig_ty(&self) -> &'a Ty { + match self.0.as_slice(Interner) { + [x, ..] => x.assert_ty_ref(Interner), + _ => { + unreachable!("Closure missing sig_ty parameter"); + } + } + } +} + #[derive(Debug)] pub(crate) struct Generics { def: GenericDefId, @@ -354,3 +393,74 @@ pub fn is_fn_unsafe_to_call(db: &dyn HirDatabase, func: FunctionId) -> bool { _ => false, } } + +pub(crate) struct UnevaluatedConstEvaluatorFolder<'a> { + pub(crate) db: &'a dyn HirDatabase, +} + +impl FallibleTypeFolder for UnevaluatedConstEvaluatorFolder<'_> { + type Error = (); + + fn as_dyn(&mut self) -> &mut dyn FallibleTypeFolder { + self + } + + fn interner(&self) -> Interner { + Interner + } + + fn try_fold_const( + &mut self, + constant: Const, + _outer_binder: DebruijnIndex, + ) -> Result { + if let chalk_ir::ConstValue::Concrete(c) = &constant.data(Interner).value { + if let ConstScalar::UnevaluatedConst(id, subst) = &c.interned { + if let Ok(eval) = self.db.const_eval(*id, subst.clone()) { + return Ok(eval); + } else { + return Ok(unknown_const(constant.data(Interner).ty.clone())); + } + } + } + Ok(constant) + } +} + +pub(crate) fn detect_variant_from_bytes<'a>( + layout: &'a Layout, + db: &dyn HirDatabase, + krate: CrateId, + b: &[u8], + e: EnumId, +) -> Option<(LocalEnumVariantId, &'a Layout)> { + let (var_id, var_layout) = match &layout.variants { + hir_def::layout::Variants::Single { index } => (index.0, &*layout), + hir_def::layout::Variants::Multiple { tag, tag_encoding, variants, .. } => { + let target_data_layout = db.target_data_layout(krate)?; + let size = tag.size(&*target_data_layout).bytes_usize(); + let offset = layout.fields.offset(0).bytes_usize(); // The only field on enum variants is the tag field + let tag = i128::from_le_bytes(pad16(&b[offset..offset + size], false)); + match tag_encoding { + TagEncoding::Direct => { + let x = variants.iter_enumerated().find(|x| { + db.const_eval_discriminant(EnumVariantId { parent: e, local_id: x.0 .0 }) + == Ok(tag) + })?; + (x.0 .0, x.1) + } + TagEncoding::Niche { untagged_variant, niche_start, .. } => { + let candidate_tag = tag.wrapping_sub(*niche_start as i128) as usize; + let variant = variants + .iter_enumerated() + .map(|(x, _)| x) + .filter(|x| x != untagged_variant) + .nth(candidate_tag) + .unwrap_or(*untagged_variant); + (variant.0, &variants[variant]) + } + } + } + }; + Some((var_id, var_layout)) +} diff --git a/src/tools/rust-analyzer/crates/hir/Cargo.toml b/src/tools/rust-analyzer/crates/hir/Cargo.toml index ef40a8902d73f..a20aff93f19b7 100644 --- a/src/tools/rust-analyzer/crates/hir/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir/Cargo.toml @@ -17,6 +17,7 @@ either = "1.7.0" arrayvec = "0.7.2" itertools = "0.10.5" smallvec.workspace = true +triomphe.workspace = true once_cell = "1.17.0" # local deps diff --git a/src/tools/rust-analyzer/crates/hir/src/attrs.rs b/src/tools/rust-analyzer/crates/hir/src/attrs.rs index db0b84ef0887b..b817937296d20 100644 --- a/src/tools/rust-analyzer/crates/hir/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir/src/attrs.rs @@ -4,7 +4,6 @@ use hir_def::{ attr::{AttrsWithOwner, Documentation}, item_scope::ItemInNs, path::ModPath, - per_ns::PerNs, resolver::HasResolver, AttrDefId, GenericParamId, ModuleDefId, }; @@ -41,7 +40,7 @@ macro_rules! impl_has_attrs { impl HasAttrs for $def { fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner { let def = AttrDefId::$def_id(self.into()); - db.attrs(def) + db.attrs_with_owner(def) } fn docs(self, db: &dyn HirDatabase) -> Option { let def = AttrDefId::$def_id(self.into()); @@ -121,6 +120,7 @@ impl HasAttrs for AssocItem { } } +/// Resolves the item `link` points to in the scope of `def`. fn resolve_doc_path( db: &dyn HirDatabase, def: AttrDefId, @@ -155,14 +155,14 @@ fn resolve_doc_path( .syntax_node() .descendants() .find_map(ast::Path::cast)?; - if ast_path.to_string() != link { + if ast_path.syntax().text() != link { return None; } ModPath::from_src(db.upcast(), ast_path, &Hygiene::new_unhygienic())? }; let resolved = resolver.resolve_module_path_in_items(db.upcast(), &modpath); - let resolved = if resolved == PerNs::none() { + let resolved = if resolved.is_none() { resolver.resolve_module_path_in_trait_assoc_items(db.upcast(), &modpath)? } else { resolved diff --git a/src/tools/rust-analyzer/crates/hir/src/db.rs b/src/tools/rust-analyzer/crates/hir/src/db.rs index 0935b5ea51945..e0cde689fed88 100644 --- a/src/tools/rust-analyzer/crates/hir/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir/src/db.rs @@ -6,8 +6,8 @@ pub use hir_def::db::*; pub use hir_expand::db::{ AstIdMapQuery, ExpandDatabase, ExpandDatabaseStorage, ExpandProcMacroQuery, HygieneFrameQuery, - InternMacroCallQuery, MacroArgTextQuery, MacroDefQuery, MacroExpandErrorQuery, - MacroExpandQuery, ParseMacroExpansionQuery, + InternMacroCallQuery, MacroArgTextQuery, MacroDefQuery, MacroExpandQuery, + ParseMacroExpansionErrorQuery, ParseMacroExpansionQuery, }; pub use hir_ty::db::*; diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index 253d62dafc60b..b64d81490bb11 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -10,7 +10,7 @@ use cfg::{CfgExpr, CfgOptions}; use either::Either; use hir_def::path::ModPath; use hir_expand::{name::Name, HirFileId, InFile}; -use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange}; +use syntax::{ast, AstPtr, SyntaxError, SyntaxNodePtr, TextRange}; use crate::{AssocItem, Field, Local, MacroKind, Type}; @@ -38,19 +38,25 @@ diagnostics![ IncorrectCase, InvalidDeriveTarget, IncoherentImpl, + MacroDefError, MacroError, + MacroExpansionParseError, MalformedDerive, MismatchedArgCount, MissingFields, MissingMatchArms, MissingUnsafe, + MovedOutOfRef, NeedMut, NoSuchField, PrivateAssocItem, PrivateField, ReplaceFilterMapNextWithFindMap, + TypedHole, TypeMismatch, + UndeclaredLabel, UnimplementedBuiltinMacro, + UnreachableLabel, UnresolvedExternCrate, UnresolvedField, UnresolvedImport, @@ -61,6 +67,19 @@ diagnostics![ UnusedMut, ]; +#[derive(Debug)] +pub struct BreakOutsideOfLoop { + pub expr: InFile>, + pub is_break: bool, + pub bad_value_break: bool, +} + +#[derive(Debug)] +pub struct TypedHole { + pub expr: InFile>, + pub expected: Type, +} + #[derive(Debug)] pub struct UnresolvedModule { pub decl: InFile>, @@ -84,6 +103,17 @@ pub struct UnresolvedMacroCall { pub path: ModPath, pub is_bang: bool, } +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct UnreachableLabel { + pub node: InFile>, + pub name: Name, +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct UndeclaredLabel { + pub node: InFile>, + pub name: Name, +} #[derive(Debug, Clone, Eq, PartialEq)] pub struct InactiveCode { @@ -111,6 +141,20 @@ pub struct MacroError { pub message: String, } +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct MacroExpansionParseError { + pub node: InFile, + pub precise_location: Option, + pub errors: Box<[SyntaxError]>, +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct MacroDefError { + pub node: InFile>, + pub message: String, + pub name: Option, +} + #[derive(Debug)] pub struct UnimplementedBuiltinMacro { pub node: InFile, @@ -166,13 +210,6 @@ pub struct PrivateField { pub field: Field, } -#[derive(Debug)] -pub struct BreakOutsideOfLoop { - pub expr: InFile>, - pub is_break: bool, - pub bad_value_break: bool, -} - #[derive(Debug)] pub struct MissingUnsafe { pub expr: InFile>, @@ -223,3 +260,9 @@ pub struct NeedMut { pub struct UnusedMut { pub local: Local, } + +#[derive(Debug)] +pub struct MovedOutOfRef { + pub ty: Type, + pub span: InFile, +} diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs index 5aae92efd19ed..9a2090ab79a2c 100644 --- a/src/tools/rust-analyzer/crates/hir/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir/src/display.rs @@ -1,6 +1,6 @@ //! HirDisplay implementations for various hir types. use hir_def::{ - adt::VariantData, + data::adt::VariantData, generics::{ TypeOrConstParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget, }, @@ -8,6 +8,7 @@ use hir_def::{ type_ref::{TypeBound, TypeRef}, AdtId, GenericDefId, }; +use hir_expand::name; use hir_ty::{ display::{ write_bounds_like_dyn_trait_with_prefix, write_visibility, HirDisplay, HirDisplayError, @@ -50,7 +51,7 @@ impl HirDisplay for Function { // FIXME: String escape? write!(f, "extern \"{}\" ", &**abi)?; } - write!(f, "fn {}", data.name)?; + write!(f, "fn {}", data.name.display(f.db.upcast()))?; write_generic_params(GenericDefId::FunctionId(self.id), f)?; @@ -62,7 +63,7 @@ impl HirDisplay for Function { { f.write_char('&')?; if let Some(lifetime) = lifetime { - write!(f, "{} ", lifetime.name)?; + write!(f, "{} ", lifetime.name.display(f.db.upcast()))?; } if let hir_def::type_ref::Mutability::Mut = mut_ { f.write_str("mut ")?; @@ -76,22 +77,22 @@ impl HirDisplay for Function { }; let mut first = true; - for (name, type_ref) in &data.params { + // FIXME: Use resolved `param.ty` once we no longer discard lifetimes + for (type_ref, param) in data.params.iter().zip(self.assoc_fn_params(db)) { + let local = param.as_local(db).map(|it| it.name(db)); if !first { f.write_str(", ")?; } else { first = false; - if data.has_self_param() { + if local == Some(name!(self)) { write_self_param(type_ref, f)?; continue; } } - match name { - Some(name) => write!(f, "{name}: ")?, + match local { + Some(name) => write!(f, "{}: ", name.display(f.db.upcast()))?, None => f.write_str("_: ")?, } - // FIXME: Use resolved `param.ty` or raw `type_ref`? - // The former will ignore lifetime arguments currently. type_ref.hir_fmt(f)?; } @@ -150,7 +151,7 @@ impl HirDisplay for Struct { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { write_visibility(self.module(f.db).id, self.visibility(f.db), f)?; f.write_str("struct ")?; - write!(f, "{}", self.name(f.db))?; + write!(f, "{}", self.name(f.db).display(f.db.upcast()))?; let def_id = GenericDefId::AdtId(AdtId::StructId(self.id)); write_generic_params(def_id, f)?; write_where_clause(def_id, f)?; @@ -162,7 +163,7 @@ impl HirDisplay for Enum { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { write_visibility(self.module(f.db).id, self.visibility(f.db), f)?; f.write_str("enum ")?; - write!(f, "{}", self.name(f.db))?; + write!(f, "{}", self.name(f.db).display(f.db.upcast()))?; let def_id = GenericDefId::AdtId(AdtId::EnumId(self.id)); write_generic_params(def_id, f)?; write_where_clause(def_id, f)?; @@ -174,7 +175,7 @@ impl HirDisplay for Union { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { write_visibility(self.module(f.db).id, self.visibility(f.db), f)?; f.write_str("union ")?; - write!(f, "{}", self.name(f.db))?; + write!(f, "{}", self.name(f.db).display(f.db.upcast()))?; let def_id = GenericDefId::AdtId(AdtId::UnionId(self.id)); write_generic_params(def_id, f)?; write_where_clause(def_id, f)?; @@ -185,14 +186,14 @@ impl HirDisplay for Union { impl HirDisplay for Field { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { write_visibility(self.parent.module(f.db).id, self.visibility(f.db), f)?; - write!(f, "{}: ", self.name(f.db))?; + write!(f, "{}: ", self.name(f.db).display(f.db.upcast()))?; self.ty(f.db).hir_fmt(f) } } impl HirDisplay for Variant { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { - write!(f, "{}", self.name(f.db))?; + write!(f, "{}", self.name(f.db).display(f.db.upcast()))?; let data = self.variant_data(f.db); match &*data { VariantData::Unit => {} @@ -221,7 +222,7 @@ impl HirDisplay for Variant { f.write_str(", ")?; } // Enum variant fields must be pub. - write!(f, "{}: ", field.name)?; + write!(f, "{}: ", field.name.display(f.db.upcast()))?; field.type_ref.hir_fmt(f)?; } f.write_str(" }")?; @@ -258,7 +259,7 @@ impl HirDisplay for TypeOrConstParam { impl HirDisplay for TypeParam { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { - write!(f, "{}", self.name(f.db))?; + write!(f, "{}", self.name(f.db).display(f.db.upcast()))?; if f.omit_verbose_types() { return Ok(()); } @@ -285,13 +286,13 @@ impl HirDisplay for TypeParam { impl HirDisplay for LifetimeParam { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { - write!(f, "{}", self.name(f.db)) + write!(f, "{}", self.name(f.db).display(f.db.upcast())) } } impl HirDisplay for ConstParam { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { - write!(f, "const {}: ", self.name(f.db))?; + write!(f, "const {}: ", self.name(f.db).display(f.db.upcast()))?; self.ty(f.db).hir_fmt(f) } } @@ -324,7 +325,7 @@ fn write_generic_params( }; for (_, lifetime) in params.lifetimes.iter() { delim(f)?; - write!(f, "{}", lifetime.name)?; + write!(f, "{}", lifetime.name.display(f.db.upcast()))?; } for (_, ty) in params.type_or_consts.iter() { if let Some(name) = &ty.name() { @@ -334,7 +335,7 @@ fn write_generic_params( continue; } delim(f)?; - write!(f, "{name}")?; + write!(f, "{}", name.display(f.db.upcast()))?; if let Some(default) = &ty.default { f.write_str(" = ")?; default.hir_fmt(f)?; @@ -342,7 +343,7 @@ fn write_generic_params( } TypeOrConstParamData::ConstParamData(c) => { delim(f)?; - write!(f, "const {name}: ")?; + write!(f, "const {}: ", name.display(f.db.upcast()))?; c.ty.hir_fmt(f)?; } } @@ -379,7 +380,7 @@ fn write_where_clause(def: GenericDefId, f: &mut HirFormatter<'_>) -> Result<(), WherePredicateTypeTarget::TypeRef(ty) => ty.hir_fmt(f), WherePredicateTypeTarget::TypeOrConstParam(id) => { match ¶ms.type_or_consts[*id].name() { - Some(name) => write!(f, "{name}"), + Some(name) => write!(f, "{}", name.display(f.db.upcast())), None => f.write_str("{unnamed}"), } } @@ -411,10 +412,15 @@ fn write_where_clause(def: GenericDefId, f: &mut HirFormatter<'_>) -> Result<(), WherePredicate::Lifetime { target, bound } => { if matches!(prev_pred, Some(WherePredicate::Lifetime { target: target_, .. }) if target_ == target) { - write!(f, " + {}", bound.name)?; + write!(f, " + {}", bound.name.display(f.db.upcast()))?; } else { new_predicate(f)?; - write!(f, "{}: {}", target.name, bound.name)?; + write!( + f, + "{}: {}", + target.name.display(f.db.upcast()), + bound.name.display(f.db.upcast()) + )?; } } WherePredicate::ForLifetime { lifetimes, target, bound } => { @@ -431,7 +437,7 @@ fn write_where_clause(def: GenericDefId, f: &mut HirFormatter<'_>) -> Result<(), if idx != 0 { f.write_str(", ")?; } - write!(f, "{lifetime}")?; + write!(f, "{}", lifetime.display(f.db.upcast()))?; } f.write_str("> ")?; write_target(target, f)?; @@ -461,7 +467,7 @@ impl HirDisplay for Const { let data = db.const_data(self.id); f.write_str("const ")?; match &data.name { - Some(name) => write!(f, "{name}: ")?, + Some(name) => write!(f, "{}: ", name.display(f.db.upcast()))?, None => f.write_str("_: ")?, } data.type_ref.hir_fmt(f)?; @@ -477,7 +483,7 @@ impl HirDisplay for Static { if data.mutable { f.write_str("mut ")?; } - write!(f, "{}: ", &data.name)?; + write!(f, "{}: ", data.name.display(f.db.upcast()))?; data.type_ref.hir_fmt(f)?; Ok(()) } @@ -493,7 +499,7 @@ impl HirDisplay for Trait { if data.is_auto { f.write_str("auto ")?; } - write!(f, "trait {}", data.name)?; + write!(f, "trait {}", data.name.display(f.db.upcast()))?; let def_id = GenericDefId::TraitId(self.id); write_generic_params(def_id, f)?; write_where_clause(def_id, f)?; @@ -505,7 +511,7 @@ impl HirDisplay for TraitAlias { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { write_visibility(self.module(f.db).id, self.visibility(f.db), f)?; let data = f.db.trait_alias_data(self.id); - write!(f, "trait {}", data.name)?; + write!(f, "trait {}", data.name.display(f.db.upcast()))?; let def_id = GenericDefId::TraitAliasId(self.id); write_generic_params(def_id, f)?; f.write_str(" = ")?; @@ -521,7 +527,7 @@ impl HirDisplay for TypeAlias { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { write_visibility(self.module(f.db).id, self.visibility(f.db), f)?; let data = f.db.type_alias_data(self.id); - write!(f, "type {}", data.name)?; + write!(f, "type {}", data.name.display(f.db.upcast()))?; let def_id = GenericDefId::TypeAliasId(self.id); write_generic_params(def_id, f)?; write_where_clause(def_id, f)?; @@ -541,8 +547,8 @@ impl HirDisplay for Module { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { // FIXME: Module doesn't have visibility saved in data. match self.name(f.db) { - Some(name) => write!(f, "mod {name}"), - None if self.is_crate_root(f.db) => match self.krate(f.db).display_name(f.db) { + Some(name) => write!(f, "mod {}", name.display(f.db.upcast())), + None if self.is_crate_root() => match self.krate(f.db).display_name(f.db) { Some(name) => write!(f, "extern crate {name}"), None => f.write_str("extern crate {unknown}"), }, @@ -558,6 +564,6 @@ impl HirDisplay for Macro { hir_def::MacroId::MacroRulesId(_) => f.write_str("macro_rules!"), hir_def::MacroId::ProcMacroId(_) => f.write_str("proc_macro"), }?; - write!(f, " {}", self.name(f.db)) + write!(f, " {}", self.name(f.db).display(f.db.upcast())) } } diff --git a/src/tools/rust-analyzer/crates/hir/src/from_id.rs b/src/tools/rust-analyzer/crates/hir/src/from_id.rs index aaaa7abf3863c..883e6a29b06a6 100644 --- a/src/tools/rust-analyzer/crates/hir/src/from_id.rs +++ b/src/tools/rust-analyzer/crates/hir/src/from_id.rs @@ -4,7 +4,7 @@ //! are splitting the hir. use hir_def::{ - expr::{BindingId, LabelId}, + hir::{BindingId, LabelId}, AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, GenericDefId, GenericParamId, ModuleDefId, VariantId, }; diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 35424feec8b29..5926d8654210d 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -6,7 +6,7 @@ //! applied. So, the relation between syntax and HIR is many-to-one. //! //! HIR is the public API of the all of the compiler logic above syntax trees. -//! It is written in "OO" style. Each type is self contained (as in, it knows it's +//! It is written in "OO" style. Each type is self contained (as in, it knows its //! parents and full context). It should be "clean code". //! //! `hir_*` crates are the implementation of the compiler logic. @@ -33,24 +33,25 @@ pub mod symbols; mod display; -use std::{iter, ops::ControlFlow, sync::Arc}; +use std::{iter, ops::ControlFlow}; use arrayvec::ArrayVec; use base_db::{CrateDisplayName, CrateId, CrateOrigin, Edition, FileId, ProcMacroKind}; use either::Either; use hir_def::{ - adt::VariantData, body::{BodyDiagnostic, SyntheticSyntax}, - expr::{BindingAnnotation, BindingId, ExprOrPatId, LabelId, Pat}, + data::adt::VariantData, generics::{LifetimeParamData, TypeOrConstParamData, TypeParamProvenance}, + hir::{BindingAnnotation, BindingId, ExprOrPatId, LabelId, Pat}, item_tree::ItemTreeNode, - lang_item::{LangItem, LangItemTarget}, - layout::{Layout, LayoutError, ReprOptions}, + lang_item::LangItemTarget, + layout::{self, ReprOptions, TargetDataLayout}, + macro_id_to_def_id, nameres::{self, diagnostics::DefDiagnostic, ModuleOrigin}, per_ns::PerNs, resolver::{HasResolver, Resolver}, src::HasSource as _, - AdtId, AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId, DefWithBodyId, EnumId, + AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, FunctionId, GenericDefId, HasModule, ImplId, ItemContainerId, LifetimeParamId, LocalEnumVariantId, LocalFieldId, Lookup, MacroExpander, MacroId, ModuleId, StaticId, StructId, TraitAliasId, TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId, @@ -61,14 +62,15 @@ use hir_ty::{ consteval::{try_const_usize, unknown_const_as_generic, ConstEvalError, ConstExt}, diagnostics::BodyValidationDiagnostic, display::HexifiedConst, - layout::layout_of_ty, + layout::{Layout as TyLayout, RustcEnumVariantIdx, TagEncoding}, method_resolution::{self, TyFingerprint}, mir::{self, interpret_mir}, primitive::UintTy, traits::FnTrait, AliasTy, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast, ClosureId, GenericArgData, Interner, ParamKind, QuantifiedWhereClause, Scalar, Substitution, - TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind, WhereClause, + TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind, ValueTyDefId, + WhereClause, }; use itertools::Itertools; use nameres::diagnostics::DefDiagnosticKind; @@ -79,6 +81,7 @@ use syntax::{ ast::{self, HasAttrs as _, HasDocComments, HasName}, AstNode, AstPtr, SmolStr, SyntaxNode, SyntaxNodePtr, TextRange, T, }; +use triomphe::Arc; use crate::db::{DefDatabase, HirDatabase}; @@ -86,11 +89,13 @@ pub use crate::{ attrs::{HasAttrs, Namespace}, diagnostics::{ AnyDiagnostic, BreakOutsideOfLoop, ExpectedFunction, InactiveCode, IncoherentImpl, - IncorrectCase, InvalidDeriveTarget, MacroError, MalformedDerive, MismatchedArgCount, - MissingFields, MissingMatchArms, MissingUnsafe, NeedMut, NoSuchField, PrivateAssocItem, - PrivateField, ReplaceFilterMapNextWithFindMap, TypeMismatch, UnimplementedBuiltinMacro, - UnresolvedExternCrate, UnresolvedField, UnresolvedImport, UnresolvedMacroCall, - UnresolvedMethodCall, UnresolvedModule, UnresolvedProcMacro, UnusedMut, + IncorrectCase, InvalidDeriveTarget, MacroDefError, MacroError, MacroExpansionParseError, + MalformedDerive, MismatchedArgCount, MissingFields, MissingMatchArms, MissingUnsafe, + MovedOutOfRef, NeedMut, NoSuchField, PrivateAssocItem, PrivateField, + ReplaceFilterMapNextWithFindMap, TypeMismatch, TypedHole, UndeclaredLabel, + UnimplementedBuiltinMacro, UnreachableLabel, UnresolvedExternCrate, UnresolvedField, + UnresolvedImport, UnresolvedMacroCall, UnresolvedMethodCall, UnresolvedModule, + UnresolvedProcMacro, UnusedMut, }, has_source::HasSource, semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo, VisibleTraits}, @@ -108,20 +113,18 @@ pub use crate::{ pub use { cfg::{CfgAtom, CfgExpr, CfgOptions}, hir_def::{ - adt::StructKind, - attr::{Attrs, AttrsWithOwner, Documentation}, - builtin_attr::AttributeTemplate, + attr::{builtin::AttributeTemplate, Attrs, AttrsWithOwner, Documentation}, + data::adt::StructKind, find_path::PrefixKind, import_map, - nameres::ModuleSource, + lang_item::LangItem, + nameres::{DefMap, ModuleSource}, path::{ModPath, PathKind}, type_ref::{Mutability, TypeRef}, visibility::Visibility, - // FIXME: This is here since it is input of a method in `HirWrite` - // and things outside of hir need to implement that trait. We probably - // should move whole `hir_ty::display` to this crate so we will become - // able to use `ModuleDef` or `Definition` instead of `ModuleDefId`. - ModuleDefId, + // FIXME: This is here since some queries take it as input that are used + // outside of hir. + {AdtId, ModuleDefId}, }, hir_expand::{ attrs::Attr, @@ -129,7 +132,8 @@ pub use { ExpandResult, HirFileId, InFile, MacroFile, Origin, }, hir_ty::{ - display::{HirDisplay, HirDisplayError, HirWrite}, + display::{ClosureStyle, HirDisplay, HirDisplayError, HirWrite}, + layout::LayoutError, mir::MirEvalError, PointerCast, Safety, }, @@ -198,7 +202,7 @@ impl Crate { pub fn root_module(self, db: &dyn HirDatabase) -> Module { let def_map = db.crate_def_map(self.id); - Module { id: def_map.module_id(def_map.root()) } + Module { id: def_map.module_id(DefMap::ROOT) } } pub fn modules(self, db: &dyn HirDatabase) -> Vec { @@ -253,7 +257,8 @@ impl Crate { } pub fn potential_cfg(&self, db: &dyn HirDatabase) -> CfgOptions { - db.crate_graph()[self.id].potential_cfg_options.clone() + let data = &db.crate_graph()[self.id]; + data.potential_cfg_options.clone().unwrap_or_else(|| data.cfg_options.clone()) } } @@ -326,7 +331,7 @@ impl ModuleDef { segments.extend(m.name(db)) } segments.reverse(); - Some(segments.into_iter().join("::")) + Some(segments.iter().map(|it| it.display(db.upcast())).join("::")) } pub fn canonical_module_path( @@ -470,12 +475,11 @@ impl Module { /// in the module tree of any target in `Cargo.toml`. pub fn crate_root(self, db: &dyn HirDatabase) -> Module { let def_map = db.crate_def_map(self.id.krate()); - Module { id: def_map.module_id(def_map.root()) } + Module { id: def_map.module_id(DefMap::ROOT) } } - pub fn is_crate_root(self, db: &dyn HirDatabase) -> bool { - let def_map = db.crate_def_map(self.id.krate()); - def_map.root() == self.id.local_id + pub fn is_crate_root(self) -> bool { + DefMap::ROOT == self.id.local_id } /// Iterates over all child modules. @@ -552,7 +556,11 @@ impl Module { /// Fills `acc` with the module's diagnostics. pub fn diagnostics(self, db: &dyn HirDatabase, acc: &mut Vec) { let _p = profile::span("Module::diagnostics").detail(|| { - format!("{:?}", self.name(db).map_or("".into(), |name| name.to_string())) + format!( + "{:?}", + self.name(db) + .map_or("".into(), |name| name.display(db.upcast()).to_string()) + ) }); let def_map = self.id.def_map(db.upcast()); for diag in def_map.diagnostics() { @@ -562,6 +570,7 @@ impl Module { } emit_def_diagnostic(db, acc, diag); } + for decl in self.declarations(db) { match decl { ModuleDef::Module(m) => { @@ -600,9 +609,11 @@ impl Module { } acc.extend(decl.diagnostics(db)) } + ModuleDef::Macro(m) => emit_macro_def_diagnostics(db, acc, m), _ => acc.extend(decl.diagnostics(db)), } } + self.legacy_macros(db).into_iter().for_each(|m| emit_macro_def_diagnostics(db, acc, m)); let inherent_impls = db.inherent_impls_in_crate(self.id.krate()); @@ -684,8 +695,31 @@ impl Module { } } +fn emit_macro_def_diagnostics(db: &dyn HirDatabase, acc: &mut Vec, m: Macro) { + let id = macro_id_to_def_id(db.upcast(), m.id); + if let Err(e) = db.macro_def(id) { + let Some(ast) = id.ast_id().left() else { + never!("MacroDefError for proc-macro: {:?}", e); + return; + }; + emit_def_diagnostic_( + db, + acc, + &DefDiagnosticKind::MacroDefError { ast, message: e.to_string() }, + ); + } +} + fn emit_def_diagnostic(db: &dyn HirDatabase, acc: &mut Vec, diag: &DefDiagnostic) { - match &diag.kind { + emit_def_diagnostic_(db, acc, &diag.kind) +} + +fn emit_def_diagnostic_( + db: &dyn HirDatabase, + acc: &mut Vec, + diag: &DefDiagnosticKind, +) { + match diag { DefDiagnosticKind::UnresolvedModule { ast: declaration, candidates } => { let decl = declaration.to_node(db.upcast()); acc.push( @@ -725,7 +759,6 @@ fn emit_def_diagnostic(db: &dyn HirDatabase, acc: &mut Vec, diag: .into(), ); } - DefDiagnosticKind::UnresolvedProcMacro { ast, krate } => { let (node, precise_location, macro_name, kind) = precise_macro_call_location(ast, db); acc.push( @@ -733,7 +766,6 @@ fn emit_def_diagnostic(db: &dyn HirDatabase, acc: &mut Vec, diag: .into(), ); } - DefDiagnosticKind::UnresolvedMacroCall { ast, path } => { let (node, precise_location, _, _) = precise_macro_call_location(ast, db); acc.push( @@ -746,12 +778,16 @@ fn emit_def_diagnostic(db: &dyn HirDatabase, acc: &mut Vec, diag: .into(), ); } - DefDiagnosticKind::MacroError { ast, message } => { let (node, precise_location, _, _) = precise_macro_call_location(ast, db); acc.push(MacroError { node, precise_location, message: message.clone() }.into()); } - + DefDiagnosticKind::MacroExpansionParseError { ast, errors } => { + let (node, precise_location, _, _) = precise_macro_call_location(ast, db); + acc.push( + MacroExpansionParseError { node, precise_location, errors: errors.clone() }.into(), + ); + } DefDiagnosticKind::UnimplementedBuiltinMacro { ast } => { let node = ast.to_node(db.upcast()); // Must have a name, otherwise we wouldn't emit it. @@ -793,6 +829,17 @@ fn emit_def_diagnostic(db: &dyn HirDatabase, acc: &mut Vec, diag: None => stdx::never!("derive diagnostic on item without derive attribute"), } } + DefDiagnosticKind::MacroDefError { ast, message } => { + let node = ast.to_node(db.upcast()); + acc.push( + MacroDefError { + node: InFile::new(ast.file_id, AstPtr::new(&node)), + name: node.name().map(|it| it.syntax().text_range()), + message: message.clone(), + } + .into(), + ); + } } } @@ -800,7 +847,7 @@ fn precise_macro_call_location( ast: &MacroCallKind, db: &dyn HirDatabase, ) -> (InFile, Option, Option, MacroKind) { - // FIXME: maaybe we actually want slightly different ranges for the different macro diagnostics + // FIXME: maybe we actually want slightly different ranges for the different macro diagnostics // - e.g. the full attribute for macro errors, but only the name for name resolution match ast { MacroCallKind::FnLike { ast_id, .. } => { @@ -915,7 +962,8 @@ impl Field { } pub fn layout(&self, db: &dyn HirDatabase) -> Result { - layout_of_ty(db, &self.ty(db).ty, self.parent.module(db).krate().into()) + db.layout_of_ty(self.ty(db).ty.clone(), self.parent.module(db).krate().into()) + .map(|layout| Layout(layout, db.target_data_layout(self.krate(db).into()).unwrap())) } pub fn parent_def(&self, _db: &dyn HirDatabase) -> VariantDef { @@ -959,6 +1007,10 @@ impl Struct { Type::from_def(db, self.id) } + pub fn constructor_ty(self, db: &dyn HirDatabase) -> Type { + Type::from_value_def(db, self.id) + } + pub fn repr(self, db: &dyn HirDatabase) -> Option { db.struct_data(self.id).repr } @@ -996,6 +1048,10 @@ impl Union { Type::from_def(db, self.id) } + pub fn constructor_ty(self, db: &dyn HirDatabase) -> Type { + Type::from_value_def(db, self.id) + } + pub fn fields(self, db: &dyn HirDatabase) -> Vec { db.union_data(self.id) .variant_data @@ -1034,6 +1090,10 @@ impl Enum { db.enum_data(self.id).variants.iter().map(|(id, _)| Variant { parent: self, id }).collect() } + pub fn repr(self, db: &dyn HirDatabase) -> Option { + db.enum_data(self.id).repr + } + pub fn ty(self, db: &dyn HirDatabase) -> Type { Type::from_def(db, self.id) } @@ -1043,7 +1103,7 @@ impl Enum { Type::new_for_crate( self.id.lookup(db.upcast()).container.krate(), TyBuilder::builtin(match db.enum_data(self.id).variant_body_type() { - hir_def::layout::IntegerType::Pointer(sign) => match sign { + layout::IntegerType::Pointer(sign) => match sign { true => hir_def::builtin_type::BuiltinType::Int( hir_def::builtin_type::BuiltinInt::Isize, ), @@ -1051,29 +1111,34 @@ impl Enum { hir_def::builtin_type::BuiltinUint::Usize, ), }, - hir_def::layout::IntegerType::Fixed(i, sign) => match sign { + layout::IntegerType::Fixed(i, sign) => match sign { true => hir_def::builtin_type::BuiltinType::Int(match i { - hir_def::layout::Integer::I8 => hir_def::builtin_type::BuiltinInt::I8, - hir_def::layout::Integer::I16 => hir_def::builtin_type::BuiltinInt::I16, - hir_def::layout::Integer::I32 => hir_def::builtin_type::BuiltinInt::I32, - hir_def::layout::Integer::I64 => hir_def::builtin_type::BuiltinInt::I64, - hir_def::layout::Integer::I128 => hir_def::builtin_type::BuiltinInt::I128, + layout::Integer::I8 => hir_def::builtin_type::BuiltinInt::I8, + layout::Integer::I16 => hir_def::builtin_type::BuiltinInt::I16, + layout::Integer::I32 => hir_def::builtin_type::BuiltinInt::I32, + layout::Integer::I64 => hir_def::builtin_type::BuiltinInt::I64, + layout::Integer::I128 => hir_def::builtin_type::BuiltinInt::I128, }), false => hir_def::builtin_type::BuiltinType::Uint(match i { - hir_def::layout::Integer::I8 => hir_def::builtin_type::BuiltinUint::U8, - hir_def::layout::Integer::I16 => hir_def::builtin_type::BuiltinUint::U16, - hir_def::layout::Integer::I32 => hir_def::builtin_type::BuiltinUint::U32, - hir_def::layout::Integer::I64 => hir_def::builtin_type::BuiltinUint::U64, - hir_def::layout::Integer::I128 => hir_def::builtin_type::BuiltinUint::U128, + layout::Integer::I8 => hir_def::builtin_type::BuiltinUint::U8, + layout::Integer::I16 => hir_def::builtin_type::BuiltinUint::U16, + layout::Integer::I32 => hir_def::builtin_type::BuiltinUint::U32, + layout::Integer::I64 => hir_def::builtin_type::BuiltinUint::U64, + layout::Integer::I128 => hir_def::builtin_type::BuiltinUint::U128, }), }, }), ) } + /// Returns true if at least one variant of this enum is a non-unit variant. pub fn is_data_carrying(self, db: &dyn HirDatabase) -> bool { self.variants(db).iter().any(|v| !matches!(v.kind(db), StructKind::Unit)) } + + pub fn layout(self, db: &dyn HirDatabase) -> Result { + Adt::from(self).layout(db) + } } impl HasVisibility for Enum { @@ -1103,6 +1168,10 @@ impl Variant { self.parent } + pub fn constructor_ty(self, db: &dyn HirDatabase) -> Type { + Type::from_value_def(db, EnumVariantId { parent: self.parent.id, local_id: self.id }) + } + pub fn name(self, db: &dyn HirDatabase) -> Name { db.enum_data(self.parent.id).variants[self.id].name.clone() } @@ -1130,6 +1199,18 @@ impl Variant { pub fn eval(self, db: &dyn HirDatabase) -> Result { db.const_eval_discriminant(self.into()) } + + pub fn layout(&self, db: &dyn HirDatabase) -> Result { + let parent_enum = self.parent_enum(db); + let parent_layout = parent_enum.layout(db)?; + Ok(match &parent_layout.0.variants { + layout::Variants::Multiple { variants, .. } => Layout( + Arc::new(variants[RustcEnumVariantIdx(self.id)].clone()), + db.target_data_layout(parent_enum.krate(db).into()).unwrap(), + ), + _ => parent_layout, + }) + } } /// Variants inherit visibility from the parent enum. @@ -1161,7 +1242,9 @@ impl Adt { if db.generic_params(self.into()).iter().count() != 0 { return Err(LayoutError::HasPlaceholder); } - db.layout_of_adt(self.into(), Substitution::empty(Interner)) + let krate = self.krate(db).id; + db.layout_of_adt(self.into(), Substitution::empty(Interner), krate) + .map(|layout| Layout(layout, db.target_data_layout(krate).unwrap())) } /// Turns this ADT into a type. Any type parameters of the ADT will be @@ -1392,6 +1475,12 @@ impl DefWithBody { } .into(), ), + BodyDiagnostic::UnreachableLabel { node, name } => { + acc.push(UnreachableLabel { node: node.clone(), name: name.clone() }.into()) + } + BodyDiagnostic::UndeclaredLabel { node, name } => { + acc.push(UndeclaredLabel { node: node.clone(), name: name.clone() }.into()) + } } } @@ -1404,14 +1493,6 @@ impl DefWithBody { let field = source_map.field_syntax(expr); acc.push(NoSuchField { field }.into()) } - &hir_ty::InferenceDiagnostic::BreakOutsideOfLoop { - expr, - is_break, - bad_value_break, - } => { - let expr = expr_syntax(expr); - acc.push(BreakOutsideOfLoop { expr, is_break, bad_value_break }.into()) - } &hir_ty::InferenceDiagnostic::MismatchedArgCount { call_expr, expected, found } => { acc.push( MismatchedArgCount { call_expr: expr_syntax(call_expr), expected, found } @@ -1483,14 +1564,29 @@ impl DefWithBody { .into(), ) } + &hir_ty::InferenceDiagnostic::BreakOutsideOfLoop { + expr, + is_break, + bad_value_break, + } => { + let expr = expr_syntax(expr); + acc.push(BreakOutsideOfLoop { expr, is_break, bad_value_break }.into()) + } + hir_ty::InferenceDiagnostic::TypedHole { expr, expected } => { + let expr = expr_syntax(*expr); + acc.push( + TypedHole { + expr, + expected: Type::new(db, DefWithBodyId::from(self), expected.clone()), + } + .into(), + ) + } } } for (pat_or_expr, mismatch) in infer.type_mismatches() { let expr_or_pat = match pat_or_expr { ExprOrPatId::ExprId(expr) => source_map.expr_syntax(expr).map(Either::Left), - // FIXME: Re-enable these once we have less false positives - ExprOrPatId::PatId(_pat) => continue, - #[allow(unreachable_patterns)] ExprOrPatId::PatId(pat) => source_map.pat_syntax(pat).map(Either::Right), }; let expr_or_pat = match expr_or_pat { @@ -1515,7 +1611,7 @@ impl DefWithBody { match source_map.expr_syntax(expr) { Ok(expr) => acc.push(MissingUnsafe { expr }.into()), Err(SyntheticSyntax) => { - // FIXME: Here and eslwhere in this file, the `expr` was + // FIXME: Here and elsewhere in this file, the `expr` was // desugared, report or assert that this doesn't happen. } } @@ -1523,35 +1619,71 @@ impl DefWithBody { let hir_body = db.body(self.into()); - if let Ok(borrowck_result) = db.borrowck(self.into()) { - let mir_body = &borrowck_result.mir_body; - let mol = &borrowck_result.mutability_of_locals; - for (binding_id, _) in hir_body.bindings.iter() { - let need_mut = &mol[mir_body.binding_locals[binding_id]]; - let local = Local { parent: self.into(), binding_id }; - match (need_mut, local.is_mut(db)) { - (mir::MutabilityReason::Mut { .. }, true) - | (mir::MutabilityReason::Not, false) => (), - (mir::MutabilityReason::Mut { spans }, false) => { - for span in spans { - let span: InFile = match span { - mir::MirSpan::ExprId(e) => match source_map.expr_syntax(*e) { - Ok(s) => s.map(|x| x.into()), - Err(_) => continue, - }, - mir::MirSpan::PatId(p) => match source_map.pat_syntax(*p) { - Ok(s) => s.map(|x| match x { - Either::Left(e) => e.into(), - Either::Right(e) => e.into(), - }), - Err(_) => continue, - }, - mir::MirSpan::Unknown => continue, - }; - acc.push(NeedMut { local, span }.into()); + if let Ok(borrowck_results) = db.borrowck(self.into()) { + for borrowck_result in borrowck_results.iter() { + let mir_body = &borrowck_result.mir_body; + for moof in &borrowck_result.moved_out_of_ref { + let span: InFile = match moof.span { + mir::MirSpan::ExprId(e) => match source_map.expr_syntax(e) { + Ok(s) => s.map(|x| x.into()), + Err(_) => continue, + }, + mir::MirSpan::PatId(p) => match source_map.pat_syntax(p) { + Ok(s) => s.map(|x| match x { + Either::Left(e) => e.into(), + Either::Right(e) => e.into(), + }), + Err(_) => continue, + }, + mir::MirSpan::Unknown => continue, + }; + acc.push( + MovedOutOfRef { ty: Type::new_for_crate(krate, moof.ty.clone()), span } + .into(), + ) + } + let mol = &borrowck_result.mutability_of_locals; + for (binding_id, binding_data) in hir_body.bindings.iter() { + if binding_data.problems.is_some() { + // We should report specific diagnostics for these problems, not `need-mut` and `unused-mut`. + continue; + } + let Some(&local) = mir_body.binding_locals.get(binding_id) else { + continue; + }; + let need_mut = &mol[local]; + let local = Local { parent: self.into(), binding_id }; + match (need_mut, local.is_mut(db)) { + (mir::MutabilityReason::Mut { .. }, true) + | (mir::MutabilityReason::Not, false) => (), + (mir::MutabilityReason::Mut { spans }, false) => { + for span in spans { + let span: InFile = match span { + mir::MirSpan::ExprId(e) => match source_map.expr_syntax(*e) { + Ok(s) => s.map(|x| x.into()), + Err(_) => continue, + }, + mir::MirSpan::PatId(p) => match source_map.pat_syntax(*p) { + Ok(s) => s.map(|x| match x { + Either::Left(e) => e.into(), + Either::Right(e) => e.into(), + }), + Err(_) => continue, + }, + mir::MirSpan::Unknown => continue, + }; + acc.push(NeedMut { local, span }.into()); + } + } + (mir::MutabilityReason::Not, true) => { + if !infer.mutated_bindings_in_closure.contains(&binding_id) { + let should_ignore = matches!(body[binding_id].name.as_str(), Some(x) if x.starts_with("_")); + if !should_ignore { + acc.push(UnusedMut { local }.into()) + } + } } } - (mir::MutabilityReason::Not, true) => acc.push(UnusedMut { local }.into()), } } } @@ -1686,6 +1818,10 @@ impl Function { db.function_data(self.id).name.clone() } + pub fn ty(self, db: &dyn HirDatabase) -> Type { + Type::from_value_def(db, self.id) + } + /// Get this function's return type pub fn ret_type(self, db: &dyn HirDatabase) -> Type { let resolver = self.id.resolver(db.upcast()); @@ -1797,12 +1933,41 @@ impl Function { def_map.fn_as_proc_macro(self.id).map(|id| Macro { id: id.into() }) } - pub fn eval(self, db: &dyn HirDatabase) -> Result<(), MirEvalError> { - let body = db - .mir_body(self.id.into()) - .map_err(|e| MirEvalError::MirLowerError(self.id.into(), e))?; - interpret_mir(db, &body, false)?; - Ok(()) + pub fn eval( + self, + db: &dyn HirDatabase, + span_formatter: impl Fn(FileId, TextRange) -> String, + ) -> String { + let body = match db.monomorphized_mir_body( + self.id.into(), + Substitution::empty(Interner), + db.trait_environment(self.id.into()), + ) { + Ok(body) => body, + Err(e) => { + let mut r = String::new(); + _ = e.pretty_print(&mut r, db, &span_formatter); + return r; + } + }; + let (result, stdout, stderr) = interpret_mir(db, &body, false); + let mut text = match result { + Ok(_) => "pass".to_string(), + Err(e) => { + let mut r = String::new(); + _ = e.pretty_print(&mut r, db, &span_formatter); + r + } + }; + if !stdout.is_empty() { + text += "\n--------- stdout ---------\n"; + text += &stdout; + } + if !stderr.is_empty() { + text += "\n--------- stderr ---------\n"; + text += &stderr; + } + text } } @@ -1837,7 +2002,7 @@ impl Param { } pub fn name(&self, db: &dyn HirDatabase) -> Option { - db.function_data(self.func.id).params[self.idx].0.clone() + Some(self.as_local(db)?.name(db)) } pub fn as_local(&self, db: &dyn HirDatabase) -> Option { @@ -1878,7 +2043,7 @@ impl SelfParam { func_data .params .first() - .map(|(_, param)| match &**param { + .map(|param| match &**param { TypeRef::Reference(.., mutability) => match mutability { hir_def::type_ref::Mutability::Shared => Access::Shared, hir_def::type_ref::Mutability::Mut => Access::Exclusive, @@ -1939,24 +2104,12 @@ impl Const { } pub fn ty(self, db: &dyn HirDatabase) -> Type { - let data = db.const_data(self.id); - let resolver = self.id.resolver(db.upcast()); - let ctx = hir_ty::TyLoweringContext::new(db, &resolver); - let ty = ctx.lower_ty(&data.type_ref); - Type::new_with_resolver_inner(db, &resolver, ty) + Type::from_value_def(db, self.id) } pub fn render_eval(self, db: &dyn HirDatabase) -> Result { - let c = db.const_eval(self.id)?; + let c = db.const_eval(self.id.into(), Substitution::empty(Interner))?; let r = format!("{}", HexifiedConst(c).display(db)); - // We want to see things like `` and `` as they are probably bug in our - // implementation, but there is no need to show things like `` or `` to - // the user. - if r.contains("not-supported>") { - return Err(ConstEvalError::MirEvalError(MirEvalError::NotSupported( - "rendering complex constants".to_string(), - ))); - } return Ok(r); } } @@ -1990,11 +2143,7 @@ impl Static { } pub fn ty(self, db: &dyn HirDatabase) -> Type { - let data = db.static_data(self.id); - let resolver = self.id.resolver(db.upcast()); - let ctx = hir_ty::TyLoweringContext::new(db, &resolver); - let ty = ctx.lower_ty(&data.type_ref); - Type::new_with_resolver_inner(db, &resolver, ty) + Type::from_value_def(db, self.id) } } @@ -2545,8 +2694,12 @@ impl LocalSource { self.source.file_id.original_file(db.upcast()) } - pub fn name(&self) -> Option { - self.source.value.name() + pub fn file(&self) -> HirFileId { + self.source.file_id + } + + pub fn name(&self) -> Option> { + self.source.as_ref().map(|it| it.name()).transpose() } pub fn syntax(&self) -> &SyntaxNode { @@ -2689,9 +2842,7 @@ impl BuiltinAttr { } fn builtin(name: &str) -> Option { - hir_def::builtin_attr::INERT_ATTRIBUTES - .iter() - .position(|tool| tool.name == name) + hir_def::attr::builtin::find_builtin_attr_idx(name) .map(|idx| BuiltinAttr { krate: None, idx: idx as u32 }) } @@ -2699,14 +2850,14 @@ impl BuiltinAttr { // FIXME: Return a `Name` here match self.krate { Some(krate) => db.crate_def_map(krate).registered_attrs()[self.idx as usize].clone(), - None => SmolStr::new(hir_def::builtin_attr::INERT_ATTRIBUTES[self.idx as usize].name), + None => SmolStr::new(hir_def::attr::builtin::INERT_ATTRIBUTES[self.idx as usize].name), } } pub fn template(&self, _: &dyn HirDatabase) -> Option { match self.krate { Some(_) => None, - None => Some(hir_def::builtin_attr::INERT_ATTRIBUTES[self.idx as usize].template), + None => Some(hir_def::attr::builtin::INERT_ATTRIBUTES[self.idx as usize].template), } } } @@ -2729,7 +2880,7 @@ impl ToolModule { } fn builtin(name: &str) -> Option { - hir_def::builtin_attr::TOOL_MODULES + hir_def::attr::builtin::TOOL_MODULES .iter() .position(|&tool| tool == name) .map(|idx| ToolModule { krate: None, idx: idx as u32 }) @@ -2739,7 +2890,7 @@ impl ToolModule { // FIXME: Return a `Name` here match self.krate { Some(krate) => db.crate_def_map(krate).registered_tools()[self.idx as usize].clone(), - None => SmolStr::new(hir_def::builtin_attr::TOOL_MODULES[self.idx as usize]), + None => SmolStr::new(hir_def::attr::builtin::TOOL_MODULES[self.idx as usize]), } } } @@ -3117,6 +3268,103 @@ impl TraitRef { } } +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct Closure { + id: ClosureId, + subst: Substitution, +} + +impl From for ClosureId { + fn from(value: Closure) -> Self { + value.id + } +} + +impl Closure { + fn as_ty(self) -> Ty { + TyKind::Closure(self.id, self.subst).intern(Interner) + } + + pub fn display_with_id(&self, db: &dyn HirDatabase) -> String { + self.clone().as_ty().display(db).with_closure_style(ClosureStyle::ClosureWithId).to_string() + } + + pub fn display_with_impl(&self, db: &dyn HirDatabase) -> String { + self.clone().as_ty().display(db).with_closure_style(ClosureStyle::ImplFn).to_string() + } + + pub fn captured_items(&self, db: &dyn HirDatabase) -> Vec { + let owner = db.lookup_intern_closure((self.id).into()).0; + let infer = &db.infer(owner); + let info = infer.closure_info(&self.id); + info.0 + .iter() + .cloned() + .map(|capture| ClosureCapture { owner, closure: self.id, capture }) + .collect() + } + + pub fn capture_types(&self, db: &dyn HirDatabase) -> Vec { + let owner = db.lookup_intern_closure((self.id).into()).0; + let infer = &db.infer(owner); + let (captures, _) = infer.closure_info(&self.id); + captures + .iter() + .cloned() + .map(|capture| Type { + env: db.trait_environment_for_body(owner), + ty: capture.ty(&self.subst), + }) + .collect() + } + + pub fn fn_trait(&self, db: &dyn HirDatabase) -> FnTrait { + let owner = db.lookup_intern_closure((self.id).into()).0; + let infer = &db.infer(owner); + let info = infer.closure_info(&self.id); + info.1 + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ClosureCapture { + owner: DefWithBodyId, + closure: ClosureId, + capture: hir_ty::CapturedItem, +} + +impl ClosureCapture { + pub fn local(&self) -> Local { + Local { parent: self.owner, binding_id: self.capture.local() } + } + + pub fn kind(&self) -> CaptureKind { + match self.capture.kind() { + hir_ty::CaptureKind::ByRef( + hir_ty::mir::BorrowKind::Shallow | hir_ty::mir::BorrowKind::Shared, + ) => CaptureKind::SharedRef, + hir_ty::CaptureKind::ByRef(hir_ty::mir::BorrowKind::Unique) => { + CaptureKind::UniqueSharedRef + } + hir_ty::CaptureKind::ByRef(hir_ty::mir::BorrowKind::Mut { .. }) => { + CaptureKind::MutableRef + } + hir_ty::CaptureKind::ByValue => CaptureKind::Move, + } + } + + pub fn display_place(&self, db: &dyn HirDatabase) -> String { + self.capture.display_place(self.owner, db) + } +} + +pub enum CaptureKind { + SharedRef, + UniqueSharedRef, + MutableRef, + Move, +} + #[derive(Clone, PartialEq, Eq, Debug)] pub struct Type { env: Arc, @@ -3164,24 +3412,33 @@ impl Type { Type { env: environment, ty } } - fn from_def(db: &dyn HirDatabase, def: impl HasResolver + Into) -> Type { - let ty_def = def.into(); - let parent_subst = match ty_def { - TyDefId::TypeAliasId(id) => match id.lookup(db.upcast()).container { - ItemContainerId::TraitId(id) => { - let subst = TyBuilder::subst_for_def(db, id, None).fill_with_unknown().build(); - Some(subst) - } - ItemContainerId::ImplId(id) => { - let subst = TyBuilder::subst_for_def(db, id, None).fill_with_unknown().build(); - Some(subst) - } - _ => None, + fn from_def(db: &dyn HirDatabase, def: impl Into + HasResolver) -> Type { + let ty = db.ty(def.into()); + let substs = TyBuilder::unknown_subst( + db, + match def.into() { + TyDefId::AdtId(it) => GenericDefId::AdtId(it), + TyDefId::TypeAliasId(it) => GenericDefId::TypeAliasId(it), + TyDefId::BuiltinType(_) => return Type::new(db, def, ty.skip_binders().clone()), }, - _ => None, - }; - let ty = TyBuilder::def_ty(db, ty_def, parent_subst).fill_with_unknown().build(); - Type::new(db, def, ty) + ); + Type::new(db, def, ty.substitute(Interner, &substs)) + } + + fn from_value_def(db: &dyn HirDatabase, def: impl Into + HasResolver) -> Type { + let ty = db.value_ty(def.into()); + let substs = TyBuilder::unknown_subst( + db, + match def.into() { + ValueTyDefId::ConstId(it) => GenericDefId::ConstId(it), + ValueTyDefId::FunctionId(it) => GenericDefId::FunctionId(it), + ValueTyDefId::StructId(it) => GenericDefId::AdtId(AdtId::StructId(it)), + ValueTyDefId::UnionId(it) => GenericDefId::AdtId(AdtId::UnionId(it)), + ValueTyDefId::EnumVariantId(it) => GenericDefId::EnumVariantId(it), + ValueTyDefId::StaticId(_) => return Type::new(db, def, ty.skip_binders().clone()), + }, + ); + Type::new(db, def, ty.substitute(Interner, &substs)) } pub fn new_slice(ty: Type) -> Type { @@ -3331,7 +3588,7 @@ impl Type { binders: CanonicalVarKinds::empty(Interner), }; - db.trait_solve(self.env.krate, goal).is_some() + db.trait_solve(self.env.krate, self.env.block, goal).is_some() } pub fn normalize_trait_assoc_type( @@ -3378,7 +3635,12 @@ impl Type { } pub fn as_callable(&self, db: &dyn HirDatabase) -> Option { + let mut the_ty = &self.ty; let callee = match self.ty.kind(Interner) { + TyKind::Ref(_, _, ty) if ty.as_closure().is_some() => { + the_ty = ty; + Callee::Closure(ty.as_closure().unwrap()) + } TyKind::Closure(id, _) => Callee::Closure(*id), TyKind::Function(_) => Callee::FnPtr, TyKind::FnDef(..) => Callee::Def(self.ty.callable_def(db)?), @@ -3393,7 +3655,7 @@ impl Type { } }; - let sig = self.ty.callable_sig(db)?; + let sig = the_ty.callable_sig(db)?; Some(Callable { ty: self.clone(), sig, callee, is_bound_method: false }) } @@ -3401,6 +3663,13 @@ impl Type { matches!(self.ty.kind(Interner), TyKind::Closure { .. }) } + pub fn as_closure(&self) -> Option { + match self.ty.kind(Interner) { + TyKind::Closure(id, subst) => Some(Closure { id: *id, subst: subst.clone() }), + _ => None, + } + } + pub fn is_fn(&self) -> bool { matches!(self.ty.kind(Interner), TyKind::FnDef(..) | TyKind::Function { .. }) } @@ -3502,9 +3771,9 @@ impl Type { } } - pub fn as_array(&self, _db: &dyn HirDatabase) -> Option<(Type, usize)> { + pub fn as_array(&self, db: &dyn HirDatabase) -> Option<(Type, usize)> { if let TyKind::Array(ty, len) = &self.ty.kind(Interner) { - try_const_usize(len).map(|x| (self.derived(ty.clone()), x as usize)) + try_const_usize(db, len).map(|x| (self.derived(ty.clone()), x as usize)) } else { None } @@ -3517,8 +3786,7 @@ impl Type { fn autoderef_<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator + 'a { // There should be no inference vars in types passed here let canonical = hir_ty::replace_errors_with_variables(&self.ty); - let environment = self.env.clone(); - autoderef(db, environment, canonical).map(|canonical| canonical.value) + autoderef(db, self.env.clone(), canonical).map(|canonical| canonical.value) } // This would be nicer if it just returned an iterator, but that runs into @@ -3636,7 +3904,7 @@ impl Type { self.as_adt() .and_then(|a| a.lifetime(db).and_then(|lt| Some((<.name).to_smol_str()))) .into_iter() - // add the type and const paramaters + // add the type and const parameters .chain(self.type_and_const_arguments(db)) } @@ -3955,6 +4223,11 @@ impl Type { .map(|id| TypeOrConstParam { id }.split(db).either_into()) .collect() } + + pub fn layout(&self, db: &dyn HirDatabase) -> Result { + db.layout_of_ty(self.ty.clone(), self.env.krate) + .map(|layout| Layout(layout, db.target_data_layout(self.env.krate).unwrap())) + } } // FIXME: Document this @@ -4064,6 +4337,48 @@ fn closure_source(db: &dyn HirDatabase, closure: ClosureId) -> Option, Arc); + +impl Layout { + pub fn size(&self) -> u64 { + self.0.size.bytes() + } + + pub fn align(&self) -> u64 { + self.0.align.abi.bytes() + } + + pub fn niches(&self) -> Option { + Some(self.0.largest_niche?.available(&*self.1)) + } + + pub fn field_offset(&self, idx: usize) -> Option { + match self.0.fields { + layout::FieldsShape::Primitive => None, + layout::FieldsShape::Union(_) => Some(0), + layout::FieldsShape::Array { stride, count } => { + let i = u64::try_from(idx).ok()?; + (i < count).then_some((stride * i).bytes()) + } + layout::FieldsShape::Arbitrary { ref offsets, .. } => Some(offsets.get(idx)?.bytes()), + } + } + + pub fn enum_tag_size(&self) -> Option { + let tag_size = + if let layout::Variants::Multiple { tag, tag_encoding, .. } = &self.0.variants { + match tag_encoding { + TagEncoding::Direct => tag.size(&*self.1).bytes_usize(), + TagEncoding::Niche { .. } => 0, + } + } else { + return None; + }; + Some(tag_size) + } +} + #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum BindingMode { Move, @@ -4215,6 +4530,12 @@ impl HasCrate for Union { } } +impl HasCrate for Enum { + fn krate(&self, db: &dyn HirDatabase) -> Crate { + self.module(db).krate() + } +} + impl HasCrate for Field { fn krate(&self, db: &dyn HirDatabase) -> Crate { self.parent_def(db).module(db).krate() @@ -4286,3 +4607,90 @@ impl HasCrate for Module { Module::krate(*self) } } + +pub trait HasContainer { + fn container(&self, db: &dyn HirDatabase) -> ItemContainer; +} + +impl HasContainer for Module { + fn container(&self, db: &dyn HirDatabase) -> ItemContainer { + // FIXME: handle block expressions as modules (their parent is in a different DefMap) + let def_map = self.id.def_map(db.upcast()); + match def_map[self.id.local_id].parent { + Some(parent_id) => ItemContainer::Module(Module { id: def_map.module_id(parent_id) }), + None => ItemContainer::Crate(def_map.krate()), + } + } +} + +impl HasContainer for Function { + fn container(&self, db: &dyn HirDatabase) -> ItemContainer { + container_id_to_hir(self.id.lookup(db.upcast()).container) + } +} + +impl HasContainer for Struct { + fn container(&self, db: &dyn HirDatabase) -> ItemContainer { + ItemContainer::Module(Module { id: self.id.lookup(db.upcast()).container }) + } +} + +impl HasContainer for Union { + fn container(&self, db: &dyn HirDatabase) -> ItemContainer { + ItemContainer::Module(Module { id: self.id.lookup(db.upcast()).container }) + } +} + +impl HasContainer for Enum { + fn container(&self, db: &dyn HirDatabase) -> ItemContainer { + ItemContainer::Module(Module { id: self.id.lookup(db.upcast()).container }) + } +} + +impl HasContainer for TypeAlias { + fn container(&self, db: &dyn HirDatabase) -> ItemContainer { + container_id_to_hir(self.id.lookup(db.upcast()).container) + } +} + +impl HasContainer for Const { + fn container(&self, db: &dyn HirDatabase) -> ItemContainer { + container_id_to_hir(self.id.lookup(db.upcast()).container) + } +} + +impl HasContainer for Static { + fn container(&self, db: &dyn HirDatabase) -> ItemContainer { + container_id_to_hir(self.id.lookup(db.upcast()).container) + } +} + +impl HasContainer for Trait { + fn container(&self, db: &dyn HirDatabase) -> ItemContainer { + ItemContainer::Module(Module { id: self.id.lookup(db.upcast()).container }) + } +} + +impl HasContainer for TraitAlias { + fn container(&self, db: &dyn HirDatabase) -> ItemContainer { + ItemContainer::Module(Module { id: self.id.lookup(db.upcast()).container }) + } +} + +fn container_id_to_hir(c: ItemContainerId) -> ItemContainer { + match c { + ItemContainerId::ExternBlockId(_id) => ItemContainer::ExternBlock(), + ItemContainerId::ModuleId(id) => ItemContainer::Module(Module { id }), + ItemContainerId::ImplId(id) => ItemContainer::Impl(Impl { id }), + ItemContainerId::TraitId(id) => ItemContainer::Trait(Trait { id }), + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum ItemContainer { + Trait(Trait), + Impl(Impl), + Module(Module), + ExternBlock(), + Crate(CrateId), +} diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 407ba6f65844e..2d2b00b147e55 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -7,9 +7,10 @@ use std::{cell::RefCell, fmt, iter, mem, ops}; use base_db::{FileId, FileRange}; use either::Either; use hir_def::{ - body, - expr::Expr, + hir::Expr, + lower::LowerCtx, macro_id_to_def_id, + nameres::MacroSubNs, resolver::{self, HasResolver, Resolver, TypeNs}, type_ref::Mutability, AsMacroCall, DefWithBodyId, FieldId, FunctionId, MacroId, TraitId, VariantId, @@ -140,7 +141,7 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { self.imp.parse(file_id) } - pub fn parse_or_expand(&self, file_id: HirFileId) -> Option { + pub fn parse_or_expand(&self, file_id: HirFileId) -> SyntaxNode { self.imp.parse_or_expand(file_id) } @@ -350,6 +351,13 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { self.imp.type_of_pat(pat) } + /// It also includes the changes that binding mode makes in the type. For example in + /// `let ref x @ Some(_) = None` the result of `type_of_pat` is `Option` but the result + /// of this function is `&mut Option` + pub fn type_of_binding_in_pat(&self, pat: &ast::IdentPat) -> Option { + self.imp.type_of_binding_in_pat(pat) + } + pub fn type_of_self(&self, param: &ast::SelfParam) -> Option { self.imp.type_of_self(param) } @@ -518,23 +526,23 @@ impl<'db> SemanticsImpl<'db> { tree } - fn parse_or_expand(&self, file_id: HirFileId) -> Option { - let node = self.db.parse_or_expand(file_id)?; + fn parse_or_expand(&self, file_id: HirFileId) -> SyntaxNode { + let node = self.db.parse_or_expand(file_id); self.cache(node.clone(), file_id); - Some(node) + node } fn expand(&self, macro_call: &ast::MacroCall) -> Option { let sa = self.analyze_no_infer(macro_call.syntax())?; let file_id = sa.expand(self.db, InFile::new(sa.file_id, macro_call))?; - let node = self.parse_or_expand(file_id)?; + let node = self.parse_or_expand(file_id); Some(node) } fn expand_attr_macro(&self, item: &ast::Item) -> Option { let src = self.wrap_node_infile(item.clone()); let macro_call_id = self.with_ctx(|ctx| ctx.item_to_macro_call(src))?; - self.parse_or_expand(macro_call_id.as_file()) + Some(self.parse_or_expand(macro_call_id.as_file())) } fn expand_derive_as_pseudo_attr_macro(&self, attr: &ast::Attr) -> Option { @@ -543,7 +551,7 @@ impl<'db> SemanticsImpl<'db> { let call_id = self.with_ctx(|ctx| { ctx.attr_to_derive_macro_call(src.with_value(&adt), src).map(|(_, it, _)| it) })?; - self.parse_or_expand(call_id.as_file()) + Some(self.parse_or_expand(call_id.as_file())) } fn resolve_derive_macro(&self, attr: &ast::Attr) -> Option>> { @@ -566,7 +574,7 @@ impl<'db> SemanticsImpl<'db> { .into_iter() .flat_map(|call| { let file_id = call?.as_file(); - let node = self.db.parse_or_expand(file_id)?; + let node = self.db.parse_or_expand(file_id); self.cache(node.clone(), file_id); Some(node) }) @@ -609,7 +617,7 @@ impl<'db> SemanticsImpl<'db> { let krate = resolver.krate(); let macro_call_id = macro_call.as_call_id(self.db.upcast(), krate, |path| { resolver - .resolve_path_as_macro(self.db.upcast(), &path) + .resolve_path_as_macro(self.db.upcast(), &path, Some(MacroSubNs::Bang)) .map(|it| macro_id_to_def_id(self.db.upcast(), it)) })?; hir_expand::db::expand_speculative( @@ -990,7 +998,7 @@ impl<'db> SemanticsImpl<'db> { } fn diagnostics_display_range(&self, src: InFile) -> FileRange { - let root = self.parse_or_expand(src.file_id).unwrap(); + let root = self.parse_or_expand(src.file_id); let node = src.map(|it| it.to_node(&root)); node.as_ref().original_file_range(self.db.upcast()) } @@ -1065,7 +1073,7 @@ impl<'db> SemanticsImpl<'db> { fn resolve_type(&self, ty: &ast::Type) -> Option { let analyze = self.analyze(ty.syntax())?; - let ctx = body::LowerCtx::new(self.db.upcast(), analyze.file_id); + let ctx = LowerCtx::with_file_id(self.db.upcast(), analyze.file_id); let ty = hir_ty::TyLoweringContext::new(self.db, &analyze.resolver) .lower_ty(&crate::TypeRef::from_ast(&ctx, ty.clone())); Some(Type::new_with_resolver(self.db, &analyze.resolver, ty)) @@ -1074,12 +1082,9 @@ impl<'db> SemanticsImpl<'db> { 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 ctx = 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())? - { + match analyze.resolver.resolve_path_in_type_ns_fully(self.db.upcast(), &hir_path)? { TypeNs::TraitId(id) => Some(Trait { id }), _ => None, } @@ -1141,6 +1146,10 @@ impl<'db> SemanticsImpl<'db> { .map(|(ty, coerced)| TypeInfo { original: ty, adjusted: coerced }) } + fn type_of_binding_in_pat(&self, pat: &ast::IdentPat) -> Option { + self.analyze(pat.syntax())?.type_of_binding_in_pat(self.db, pat) + } + fn type_of_self(&self, param: &ast::SelfParam) -> Option { self.analyze(param.syntax())?.type_of_self(self.db, param) } @@ -1647,6 +1656,7 @@ impl<'a> SemanticsScope<'a> { VisibleTraits(resolver.traits_in_scope(self.db.upcast())) } + /// Calls the passed closure `f` on all names in scope. pub fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef)) { let scope = self.resolver.names_in_scope(self.db.upcast()); for (name, entries) in scope { @@ -1674,7 +1684,7 @@ impl<'a> SemanticsScope<'a> { /// Resolve a path as-if it was written at the given scope. This is /// necessary a heuristic, as it doesn't take hygiene into account. pub fn speculative_resolve(&self, path: &ast::Path) -> Option { - let ctx = body::LowerCtx::new(self.db.upcast(), self.file_id); + let ctx = LowerCtx::with_file_id(self.db.upcast(), self.file_id); let path = Path::from_src(path.clone(), &ctx)?; resolve_hir_path(self.db, &self.resolver, &path) } diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs index f6f8c9a250f06..c50ffa4f8b703 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs @@ -14,7 +14,7 @@ //! expression, an item definition. //! //! Knowing only the syntax gives us relatively little info. For example, -//! looking at the syntax of the function we can realise that it is a part of an +//! looking at the syntax of the function we can realize that it is a part of an //! `impl` block, but we won't be able to tell what trait function the current //! function overrides, and whether it does that correctly. For that, we need to //! go from [`ast::Fn`] to [`crate::Function`], and that's exactly what this @@ -88,9 +88,11 @@ use base_db::FileId; use hir_def::{ child_by_source::ChildBySource, - dyn_map::DynMap, - expr::{BindingId, LabelId}, - keys::{self, Key}, + dyn_map::{ + keys::{self, Key}, + DynMap, + }, + hir::{BindingId, LabelId}, AdtId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, FieldId, FunctionId, GenericDefId, GenericParamId, ImplId, LifetimeParamId, MacroId, ModuleId, StaticId, StructId, TraitAliasId, TraitId, TypeAliasId, TypeParamId, UnionId, VariantId, diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index c24d196e1b624..1374fa332c648 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -5,21 +5,19 @@ //! //! So, this modules should not be used during hir construction, it exists //! purely for "IDE needs". -use std::{ - iter::{self, once}, - sync::Arc, -}; +use std::iter::{self, once}; use either::Either; use hir_def::{ body::{ - self, scope::{ExprScopes, ScopeId}, Body, BodySourceMap, }, - expr::{ExprId, Pat, PatId}, + hir::{BindingId, ExprId, Pat, PatId}, lang_item::LangItem, + lower::LowerCtx, macro_id_to_def_id, + nameres::MacroSubNs, path::{ModPath, Path, PathKind}, resolver::{resolver_for_scope, Resolver, TypeNs, ValueNs}, type_ref::Mutability, @@ -39,7 +37,8 @@ use hir_ty::{ record_literal_missing_fields, record_pattern_missing_fields, unsafe_expressions, UnsafeExpr, }, - method_resolution::{self, lang_items_for_bin_op}, + lang_items::lang_items_for_bin_op, + method_resolution::{self}, Adjustment, InferenceResult, Interner, Substitution, Ty, TyExt, TyKind, TyLoweringContext, }; use itertools::Itertools; @@ -48,6 +47,7 @@ use syntax::{ ast::{self, AstNode}, SyntaxKind, SyntaxNode, TextRange, TextSize, }; +use triomphe::Arc; use crate::{ db::HirDatabase, semantics::PathResolution, Adt, AssocItem, BindingMode, BuiltinAttr, @@ -134,13 +134,22 @@ impl SourceAnalyzer { self.body_source_map()?.node_pat(src) } + fn binding_id_of_pat(&self, pat: &ast::IdentPat) -> Option { + let pat_id = self.pat_id(&pat.clone().into())?; + if let Pat::Bind { id, .. } = self.body()?.pats[pat_id] { + Some(id) + } else { + None + } + } + fn expand_expr( &self, db: &dyn HirDatabase, expr: InFile, ) -> Option> { let macro_file = self.body_source_map()?.node_macro_file(expr.as_ref())?; - let expanded = db.parse_or_expand(macro_file)?; + let expanded = db.parse_or_expand(macro_file); let res = if let Some(stmts) = ast::MacroStmts::cast(expanded.clone()) { match stmts.expr()? { ast::Expr::MacroExpr(mac) => { @@ -199,6 +208,18 @@ impl SourceAnalyzer { Some((mk_ty(ty), coerced.map(mk_ty))) } + pub(crate) fn type_of_binding_in_pat( + &self, + db: &dyn HirDatabase, + pat: &ast::IdentPat, + ) -> Option { + let binding_id = self.binding_id_of_pat(pat)?; + let infer = self.infer.as_ref()?; + let ty = infer[binding_id].clone(); + let mk_ty = |ty| Type::new_with_resolver(db, &self.resolver, ty); + Some(mk_ty(ty)) + } + pub(crate) fn type_of_self( &self, db: &dyn HirDatabase, @@ -215,9 +236,9 @@ impl SourceAnalyzer { _db: &dyn HirDatabase, pat: &ast::IdentPat, ) -> Option { - let pat_id = self.pat_id(&pat.clone().into())?; + let binding_id = self.binding_id_of_pat(pat)?; let infer = self.infer.as_ref()?; - infer.pat_binding_modes.get(&pat_id).map(|bm| match bm { + infer.binding_modes.get(binding_id).map(|bm| match bm { hir_ty::BindingMode::Move => BindingMode::Move, hir_ty::BindingMode::Ref(hir_ty::Mutability::Mut) => BindingMode::Ref(Mutability::Mut), hir_ty::BindingMode::Ref(hir_ty::Mutability::Not) => { @@ -420,7 +441,10 @@ impl SourceAnalyzer { None } else { // Shorthand syntax, resolve to the local - let path = ModPath::from_segments(PathKind::Plain, once(local_name.clone())); + let path = Path::from_known_path_with_no_generic(ModPath::from_segments( + PathKind::Plain, + once(local_name.clone()), + )); match self.resolver.resolve_path_in_value_ns_fully(db.upcast(), &path) { Some(ValueNs::LocalBinding(binding_id)) => { Some(Local { binding_id, parent: self.resolver.body_owner()? }) @@ -459,9 +483,11 @@ impl SourceAnalyzer { db: &dyn HirDatabase, macro_call: InFile<&ast::MacroCall>, ) -> Option { - let ctx = body::LowerCtx::new(db.upcast(), macro_call.file_id); + let ctx = LowerCtx::with_file_id(db.upcast(), macro_call.file_id); let path = macro_call.value.path().and_then(|ast| Path::from_src(ast, &ctx))?; - self.resolver.resolve_path_as_macro(db.upcast(), path.mod_path()).map(|it| it.into()) + self.resolver + .resolve_path_as_macro(db.upcast(), path.mod_path()?, Some(MacroSubNs::Bang)) + .map(|it| it.into()) } pub(crate) fn resolve_bind_pat_to_const( @@ -571,7 +597,7 @@ impl SourceAnalyzer { // This must be a normal source file rather than macro file. let hygiene = Hygiene::new(db.upcast(), self.file_id); - let ctx = body::LowerCtx::with_hygiene(db.upcast(), &hygiene); + let ctx = LowerCtx::with_hygiene(db.upcast(), &hygiene); let hir_path = Path::from_src(path.clone(), &ctx)?; // Case where path is a qualifier of a use tree, e.g. foo::bar::{Baz, Qux} where we are @@ -655,7 +681,7 @@ impl SourceAnalyzer { } } } - return match resolve_hir_path_as_macro(db, &self.resolver, &hir_path) { + return match resolve_hir_path_as_attr_macro(db, &self.resolver, &hir_path) { Some(m) => Some(PathResolution::Def(ModuleDef::Macro(m))), // this labels any path that starts with a tool module as the tool itself, this is technically wrong // but there is no benefit in differentiating these two cases for the time being @@ -733,7 +759,7 @@ impl SourceAnalyzer { let krate = self.resolver.krate(); let macro_call_id = macro_call.as_call_id(db.upcast(), krate, |path| { self.resolver - .resolve_path_as_macro(db.upcast(), &path) + .resolve_path_as_macro(db.upcast(), &path, Some(MacroSubNs::Bang)) .map(|it| macro_id_to_def_id(db.upcast(), it)) })?; Some(macro_call_id.as_file()).filter(|it| it.expansion_level(db.upcast()) < 64) @@ -801,15 +827,11 @@ impl SourceAnalyzer { func: FunctionId, substs: Substitution, ) -> FunctionId { - let krate = self.resolver.krate(); let owner = match self.resolver.body_owner() { Some(it) => it, None => return func, }; - let env = owner.as_generic_def_id().map_or_else( - || Arc::new(hir_ty::TraitEnvironment::empty(krate)), - |d| db.trait_environment(d), - ); + let env = db.trait_environment_for_body(owner); method_resolution::lookup_impl_method(db, env, func, substs).0 } @@ -819,15 +841,11 @@ impl SourceAnalyzer { const_id: ConstId, subs: Substitution, ) -> ConstId { - let krate = self.resolver.krate(); let owner = match self.resolver.body_owner() { Some(it) => it, None => return const_id, }; - let env = owner.as_generic_def_id().map_or_else( - || Arc::new(hir_ty::TraitEnvironment::empty(krate)), - |d| db.trait_environment(d), - ); + let env = db.trait_environment_for_body(owner); method_resolution::lookup_impl_const(db, env, const_id, subs).0 } @@ -941,12 +959,14 @@ pub(crate) fn resolve_hir_path( } #[inline] -pub(crate) fn resolve_hir_path_as_macro( +pub(crate) fn resolve_hir_path_as_attr_macro( db: &dyn HirDatabase, resolver: &Resolver, path: &Path, ) -> Option { - resolver.resolve_path_as_macro(db.upcast(), path.mod_path()).map(Into::into) + resolver + .resolve_path_as_macro(db.upcast(), path.mod_path()?, Some(MacroSubNs::Attr)) + .map(Into::into) } fn resolve_hir_path_( @@ -962,8 +982,7 @@ fn resolve_hir_path_( res.map(|ty_ns| (ty_ns, path.segments().first())) } None => { - let (ty, remaining_idx) = - resolver.resolve_path_in_type_ns(db.upcast(), path.mod_path())?; + let (ty, remaining_idx) = resolver.resolve_path_in_type_ns(db.upcast(), path)?; match remaining_idx { Some(remaining_idx) => { if remaining_idx + 1 == path.segments().len() { @@ -1019,7 +1038,7 @@ fn resolve_hir_path_( let body_owner = resolver.body_owner(); let values = || { - resolver.resolve_path_in_value_ns_fully(db.upcast(), path.mod_path()).and_then(|val| { + resolver.resolve_path_in_value_ns_fully(db.upcast(), path).and_then(|val| { let res = match val { ValueNs::LocalBinding(binding_id) => { let var = Local { parent: body_owner?, binding_id }; @@ -1039,14 +1058,14 @@ fn resolve_hir_path_( let items = || { resolver - .resolve_module_path_in_items(db.upcast(), path.mod_path()) + .resolve_module_path_in_items(db.upcast(), path.mod_path()?) .take_types() .map(|it| PathResolution::Def(it.into())) }; let macros = || { resolver - .resolve_path_as_macro(db.upcast(), path.mod_path()) + .resolve_path_as_macro(db.upcast(), path.mod_path()?, None) .map(|def| PathResolution::Def(ModuleDef::Macro(def.into()))) }; @@ -1074,7 +1093,7 @@ fn resolve_hir_path_qualifier( path: &Path, ) -> Option { resolver - .resolve_path_in_type_ns_fully(db.upcast(), path.mod_path()) + .resolve_path_in_type_ns_fully(db.upcast(), &path) .map(|ty| match ty { TypeNs::SelfType(it) => PathResolution::SelfType(it.into()), TypeNs::GenericParam(id) => PathResolution::TypeParam(id.into()), @@ -1089,7 +1108,7 @@ fn resolve_hir_path_qualifier( }) .or_else(|| { resolver - .resolve_module_path_in_items(db.upcast(), path.mod_path()) + .resolve_module_path_in_items(db.upcast(), path.mod_path()?) .take_types() .map(|it| PathResolution::Def(it.into())) }) diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs index a9afa1c6f4519..207e8206c92d5 100644 --- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs +++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs @@ -2,23 +2,25 @@ use base_db::FileRange; use hir_def::{ - item_tree::ItemTreeNode, src::HasSource, AdtId, AssocItemId, AssocItemLoc, DefWithBodyId, - HasModule, ImplId, ItemContainerId, Lookup, MacroId, ModuleDefId, ModuleId, TraitId, + src::HasSource, AdtId, AssocItemId, DefWithBodyId, HasModule, ImplId, Lookup, MacroId, + ModuleDefId, ModuleId, TraitId, }; use hir_expand::{HirFileId, InFile}; use hir_ty::db::HirDatabase; use syntax::{ast::HasName, AstNode, SmolStr, SyntaxNode, SyntaxNodePtr}; -use crate::{Module, Semantics}; +use crate::{Module, ModuleDef, Semantics}; /// The actual data that is stored in the index. It should be as compact as /// possible. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct FileSymbol { + // even though name can be derived from the def, we store it for efficiency pub name: SmolStr, + pub def: ModuleDef, pub loc: DeclarationLocation, - pub kind: FileSymbolKind, pub container_name: Option, + pub is_alias: bool, } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -32,18 +34,26 @@ pub struct DeclarationLocation { } impl DeclarationLocation { - pub fn syntax(&self, sema: &Semantics<'_, DB>) -> Option { - let root = sema.parse_or_expand(self.hir_file_id)?; - Some(self.ptr.to_node(&root)) + pub fn syntax(&self, sema: &Semantics<'_, DB>) -> SyntaxNode { + let root = sema.parse_or_expand(self.hir_file_id); + self.ptr.to_node(&root) } - pub fn original_range(&self, db: &dyn HirDatabase) -> Option { - let node = resolve_node(db, self.hir_file_id, &self.ptr)?; - Some(node.as_ref().original_file_range(db.upcast())) + pub fn original_range(&self, db: &dyn HirDatabase) -> FileRange { + if let Some(file_id) = self.hir_file_id.file_id() { + // fast path to prevent parsing + return FileRange { file_id, range: self.ptr.text_range() }; + } + let node = resolve_node(db, self.hir_file_id, &self.ptr); + node.as_ref().original_file_range(db.upcast()) } pub fn original_name_range(&self, db: &dyn HirDatabase) -> Option { - let node = resolve_node(db, self.hir_file_id, &self.name_ptr)?; + if let Some(file_id) = self.hir_file_id.file_id() { + // fast path to prevent parsing + return Some(FileRange { file_id, range: self.name_ptr.text_range() }); + } + let node = resolve_node(db, self.hir_file_id, &self.name_ptr); node.as_ref().original_file_range_opt(db.upcast()) } } @@ -52,38 +62,10 @@ fn resolve_node( db: &dyn HirDatabase, file_id: HirFileId, ptr: &SyntaxNodePtr, -) -> Option> { - let root = db.parse_or_expand(file_id)?; +) -> InFile { + let root = db.parse_or_expand(file_id); let node = ptr.to_node(&root); - Some(InFile::new(file_id, node)) -} - -#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)] -pub enum FileSymbolKind { - Const, - Enum, - Function, - Macro, - Module, - Static, - Struct, - Trait, - TraitAlias, - TypeAlias, - Union, -} - -impl FileSymbolKind { - pub fn is_type(self: FileSymbolKind) -> bool { - matches!( - self, - FileSymbolKind::Struct - | FileSymbolKind::Enum - | FileSymbolKind::Trait - | FileSymbolKind::TypeAlias - | FileSymbolKind::Union - ) - } + InFile::new(file_id, node) } /// Represents an outstanding module that the symbol collector must collect symbols from. @@ -102,21 +84,33 @@ pub struct SymbolCollector<'a> { /// Given a [`ModuleId`] and a [`HirDatabase`], use the DefMap for the module's crate to collect /// all symbols that should be indexed for the given module. impl<'a> SymbolCollector<'a> { - pub fn collect(db: &dyn HirDatabase, module: Module) -> Vec { - let mut symbol_collector = SymbolCollector { + pub fn new(db: &'a dyn HirDatabase) -> Self { + SymbolCollector { db, symbols: Default::default(), + work: Default::default(), current_container_name: None, - // The initial work is the root module we're collecting, additional work will - // be populated as we traverse the module's definitions. - work: vec![SymbolCollectorWork { module_id: module.into(), parent: None }], - }; + } + } + + pub fn collect(&mut self, module: Module) { + // The initial work is the root module we're collecting, additional work will + // be populated as we traverse the module's definitions. + self.work.push(SymbolCollectorWork { module_id: module.into(), parent: None }); - while let Some(work) = symbol_collector.work.pop() { - symbol_collector.do_work(work); + while let Some(work) = self.work.pop() { + self.do_work(work); } + } + + pub fn finish(self) -> Vec { + self.symbols + } - symbol_collector.symbols + pub fn collect_module(db: &dyn HirDatabase, module: Module) -> Vec { + let mut symbol_collector = SymbolCollector::new(db); + symbol_collector.collect(module); + symbol_collector.finish() } fn do_work(&mut self, work: SymbolCollectorWork) { @@ -134,36 +128,34 @@ impl<'a> SymbolCollector<'a> { match module_def_id { ModuleDefId::ModuleId(id) => self.push_module(id), ModuleDefId::FunctionId(id) => { - self.push_decl_assoc(id, FileSymbolKind::Function); + self.push_decl(id); self.collect_from_body(id); } - ModuleDefId::AdtId(AdtId::StructId(id)) => { - self.push_decl(id, FileSymbolKind::Struct) - } - ModuleDefId::AdtId(AdtId::EnumId(id)) => self.push_decl(id, FileSymbolKind::Enum), - ModuleDefId::AdtId(AdtId::UnionId(id)) => self.push_decl(id, FileSymbolKind::Union), + ModuleDefId::AdtId(AdtId::StructId(id)) => self.push_decl(id), + ModuleDefId::AdtId(AdtId::EnumId(id)) => self.push_decl(id), + ModuleDefId::AdtId(AdtId::UnionId(id)) => self.push_decl(id), ModuleDefId::ConstId(id) => { - self.push_decl_assoc(id, FileSymbolKind::Const); + self.push_decl(id); self.collect_from_body(id); } ModuleDefId::StaticId(id) => { - self.push_decl_assoc(id, FileSymbolKind::Static); + self.push_decl(id); self.collect_from_body(id); } ModuleDefId::TraitId(id) => { - self.push_decl(id, FileSymbolKind::Trait); + self.push_decl(id); self.collect_from_trait(id); } ModuleDefId::TraitAliasId(id) => { - self.push_decl(id, FileSymbolKind::TraitAlias); + self.push_decl(id); } ModuleDefId::TypeAliasId(id) => { - self.push_decl_assoc(id, FileSymbolKind::TypeAlias); + self.push_decl(id); } ModuleDefId::MacroId(id) => match id { - MacroId::Macro2Id(id) => self.push_decl(id, FileSymbolKind::Macro), - MacroId::MacroRulesId(id) => self.push_decl(id, FileSymbolKind::Macro), - MacroId::ProcMacroId(id) => self.push_decl(id, FileSymbolKind::Macro), + MacroId::Macro2Id(id) => self.push_decl(id), + MacroId::MacroRulesId(id) => self.push_decl(id), + MacroId::ProcMacroId(id) => self.push_decl(id), }, // Don't index these. ModuleDefId::BuiltinType(_) => {} @@ -183,9 +175,9 @@ impl<'a> SymbolCollector<'a> { for &id in id { if id.module(self.db.upcast()) == module_id { match id { - MacroId::Macro2Id(id) => self.push_decl(id, FileSymbolKind::Macro), - MacroId::MacroRulesId(id) => self.push_decl(id, FileSymbolKind::Macro), - MacroId::ProcMacroId(id) => self.push_decl(id, FileSymbolKind::Macro), + MacroId::Macro2Id(id) => self.push_decl(id), + MacroId::MacroRulesId(id) => self.push_decl(id), + MacroId::ProcMacroId(id) => self.push_decl(id), } } } @@ -233,124 +225,94 @@ impl<'a> SymbolCollector<'a> { } } - fn current_container_name(&self) -> Option { - self.current_container_name.clone() - } - fn def_with_body_id_name(&self, body_id: DefWithBodyId) -> Option { match body_id { - DefWithBodyId::FunctionId(id) => Some( - id.lookup(self.db.upcast()).source(self.db.upcast()).value.name()?.text().into(), - ), - DefWithBodyId::StaticId(id) => Some( - id.lookup(self.db.upcast()).source(self.db.upcast()).value.name()?.text().into(), - ), - DefWithBodyId::ConstId(id) => Some( - id.lookup(self.db.upcast()).source(self.db.upcast()).value.name()?.text().into(), - ), - DefWithBodyId::VariantId(id) => Some({ - let db = self.db.upcast(); - id.parent.lookup(db).source(db).value.name()?.text().into() - }), + DefWithBodyId::FunctionId(id) => Some(self.db.function_data(id).name.to_smol_str()), + DefWithBodyId::StaticId(id) => Some(self.db.static_data(id).name.to_smol_str()), + DefWithBodyId::ConstId(id) => Some(self.db.const_data(id).name.as_ref()?.to_smol_str()), + DefWithBodyId::VariantId(id) => { + Some(self.db.enum_data(id.parent).variants[id.local_id].name.to_smol_str()) + } } } fn push_assoc_item(&mut self, assoc_item_id: AssocItemId) { match assoc_item_id { - AssocItemId::FunctionId(id) => self.push_decl_assoc(id, FileSymbolKind::Function), - AssocItemId::ConstId(id) => self.push_decl_assoc(id, FileSymbolKind::Const), - AssocItemId::TypeAliasId(id) => self.push_decl_assoc(id, FileSymbolKind::TypeAlias), + AssocItemId::FunctionId(id) => self.push_decl(id), + AssocItemId::ConstId(id) => self.push_decl(id), + AssocItemId::TypeAliasId(id) => self.push_decl(id), } } - fn push_decl_assoc(&mut self, id: L, kind: FileSymbolKind) + fn push_decl(&mut self, id: L) where - L: Lookup>, - T: ItemTreeNode, - ::Source: HasName, + L: Lookup + Into, + ::Data: HasSource, + <::Data as HasSource>::Value: HasName, { - fn container_name(db: &dyn HirDatabase, container: ItemContainerId) -> Option { - match container { - ItemContainerId::ModuleId(module_id) => { - let module = Module::from(module_id); - module.name(db).and_then(|name| name.as_text()) - } - ItemContainerId::TraitId(trait_id) => { - let trait_data = db.trait_data(trait_id); - trait_data.name.as_text() - } - ItemContainerId::ImplId(_) | ItemContainerId::ExternBlockId(_) => None, + let loc = id.lookup(self.db.upcast()); + let source = loc.source(self.db.upcast()); + let Some(name_node) = source.value.name() else { return }; + let def = ModuleDef::from(id.into()); + let dec_loc = DeclarationLocation { + hir_file_id: source.file_id, + ptr: SyntaxNodePtr::new(source.value.syntax()), + name_ptr: SyntaxNodePtr::new(name_node.syntax()), + }; + + if let Some(attrs) = def.attrs(self.db) { + for alias in attrs.doc_aliases() { + self.symbols.push(FileSymbol { + name: alias, + def, + loc: dec_loc.clone(), + container_name: self.current_container_name.clone(), + is_alias: true, + }); } } - self.push_file_symbol(|s| { - let loc = id.lookup(s.db.upcast()); - let source = loc.source(s.db.upcast()); - let name_node = source.value.name()?; - let container_name = - container_name(s.db, loc.container).or_else(|| s.current_container_name()); - - Some(FileSymbol { - name: name_node.text().into(), - kind, - container_name, - loc: DeclarationLocation { - hir_file_id: source.file_id, - ptr: SyntaxNodePtr::new(source.value.syntax()), - name_ptr: SyntaxNodePtr::new(name_node.syntax()), - }, - }) - }) - } - - fn push_decl(&mut self, id: L, kind: FileSymbolKind) - where - L: Lookup, - ::Data: HasSource, - <::Data as HasSource>::Value: HasName, - { - self.push_file_symbol(|s| { - let loc = id.lookup(s.db.upcast()); - let source = loc.source(s.db.upcast()); - let name_node = source.value.name()?; - - Some(FileSymbol { - name: name_node.text().into(), - kind, - container_name: s.current_container_name(), - loc: DeclarationLocation { - hir_file_id: source.file_id, - ptr: SyntaxNodePtr::new(source.value.syntax()), - name_ptr: SyntaxNodePtr::new(name_node.syntax()), - }, - }) - }) + self.symbols.push(FileSymbol { + name: name_node.text().into(), + def, + container_name: self.current_container_name.clone(), + loc: dec_loc, + is_alias: false, + }); } fn push_module(&mut self, module_id: ModuleId) { - self.push_file_symbol(|s| { - let def_map = module_id.def_map(s.db.upcast()); - let module_data = &def_map[module_id.local_id]; - let declaration = module_data.origin.declaration()?; - let module = declaration.to_node(s.db.upcast()); - let name_node = module.name()?; - - Some(FileSymbol { - name: name_node.text().into(), - kind: FileSymbolKind::Module, - container_name: s.current_container_name(), - loc: DeclarationLocation { - hir_file_id: declaration.file_id, - ptr: SyntaxNodePtr::new(module.syntax()), - name_ptr: SyntaxNodePtr::new(name_node.syntax()), - }, - }) - }) - } + let def_map = module_id.def_map(self.db.upcast()); + let module_data = &def_map[module_id.local_id]; + let Some(declaration) = module_data.origin.declaration() else { return }; + let module = declaration.to_node(self.db.upcast()); + let Some(name_node) = module.name() else { return }; + let dec_loc = DeclarationLocation { + hir_file_id: declaration.file_id, + ptr: SyntaxNodePtr::new(module.syntax()), + name_ptr: SyntaxNodePtr::new(name_node.syntax()), + }; - fn push_file_symbol(&mut self, f: impl FnOnce(&Self) -> Option) { - if let Some(file_symbol) = f(self) { - self.symbols.push(file_symbol); + let def = ModuleDef::Module(module_id.into()); + + if let Some(attrs) = def.attrs(self.db) { + for alias in attrs.doc_aliases() { + self.symbols.push(FileSymbol { + name: alias, + def, + loc: dec_loc.clone(), + container_name: self.current_container_name.clone(), + is_alias: true, + }); + } } + + self.symbols.push(FileSymbol { + name: name_node.text().into(), + def: ModuleDef::Module(module_id.into()), + container_name: self.current_container_name.clone(), + loc: dec_loc, + is_alias: false, + }); } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_type.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_type.rs index 785ae3d09c6e3..8bc285614e039 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_type.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_type.rs @@ -69,7 +69,7 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> O return None; } - let inferred_type = ty.display_source_code(ctx.db(), module.into()).ok()?; + let inferred_type = ty.display_source_code(ctx.db(), module.into(), false).ok()?; acc.add( AssistId("add_explicit_type", AssistKind::RefactorRewrite), format!("Insert explicit type `{inferred_type}`"), diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs index 4e11b31deb02b..6340feda452e1 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs @@ -184,7 +184,8 @@ fn try_gen_trait_body( trait_ref: hir::TraitRef, impl_def: &ast::Impl, ) -> Option<()> { - let trait_path = make::ext::ident_path(&trait_ref.trait_().name(ctx.db()).to_string()); + let trait_path = + make::ext::ident_path(&trait_ref.trait_().name(ctx.db()).display(ctx.db()).to_string()); let hir_ty = ctx.sema.resolve_type(&impl_def.self_ty()?)?; let adt = hir_ty.as_adt()?.source(ctx.db())?; gen_trait_fn_body(func, &trait_path, &adt.value, Some(trait_ref)) @@ -252,7 +253,7 @@ impl Foo for S { } #[test] - fn test_copied_overriden_members() { + fn test_copied_overridden_members() { check_assist( add_missing_impl_members, r#" @@ -1346,8 +1347,8 @@ struct SomeStruct { } impl PartialEq for SomeStruct { $0fn ne(&self, other: &Self) -> bool { - !self.eq(other) - } + !self.eq(other) + } } "#, ); @@ -1511,11 +1512,175 @@ fn main() { struct S; impl Tr for S { fn method() { - ${0:todo!()} - } + ${0:todo!()} + } } } "#, ); } + + #[test] + fn test_add_missing_preserves_indentation() { + // in different modules + check_assist( + add_missing_impl_members, + r#" +mod m { + pub trait Foo { + const CONST_MULTILINE: ( + i32, + i32 + ); + + fn foo(&self); + } +} +struct S; +impl m::Foo for S { $0 }"#, + r#" +mod m { + pub trait Foo { + const CONST_MULTILINE: ( + i32, + i32 + ); + + fn foo(&self); + } +} +struct S; +impl m::Foo for S { + $0const CONST_MULTILINE: ( + i32, + i32 + ); + + fn foo(&self) { + todo!() + } +}"#, + ); + // in the same module + check_assist( + add_missing_impl_members, + r#" +mod m { + trait Foo { + type Output; + + const CONST: usize = 42; + const CONST_2: i32; + const CONST_MULTILINE: ( + i32, + i32 + ); + + fn foo(&self); + fn bar(&self); + fn baz(&self); + } + + struct S; + + impl Foo for S { + fn bar(&self) {} +$0 + } +}"#, + r#" +mod m { + trait Foo { + type Output; + + const CONST: usize = 42; + const CONST_2: i32; + const CONST_MULTILINE: ( + i32, + i32 + ); + + fn foo(&self); + fn bar(&self); + fn baz(&self); + } + + struct S; + + impl Foo for S { + fn bar(&self) {} + + $0type Output; + + const CONST_2: i32; + + const CONST_MULTILINE: ( + i32, + i32 + ); + + fn foo(&self) { + todo!() + } + + fn baz(&self) { + todo!() + } + + } +}"#, + ); + } + + #[test] + fn test_add_default_preserves_indentation() { + check_assist( + add_missing_default_members, + r#" +mod m { + pub trait Foo { + type Output; + + const CONST: usize = 42; + const CONST_2: i32; + const CONST_MULTILINE: = ( + i32, + i32, + ) = (3, 14); + + fn valid(some: u32) -> bool { false } + fn foo(some: u32) -> bool; + } +} +struct S; +impl m::Foo for S { $0 }"#, + r#" +mod m { + pub trait Foo { + type Output; + + const CONST: usize = 42; + const CONST_2: i32; + const CONST_MULTILINE: = ( + i32, + i32, + ) = (3, 14); + + fn valid(some: u32) -> bool { false } + fn foo(some: u32) -> bool; + } +} +struct S; +impl m::Foo for S { + $0const CONST: usize = 42; + + const CONST_MULTILINE: = ( + i32, + i32, + ) = (3, 14); + + fn valid(some: u32) -> bool { false } +}"#, + ) + } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs index 5d81e8cfeacbc..7384390f28b57 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs @@ -148,7 +148,7 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) return None; } - let variants_of_enums = vec![variants.clone(); len]; + let variants_of_enums = vec![variants; len]; let missing_pats = variants_of_enums .into_iter() diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_return_type.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_return_type.rs index 879c478acf882..e5f0201bd527e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_return_type.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_return_type.rs @@ -22,7 +22,7 @@ pub(crate) fn add_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt if ty.is_unit() { return None; } - let ty = ty.display_source_code(ctx.db(), module.into()).ok()?; + let ty = ty.display_source_code(ctx.db(), module.into(), true).ok()?; acc.add( AssistId("add_return_type", AssistKind::RefactorRewrite), @@ -34,8 +34,8 @@ pub(crate) fn add_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt |builder| { 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} ")) + let preceding_whitespace = if needs_whitespace { " " } else { "" }; + builder.insert(insert_pos, format!("{preceding_whitespace}-> {ty} ")) } InsertOrReplace::Replace(text_range) => { builder.replace(text_range, format!("-> {ty}")) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs index 698ad78cce6ff..7acf2ea0a0de6 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs @@ -132,7 +132,7 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option< acc.add_group( &group_label, AssistId("auto_import", AssistKind::QuickFix), - format!("Import `{import_path}`"), + format!("Import `{}`", import_path.display(ctx.db())), range, |builder| { let scope = match scope.clone() { @@ -203,7 +203,7 @@ fn relevance_score( // get the distance between the imported path and the current module // (prefer items that are more local) Some((item_module, current_module)) => { - score -= module_distance_hueristic(db, current_module, &item_module) as i32; + score -= module_distance_heuristic(db, current_module, &item_module) as i32; } // could not find relevant modules, so just use the length of the path as an estimate @@ -214,7 +214,7 @@ fn relevance_score( } /// A heuristic that gives a higher score to modules that are more separated. -fn module_distance_hueristic(db: &dyn HirDatabase, current: &Module, item: &Module) -> usize { +fn module_distance_heuristic(db: &dyn HirDatabase, current: &Module, item: &Module) -> usize { // get the path starting from the item to the respective crate roots let mut current_path = current.path_to_root(db); let mut item_path = item.path_to_root(db); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs index db96ad33047fb..1af52c592183d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs @@ -160,7 +160,7 @@ pub(crate) fn convert_bool_then_to_if(acc: &mut Assists, ctx: &AssistContext<'_> }; // Verify this is `bool::then` that is being called. let func = ctx.sema.resolve_method_call(&mcall)?; - if func.name(ctx.sema.db).to_string() != "then" { + if func.name(ctx.sema.db).display(ctx.db()).to_string() != "then" { return None; } let assoc = func.as_assoc_item(ctx.sema.db)?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs index 9e1d9a702a15f..db96c8fe40aa5 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs @@ -119,7 +119,7 @@ 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.display(ctx.db())); } else if let ast::Expr::RangeExpr(..) = iterable { // range expressions need to be parenthesized for the syntax to be correct format_to!(buf, "({iterable})"); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs index c82a3b5303259..5f7056b9c1c43 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs @@ -5,6 +5,88 @@ use syntax::T; use crate::{AssistContext, AssistId, AssistKind, Assists}; +// Assist: convert_let_else_to_match +// +// Converts let-else statement to let statement and match expression. +// +// ``` +// fn main() { +// let Ok(mut x) = f() else$0 { return }; +// } +// ``` +// -> +// ``` +// fn main() { +// let mut x = match f() { +// Ok(x) => x, +// _ => return, +// }; +// } +// ``` +pub(crate) fn convert_let_else_to_match(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + // should focus on else token to trigger + let let_stmt = ctx + .find_token_syntax_at_offset(T![else]) + .and_then(|it| it.parent()?.parent()) + .or_else(|| ctx.find_token_syntax_at_offset(T![let])?.parent())?; + let let_stmt = LetStmt::cast(let_stmt)?; + let let_else_block = let_stmt.let_else()?.block_expr()?; + let let_init = let_stmt.initializer()?; + if let_stmt.ty().is_some() { + // don't support let with type annotation + return None; + } + let pat = let_stmt.pat()?; + let mut binders = Vec::new(); + binders_in_pat(&mut binders, &pat, &ctx.sema)?; + + let target = let_stmt.syntax().text_range(); + acc.add( + AssistId("convert_let_else_to_match", AssistKind::RefactorRewrite), + "Convert let-else to let and match", + target, + |edit| { + let indent_level = let_stmt.indent_level().0 as usize; + let indent = " ".repeat(indent_level); + let indent1 = " ".repeat(indent_level + 1); + + let binders_str = binders_to_str(&binders, false); + let binders_str_mut = binders_to_str(&binders, true); + + let init_expr = let_init.syntax().text(); + let mut pat_no_mut = pat.syntax().text().to_string(); + // remove the mut from the pattern + for (b, ismut) in binders.iter() { + if *ismut { + pat_no_mut = pat_no_mut.replace(&format!("mut {b}"), &b.to_string()); + } + } + + 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},"), + _ => let_else_block.syntax().text().to_string(), + }; + let replace = if binders.is_empty() { + format!( + "match {init_expr} {{ +{indent1}{pat_no_mut} => {binders_str} +{indent1}_ => {branch2} +{indent}}}" + ) + } else { + format!( + "let {binders_str_mut} = match {init_expr} {{ +{indent1}{pat_no_mut} => {binders_str}, +{indent1}_ => {branch2} +{indent}}};" + ) + }; + edit.replace(target, replace); + }, + ) +} + /// Gets a list of binders in a pattern, and whether they are mut. fn binders_in_pat( acc: &mut Vec<(Name, bool)>, @@ -97,85 +179,6 @@ fn binders_to_str(binders: &[(Name, bool)], addmut: bool) -> String { } } -// Assist: convert_let_else_to_match -// -// Converts let-else statement to let statement and match expression. -// -// ``` -// fn main() { -// let Ok(mut x) = f() else$0 { return }; -// } -// ``` -// -> -// ``` -// fn main() { -// let mut x = match f() { -// Ok(x) => x, -// _ => return, -// }; -// } -// ``` -pub(crate) fn convert_let_else_to_match(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { - // should focus on else token to trigger - let else_token = ctx.find_token_syntax_at_offset(T![else])?; - let let_stmt = LetStmt::cast(else_token.parent()?.parent()?)?; - let let_else_block = let_stmt.let_else()?.block_expr()?; - let let_init = let_stmt.initializer()?; - if let_stmt.ty().is_some() { - // don't support let with type annotation - return None; - } - let pat = let_stmt.pat()?; - let mut binders = Vec::new(); - binders_in_pat(&mut binders, &pat, &ctx.sema)?; - - let target = let_stmt.syntax().text_range(); - acc.add( - AssistId("convert_let_else_to_match", AssistKind::RefactorRewrite), - "Convert let-else to let and match", - target, - |edit| { - let indent_level = let_stmt.indent_level().0 as usize; - let indent = " ".repeat(indent_level); - let indent1 = " ".repeat(indent_level + 1); - - let binders_str = binders_to_str(&binders, false); - let binders_str_mut = binders_to_str(&binders, true); - - let init_expr = let_init.syntax().text(); - let mut pat_no_mut = pat.syntax().text().to_string(); - // remove the mut from the pattern - for (b, ismut) in binders.iter() { - if *ismut { - pat_no_mut = pat_no_mut.replace(&format!("mut {b}"), &b.to_string()); - } - } - - 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},"), - _ => let_else_block.syntax().text().to_string(), - }; - let replace = if binders.is_empty() { - format!( - "match {init_expr} {{ -{indent1}{pat_no_mut} => {binders_str} -{indent1}_ => {branch2} -{indent}}}" - ) - } else { - format!( - "let {binders_str_mut} = match {init_expr} {{ -{indent1}{pat_no_mut} => {binders_str}, -{indent1}_ => {branch2} -{indent}}};" - ) - }; - edit.replace(target, replace); - }, - ) -} - #[cfg(test)] mod tests { use super::*; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs index 7f2c01772baef..fc6236a17558c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs @@ -16,7 +16,7 @@ use crate::{ // ``` // # //- minicore: option // fn foo(opt: Option<()>) { -// let val = $0match opt { +// let val$0 = match opt { // Some(it) => it, // None => return, // }; @@ -30,7 +30,10 @@ use crate::{ // ``` 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 = let_stmt.pat()?; + let pat = let_stmt.pat()?; + if ctx.offset() > pat.syntax().text_range().end() { + return None; + } let Some(ast::Expr::MatchExpr(initializer)) = let_stmt.initializer() else { return None }; let initializer_expr = initializer.expr()?; @@ -56,7 +59,7 @@ pub(crate) fn convert_match_to_let_else(acc: &mut Assists, ctx: &AssistContext<' let_stmt.syntax().text_range(), |builder| { let extracting_arm_pat = - rename_variable(&extracting_arm_pat, &extracted_variable_positions, binding); + rename_variable(&extracting_arm_pat, &extracted_variable_positions, pat); builder.replace( let_stmt.syntax().text_range(), format!("let {extracting_arm_pat} = {initializer_expr} else {diverging_arm_expr};"), @@ -161,7 +164,7 @@ mod tests { r#" //- minicore: option fn foo(opt: Option<()>) { - let val = $0match opt { + let val$0 = match opt { Some(it) => it, None => (), }; @@ -211,7 +214,7 @@ fn foo(opt: Option) -> Result { r#" //- minicore: option fn foo(opt: Option) { - let val = $0match opt { + let val$0 = match opt { Some(it) => it + 1, None => return, }; @@ -224,7 +227,7 @@ fn foo(opt: Option) { r#" //- minicore: option fn foo(opt: Option<()>) { - let val = $0match opt { + let val$0 = match opt { Some(it) => { let _ = 1 + 1; it @@ -244,7 +247,7 @@ fn foo(opt: Option<()>) { r#" //- minicore: option fn foo(opt: Option<()>) { - let val = $0match opt { + let val$0 = match opt { Some(it) if 2 > 1 => it, None => return, }; @@ -260,7 +263,7 @@ fn foo(opt: Option<()>) { r#" //- minicore: option fn foo(opt: Option<()>) { - let val = $0match opt { + let val$0 = match opt { Some(it) => it, None => return, }; @@ -281,7 +284,7 @@ fn foo(opt: Option<()>) { r#" //- minicore: option fn foo(opt: Option<()>) { - let ref mut val = $0match opt { + let ref mut val$0 = match opt { Some(it) => it, None => return, }; @@ -302,7 +305,7 @@ fn foo(opt: Option<()>) { r#" //- minicore: option, result fn foo(opt: Option>) { - let val = $0match opt { + let val$0 = match opt { Some(Ok(it)) => it, _ => return, }; @@ -324,7 +327,7 @@ fn foo(opt: Option>) { //- minicore: option fn foo(opt: Option<()>) { loop { - let val = $0match opt { + let val$0 = match opt { Some(it) => it, None => break, }; @@ -346,7 +349,7 @@ fn foo(opt: Option<()>) { //- minicore: option fn foo(opt: Option<()>) { loop { - let val = $0match opt { + let val$0 = match opt { Some(it) => it, None => continue, }; @@ -370,7 +373,7 @@ fn panic() -> ! {} fn foo(opt: Option<()>) { loop { - let val = $0match opt { + let val$0 = match opt { Some(it) => it, None => panic(), }; @@ -401,7 +404,7 @@ struct Point { } fn foo(opt: Option) { - let val = $0match opt { + let val$0 = match opt { Some(Point { x: 0, y }) => y, _ => return, }; @@ -427,7 +430,7 @@ fn foo(opt: Option) { r#" //- minicore: option fn foo(opt: Option) -> Option { - let val = $0match opt { + let val$0 = match opt { it @ Some(42) => it, _ => return None, }; @@ -450,7 +453,7 @@ fn foo(opt: Option) -> Option { r#" //- minicore: option fn f() { - let (x, y) = $0match Some((0, 1)) { + let (x, y)$0 = match Some((0, 1)) { Some(it) => it, None => return, }; @@ -471,7 +474,7 @@ fn f() { r#" //- minicore: option fn f() { - let x = $0match Some(()) { + let x$0 = match Some(()) { Some(it) => it, None => {//comment println!("nope"); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs index 9dc1da2461a34..fe1cb6fce3630 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs @@ -1,9 +1,9 @@ use either::Either; -use ide_db::defs::Definition; +use ide_db::{defs::Definition, search::FileReference}; use itertools::Itertools; use syntax::{ ast::{self, AstNode, HasGenericParams, HasVisibility}, - match_ast, SyntaxKind, SyntaxNode, + match_ast, SyntaxKind, }; use crate::{assist_context::SourceChangeBuilder, AssistContext, AssistId, AssistKind, Assists}; @@ -52,7 +52,11 @@ pub(crate) fn convert_named_struct_to_tuple_struct( acc: &mut Assists, ctx: &AssistContext<'_>, ) -> Option<()> { - let strukt = ctx.find_node_at_offset::>()?; + // XXX: We don't currently provide this assist for struct definitions inside macros, but if we + // are to lift this limitation, don't forget to make `edit_struct_def()` consider macro files + // too. + let name = ctx.find_node_at_offset::()?; + let strukt = name.syntax().parent().and_then(>::cast)?; let field_list = strukt.as_ref().either(|s| s.field_list(), |v| v.field_list())?; let record_fields = match field_list { ast::FieldList::RecordFieldList(it) => it, @@ -62,12 +66,11 @@ pub(crate) fn convert_named_struct_to_tuple_struct( Either::Left(s) => Either::Left(ctx.sema.to_def(s)?), Either::Right(v) => Either::Right(ctx.sema.to_def(v)?), }; - let target = strukt.as_ref().either(|s| s.syntax(), |v| v.syntax()).text_range(); acc.add( AssistId("convert_named_struct_to_tuple_struct", AssistKind::RefactorRewrite), "Convert to tuple struct", - target, + strukt.syntax().text_range(), |edit| { edit_field_references(ctx, edit, record_fields.fields()); edit_struct_references(ctx, edit, strukt_def); @@ -82,6 +85,8 @@ fn edit_struct_def( strukt: &Either, record_fields: ast::RecordFieldList, ) { + // Note that we don't need to consider macro files in this function because this this is + // currently not triggered for struct definitions inside macro calls. let tuple_fields = record_fields .fields() .filter_map(|f| Some(ast::make::tuple_field(f.visibility(), f.ty()?))); @@ -137,50 +142,72 @@ fn edit_struct_references( }; let usages = strukt_def.usages(&ctx.sema).include_self_refs().all(); - let edit_node = |edit: &mut SourceChangeBuilder, node: SyntaxNode| -> Option<()> { - match_ast! { - match node { - ast::RecordPat(record_struct_pat) => { - edit.replace( - record_struct_pat.syntax().text_range(), - ast::make::tuple_struct_pat( - record_struct_pat.path()?, - record_struct_pat - .record_pat_field_list()? - .fields() - .filter_map(|pat| pat.pat()) - ) - .to_string() - ); - }, - ast::RecordExpr(record_expr) => { - let path = record_expr.path()?; - let args = record_expr - .record_expr_field_list()? - .fields() - .filter_map(|f| f.expr()) - .join(", "); - - edit.replace(record_expr.syntax().text_range(), format!("{path}({args})")); - }, - _ => return None, - } - } - Some(()) - }; - for (file_id, refs) in usages { edit.edit_file(file_id); for r in refs { - for node in r.name.syntax().ancestors() { - if edit_node(edit, node).is_some() { - break; - } - } + process_struct_name_reference(ctx, r, edit); } } } +fn process_struct_name_reference( + ctx: &AssistContext<'_>, + r: FileReference, + edit: &mut SourceChangeBuilder, +) -> Option<()> { + // First check if it's the last semgnet of a path that directly belongs to a record + // expression/pattern. + let name_ref = r.name.as_name_ref()?; + let path_segment = name_ref.syntax().parent().and_then(ast::PathSegment::cast)?; + // A `PathSegment` always belongs to a `Path`, so there's at least one `Path` at this point. + let full_path = + path_segment.syntax().parent()?.ancestors().map_while(ast::Path::cast).last().unwrap(); + + if full_path.segment().unwrap().name_ref()? != *name_ref { + // `name_ref` isn't the last segment of the path, so `full_path` doesn't point to the + // struct we want to edit. + return None; + } + + let parent = full_path.syntax().parent()?; + match_ast! { + match parent { + ast::RecordPat(record_struct_pat) => { + // When we failed to get the original range for the whole struct expression node, + // we can't provide any reasonable edit. Leave it untouched. + let file_range = ctx.sema.original_range_opt(record_struct_pat.syntax())?; + edit.replace( + file_range.range, + ast::make::tuple_struct_pat( + record_struct_pat.path()?, + record_struct_pat + .record_pat_field_list()? + .fields() + .filter_map(|pat| pat.pat()) + ) + .to_string() + ); + }, + ast::RecordExpr(record_expr) => { + // When we failed to get the original range for the whole struct pattern node, + // we can't provide any reasonable edit. Leave it untouched. + let file_range = ctx.sema.original_range_opt(record_expr.syntax())?; + let path = record_expr.path()?; + let args = record_expr + .record_expr_field_list()? + .fields() + .filter_map(|f| f.expr()) + .join(", "); + + edit.replace(file_range.range, format!("{path}({args})")); + }, + _ => {} + } + } + + Some(()) +} + fn edit_field_references( ctx: &AssistContext<'_>, edit: &mut SourceChangeBuilder, @@ -199,7 +226,7 @@ fn edit_field_references( if let Some(name_ref) = r.name.as_name_ref() { // Only edit the field reference if it's part of a `.field` access if name_ref.syntax().parent().and_then(ast::FieldExpr::cast).is_some() { - edit.replace(name_ref.syntax().text_range(), index.to_string()); + edit.replace(r.range, index.to_string()); } } } @@ -813,6 +840,141 @@ use crate::{A::Variant, Inner}; fn f() { let a = Variant(Inner); } +"#, + ); + } + + #[test] + fn field_access_inside_macro_call() { + check_assist( + convert_named_struct_to_tuple_struct, + r#" +struct $0Struct { + inner: i32, +} + +macro_rules! id { + ($e:expr) => { $e } +} + +fn test(c: Struct) { + id!(c.inner); +} +"#, + r#" +struct Struct(i32); + +macro_rules! id { + ($e:expr) => { $e } +} + +fn test(c: Struct) { + id!(c.0); +} +"#, + ) + } + + #[test] + fn struct_usage_inside_macro_call() { + check_assist( + convert_named_struct_to_tuple_struct, + r#" +macro_rules! id { + ($($t:tt)*) => { $($t)* } +} + +struct $0Struct { + inner: i32, +} + +fn test() { + id! { + let s = Struct { + inner: 42, + }; + let Struct { inner: value } = s; + let Struct { inner } = s; + } +} +"#, + r#" +macro_rules! id { + ($($t:tt)*) => { $($t)* } +} + +struct Struct(i32); + +fn test() { + id! { + let s = Struct(42); + let Struct(value) = s; + let Struct(inner) = s; + } +} +"#, + ); + } + + #[test] + fn struct_name_ref_may_not_be_part_of_struct_expr_or_struct_pat() { + check_assist( + convert_named_struct_to_tuple_struct, + r#" +struct $0Struct { + inner: i32, +} +struct Outer { + value: T, +} +fn foo() -> T { loop {} } + +fn test() { + Outer { + value: foo::(); + } +} + +trait HasAssoc { + type Assoc; + fn test(); +} +impl HasAssoc for Struct { + type Assoc = Outer; + fn test() { + let a = Self::Assoc { + value: 42, + }; + let Self::Assoc { value } = a; + } +} +"#, + r#" +struct Struct(i32); +struct Outer { + value: T, +} +fn foo() -> T { loop {} } + +fn test() { + Outer { + value: foo::(); + } +} + +trait HasAssoc { + type Assoc; + fn test(); +} +impl HasAssoc for Struct { + type Assoc = Outer; + fn test() { + let a = Self::Assoc { + value: 42, + }; + let Self::Assoc { value } = a; + } +} "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_nested_function_to_closure.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_nested_function_to_closure.rs new file mode 100644 index 0000000000000..399f87c8f509d --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_nested_function_to_closure.rs @@ -0,0 +1,209 @@ +use ide_db::assists::{AssistId, AssistKind}; +use syntax::ast::{self, HasGenericParams, HasName}; +use syntax::{AstNode, SyntaxKind}; + +use crate::assist_context::{AssistContext, Assists}; + +// Assist: convert_nested_function_to_closure +// +// Converts a function that is defined within the body of another function into a closure. +// +// ``` +// fn main() { +// fn fo$0o(label: &str, number: u64) { +// println!("{}: {}", label, number); +// } +// +// foo("Bar", 100); +// } +// ``` +// -> +// ``` +// fn main() { +// let foo = |label: &str, number: u64| { +// println!("{}: {}", label, number); +// }; +// +// foo("Bar", 100); +// } +// ``` +pub(crate) fn convert_nested_function_to_closure( + acc: &mut Assists, + ctx: &AssistContext<'_>, +) -> Option<()> { + let name = ctx.find_node_at_offset::()?; + let function = name.syntax().parent().and_then(ast::Fn::cast)?; + + if !is_nested_function(&function) || is_generic(&function) || has_modifiers(&function) { + return None; + } + + let target = function.syntax().text_range(); + let body = function.body()?; + let name = function.name()?; + let param_list = function.param_list()?; + + acc.add( + AssistId("convert_nested_function_to_closure", AssistKind::RefactorRewrite), + "Convert nested function to closure", + target, + |edit| { + let params = ¶m_list.syntax().text().to_string(); + let params = params.strip_prefix("(").unwrap_or(params); + let params = params.strip_suffix(")").unwrap_or(params); + + let mut body = body.to_string(); + if !has_semicolon(&function) { + body.push(';'); + } + edit.replace(target, format!("let {name} = |{params}| {body}")); + }, + ) +} + +/// Returns whether the given function is nested within the body of another function. +fn is_nested_function(function: &ast::Fn) -> bool { + function.syntax().ancestors().skip(1).find_map(ast::Item::cast).map_or(false, |it| { + matches!(it, ast::Item::Fn(_) | ast::Item::Static(_) | ast::Item::Const(_)) + }) +} + +/// Returns whether the given nested function has generic parameters. +fn is_generic(function: &ast::Fn) -> bool { + function.generic_param_list().is_some() +} + +/// Returns whether the given nested function has any modifiers: +/// +/// - `async`, +/// - `const` or +/// - `unsafe` +fn has_modifiers(function: &ast::Fn) -> bool { + function.async_token().is_some() + || function.const_token().is_some() + || function.unsafe_token().is_some() +} + +/// Returns whether the given nested function has a trailing semicolon. +fn has_semicolon(function: &ast::Fn) -> bool { + function + .syntax() + .next_sibling_or_token() + .map(|t| t.kind() == SyntaxKind::SEMICOLON) + .unwrap_or(false) +} + +#[cfg(test)] +mod tests { + use crate::tests::{check_assist, check_assist_not_applicable}; + + use super::convert_nested_function_to_closure; + + #[test] + fn convert_nested_function_to_closure_works() { + check_assist( + convert_nested_function_to_closure, + r#" +fn main() { + fn $0foo(a: u64, b: u64) -> u64 { + 2 * (a + b) + } + + _ = foo(3, 4); +} + "#, + r#" +fn main() { + let foo = |a: u64, b: u64| { + 2 * (a + b) + }; + + _ = foo(3, 4); +} + "#, + ); + } + + #[test] + fn convert_nested_function_to_closure_works_with_existing_semicolon() { + check_assist( + convert_nested_function_to_closure, + r#" +fn main() { + fn foo$0(a: u64, b: u64) -> u64 { + 2 * (a + b) + }; + + _ = foo(3, 4); +} + "#, + r#" +fn main() { + let foo = |a: u64, b: u64| { + 2 * (a + b) + }; + + _ = foo(3, 4); +} + "#, + ); + } + + #[test] + fn convert_nested_function_to_closure_is_not_suggested_on_top_level_function() { + check_assist_not_applicable( + convert_nested_function_to_closure, + r#" +fn ma$0in() {} + "#, + ); + } + + #[test] + fn convert_nested_function_to_closure_is_not_suggested_when_cursor_off_name() { + check_assist_not_applicable( + convert_nested_function_to_closure, + r#" +fn main() { + fn foo(a: u64, $0b: u64) -> u64 { + 2 * (a + b) + } + + _ = foo(3, 4); +} + "#, + ); + } + + #[test] + fn convert_nested_function_to_closure_is_not_suggested_if_function_has_generic_params() { + check_assist_not_applicable( + convert_nested_function_to_closure, + r#" +fn main() { + fn fo$0o>(s: S) -> String { + s.into() + } + + _ = foo("hello"); +} + "#, + ); + } + + #[test] + fn convert_nested_function_to_closure_is_not_suggested_if_function_has_modifier() { + check_assist_not_applicable( + convert_nested_function_to_closure, + r#" +fn main() { + const fn fo$0o(s: String) -> String { + s + } + + _ = foo("hello"); +} + "#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs index b97be34c5f7e4..dcb96ab8af44f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs @@ -504,7 +504,7 @@ fn main() { } #[test] - fn ignore_statements_aftert_if() { + fn ignore_statements_after_if() { check_assist_not_applicable( convert_to_guarded_return, r#" diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs index 772e032fb2934..017853a4a2023 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs @@ -50,7 +50,8 @@ pub(crate) fn convert_tuple_struct_to_named_struct( acc: &mut Assists, ctx: &AssistContext<'_>, ) -> Option<()> { - let strukt = ctx.find_node_at_offset::>()?; + let name = ctx.find_node_at_offset::()?; + let strukt = name.syntax().parent().and_then(>::cast)?; let field_list = strukt.as_ref().either(|s| s.field_list(), |v| v.field_list())?; let tuple_fields = match field_list { ast::FieldList::TupleFieldList(it) => it, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs index 31c2ce7c1b54a..ea71d165e6aa6 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs @@ -91,7 +91,7 @@ fn collect_data(ident_pat: IdentPat, ctx: &AssistContext<'_>) -> Option) -> let names_to_import = find_names_to_import(ctx, refs_in_target, imported_defs); let expanded = make::use_tree_list(names_to_import.iter().map(|n| { - let path = make::ext::ident_path(&n.to_string()); + let path = make::ext::ident_path(&n.display(ctx.db()).to_string()); make::use_tree(path, None, None, false) })) .clone_for_update(); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs index 0b90c9ba34f5f..2a67909e63719 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs @@ -70,6 +70,11 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op } let node = ctx.covering_element(); + if matches!(node.kind(), T!['{'] | T!['}'] | T!['('] | T![')'] | T!['['] | T![']']) { + cov_mark::hit!(extract_function_in_braces_is_not_applicable); + return None; + } + if node.kind() == COMMENT { cov_mark::hit!(extract_function_in_comment_is_not_applicable); return None; @@ -178,7 +183,9 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op fn make_function_name(semantics_scope: &hir::SemanticsScope<'_>) -> ast::NameRef { let mut names_in_scope = vec![]; - semantics_scope.process_all_names(&mut |name, _| names_in_scope.push(name.to_string())); + semantics_scope.process_all_names(&mut |name, _| { + names_in_scope.push(name.display(semantics_scope.db.upcast()).to_string()) + }); let default_name = "fun_name"; @@ -369,7 +376,7 @@ struct OutlivedLocal { /// Container of local variable usages /// -/// Semanticall same as `UsageSearchResult`, but provides more convenient interface +/// Semantically same as `UsageSearchResult`, but provides more convenient interface struct LocalUsages(ide_db::search::UsageSearchResult); impl LocalUsages { @@ -438,7 +445,7 @@ impl Param { } fn to_param(&self, ctx: &AssistContext<'_>, module: hir::Module) -> ast::Param { - let var = self.var.name(ctx.db()).to_string(); + let var = self.var.name(ctx.db()).display(ctx.db()).to_string(); let var_name = make::name(&var); let pat = match self.kind() { ParamKind::MutValue => make::ident_pat(false, true, var_name), @@ -468,7 +475,8 @@ impl TryKind { let name = adt.name(ctx.db()); // FIXME: use lang items to determine if it is std type or user defined // E.g. if user happens to define type named `Option`, we would have false positive - match name.to_string().as_str() { + let name = &name.display(ctx.db()).to_string(); + match name.as_str() { "Option" => Some(TryKind::Option), "Result" => Some(TryKind::Result { ty }), _ => None, @@ -702,7 +710,7 @@ impl FunctionBody { ) -> (FxIndexSet, Option) { let mut self_param = None; let mut res = FxIndexSet::default(); - let mut cb = |name_ref: Option<_>| { + let mut add_name_if_local = |name_ref: Option<_>| { let local_ref = match name_ref.and_then(|name_ref| NameRefClass::classify(sema, &name_ref)) { Some( @@ -726,21 +734,24 @@ impl FunctionBody { }; self.walk_expr(&mut |expr| match expr { ast::Expr::PathExpr(path_expr) => { - cb(path_expr.path().and_then(|it| it.as_single_name_ref())) + add_name_if_local(path_expr.path().and_then(|it| it.as_single_name_ref())) } ast::Expr::ClosureExpr(closure_expr) => { if let Some(body) = closure_expr.body() { - body.syntax().descendants().map(ast::NameRef::cast).for_each(|it| cb(it)); + body.syntax() + .descendants() + .map(ast::NameRef::cast) + .for_each(&mut add_name_if_local); } } ast::Expr::MacroExpr(expr) => { if let Some(tt) = expr.macro_call().and_then(|call| call.token_tree()) { tt.syntax() - .children_with_tokens() - .flat_map(SyntaxElement::into_token) - .filter(|it| it.kind() == SyntaxKind::IDENT) + .descendants_with_tokens() + .filter_map(SyntaxElement::into_token) + .filter(|it| matches!(it.kind(), SyntaxKind::IDENT | T![self])) .flat_map(|t| sema.descend_into_macros(t)) - .for_each(|t| cb(t.parent().and_then(ast::NameRef::cast))); + .for_each(|t| add_name_if_local(t.parent().and_then(ast::NameRef::cast))); } } _ => (), @@ -1286,8 +1297,8 @@ fn find_non_trait_impl(trait_impl: &SyntaxNode) -> 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 + let siblings = trait_impl.parent()?.children(); + siblings .filter_map(ast::Impl::cast) .find(|s| impl_type_name(s) == impl_type && !is_trait_impl(s)) } @@ -1333,14 +1344,15 @@ fn make_call(ctx: &AssistContext<'_>, fun: &Function, indent: IndentLevel) -> St [var] => { let modifier = mut_modifier(var); let name = var.local.name(ctx.db()); - format_to!(buf, "let {modifier}{name} = ") + format_to!(buf, "let {modifier}{} = ", name.display(ctx.db())) } vars => { buf.push_str("let ("); let bindings = vars.iter().format_with(", ", |local, f| { let modifier = mut_modifier(local); let name = local.local.name(ctx.db()); - f(&format_args!("{modifier}{name}")) + f(&format_args!("{modifier}{}", name.display(ctx.db())))?; + Ok(()) }); format_to!(buf, "{bindings}"); buf.push_str(") = "); @@ -1479,7 +1491,7 @@ impl FlowHandler { } fn path_expr_from_local(ctx: &AssistContext<'_>, var: Local) -> ast::Expr { - let name = var.name(ctx.db()).to_string(); + let name = var.name(ctx.db()).display(ctx.db()).to_string(); make::expr_path(make::ext::ident_path(&name)) } @@ -1879,7 +1891,7 @@ fn with_tail_expr(block: ast::BlockExpr, tail_expr: ast::Expr) -> ast::BlockExpr } fn format_type(ty: &hir::Type, ctx: &AssistContext<'_>, module: hir::Module) -> String { - ty.display_source_code(ctx.db(), module.into()).ok().unwrap_or_else(|| "_".to_string()) + ty.display_source_code(ctx.db(), module.into(), true).ok().unwrap_or_else(|| "_".to_string()) } fn make_ty(ty: &hir::Type, ctx: &AssistContext<'_>, module: hir::Module) -> ast::Type { @@ -4339,6 +4351,82 @@ fn $0fun_name(n: i32) -> i32 { ); } + #[test] + fn param_usage_in_macro_with_nested_tt() { + check_assist( + extract_function, + r#" +macro_rules! m { + ($val:expr) => { $val }; +} + +fn foo() { + let n = 1; + let t = 1; + $0let k = n * m!((n) + { t });$0 + let m = k + 1; +} +"#, + r#" +macro_rules! m { + ($val:expr) => { $val }; +} + +fn foo() { + let n = 1; + let t = 1; + let k = fun_name(n, t); + let m = k + 1; +} + +fn $0fun_name(n: i32, t: i32) -> i32 { + let k = n * m!((n) + { t }); + k +} +"#, + ) + } + + #[test] + fn param_usage_in_macro_with_nested_tt_2() { + check_assist( + extract_function, + r#" +macro_rules! m { + ($val:expr) => { $val }; +} + +struct S(i32); +impl S { + fn foo(&self) { + let n = 1; + $0let k = n * m!((n) + { self.0 });$0 + let m = k + 1; + } +} +"#, + r#" +macro_rules! m { + ($val:expr) => { $val }; +} + +struct S(i32); +impl S { + fn foo(&self) { + let n = 1; + let k = self.fun_name(n); + let m = k + 1; + } + + fn $0fun_name(&self, n: i32) -> i32 { + let k = n * m!((n) + { self.0 }); + k + } +} +"#, + ) + } + #[test] fn extract_with_await() { check_assist( @@ -4640,6 +4728,7 @@ const fn $0fun_name() { check_assist( extract_function, r#" +//- minicore: iterator fn foo() { let mut x = 5; for _ in 0..10 { @@ -4663,6 +4752,7 @@ fn $0fun_name(x: &mut i32) { check_assist( extract_function, r#" +//- minicore: iterator fn foo() { for _ in 0..10 { let mut x = 5; @@ -4686,6 +4776,7 @@ fn $0fun_name(mut x: i32) { check_assist( extract_function, r#" +//- minicore: iterator fn foo() { loop { let mut x = 5; @@ -5387,6 +5478,30 @@ fn $0fun_name(i: T) { ); } + #[test] + fn dont_emit_type_with_hidden_lifetime_parameter() { + // FIXME: We should emit a `` generic argument for the generated function + check_assist( + extract_function, + r#" +struct Struct<'a, T>(&'a T); +fn func(i: Struct<'_, T>) { + $0foo(i);$0 +} +"#, + r#" +struct Struct<'a, T>(&'a T); +fn func(i: Struct<'_, T>) { + fun_name(i); +} + +fn $0fun_name(i: Struct<'_, T>) { + foo(i); +} +"#, + ); + } + #[test] fn preserve_generics_from_body() { check_assist( @@ -5800,4 +5915,40 @@ fn $0fun_name() -> ControlFlow<()> { "#, ); } + + #[test] + fn in_left_curly_is_not_applicable() { + cov_mark::check!(extract_function_in_braces_is_not_applicable); + check_assist_not_applicable(extract_function, r"fn foo() { $0}$0"); + } + + #[test] + fn in_right_curly_is_not_applicable() { + cov_mark::check!(extract_function_in_braces_is_not_applicable); + check_assist_not_applicable(extract_function, r"fn foo() $0{$0 }"); + } + + #[test] + fn in_left_paren_is_not_applicable() { + cov_mark::check!(extract_function_in_braces_is_not_applicable); + check_assist_not_applicable(extract_function, r"fn foo( $0)$0 { }"); + } + + #[test] + fn in_right_paren_is_not_applicable() { + cov_mark::check!(extract_function_in_braces_is_not_applicable); + check_assist_not_applicable(extract_function, r"fn foo $0($0 ) { }"); + } + + #[test] + fn in_left_brack_is_not_applicable() { + cov_mark::check!(extract_function_in_braces_is_not_applicable); + check_assist_not_applicable(extract_function, r"fn foo(arr: &mut [i32$0]$0) {}"); + } + + #[test] + fn in_right_brack_is_not_applicable() { + cov_mark::check!(extract_function_in_braces_is_not_applicable); + check_assist_not_applicable(extract_function, r"fn foo(arr: &mut $0[$0i32]) {}"); + } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs index 0fa7bd558bbfa..de37f5f130fca 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs @@ -357,7 +357,7 @@ impl Module { fn change_visibility(&mut self, record_fields: Vec) { let (mut replacements, record_field_parents, impls) = - get_replacements_for_visibilty_change(&mut self.body_items, false); + get_replacements_for_visibility_change(&mut self.body_items, false); let mut impl_items: Vec = impls .into_iter() @@ -366,7 +366,7 @@ impl Module { .collect(); let (mut impl_item_replacements, _, _) = - get_replacements_for_visibilty_change(&mut impl_items, true); + get_replacements_for_visibility_change(&mut impl_items, true); replacements.append(&mut impl_item_replacements); @@ -824,7 +824,7 @@ fn does_source_exists_outside_sel_in_same_mod( source_exists_outside_sel_in_same_mod } -fn get_replacements_for_visibilty_change( +fn get_replacements_for_visibility_change( items: &mut [ast::Item], is_clone_for_updated: bool, ) -> ( @@ -904,7 +904,7 @@ fn compare_hir_and_ast_module( ) -> Option<()> { let hir_mod_name = hir_module.name(ctx.db())?; let ast_mod_name = ast_module.name()?; - if hir_mod_name.to_string() != ast_mod_name.to_string() { + if hir_mod_name.display(ctx.db()).to_string() != ast_mod_name.to_string() { return None; } @@ -1236,7 +1236,8 @@ mod modname { } #[test] - fn test_extract_module_for_correspoding_adt_of_impl_present_in_same_mod_but_not_in_selection() { + fn test_extract_module_for_corresponding_adt_of_impl_present_in_same_mod_but_not_in_selection() + { check_assist( extract_module, r" diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs index 49debafe1a0ad..e4f64ccc754e0 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs @@ -158,7 +158,7 @@ fn existing_definition(db: &RootDatabase, variant_name: &ast::Name, variant: &Va ), _ => false, }) - .any(|(name, _)| name.to_string() == variant_name.to_string()) + .any(|(name, _)| name.display(db).to_string() == variant_name.to_string()) } fn extract_generic_params( @@ -1006,7 +1006,7 @@ enum X<'a, 'b, 'x> { } #[test] - fn test_extract_struct_with_liftime_type_const() { + fn test_extract_struct_with_lifetime_type_const() { check_assist( extract_struct_from_enum_variant, r#" diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs index 16356141288d5..014c23197face 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs @@ -1,3 +1,4 @@ +use hir::TypeInfo; use stdx::format_to; use syntax::{ ast::{self, AstNode}, @@ -46,21 +47,24 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op .take_while(|it| ctx.selection_trimmed().contains_range(it.text_range())) .find_map(valid_target_expr)?; - if let Some(ty_info) = ctx.sema.type_of_expr(&to_extract) { - if ty_info.adjusted().is_unit() { - return None; - } + let ty = ctx.sema.type_of_expr(&to_extract).map(TypeInfo::adjusted); + if matches!(&ty, Some(ty_info) if ty_info.is_unit()) { + return None; } - let reference_modifier = match get_receiver_type(ctx, &to_extract) { + let parent = to_extract.syntax().parent().and_then(ast::Expr::cast); + let needs_adjust = parent + .as_ref() + .map_or(false, |it| matches!(it, ast::Expr::FieldExpr(_) | ast::Expr::MethodCallExpr(_))); + + let reference_modifier = match ty.filter(|_| needs_adjust) { Some(receiver_type) if receiver_type.is_mutable_reference() => "&mut ", Some(receiver_type) if receiver_type.is_reference() => "&", _ => "", }; - let parent_ref_expr = to_extract.syntax().parent().and_then(ast::RefExpr::cast); - let var_modifier = match parent_ref_expr { - Some(expr) if expr.mut_token().is_some() => "mut ", + let var_modifier = match parent { + Some(ast::Expr::RefExpr(expr)) if expr.mut_token().is_some() => "mut ", _ => "", }; @@ -164,22 +168,6 @@ fn valid_target_expr(node: SyntaxNode) -> Option { } } -fn get_receiver_type(ctx: &AssistContext<'_>, expression: &ast::Expr) -> Option { - let receiver = get_receiver(expression.clone())?; - Some(ctx.sema.type_of_expr(&receiver)?.original()) -} - -/// In the expression `a.b.c.x()`, find `a` -fn get_receiver(expression: ast::Expr) -> Option { - match expression { - ast::Expr::FieldExpr(field) if field.expr().is_some() => { - let nested_expression = &field.expr()?; - get_receiver(nested_expression.to_owned()) - } - _ => Some(expression), - } -} - #[derive(Debug)] enum Anchor { Before(SyntaxNode), @@ -944,6 +932,11 @@ struct S { vec: Vec } +struct Vec; +impl Vec { + fn push(&mut self, _:usize) {} +} + fn foo(s: &mut S) { $0s.vec$0.push(0); }"#, @@ -952,6 +945,11 @@ struct S { vec: Vec } +struct Vec; +impl Vec { + fn push(&mut self, _:usize) {} +} + fn foo(s: &mut S) { let $0vec = &mut s.vec; vec.push(0); @@ -973,6 +971,10 @@ struct X { struct S { vec: Vec } +struct Vec; +impl Vec { + fn push(&mut self, _:usize) {} +} fn foo(f: &mut Y) { $0f.field.field.vec$0.push(0); @@ -987,6 +989,10 @@ struct X { struct S { vec: Vec } +struct Vec; +impl Vec { + fn push(&mut self, _:usize) {} +} fn foo(f: &mut Y) { let $0vec = &mut f.field.field.vec; @@ -1123,7 +1129,7 @@ struct S { } fn foo(s: S) { - let $0x = s.sub; + let $0x = &s.sub; x.do_thing(); }"#, ); @@ -1189,7 +1195,7 @@ impl X { fn foo() { let local = &mut S::new(); - let $0x = &mut local.sub; + let $0x = &local.sub; x.do_thing(); }"#, ); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs index 4c61678eab4f2..d6c59a9c82950 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs @@ -62,7 +62,9 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext<'_>) let assist_label = match target_name { None => format!("Change visibility to {missing_visibility}"), - Some(name) => format!("Change visibility of {name} to {missing_visibility}"), + Some(name) => { + format!("Change visibility of {} to {missing_visibility}", name.display(ctx.db())) + } }; acc.add(AssistId("fix_visibility", AssistKind::QuickFix), assist_label, target, |builder| { @@ -117,8 +119,11 @@ fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext<'_> let target_file = in_file_source.file_id.original_file(ctx.db()); let target_name = record_field_def.name(ctx.db()); - let assist_label = - format!("Change visibility of {parent_name}.{target_name} to {missing_visibility}"); + let assist_label = format!( + "Change visibility of {}.{} to {missing_visibility}", + parent_name.display(ctx.db()), + target_name.display(ctx.db()) + ); acc.add(AssistId("fix_visibility", AssistKind::QuickFix), assist_label, target, |builder| { builder.edit_file(target_file); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_constant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_constant.rs index ccdfcb0d9e4fd..eccd7675fbaae 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_constant.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_constant.rs @@ -46,7 +46,8 @@ pub(crate) fn generate_constant(acc: &mut Assists, ctx: &AssistContext<'_>) -> O let ty = ctx.sema.type_of_expr(&expr)?; let scope = ctx.sema.scope(statement.syntax())?; let constant_module = scope.module(); - let type_name = ty.original().display_source_code(ctx.db(), constant_module.into()).ok()?; + let type_name = + ty.original().display_source_code(ctx.db(), constant_module.into(), false).ok()?; let target = statement.syntax().parent()?.text_range(); let path = constant_token.syntax().ancestors().find_map(ast::Path::cast)?; @@ -106,10 +107,10 @@ fn get_text_for_generate_constant( let mut text = format!("{vis}const {constant_token}: {type_name} = $0;"); while let Some(name_ref) = not_exist_name_ref.pop() { let vis = if not_exist_name_ref.len() == 0 && !outer_exists { "" } else { "\npub " }; - text = text.replace("\n", "\n "); + text = text.replace('\n', "\n "); text = format!("{vis}mod {name_ref} {{{text}\n}}"); } - Some(text.replace("\n", &format!("\n{indent}"))) + Some(text.replace('\n', &format!("\n{indent}"))) } fn target_data_for_generate_constant( @@ -131,7 +132,7 @@ fn target_data_for_generate_constant( let siblings_has_newline = l_curly_token .siblings_with_tokens(Direction::Next) - .find(|it| it.kind() == SyntaxKind::WHITESPACE && it.to_string().contains("\n")) + .find(|it| it.kind() == SyntaxKind::WHITESPACE && it.to_string().contains('\n')) .is_some(); let post_string = if siblings_has_newline { format!("{indent}") } else { format!("\n{indent}") }; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs index ed1b8f4e28d30..3667fc375b439 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs @@ -1,3 +1,5 @@ +use std::collections::HashSet; + use hir::{self, HasCrate, HasSource, HasVisibility}; use syntax::ast::{self, make, AstNode, HasGenericParams, HasName, HasVisibility as _}; @@ -63,25 +65,36 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' }; let sema_field_ty = ctx.sema.resolve_type(&field_ty)?; - let krate = sema_field_ty.krate(ctx.db()); let mut methods = vec![]; - sema_field_ty.iterate_assoc_items(ctx.db(), krate, |item| { - if let hir::AssocItem::Function(f) = item { - if f.self_param(ctx.db()).is_some() && f.is_visible_from(ctx.db(), current_module) { - methods.push(f) + let mut seen_names = HashSet::new(); + + for ty in sema_field_ty.autoderef(ctx.db()) { + let krate = ty.krate(ctx.db()); + ty.iterate_assoc_items(ctx.db(), krate, |item| { + if let hir::AssocItem::Function(f) = item { + if f.self_param(ctx.db()).is_some() + && f.is_visible_from(ctx.db(), current_module) + && seen_names.insert(f.name(ctx.db())) + { + methods.push(f) + } } - } - Option::<()>::None - }); + Option::<()>::None + }); + } for method in methods { let adt = ast::Adt::Struct(strukt.clone()); - let name = method.name(ctx.db()).to_string(); - let impl_def = find_struct_impl(ctx, &adt, &[name]).flatten(); + let name = method.name(ctx.db()).display(ctx.db()).to_string(); + // if `find_struct_impl` returns None, that means that a function named `name` already exists. + let Some(impl_def) = find_struct_impl(ctx, &adt, &[name]) else { continue; }; acc.add_group( &GroupLabel("Generate delegate methods…".to_owned()), AssistId("generate_delegate_methods", AssistKind::Generate), - format!("Generate delegate for `{field_name}.{}()`", method.name(ctx.db())), + format!( + "Generate delegate for `{field_name}.{}()`", + method.name(ctx.db()).display(ctx.db()) + ), target, |builder| { // Create the function @@ -91,7 +104,7 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' }; let method_name = method.name(ctx.db()); let vis = method_source.visibility(); - let name = make::name(&method.name(ctx.db()).to_string()); + let name = make::name(&method.name(ctx.db()).display(ctx.db()).to_string()); let params = method_source.param_list().unwrap_or_else(|| make::param_list(None, [])); let type_params = method_source.generic_param_list(); @@ -101,17 +114,30 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' }; let tail_expr = make::expr_method_call( make::ext::field_from_idents(["self", &field_name]).unwrap(), // This unwrap is ok because we have at least 1 arg in the list - make::name_ref(&method_name.to_string()), + make::name_ref(&method_name.display(ctx.db()).to_string()), arg_list, ); let ret_type = method_source.ret_type(); let is_async = method_source.async_token().is_some(); + let is_const = method_source.const_token().is_some(); + let is_unsafe = method_source.unsafe_token().is_some(); let tail_expr_finished = if is_async { make::expr_await(tail_expr) } else { tail_expr }; let body = make::block_expr([], Some(tail_expr_finished)); - let f = make::fn_(vis, name, type_params, None, params, body, ret_type, is_async) - .indent(ast::edit::IndentLevel(1)) - .clone_for_update(); + let f = make::fn_( + vis, + name, + type_params, + None, + params, + body, + ret_type, + is_async, + is_const, + is_unsafe, + ) + .indent(ast::edit::IndentLevel(1)) + .clone_for_update(); let cursor = Cursor::Before(f.syntax()); @@ -143,8 +169,16 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' let name = &strukt_name.to_string(); let params = strukt.generic_param_list(); let ty_params = params.clone(); - let impl_def = make::impl_(make::ext::ident_path(name), params, ty_params) - .clone_for_update(); + let where_clause = strukt.where_clause(); + + let impl_def = make::impl_( + ty_params, + None, + make::ty_path(make::ext::ident_path(name)), + where_clause, + None, + ) + .clone_for_update(); let assoc_items = impl_def.get_or_create_assoc_item_list(); assoc_items.add_item(f.clone().into()); @@ -314,6 +348,44 @@ impl Person { ); } + #[test] + fn test_generates_delegate_autoderef() { + check_assist( + generate_delegate_methods, + r#" +//- minicore: deref +struct Age(u8); +impl Age { + fn age(&self) -> u8 { + self.0 + } +} +struct AgeDeref(Age); +impl core::ops::Deref for AgeDeref { type Target = Age; } +struct Person { + ag$0e: AgeDeref, +} +impl Person {}"#, + r#" +struct Age(u8); +impl Age { + fn age(&self) -> u8 { + self.0 + } +} +struct AgeDeref(Age); +impl core::ops::Deref for AgeDeref { type Target = Age; } +struct Person { + age: AgeDeref, +} +impl Person { + $0fn age(&self) -> u8 { + self.age.age() + } +}"#, + ); + } + #[test] fn test_generate_delegate_visibility() { check_assist_not_applicable( @@ -333,4 +405,26 @@ struct Person { }"#, ) } + + #[test] + fn test_generate_not_eligible_if_fn_exists() { + check_assist_not_applicable( + generate_delegate_methods, + r#" +struct Age(u8); +impl Age { + fn age(&self) -> u8 { + self.0 + } +} + +struct Person { + ag$0e: Age, +} +impl Person { + fn age(&self) -> u8 { 0 } +} +"#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs index b6958e29193ca..81545396175ba 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs @@ -70,6 +70,7 @@ fn generate_record_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<( target, |edit| { generate_edit( + ctx.db(), edit, strukt, field_type.syntax(), @@ -109,6 +110,7 @@ fn generate_tuple_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<() target, |edit| { generate_edit( + ctx.db(), edit, strukt, field_type.syntax(), @@ -121,6 +123,7 @@ fn generate_tuple_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<() } fn generate_edit( + db: &RootDatabase, edit: &mut SourceChangeBuilder, strukt: ast::Struct, field_type_syntax: &SyntaxNode, @@ -144,7 +147,8 @@ fn generate_edit( ), }; let strukt_adt = ast::Adt::Struct(strukt); - let deref_impl = generate_trait_impl_text(&strukt_adt, &trait_path.to_string(), &impl_code); + let deref_impl = + generate_trait_impl_text(&strukt_adt, &trait_path.display(db).to_string(), &impl_code); edit.insert(start_offset, deref_impl); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs index 339245b94eca4..78ac2eb30e591 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs @@ -1,5 +1,5 @@ use syntax::{ - ast::{self, AstNode, HasAttrs}, + ast::{self, edit::IndentLevel, AstNode, HasAttrs}, SyntaxKind::{COMMENT, WHITESPACE}, TextSize, }; @@ -42,7 +42,12 @@ pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt .next(); match derive_attr { None => { - builder.insert_snippet(cap, node_start, "#[derive($0)]\n"); + let indent_level = IndentLevel::from_node(nominal.syntax()); + builder.insert_snippet( + cap, + node_start, + format!("#[derive($0)]\n{indent_level}"), + ); } Some(tt) => { // Just move the cursor. @@ -84,6 +89,20 @@ mod tests { "struct Foo { $0 a: i32, }", "#[derive($0)]\nstruct Foo { a: i32, }", ); + check_assist( + generate_derive, + " +mod m { + struct Foo { a: i32,$0 } +} + ", + " +mod m { + #[derive($0)] + struct Foo { a: i32, } +} + ", + ); } #[test] @@ -111,6 +130,24 @@ struct Foo { a: i32$0, } struct Foo { a: i32, } ", ); + check_assist( + generate_derive, + " +mod m { + /// `Foo` is a pretty important struct. + /// It does stuff. + struct Foo { a: i32,$0 } +} + ", + " +mod m { + /// `Foo` is a pretty important struct. + /// It does stuff. + #[derive($0)] + struct Foo { a: i32, } +} + ", + ); } #[test] diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_documentation_template.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_documentation_template.rs index b8415c72a2a32..e87132218ea90 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_documentation_template.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_documentation_template.rs @@ -324,7 +324,7 @@ fn self_name(ast_func: &ast::Fn) -> Option { self_partial_type(ast_func).map(|name| to_lower_snake_case(&name)) } -/// Heper function to get the name of the type of `self` +/// Helper function to get the name of the type of `self` fn self_type(ast_func: &ast::Fn) -> Option { ast_func.syntax().ancestors().find_map(ast::Impl::cast).and_then(|i| i.self_ty()) } @@ -350,7 +350,7 @@ fn self_type_without_lifetimes(ast_func: &ast::Fn) -> Option { Some(name) } -/// Heper function to get the name of the type of `self` without generic arguments +/// Helper function to get the name of the type of `self` without generic arguments fn self_partial_type(ast_func: &ast::Fn) -> Option { let mut self_type = self_type(ast_func)?.to_string(); if let Some(idx) = self_type.find(|c| ['<', ' '].contains(&c)) { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_variant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_variant.rs index cd037f7492c66..184f523e01bd9 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_variant.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_variant.rs @@ -192,7 +192,7 @@ fn expr_ty( 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()?; + let text = ty.display_source_code(ctx.db(), scope.module().into(), false).ok()?; Some(make::ty(&text)) } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs index 0768389281ca3..850be21c300ca 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs @@ -196,7 +196,7 @@ fn add_func_to_accumulator( let mut func = function_template.to_string(ctx.config.snippet_cap); if let Some(name) = adt_name { // FIXME: adt may have generic params. - func = format!("\n{indent}impl {name} {{\n{func}\n{indent}}}"); + func = format!("\n{indent}impl {} {{\n{func}\n{indent}}}", name.display(ctx.db())); } builder.edit_file(file); match ctx.config.snippet_cap { @@ -378,6 +378,8 @@ impl FunctionBuilder { fn_body, self.ret_type, self.is_async, + false, // FIXME : const and unsafe are not handled yet. + false, ); let leading_ws; let trailing_ws; @@ -438,7 +440,7 @@ fn make_return_type( Some(ty) if ty.is_unit() => (None, false), Some(ty) => { necessary_generic_params.extend(ty.generic_params(ctx.db())); - let rendered = ty.display_source_code(ctx.db(), target_module.into()); + let rendered = ty.display_source_code(ctx.db(), target_module.into(), true); match rendered { Ok(rendered) => (Some(make::ty(&rendered)), false), Err(_) => (Some(make::ty_placeholder()), true), @@ -893,14 +895,14 @@ fn filter_bounds_in_scope( let target_impl = target.parent().ancestors().find_map(ast::Impl::cast)?; let target_impl = ctx.sema.to_def(&target_impl)?; // It's sufficient to test only the first element of `generic_params` because of the order of - // insertion (see `relevant_parmas_and_where_clauses()`). + // insertion (see `params_and_where_preds_in_scope()`). let def = generic_params.first()?.self_ty_param.parent(); if def != hir::GenericDef::Impl(target_impl) { return None; } // Now we know every element that belongs to an impl would be in scope at `target`, we can - // filter them out just by lookint at their parent. + // filter them out just by looking at their parent. generic_params.retain(|it| !matches!(it.self_ty_param.parent(), hir::GenericDef::Impl(_))); where_preds.retain(|it| { it.node.syntax().parent().and_then(|it| it.parent()).and_then(ast::Impl::cast).is_none() @@ -992,9 +994,9 @@ fn fn_arg_type( let famous_defs = &FamousDefs(&ctx.sema, ctx.sema.scope(fn_arg.syntax())?.krate()); convert_reference_type(ty.strip_references(), ctx.db(), famous_defs) .map(|conversion| conversion.convert_type(ctx.db())) - .or_else(|| ty.display_source_code(ctx.db(), target_module.into()).ok()) + .or_else(|| ty.display_source_code(ctx.db(), target_module.into(), true).ok()) } else { - ty.display_source_code(ctx.db(), target_module.into()).ok() + ty.display_source_code(ctx.db(), target_module.into(), true).ok() } } @@ -1087,7 +1089,7 @@ fn calculate_necessary_visibility( } } -// This is never intended to be used as a generic graph strucuture. If there's ever another need of +// This is never intended to be used as a generic graph structure. If there's ever another need of // graph algorithm, consider adding a library for that (and replace the following). /// Minimally implemented directed graph structure represented by adjacency list. struct Graph { @@ -1910,7 +1912,6 @@ fn bar(new: fn) ${0:-> _} { #[test] fn add_function_with_closure_arg() { - // FIXME: The argument in `bar` is wrong. check_assist( generate_function, r" @@ -1925,7 +1926,7 @@ fn foo() { bar(closure) } -fn bar(closure: _) { +fn bar(closure: impl Fn(i64) -> i64) { ${0:todo!()} } ", @@ -2381,7 +2382,7 @@ mod s { } #[test] - fn create_method_with_cursor_anywhere_on_call_expresion() { + fn create_method_with_cursor_anywhere_on_call_expression() { check_assist( generate_function, r" @@ -2488,7 +2489,7 @@ fn foo() {s::S::bar();} } #[test] - fn create_static_method_with_cursor_anywhere_on_call_expresion() { + fn create_static_method_with_cursor_anywhere_on_call_expression() { check_assist( generate_function, r" diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter.rs index 4595cfe29c85d..dd6bbd84afccf 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter.rs @@ -174,7 +174,7 @@ pub(crate) fn generate_getter_impl( // this buf inserts a newline at the end of a getter // automatically, if one wants to add one more newline // for separating it from other assoc items, that needs - // to be handled spearately + // to be handled separately let mut getter_buf = generate_getter_from_info(ctx, &getter_info, record_field_info); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs index e30a3e942c4cc..824255e4f8e32 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs @@ -98,9 +98,9 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option .fields() .enumerate() .filter_map(|(i, f)| { - let contructor = trivial_constructors[i].clone(); - if contructor.is_some() { - contructor + let constructor = trivial_constructors[i].clone(); + if constructor.is_some() { + constructor } else { Some(f.name()?.to_string()) } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs index 3fc552306a6ff..5aa8e56f5626c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs @@ -148,7 +148,7 @@ macro_rules! num { #[test] fn inline_macro_simple_not_applicable_broken_macro() { // FIXME: This is a bug. The macro should not expand, but it's - // the same behaviour as the "Expand Macro Recursively" commmand + // the same behaviour as the "Expand Macro Recursively" command // so it's presumably OK for the time being. check_assist( inline_macro, @@ -254,4 +254,49 @@ fn f() { if true{}; } "#, ) } + + #[test] + fn whitespace_between_text_and_pound() { + check_assist( + inline_macro, + r#" +macro_rules! foo { + () => { + cfg_if! { + if #[cfg(test)] { + 1; + } else { + 1; + } + } + } +} +fn main() { + $0foo!(); +} +"#, + r#" +macro_rules! foo { + () => { + cfg_if! { + if #[cfg(test)] { + 1; + } else { + 1; + } + } + } +} +fn main() { + cfg_if!{ + if #[cfg(test)]{ + 1; + }else { + 1; + } +}; +} +"#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_generic.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_generic.rs index 062c816aef0da..b0d35c02d67ba 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_generic.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_generic.rs @@ -1,5 +1,5 @@ use syntax::{ - ast::{self, edit_in_place::GenericParamsOwnerEdit, make, AstNode}, + ast::{self, edit_in_place::GenericParamsOwnerEdit, make, AstNode, HasGenericParams}, ted, }; @@ -14,7 +14,7 @@ use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists}; // ``` // -> // ``` -// fn foo(bar: B) {} +// fn foo<$0B: Bar>(bar: B) {} // ``` pub(crate) fn introduce_named_generic(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { let impl_trait_type = ctx.find_node_at_offset::()?; @@ -39,7 +39,15 @@ pub(crate) fn introduce_named_generic(acc: &mut Assists, ctx: &AssistContext<'_> let new_ty = make::ty(&type_param_name).clone_for_update(); ted::replace(impl_trait_type.syntax(), new_ty.syntax()); - fn_.get_or_create_generic_param_list().add_generic_param(type_param.into()) + fn_.get_or_create_generic_param_list().add_generic_param(type_param.into()); + + if let Some(cap) = ctx.config.snippet_cap { + if let Some(generic_param) = + fn_.generic_param_list().and_then(|it| it.generic_params().last()) + { + edit.add_tabstop_before(cap, generic_param); + } + } }, ) } @@ -55,7 +63,7 @@ mod tests { check_assist( introduce_named_generic, r#"fn foo(bar: $0impl Bar) {}"#, - r#"fn foo(bar: B) {}"#, + r#"fn foo(bar: B) {}"#, ); } @@ -64,7 +72,7 @@ mod tests { check_assist( introduce_named_generic, r#"fn foo(bar: $0impl Bar) {}"#, - r#"fn foo(bar: B) {}"#, + r#"fn foo<$0B: Bar>(bar: B) {}"#, ); } @@ -73,7 +81,7 @@ mod tests { check_assist( introduce_named_generic, r#"fn foo(foo: impl Foo, bar: $0impl Bar) {}"#, - r#"fn foo(foo: impl Foo, bar: B) {}"#, + r#"fn foo(foo: impl Foo, bar: B) {}"#, ); } @@ -82,7 +90,7 @@ mod tests { check_assist( introduce_named_generic, r#"fn foo<>(bar: $0impl Bar) {}"#, - r#"fn foo(bar: B) {}"#, + r#"fn foo<$0B: Bar>(bar: B) {}"#, ); } @@ -95,7 +103,7 @@ fn foo< >(bar: $0impl Bar) {} "#, r#" -fn foo(bar: B) {} "#, ); @@ -108,7 +116,7 @@ fn foo(bar: $0impl Bar) {}"#, - r#"fn foo(bar: B) {}"#, + r#"fn foo(bar: B) {}"#, ); } @@ -127,7 +135,7 @@ fn foo< fn foo< G: Foo, F, - H, B: Bar, + H, $0B: Bar, >(bar: B) {} "#, ); @@ -138,7 +146,7 @@ fn foo< check_assist( introduce_named_generic, r#"fn foo(bar: $0impl Foo + Bar) {}"#, - r#"fn foo(bar: F) {}"#, + r#"fn foo<$0F: Foo + Bar>(bar: F) {}"#, ); } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_lifetime.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_lifetime.rs index a54dc4f96de00..c5aa9755bc0b2 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_lifetime.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_lifetime.rs @@ -66,7 +66,7 @@ fn generate_fn_def_assist( // if we have a self reference, use that Some(NeedsLifetime::SelfParam(self_param)) } else { - // otherwise, if there's a single reference parameter without a named liftime, use that + // otherwise, if there's a single reference parameter without a named lifetime, use that let fn_params_without_lifetime: Vec<_> = param_list .params() .filter_map(|param| match param.ty() { @@ -79,7 +79,7 @@ fn generate_fn_def_assist( match fn_params_without_lifetime.len() { 1 => Some(fn_params_without_lifetime.into_iter().next()?), 0 => None, - // multiple unnnamed is invalid. assist is not applicable + // multiple unnamed is invalid. assist is not applicable _ => return None, } }; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_match_arms.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_match_arms.rs index 641c90885bf53..aae9f20d4ea5c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_match_arms.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_match_arms.rs @@ -1,4 +1,4 @@ -use hir::TypeInfo; +use hir::Type; use std::{collections::HashMap, iter::successors}; use syntax::{ algo::neighbor, @@ -95,7 +95,7 @@ fn contains_placeholder(a: &ast::MatchArm) -> bool { } fn are_same_types( - current_arm_types: &HashMap>, + current_arm_types: &HashMap>, arm: &ast::MatchArm, ctx: &AssistContext<'_>, ) -> bool { @@ -103,7 +103,7 @@ fn are_same_types( for (other_arm_type_name, other_arm_type) in arm_types { match (current_arm_types.get(&other_arm_type_name), other_arm_type) { (Some(Some(current_arm_type)), Some(other_arm_type)) - if other_arm_type.original == current_arm_type.original => {} + if other_arm_type == *current_arm_type => {} _ => return false, } } @@ -114,44 +114,44 @@ fn are_same_types( fn get_arm_types( context: &AssistContext<'_>, arm: &ast::MatchArm, -) -> HashMap> { - let mut mapping: HashMap> = HashMap::new(); +) -> HashMap> { + let mut mapping: HashMap> = HashMap::new(); fn recurse( - map: &mut HashMap>, + map: &mut HashMap>, ctx: &AssistContext<'_>, pat: &Option, ) { if let Some(local_pat) = pat { - match pat { - Some(ast::Pat::TupleStructPat(tuple)) => { + match local_pat { + ast::Pat::TupleStructPat(tuple) => { for field in tuple.fields() { recurse(map, ctx, &Some(field)); } } - Some(ast::Pat::TuplePat(tuple)) => { + ast::Pat::TuplePat(tuple) => { for field in tuple.fields() { recurse(map, ctx, &Some(field)); } } - Some(ast::Pat::RecordPat(record)) => { + ast::Pat::RecordPat(record) => { if let Some(field_list) = record.record_pat_field_list() { for field in field_list.fields() { recurse(map, ctx, &field.pat()); } } } - Some(ast::Pat::ParenPat(parentheses)) => { + ast::Pat::ParenPat(parentheses) => { recurse(map, ctx, &parentheses.pat()); } - Some(ast::Pat::SlicePat(slice)) => { + ast::Pat::SlicePat(slice) => { for slice_pat in slice.pats() { recurse(map, ctx, &Some(slice_pat)); } } - Some(ast::Pat::IdentPat(ident_pat)) => { + ast::Pat::IdentPat(ident_pat) => { if let Some(name) = ident_pat.name() { - let pat_type = ctx.sema.type_of_pat(local_pat); + let pat_type = ctx.sema.type_of_binding_in_pat(ident_pat); map.insert(name.text().to_string(), pat_type); } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_const_to_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_const_to_impl.rs index d848fce4be821..b6027eac55dee 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_const_to_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_const_to_impl.rs @@ -98,7 +98,7 @@ pub(crate) fn move_const_to_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> }; builder.delete(range_to_delete); - let const_ref = format!("Self::{name}"); + let const_ref = format!("Self::{}", name.display(ctx.db())); for range in usages.all().file_ranges().map(|it| it.range) { builder.replace(range, const_ref.clone()); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs index 1728c03cd03e2..917d0b3671e8e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs @@ -39,7 +39,7 @@ 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 module_name = module.name(ctx.db())?.display(ctx.db()).to_string(); let path = format!("../{module_name}.rs"); let dst = AnchoredPathBuf { anchor: ctx.file_id(), path }; acc.add( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs index a7c605325ea69..166b25c69e188 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs +++ b/src/tools/rust-analyzer/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.display(ctx.db())) } _ => (), } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs index 076d25411a818..b73270cd05fb5 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs @@ -39,7 +39,7 @@ 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 module_name = module.name(ctx.db())?.display(ctx.db()).to_string(); let path = format!("./{module_name}/mod.rs"); let dst = AnchoredPathBuf { anchor: ctx.file_id(), path }; acc.add( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs index cbbea6c1e6373..23153b4c566c8 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs @@ -57,11 +57,13 @@ pub(crate) fn promote_local_to_const(acc: &mut Assists, ctx: &AssistContext<'_>) let local = ctx.sema.to_def(&pat)?; let ty = ctx.sema.type_of_pat(&pat.into())?.original; - if ty.contains_unknown() || ty.is_closure() { - cov_mark::hit!(promote_lcoal_not_applicable_if_ty_not_inferred); - return None; - } - let ty = ty.display_source_code(ctx.db(), module.into()).ok()?; + let ty = match ty.display_source_code(ctx.db(), module.into(), false) { + Ok(ty) => ty, + Err(_) => { + cov_mark::hit!(promote_local_not_applicable_if_ty_not_inferred); + return None; + } + }; let initializer = let_stmt.initializer()?; if !is_body_const(&ctx.sema, &initializer) { @@ -187,7 +189,7 @@ fn foo() { #[test] fn not_applicable_unknown_ty() { - cov_mark::check!(promote_lcoal_not_applicable_if_ty_not_inferred); + cov_mark::check!(promote_local_not_applicable_if_ty_not_inferred); check_assist_not_applicable( promote_local_to_const, r" diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/pull_assignment_up.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/pull_assignment_up.rs index 4cfe6c99b23c2..a5c7fea403d31 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/pull_assignment_up.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/pull_assignment_up.rs @@ -386,7 +386,7 @@ fn foo() { } #[test] - fn pull_assignment_up_if_missing_assigment_not_applicable() { + fn pull_assignment_up_if_missing_assignment_not_applicable() { check_assist_not_applicable( pull_assignment_up, r#" @@ -401,7 +401,7 @@ fn foo() { } #[test] - fn pull_assignment_up_match_missing_assigment_not_applicable() { + fn pull_assignment_up_match_missing_assignment_not_applicable() { check_assist_not_applicable( pull_assignment_up, r#" diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs index e7014597a1d8b..4bf974a5655af 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs @@ -507,7 +507,7 @@ fn main() { } #[test] - fn struct_method_over_stuct_instance() { + fn struct_method_over_struct_instance() { check_assist_not_applicable( qualify_method_call, r#" @@ -525,7 +525,7 @@ fn main() { } #[test] - fn trait_method_over_stuct_instance() { + fn trait_method_over_struct_instance() { check_assist_not_applicable( qualify_method_call, r#" diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs index e759e1561cbd0..239149dc411d6 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs @@ -86,7 +86,7 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option acc.add_group( &group_label, AssistId("qualify_path", AssistKind::QuickFix), - label(candidate, &import), + label(ctx.db(), candidate, &import), range, |builder| { qualify_candidate.qualify( @@ -186,7 +186,7 @@ fn find_trait_method( if let Some(hir::AssocItem::Function(method)) = trait_.items(db).into_iter().find(|item: &hir::AssocItem| { item.name(db) - .map(|name| name.to_string() == trait_method_name.to_string()) + .map(|name| name.display(db).to_string() == trait_method_name.to_string()) .unwrap_or(false) }) { @@ -216,14 +216,14 @@ fn group_label(candidate: &ImportCandidate) -> GroupLabel { GroupLabel(format!("Qualify {name}")) } -fn label(candidate: &ImportCandidate, import: &LocatedImport) -> String { +fn label(db: &RootDatabase, 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_path}`") + format!("Qualify as `{}`", import_path.display(db)) } - _ => format!("Qualify with `{import_path}`"), + _ => format!("Qualify with `{}`", import_path.display(db)), } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs index 01420430bb419..40ee4771d1707 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs @@ -20,6 +20,7 @@ use crate::{utils::required_hashes, AssistContext, AssistId, AssistKind, Assists // } // ``` pub(crate) fn make_raw_string(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + // FIXME: This should support byte and c strings as well. let token = ctx.find_token_at_offset::()?; if token.is_raw() { return None; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_parentheses.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_parentheses.rs index e9c7c6bae9cee..ffc32f8049968 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_parentheses.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_parentheses.rs @@ -124,7 +124,7 @@ mod tests { } #[test] - fn remove_parens_doesnt_apply_weird_syntax_and_adge_cases() { + fn remove_parens_doesnt_apply_weird_syntax_and_edge_cases() { // removing `()` would break code because {} would be counted as the loop/if body check_assist_not_applicable(remove_parentheses, r#"fn f() { for _ in $0(0..{3}) {} }"#); check_assist_not_applicable(remove_parentheses, r#"fn f() { for _ in $0(S {}) {} }"#); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_param.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_param.rs index bd2e8fbe38966..0772b168d49c8 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_param.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_param.rs @@ -232,7 +232,7 @@ fn b() { foo( ) } } #[test] - fn remove_unused_surrounded_by_parms() { + fn remove_unused_surrounded_by_params() { check_assist( remove_unused_param, r#" diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_fields.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_fields.rs index 58dcaf9a221d8..0256256697740 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_fields.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_fields.rs @@ -20,9 +20,10 @@ use crate::{AssistContext, AssistId, AssistKind, Assists}; // const test: Foo = Foo {foo: 1, bar: 0} // ``` pub(crate) fn reorder_fields(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { - let record = ctx.find_node_at_offset::>()?; + let path = ctx.find_node_at_offset::()?; + let record = + path.syntax().parent().and_then(>::cast)?; - let path = record.as_ref().either(|it| it.path(), |it| it.path())?; let ranks = compute_fields_ranks(&path, ctx)?; let get_rank_of_field = |of: Option<_>| *ranks.get(&of.unwrap_or_default()).unwrap_or(&usize::MAX); @@ -96,7 +97,7 @@ fn compute_fields_ranks( .fields(ctx.db()) .into_iter() .enumerate() - .map(|(idx, field)| (field.name(ctx.db()).to_string(), idx)) + .map(|(idx, field)| (field.name(ctx.db()).display(ctx.db()).to_string(), idx)) .collect(); Some(res) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs index 208c3e109ddec..66669662316fe 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs @@ -21,7 +21,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists}; // } // // struct Bar; -// $0impl Foo for Bar { +// $0impl Foo for Bar$0 { // const B: u8 = 17; // fn c() {} // type A = String; @@ -45,6 +45,16 @@ use crate::{AssistContext, AssistId, AssistKind, Assists}; pub(crate) fn reorder_impl_items(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { let impl_ast = ctx.find_node_at_offset::()?; let items = impl_ast.assoc_item_list()?; + + // restrict the range + // if cursor is in assoc_items, abort + let assoc_range = items.syntax().text_range(); + let cursor_position = ctx.offset(); + if assoc_range.contains_inclusive(cursor_position) { + cov_mark::hit!(not_applicable_editing_assoc_items); + return None; + } + let assoc_items = items.assoc_items().collect::>(); let path = impl_ast @@ -104,7 +114,7 @@ fn compute_item_ranks( .iter() .flat_map(|i| i.name(ctx.db())) .enumerate() - .map(|(idx, name)| (name.to_string(), idx)) + .map(|(idx, name)| (name.display(ctx.db()).to_string(), idx)) .collect(), ) } @@ -264,9 +274,9 @@ trait Bar { } struct Foo; -impl Bar for Foo { +$0impl Bar for Foo { type Fooo = (); - type Foo = ();$0 + type Foo = (); }"#, r#" trait Bar { @@ -281,4 +291,29 @@ impl Bar for Foo { }"#, ) } + + #[test] + fn not_applicable_editing_assoc_items() { + cov_mark::check!(not_applicable_editing_assoc_items); + check_assist_not_applicable( + reorder_impl_items, + r#" +trait Bar { + type T; + const C: (); + fn a() {} + fn z() {} + fn b() {} +} +struct Foo; +impl Bar for Foo { + type T = ();$0 + const C: () = (); + fn z() {} + fn a() {} + fn b() {} +} + "#, + ) + } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs index 4cfae0c7212c2..36ac8c71d8154 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs @@ -59,7 +59,7 @@ pub(crate) fn replace_derive_with_manual_impl( // collect the derive paths from the #[derive] expansion let current_derives = ctx .sema - .parse_or_expand(hir_file)? + .parse_or_expand(hir_file) .descendants() .filter_map(ast::Attr::cast) .filter_map(|attr| attr.path()) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs new file mode 100644 index 0000000000000..e7b62d49bb814 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs @@ -0,0 +1,351 @@ +use hir::Semantics; +use ide_db::{ + base_db::{FileId, FileRange}, + defs::Definition, + search::{SearchScope, UsageSearchResult}, + RootDatabase, +}; +use syntax::{ + ast::{ + self, make::impl_trait_type, HasGenericParams, HasName, HasTypeBounds, Name, NameLike, + PathType, + }, + match_ast, ted, AstNode, +}; +use text_edit::TextRange; + +use crate::{AssistContext, AssistId, AssistKind, Assists}; + +// Assist: replace_named_generic_with_impl +// +// Replaces named generic with an `impl Trait` in function argument. +// +// ``` +// fn new>(location: P) -> Self {} +// ``` +// -> +// ``` +// fn new(location: impl AsRef) -> Self {} +// ``` +pub(crate) fn replace_named_generic_with_impl( + acc: &mut Assists, + ctx: &AssistContext<'_>, +) -> Option<()> { + // finds `>` + let type_param = ctx.find_node_at_offset::()?; + // returns `P` + let type_param_name = type_param.name()?; + + // The list of type bounds / traits: `AsRef` + let type_bound_list = type_param.type_bound_list()?; + + let fn_ = type_param.syntax().ancestors().find_map(ast::Fn::cast)?; + let param_list_text_range = fn_.param_list()?.syntax().text_range(); + + let type_param_hir_def = ctx.sema.to_def(&type_param)?; + let type_param_def = Definition::GenericParam(hir::GenericParam::TypeParam(type_param_hir_def)); + + // get all usage references for the type param + let usage_refs = find_usages(&ctx.sema, &fn_, type_param_def, ctx.file_id()); + if usage_refs.is_empty() { + return None; + } + + // All usage references need to be valid (inside the function param list) + if !check_valid_usages(&usage_refs, param_list_text_range) { + return None; + } + + let mut path_types_to_replace = Vec::new(); + for (_a, refs) in usage_refs.iter() { + for usage_ref in refs { + let param_node = find_path_type(&ctx.sema, &type_param_name, &usage_ref.name)?; + path_types_to_replace.push(param_node); + } + } + + let target = type_param.syntax().text_range(); + + acc.add( + AssistId("replace_named_generic_with_impl", AssistKind::RefactorRewrite), + "Replace named generic with impl trait", + target, + |edit| { + let type_param = edit.make_mut(type_param); + let fn_ = edit.make_mut(fn_); + + let path_types_to_replace = path_types_to_replace + .into_iter() + .map(|param| edit.make_mut(param)) + .collect::>(); + + // remove trait from generic param list + if let Some(generic_params) = fn_.generic_param_list() { + generic_params.remove_generic_param(ast::GenericParam::TypeParam(type_param)); + if generic_params.generic_params().count() == 0 { + ted::remove(generic_params.syntax()); + } + } + + let new_bounds = impl_trait_type(type_bound_list); + for path_type in path_types_to_replace.iter().rev() { + ted::replace(path_type.syntax(), new_bounds.clone_for_update().syntax()); + } + }, + ) +} + +fn find_path_type( + sema: &Semantics<'_, RootDatabase>, + type_param_name: &Name, + param: &NameLike, +) -> Option { + let path_type = + sema.ancestors_with_macros(param.syntax().clone()).find_map(ast::PathType::cast)?; + + // Ignore any path types that look like `P::Assoc` + if path_type.path()?.as_single_name_ref()?.text() != type_param_name.text() { + return None; + } + + let ancestors = sema.ancestors_with_macros(path_type.syntax().clone()); + + let mut in_generic_arg_list = false; + let mut is_associated_type = false; + + // walking the ancestors checks them in a heuristic way until the `Fn` node is reached. + for ancestor in ancestors { + match_ast! { + match ancestor { + ast::PathSegment(ps) => { + match ps.kind()? { + ast::PathSegmentKind::Name(_name_ref) => (), + ast::PathSegmentKind::Type { .. } => return None, + _ => return None, + } + }, + ast::GenericArgList(_) => { + in_generic_arg_list = true; + }, + ast::AssocTypeArg(_) => { + is_associated_type = true; + }, + ast::ImplTraitType(_) => { + if in_generic_arg_list && !is_associated_type { + return None; + } + }, + ast::DynTraitType(_) => { + if !is_associated_type { + return None; + } + }, + ast::Fn(_) => return Some(path_type), + _ => (), + } + } + } + + None +} + +/// Returns all usage references for the given type parameter definition. +fn find_usages( + sema: &Semantics<'_, RootDatabase>, + fn_: &ast::Fn, + type_param_def: Definition, + file_id: FileId, +) -> UsageSearchResult { + let file_range = FileRange { file_id, range: fn_.syntax().text_range() }; + type_param_def.usages(sema).in_scope(SearchScope::file_range(file_range)).all() +} + +fn check_valid_usages(usages: &UsageSearchResult, param_list_range: TextRange) -> bool { + usages + .iter() + .flat_map(|(_, usage_refs)| usage_refs) + .all(|usage_ref| param_list_range.contains_range(usage_ref.range)) +} + +#[cfg(test)] +mod tests { + use super::*; + + use crate::tests::{check_assist, check_assist_not_applicable}; + + #[test] + fn replace_generic_moves_into_function() { + check_assist( + replace_named_generic_with_impl, + r#"fn new(input: T) -> Self {}"#, + r#"fn new(input: impl ToString) -> Self {}"#, + ); + } + + #[test] + fn replace_generic_with_inner_associated_type() { + check_assist( + replace_named_generic_with_impl, + r#"fn new>(input: P) -> Self {}"#, + r#"fn new(input: impl AsRef) -> Self {}"#, + ); + } + + #[test] + fn replace_generic_trait_applies_to_all_matching_params() { + check_assist( + replace_named_generic_with_impl, + r#"fn new(a: T, b: T) -> Self {}"#, + r#"fn new(a: impl ToString, b: impl ToString) -> Self {}"#, + ); + } + + #[test] + fn replace_generic_trait_applies_to_generic_arguments_in_params() { + check_assist( + replace_named_generic_with_impl, + r#" + fn foo( + _: P, + _: Option

, + _: Option>, + _: impl Iterator, + _: &dyn Iterator, + ) {} + "#, + r#" + fn foo( + _: impl Trait, + _: Option, + _: Option>, + _: impl Iterator, + _: &dyn Iterator, + ) {} + "#, + ); + } + + #[test] + fn replace_generic_not_applicable_when_one_param_type_is_invalid() { + check_assist_not_applicable( + replace_named_generic_with_impl, + r#" + fn foo( + _: i32, + _: Option

, + _: Option>, + _: impl Iterator, + _: &dyn Iterator, + _:

::Assoc, + ) {} + "#, + ); + } + + #[test] + fn replace_generic_not_applicable_when_referenced_in_where_clause() { + check_assist_not_applicable( + replace_named_generic_with_impl, + r#"fn foo() where I: FromRef

{}"#, + ); + } + + #[test] + fn replace_generic_not_applicable_when_used_with_type_alias() { + check_assist_not_applicable( + replace_named_generic_with_impl, + r#"fn foo(p:

::Assoc) {}"#, + ); + } + + #[test] + fn replace_generic_not_applicable_when_used_as_argument_in_outer_trait_alias() { + check_assist_not_applicable( + replace_named_generic_with_impl, + r#"fn foo(_: <() as OtherTrait

>::Assoc) {}"#, + ); + } + + #[test] + fn replace_generic_not_applicable_with_inner_associated_type() { + check_assist_not_applicable( + replace_named_generic_with_impl, + r#"fn foo(_: P::Assoc) {}"#, + ); + } + + #[test] + fn replace_generic_not_applicable_when_passed_into_outer_impl_trait() { + check_assist_not_applicable( + replace_named_generic_with_impl, + r#"fn foo(_: impl OtherTrait

) {}"#, + ); + } + + #[test] + fn replace_generic_not_applicable_when_used_in_passed_function_parameter() { + check_assist_not_applicable( + replace_named_generic_with_impl, + r#"fn foo(_: &dyn Fn(P)) {}"#, + ); + } + + #[test] + fn replace_generic_with_multiple_generic_params() { + check_assist( + replace_named_generic_with_impl, + r#"fn new, T$0: ToString>(t: T, p: P) -> Self {}"#, + r#"fn new>(t: impl ToString, p: P) -> Self {}"#, + ); + check_assist( + replace_named_generic_with_impl, + r#"fn new>(t: T, p: P) -> Self {}"#, + r#"fn new>(t: impl ToString, p: P) -> Self {}"#, + ); + check_assist( + replace_named_generic_with_impl, + r#"fn new(a: A, b: B, c: C) -> Self {}"#, + r#"fn new(a: A, b: impl ToString, c: C) -> Self {}"#, + ); + } + + #[test] + fn replace_generic_with_multiple_trait_bounds() { + check_assist( + replace_named_generic_with_impl, + r#"fn new(p: P) -> Self {}"#, + r#"fn new(p: impl Send + Sync) -> Self {}"#, + ); + } + + #[test] + fn replace_generic_not_applicable_if_param_used_as_return_type() { + check_assist_not_applicable( + replace_named_generic_with_impl, + r#"fn new(p: P) -> P {}"#, + ); + } + + #[test] + fn replace_generic_not_applicable_if_param_used_in_fn_body() { + check_assist_not_applicable( + replace_named_generic_with_impl, + r#"fn new(p: P) { let x: &dyn P = &O; }"#, + ); + } + + #[test] + fn replace_generic_ignores_another_function_with_same_param_type() { + check_assist( + replace_named_generic_with_impl, + r#" + fn new(p: P) {} + fn hello(p: P) { println!("{:?}", p); } + "#, + r#" + fn new(p: impl Send + Sync) {} + fn hello(p: P) { println!("{:?}", p); } + "#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_string_with_char.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_string_with_char.rs index decb5fb62a772..6310981ccce7a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_string_with_char.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_string_with_char.rs @@ -31,14 +31,14 @@ pub(crate) fn replace_string_with_char(acc: &mut Assists, ctx: &AssistContext<'_ if value.chars().take(2).count() != 1 { return None; } - let quote_offets = token.quote_offsets()?; + let quote_offsets = token.quote_offsets()?; acc.add( AssistId("replace_string_with_char", AssistKind::RefactorRewrite), "Replace string with char", target, |edit| { - let (left, right) = quote_offets.quotes; + let (left, right) = quote_offsets.quotes; edit.replace(left, '\''); edit.replace(right, '\''); if value == "'" { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs index 38fccb33829a3..2e26f59d030ff 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs @@ -20,7 +20,7 @@ use crate::assist_context::{AssistContext, Assists}; // Replaces a `try` expression with a `match` expression. // // ``` -// # //- minicore:option +// # //- minicore: try, option // fn handle() { // let pat = Some(true)$0?; // } @@ -111,7 +111,7 @@ mod tests { check_assist( replace_try_expr_with_match, r#" -//- minicore:option +//- minicore: try, option fn test() { let pat = Some(true)$0?; } @@ -132,7 +132,7 @@ fn test() { check_assist( replace_try_expr_with_match, r#" -//- minicore:result +//- minicore: try, from, result fn test() { let pat = Ok(true)$0?; } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs index 6626ce0795997..43a97d7d3a551 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs @@ -55,7 +55,7 @@ pub(crate) fn replace_turbofish_with_explicit_type( let returned_type = match ctx.sema.type_of_expr(&initializer) { Some(returned_type) if !returned_type.original.contains_unknown() => { let module = ctx.sema.scope(let_stmt.syntax())?.module(); - returned_type.original.display_source_code(ctx.db(), module.into()).ok()? + returned_type.original.display_source_code(ctx.db(), module.into(), false).ok()? } _ => { cov_mark::hit!(fallback_to_turbofish_type_if_type_info_not_available); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/sort_items.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/sort_items.rs index a93704b39474b..3a0121f55fa02 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/sort_items.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/sort_items.rs @@ -87,11 +87,7 @@ pub(crate) fn sort_items(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<( return None; } - if let Some(trait_ast) = ctx.find_node_at_offset::() { - add_sort_methods_assist(acc, trait_ast.assoc_item_list()?) - } else if let Some(impl_ast) = ctx.find_node_at_offset::() { - add_sort_methods_assist(acc, impl_ast.assoc_item_list()?) - } else if let Some(struct_ast) = ctx.find_node_at_offset::() { + if let Some(struct_ast) = ctx.find_node_at_offset::() { add_sort_field_list_assist(acc, struct_ast.field_list()) } else if let Some(union_ast) = ctx.find_node_at_offset::() { add_sort_fields_assist(acc, union_ast.record_field_list()?) @@ -103,6 +99,10 @@ pub(crate) fn sort_items(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<( add_sort_fields_assist(acc, enum_struct_variant_ast) } else if let Some(enum_ast) = ctx.find_node_at_offset::() { add_sort_variants_assist(acc, enum_ast.variant_list()?) + } else if let Some(trait_ast) = ctx.find_node_at_offset::() { + add_sort_methods_assist(acc, ctx, trait_ast.assoc_item_list()?) + } else if let Some(impl_ast) = ctx.find_node_at_offset::() { + add_sort_methods_assist(acc, ctx, impl_ast.assoc_item_list()?) } else { None } @@ -116,9 +116,11 @@ trait AddRewrite { new: Vec, target: TextRange, ) -> Option<()>; + fn yeet() {} } impl AddRewrite for Assists { + fn yeet() {} fn add_rewrite( &mut self, label: &str, @@ -146,7 +148,19 @@ fn add_sort_field_list_assist(acc: &mut Assists, field_list: Option Option<()> { +fn add_sort_methods_assist( + acc: &mut Assists, + ctx: &AssistContext<'_>, + item_list: ast::AssocItemList, +) -> Option<()> { + let selection = ctx.selection_trimmed(); + + // ignore assist if the selection intersects with an associated item. + if item_list.assoc_items().any(|item| item.syntax().text_range().intersect(selection).is_some()) + { + return None; + } + let methods = get_methods(&item_list); let sorted = sort_by_name(&methods); @@ -216,6 +230,51 @@ mod tests { use super::*; + #[test] + fn not_applicable_if_selection_in_fn_body() { + check_assist_not_applicable( + sort_items, + r#" +struct S; +impl S { + fn func2() { + $0 bar $0 + } + fn func() {} +} + "#, + ) + } + + #[test] + fn not_applicable_if_selection_at_associated_const() { + check_assist_not_applicable( + sort_items, + r#" +struct S; +impl S { + fn func2() {} + fn func() {} + const C: () = $0()$0; +} + "#, + ) + } + + #[test] + fn not_applicable_if_selection_overlaps_nodes() { + check_assist_not_applicable( + sort_items, + r#" +struct S; +impl $0S { + fn$0 func2() {} + fn func() {} +} + "#, + ) + } + #[test] fn not_applicable_if_no_selection() { cov_mark::check!(not_applicable_if_no_selection); @@ -231,6 +290,21 @@ t$0rait Bar { ) } + #[test] + fn not_applicable_if_selection_in_trait_fn_body() { + check_assist_not_applicable( + sort_items, + r#" +trait Bar { + fn b() { + $0 hello $0 + } + fn a(); +} + "#, + ) + } + #[test] fn not_applicable_if_trait_empty() { cov_mark::check!(not_applicable_if_sorted_or_empty_or_single); @@ -458,6 +532,31 @@ struct Bar { ) } + #[test] + fn sort_struct_inside_a_function() { + check_assist( + sort_items, + r#" +fn hello() { + $0struct Bar$0 { + b: u8, + a: u32, + c: u64, + } +} + "#, + r#" +fn hello() { + struct Bar { + a: u32, + b: u8, + c: u64, + } +} + "#, + ) + } + #[test] fn sort_generic_struct_with_lifetime() { check_assist( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_block.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_block.rs index 33b19a354b9aa..939055f148c45 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_block.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_block.rs @@ -51,15 +51,11 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option let replaced = match list.syntax().last_child() { Some(last) => { let stmts: Vec = list.statements().collect(); - let initializer = ast::Expr::cast(last.clone())?; + let initializer = ast::Expr::cast(last)?; let let_stmt = make::let_stmt(pattern, ty, Some(initializer)); if stmts.len() > 0 { let block = make::block_expr(stmts, None); - format!( - "{}\n {}", - update_expr_string(block.to_string()), - let_stmt.to_string() - ) + format!("{}\n {}", update_expr_string(block.to_string()), let_stmt) } else { let_stmt.to_string() } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_result_return_type.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_result_return_type.rs index 9ef4ae047ef0e..26f3c192617e1 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_result_return_type.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_result_return_type.rs @@ -5,7 +5,7 @@ use ide_db::{ use itertools::Itertools; use syntax::{ ast::{self, Expr}, - match_ast, AstNode, TextRange, TextSize, + match_ast, AstNode, NodeOrToken, SyntaxKind, TextRange, }; use crate::{AssistContext, AssistId, AssistKind, Assists}; @@ -38,14 +38,15 @@ pub(crate) fn unwrap_result_return_type(acc: &mut Assists, ctx: &AssistContext<' }; let type_ref = &ret_type.ty()?; - let ty = ctx.sema.resolve_type(type_ref)?.as_adt(); + let Some(hir::Adt::Enum(ret_enum)) = ctx.sema.resolve_type(type_ref)?.as_adt() else { return None; }; let result_enum = FamousDefs(&ctx.sema, ctx.sema.scope(type_ref.syntax())?.krate()).core_result_Result()?; - - if !matches!(ty, Some(hir::Adt::Enum(ret_type)) if ret_type == result_enum) { + if ret_enum != result_enum { return None; } + let Some(ok_type) = unwrap_result_type(type_ref) else { return None; }; + acc.add( AssistId("unwrap_result_return_type", AssistKind::RefactorRewrite), "Unwrap Result return type", @@ -64,26 +65,19 @@ pub(crate) fn unwrap_result_return_type(acc: &mut Assists, ctx: &AssistContext<' }); for_each_tail_expr(&body, tail_cb); - let mut is_unit_type = false; - if let Some((_, inner_type)) = type_ref.to_string().split_once('<') { - let inner_type = match inner_type.split_once(',') { - Some((success_inner_type, _)) => success_inner_type, - None => inner_type, - }; - let new_ret_type = inner_type.strip_suffix('>').unwrap_or(inner_type); - if new_ret_type == "()" { - is_unit_type = true; - let text_range = TextRange::new( - ret_type.syntax().text_range().start(), - ret_type.syntax().text_range().end() + TextSize::from(1u32), - ); - builder.delete(text_range) - } else { - builder.replace( - type_ref.syntax().text_range(), - inner_type.strip_suffix('>').unwrap_or(inner_type), - ) + let is_unit_type = is_unit_type(&ok_type); + if is_unit_type { + let mut text_range = ret_type.syntax().text_range(); + + if let Some(NodeOrToken::Token(token)) = ret_type.syntax().next_sibling_or_token() { + if token.kind() == SyntaxKind::WHITESPACE { + text_range = TextRange::new(text_range.start(), token.text_range().end()); + } } + + builder.delete(text_range); + } else { + builder.replace(type_ref.syntax().text_range(), ok_type.syntax().text()); } for ret_expr_arg in exprs_to_unwrap { @@ -134,6 +128,22 @@ fn tail_cb_impl(acc: &mut Vec, e: &ast::Expr) { } } +// Tries to extract `T` from `Result`. +fn unwrap_result_type(ty: &ast::Type) -> Option { + let ast::Type::PathType(path_ty) = ty else { return None; }; + let path = path_ty.path()?; + let segment = path.first_segment()?; + let generic_arg_list = segment.generic_arg_list()?; + let generic_args: Vec<_> = generic_arg_list.generic_args().collect(); + let ast::GenericArg::TypeArg(ok_type) = generic_args.first()? else { return None; }; + ok_type.ty() +} + +fn is_unit_type(ty: &ast::Type) -> bool { + let ast::Type::TupleType(tuple) = ty else { return false }; + tuple.fields().next().is_none() +} + #[cfg(test)] mod tests { use crate::tests::{check_assist, check_assist_not_applicable}; @@ -173,6 +183,21 @@ fn foo() -> Result<(), Box> { r#" fn foo() { } +"#, + ); + + // Unformatted return type + check_assist( + unwrap_result_return_type, + r#" +//- minicore: result +fn foo() -> Result<(), Box>{ + Ok(()) +} +"#, + r#" +fn foo() { +} "#, ); } @@ -1014,6 +1039,54 @@ fn foo(the_field: u32) -> u32 { } the_field } +"#, + ); + } + + #[test] + fn unwrap_result_return_type_nested_type() { + check_assist( + unwrap_result_return_type, + r#" +//- minicore: result, option +fn foo() -> Result, ()> { + Ok(Some(42)) +} +"#, + r#" +fn foo() -> Option { + Some(42) +} +"#, + ); + + check_assist( + unwrap_result_return_type, + r#" +//- minicore: result, option +fn foo() -> Result>, ()> { + Ok(None) +} +"#, + r#" +fn foo() -> Option> { + None +} +"#, + ); + + check_assist( + unwrap_result_return_type, + r#" +//- minicore: result, option, iterators +fn foo() -> Result$0, ()> { + Ok(Some(42).into_iter()) +} +"#, + r#" +fn foo() -> impl Iterator { + Some(42).into_iter() +} "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs index 8b07e29a5879f..bd282e5343409 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs @@ -122,6 +122,7 @@ mod handlers { mod convert_iter_for_each_to_for; mod convert_let_else_to_match; mod convert_match_to_let_else; + mod convert_nested_function_to_closure; mod convert_tuple_struct_to_named_struct; mod convert_named_struct_to_tuple_struct; mod convert_to_guarded_return; @@ -192,6 +193,7 @@ mod handlers { mod replace_arith_op; mod introduce_named_generic; mod replace_let_with_if_let; + mod replace_named_generic_with_impl; mod replace_qualified_name_with_use; mod replace_string_with_char; mod replace_turbofish_with_explicit_type; @@ -228,8 +230,9 @@ mod handlers { convert_iter_for_each_to_for::convert_iter_for_each_to_for, 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_named_struct_to_tuple_struct::convert_named_struct_to_tuple_struct, + convert_nested_function_to_closure::convert_nested_function_to_closure, 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, @@ -297,6 +300,7 @@ mod handlers { replace_let_with_if_let::replace_let_with_if_let, replace_method_eager_lazy::replace_with_eager_method, replace_method_eager_lazy::replace_with_lazy_method, + replace_named_generic_with_impl::replace_named_generic_with_impl, replace_turbofish_with_explicit_type::replace_turbofish_with_explicit_type, replace_qualified_name_with_use::replace_qualified_name_with_use, replace_arith_op::replace_arith_with_wrapping, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs index 94be99fd7abf8..344f2bfcce14a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs @@ -3,7 +3,7 @@ mod generated; mod sourcegen; use expect_test::expect; -use hir::{db::DefDatabase, Semantics}; +use hir::Semantics; use ide_db::{ base_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt}, imports::insert_use::{ImportGranularity, InsertUseConfig}, @@ -161,7 +161,7 @@ fn check_with_config( assist_label: Option<&str>, ) { let (mut db, file_with_caret_id, range_or_offset) = RootDatabase::with_range_or_offset(before); - db.set_enable_proc_attr_macros(true); + db.enable_proc_attr_macros(); let text_without_caret = db.file_text(file_with_caret_id).to_string(); let frange = FileRange { file_id: file_with_caret_id, range: range_or_offset.into() }; @@ -273,8 +273,9 @@ fn assist_order_field_struct() { assert_eq!(assists.next().expect("expected assist").label, "Generate a getter method"); assert_eq!(assists.next().expect("expected assist").label, "Generate a mut getter method"); assert_eq!(assists.next().expect("expected assist").label, "Generate a setter method"); - assert_eq!(assists.next().expect("expected assist").label, "Convert to tuple struct"); assert_eq!(assists.next().expect("expected assist").label, "Add `#[derive]`"); + assert_eq!(assists.next().expect("expected assist").label, "Generate `new`"); + assert_eq!(assists.next().map(|it| it.label.to_string()), None); } #[test] diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs index e5a8d675a9ead..8a35fd290e686 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs @@ -439,7 +439,7 @@ fn doctest_convert_match_to_let_else() { r#####" //- minicore: option fn foo(opt: Option<()>) { - let val = $0match opt { + let val$0 = match opt { Some(it) => it, None => return, }; @@ -494,6 +494,31 @@ impl Point { ) } +#[test] +fn doctest_convert_nested_function_to_closure() { + check_doc_test( + "convert_nested_function_to_closure", + r#####" +fn main() { + fn fo$0o(label: &str, number: u64) { + println!("{}: {}", label, number); + } + + foo("Bar", 100); +} +"#####, + r#####" +fn main() { + let foo = |label: &str, number: u64| { + println!("{}: {}", label, number); + }; + + foo("Bar", 100); +} +"#####, + ) +} + #[test] fn doctest_convert_to_guarded_return() { check_doc_test( @@ -1596,7 +1621,7 @@ fn doctest_introduce_named_generic() { fn foo(bar: $0impl Bar) {} "#####, r#####" -fn foo(bar: B) {} +fn foo<$0B: Bar>(bar: B) {} "#####, ) } @@ -2116,7 +2141,7 @@ trait Foo { } struct Bar; -$0impl Foo for Bar { +$0impl Foo for Bar$0 { const B: u8 = 17; fn c() {} type A = String; @@ -2313,6 +2338,19 @@ fn handle(action: Action) { ) } +#[test] +fn doctest_replace_named_generic_with_impl() { + check_doc_test( + "replace_named_generic_with_impl", + r#####" +fn new>(location: P) -> Self {} +"#####, + r#####" +fn new(location: impl AsRef) -> Self {} +"#####, + ) +} + #[test] fn doctest_replace_qualified_name_with_use() { check_doc_test( @@ -2352,7 +2390,7 @@ fn doctest_replace_try_expr_with_match() { check_doc_test( "replace_try_expr_with_match", r#####" -//- minicore:option +//- minicore: try, option fn handle() { let pat = Some(true)$0?; } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests/sourcegen.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests/sourcegen.rs index b4f50c7fb26a4..3da90e9052f2f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/tests/sourcegen.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests/sourcegen.rs @@ -90,8 +90,6 @@ impl Assist { let comment_blocks = sourcegen::CommentBlock::extract("Assist", &text); for block in comment_blocks { - // FIXME: doesn't support blank lines yet, need to tweak - // `extract_comment_blocks` for that. let id = block.id; assert!( id.chars().all(|it| it.is_ascii_lowercase() || it == '_'), diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs index f323ebcf7a3bd..8f7ea26306c42 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -9,8 +9,8 @@ use stdx::format_to; use syntax::{ ast::{ self, - edit::{self, AstNodeEdit}, - edit_in_place::{AttrsOwnerEdit, Removable}, + edit::{AstNodeEdit, IndentLevel}, + edit_in_place::{AttrsOwnerEdit, Indent, Removable}, make, HasArgList, HasAttrs, HasGenericParams, HasName, HasTypeBounds, Whitespace, }, ted, AstNode, AstToken, Direction, SourceFile, @@ -139,9 +139,11 @@ pub fn add_trait_assoc_items_to_impl( let transform = PathTransform::trait_impl(&target_scope, &source_scope, trait_, impl_.clone()); + let new_indent_level = IndentLevel::from_node(impl_.syntax()) + 1; let items = items.into_iter().map(|assoc_item| { transform.apply(assoc_item.syntax()); assoc_item.remove_attrs_and_docs(); + assoc_item.reindent_to(new_indent_level); assoc_item }); @@ -153,8 +155,10 @@ pub fn add_trait_assoc_items_to_impl( first_item.get_or_insert_with(|| item.clone()); match &item { ast::AssocItem::Fn(fn_) if fn_.body().is_none() => { - let body = make::block_expr(None, Some(make::ext::expr_todo())) - .indent(edit::IndentLevel(1)); + let body = AstNodeEdit::indent( + &make::block_expr(None, Some(make::ext::expr_todo())), + new_indent_level, + ); ted::replace(fn_.get_or_create_body().syntax(), body.clone_for_update().syntax()) } ast::AssocItem::TypeAlias(type_alias) => { @@ -338,7 +342,12 @@ fn calc_depth(pat: &ast::Pat, depth: usize) -> usize { /// `find_struct_impl` looks for impl of a struct, but this also has additional feature /// where it takes a list of function names and check if they exist inside impl_, if -/// even one match is found, it returns None +/// even one match is found, it returns None. +/// +/// That means this function can have 3 potential return values: +/// - `None`: an impl exists, but one of the function names within the impl matches one of the provided names. +/// - `Some(None)`: no impl exists. +/// - `Some(Some(_))`: an impl exists, with no matching function names. pub(crate) fn find_struct_impl( ctx: &AssistContext<'_>, adt: &ast::Adt, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs index c521a10fccfc3..f74ebfae02c77 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs @@ -234,7 +234,7 @@ fn from_type(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option Option { let name = if let Some(adt) = ty.as_adt() { - let name = adt.name(db).to_string(); + let name = adt.name(db).display(db).to_string(); if WRAPPER_TYPES.contains(&name.as_str()) { let inner_ty = ty.type_arguments().next()?; @@ -258,7 +258,7 @@ fn name_of_type(ty: &hir::Type, db: &RootDatabase) -> Option { } fn trait_name(trait_: &hir::Trait, db: &RootDatabase) -> Option { - let name = trait_.name(db).to_string(); + let name = trait_.name(db).display(db).to_string(); if USELESS_TRAITS.contains(&name.as_str()) { return None; } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs index c3136f6df4b39..480cb77b4fd6a 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs @@ -23,8 +23,8 @@ pub(crate) mod env_vars; use std::iter; -use hir::{known, ScopeDef, Variant}; -use ide_db::{imports::import_assets::LocatedImport, SymbolKind}; +use hir::{known, HasAttrs, ScopeDef, Variant}; +use ide_db::{imports::import_assets::LocatedImport, RootDatabase, SymbolKind}; use syntax::ast; use crate::{ @@ -62,8 +62,8 @@ impl From for Vec { impl Builder { /// Convenience method, which allows to add a freshly created completion into accumulator /// without binding it to the variable. - pub(crate) fn add_to(self, acc: &mut Completions) { - acc.add(self.build()) + pub(crate) fn add_to(self, acc: &mut Completions, db: &RootDatabase) { + acc.add(self.build(db)) } } @@ -78,17 +78,9 @@ impl Completions { } } - pub(crate) fn add_all(&mut self, items: I) - where - I: IntoIterator, - I::Item: Into, - { - items.into_iter().for_each(|item| self.add(item.into())) - } - pub(crate) fn add_keyword(&mut self, ctx: &CompletionContext<'_>, keyword: &'static str) { let item = CompletionItem::new(CompletionItemKind::Keyword, ctx.source_range(), keyword); - item.add_to(self); + item.add_to(self, ctx.db); } pub(crate) fn add_nameref_keywords_with_colon(&mut self, ctx: &CompletionContext<'_>) { @@ -142,7 +134,7 @@ impl Completions { item.insert_text(if snippet.contains('$') { kw } else { snippet }); } }; - item.add_to(self); + item.add_to(self, ctx.db); } pub(crate) fn add_keyword_snippet( @@ -157,7 +149,7 @@ impl Completions { Some(cap) => item.insert_snippet(cap, snippet), None => item.insert_text(if snippet.contains('$') { kw } else { snippet }), }; - item.add_to(self); + item.add_to(self, ctx.db); } pub(crate) fn add_crate_roots( @@ -165,9 +157,9 @@ impl Completions { ctx: &CompletionContext<'_>, path_ctx: &PathCompletionCtx, ) { - ctx.process_all_names(&mut |name, res| match res { - ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) if m.is_crate_root(ctx.db) => { - self.add_module(ctx, path_ctx, m, name); + ctx.process_all_names(&mut |name, res, doc_aliases| match res { + ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) if m.is_crate_root() => { + self.add_module(ctx, path_ctx, m, name, doc_aliases); } _ => (), }); @@ -179,7 +171,11 @@ impl Completions { path_ctx: &PathCompletionCtx, local_name: hir::Name, resolution: hir::ScopeDef, + doc_aliases: Vec, ) { + if !ctx.check_stability(resolution.attrs(ctx.db).as_deref()) { + return; + } let is_private_editable = match ctx.def_is_visible(&resolution) { Visible::Yes => false, Visible::Editable => true, @@ -187,12 +183,14 @@ impl Completions { }; self.add( render_path_resolution( - RenderContext::new(ctx).private_editable(is_private_editable), + RenderContext::new(ctx) + .private_editable(is_private_editable) + .doc_aliases(doc_aliases), path_ctx, local_name, resolution, ) - .build(), + .build(ctx.db), ); } @@ -203,6 +201,9 @@ impl Completions { local_name: hir::Name, resolution: hir::ScopeDef, ) { + if !ctx.check_stability(resolution.attrs(ctx.db).as_deref()) { + return; + } let is_private_editable = match ctx.def_is_visible(&resolution) { Visible::Yes => false, Visible::Editable => true, @@ -215,7 +216,7 @@ impl Completions { local_name, resolution, ) - .build(), + .build(ctx.db), ); } @@ -225,6 +226,9 @@ impl Completions { path_ctx: &PathCompletionCtx, e: hir::Enum, ) { + if !ctx.check_stability(Some(&e.attrs(ctx.db))) { + return; + } e.variants(ctx.db) .into_iter() .for_each(|variant| self.add_enum_variant(ctx, path_ctx, variant, None)); @@ -236,12 +240,17 @@ impl Completions { path_ctx: &PathCompletionCtx, module: hir::Module, local_name: hir::Name, + doc_aliases: Vec, ) { + if !ctx.check_stability(Some(&module.attrs(ctx.db))) { + return; + } self.add_path_resolution( ctx, path_ctx, local_name, hir::ScopeDef::ModuleDef(module.into()), + doc_aliases, ); } @@ -252,6 +261,9 @@ impl Completions { mac: hir::Macro, local_name: hir::Name, ) { + if !ctx.check_stability(Some(&mac.attrs(ctx.db))) { + return; + } let is_private_editable = match ctx.is_visible(&mac) { Visible::Yes => false, Visible::Editable => true, @@ -264,7 +276,7 @@ impl Completions { local_name, mac, ) - .build(), + .build(ctx.db), ); } @@ -275,19 +287,25 @@ impl Completions { func: hir::Function, local_name: Option, ) { + if !ctx.check_stability(Some(&func.attrs(ctx.db))) { + return; + } let is_private_editable = match ctx.is_visible(&func) { Visible::Yes => false, Visible::Editable => true, Visible::No => return, }; + let doc_aliases = ctx.doc_aliases(&func); self.add( render_fn( - RenderContext::new(ctx).private_editable(is_private_editable), + RenderContext::new(ctx) + .private_editable(is_private_editable) + .doc_aliases(doc_aliases), path_ctx, local_name, func, ) - .build(), + .build(ctx.db), ); } @@ -299,20 +317,26 @@ impl Completions { receiver: Option, local_name: Option, ) { + if !ctx.check_stability(Some(&func.attrs(ctx.db))) { + return; + } let is_private_editable = match ctx.is_visible(&func) { Visible::Yes => false, Visible::Editable => true, Visible::No => return, }; + let doc_aliases = ctx.doc_aliases(&func); self.add( render_method( - RenderContext::new(ctx).private_editable(is_private_editable), + RenderContext::new(ctx) + .private_editable(is_private_editable) + .doc_aliases(doc_aliases), dot_access, receiver, local_name, func, ) - .build(), + .build(ctx.db), ); } @@ -323,26 +347,34 @@ impl Completions { func: hir::Function, import: LocatedImport, ) { + if !ctx.check_stability(Some(&func.attrs(ctx.db))) { + return; + } let is_private_editable = match ctx.is_visible(&func) { Visible::Yes => false, Visible::Editable => true, Visible::No => return, }; + let doc_aliases = ctx.doc_aliases(&func); self.add( render_method( RenderContext::new(ctx) .private_editable(is_private_editable) + .doc_aliases(doc_aliases) .import_to_add(Some(import)), dot_access, None, None, func, ) - .build(), + .build(ctx.db), ); } pub(crate) fn add_const(&mut self, ctx: &CompletionContext<'_>, konst: hir::Const) { + if !ctx.check_stability(Some(&konst.attrs(ctx.db))) { + return; + } let is_private_editable = match ctx.is_visible(&konst) { Visible::Yes => false, Visible::Editable => true, @@ -359,6 +391,9 @@ impl Completions { ctx: &CompletionContext<'_>, type_alias: hir::TypeAlias, ) { + if !ctx.check_stability(Some(&type_alias.attrs(ctx.db))) { + return; + } let is_private_editable = match ctx.is_visible(&type_alias) { Visible::Yes => false, Visible::Editable => true, @@ -375,6 +410,9 @@ impl Completions { ctx: &CompletionContext<'_>, type_alias: hir::TypeAlias, ) { + if !ctx.check_stability(Some(&type_alias.attrs(ctx.db))) { + return; + } self.add_opt(render_type_alias_with_eq(RenderContext::new(ctx), type_alias)); } @@ -385,10 +423,13 @@ impl Completions { variant: hir::Variant, path: hir::ModPath, ) { + if !ctx.check_stability(Some(&variant.attrs(ctx.db))) { + return; + } if let Some(builder) = render_variant_lit(RenderContext::new(ctx), path_ctx, None, variant, Some(path)) { - self.add(builder.build()); + self.add(builder.build(ctx.db)); } } @@ -399,6 +440,9 @@ impl Completions { variant: hir::Variant, local_name: Option, ) { + if !ctx.check_stability(Some(&variant.attrs(ctx.db))) { + return; + } if let PathCompletionCtx { kind: PathKind::Pat { pat_ctx }, .. } = path_ctx { cov_mark::hit!(enum_variant_pattern_path); self.add_variant_pat(ctx, pat_ctx, Some(path_ctx), variant, local_name); @@ -408,7 +452,7 @@ impl Completions { if let Some(builder) = render_variant_lit(RenderContext::new(ctx), path_ctx, local_name, variant, None) { - self.add(builder.build()); + self.add(builder.build(ctx.db)); } } @@ -420,13 +464,17 @@ impl Completions { field: hir::Field, ty: &hir::Type, ) { + if !ctx.check_stability(Some(&field.attrs(ctx.db))) { + return; + } let is_private_editable = match ctx.is_visible(&field) { Visible::Yes => false, Visible::Editable => true, Visible::No => return, }; + let doc_aliases = ctx.doc_aliases(&field); let item = render_field( - RenderContext::new(ctx).private_editable(is_private_editable), + RenderContext::new(ctx).private_editable(is_private_editable).doc_aliases(doc_aliases), dot_access, receiver, field, @@ -443,10 +491,13 @@ impl Completions { path: Option, local_name: Option, ) { + if !ctx.check_stability(Some(&strukt.attrs(ctx.db))) { + return; + } if let Some(builder) = render_struct_literal(RenderContext::new(ctx), path_ctx, strukt, path, local_name) { - self.add(builder.build()); + self.add(builder.build(ctx.db)); } } @@ -457,6 +508,9 @@ impl Completions { path: Option, local_name: Option, ) { + if !ctx.check_stability(Some(&un.attrs(ctx.db))) { + return; + } let item = render_union_literal(RenderContext::new(ctx), un, path, local_name); self.add_opt(item); } @@ -468,17 +522,20 @@ impl Completions { field: usize, ty: &hir::Type, ) { + // Only used for (unnamed) tuples, whose all fields *are* stable. No need to check + // stability here. let item = render_tuple_field(RenderContext::new(ctx), receiver, field, ty); self.add(item); } pub(crate) fn add_lifetime(&mut self, ctx: &CompletionContext<'_>, name: hir::Name) { CompletionItem::new(SymbolKind::LifetimeParam, ctx.source_range(), name.to_smol_str()) - .add_to(self) + .add_to(self, ctx.db) } pub(crate) fn add_label(&mut self, ctx: &CompletionContext<'_>, name: hir::Name) { - CompletionItem::new(SymbolKind::Label, ctx.source_range(), name.to_smol_str()).add_to(self) + CompletionItem::new(SymbolKind::Label, ctx.source_range(), name.to_smol_str()) + .add_to(self, ctx.db) } pub(crate) fn add_variant_pat( @@ -489,6 +546,9 @@ impl Completions { variant: hir::Variant, local_name: Option, ) { + if !ctx.check_stability(Some(&variant.attrs(ctx.db))) { + return; + } self.add_opt(render_variant_pat( RenderContext::new(ctx), pattern_ctx, @@ -506,6 +566,9 @@ impl Completions { variant: hir::Variant, path: hir::ModPath, ) { + if !ctx.check_stability(Some(&variant.attrs(ctx.db))) { + return; + } let path = Some(&path); self.add_opt(render_variant_pat( RenderContext::new(ctx), @@ -524,6 +587,9 @@ impl Completions { strukt: hir::Struct, local_name: Option, ) { + if !ctx.check_stability(Some(&strukt.attrs(ctx.db))) { + return; + } self.add_opt(render_struct_pat(RenderContext::new(ctx), pattern_ctx, strukt, local_name)); } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs index bb950c76f8831..466f0b1fb7f9b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs @@ -93,7 +93,7 @@ pub(crate) fn complete_attribute_path( acc.add_macro(ctx, path_ctx, m, name) } hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => { - acc.add_module(ctx, path_ctx, m, name) + acc.add_module(ctx, path_ctx, m, name, vec![]) } _ => (), } @@ -104,12 +104,12 @@ pub(crate) fn complete_attribute_path( Qualified::Absolute => acc.add_crate_roots(ctx, path_ctx), // only show modules in a fresh UseTree Qualified::No => { - ctx.process_all_names(&mut |name, def| match def { + ctx.process_all_names(&mut |name, def, doc_aliases| match def { hir::ScopeDef::ModuleDef(hir::ModuleDef::Macro(m)) if m.is_attr(ctx.db) => { acc.add_macro(ctx, path_ctx, m, name) } hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => { - acc.add_module(ctx, path_ctx, m, name) + acc.add_module(ctx, path_ctx, m, name, doc_aliases) } _ => (), }); @@ -139,7 +139,7 @@ pub(crate) fn complete_attribute_path( } if is_inner || !attr_completion.prefer_inner { - item.add_to(acc); + item.add_to(acc, ctx.db); } }; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs index 7ef4ff30b56c7..19bfd294b25cc 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs @@ -12,7 +12,7 @@ pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_>) { let add_completion = |item: &str| { let mut completion = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), item); completion.insert_text(format!(r#""{item}""#)); - acc.add(completion.build()); + acc.add(completion.build(ctx.db)); }; let previous = iter::successors(ctx.original_token.prev_token(), |t| { @@ -33,11 +33,11 @@ pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_>) { let mut item = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), s); item.insert_text(insert_text); - acc.add(item.build()); + acc.add(item.build(ctx.db)); }), None => ctx.krate.potential_cfg(ctx.db).get_cfg_keys().cloned().unique().for_each(|s| { let item = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), s); - acc.add(item.build()); + acc.add(item.build(ctx.db)); }), }; } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs index 793c22630bf89..9447bc7db0a79 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs @@ -34,7 +34,7 @@ pub(crate) fn complete_derive_path( acc.add_macro(ctx, path_ctx, mac, name) } ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => { - acc.add_module(ctx, path_ctx, m, name) + acc.add_module(ctx, path_ctx, m, name, vec![]) } _ => (), } @@ -43,7 +43,7 @@ pub(crate) fn complete_derive_path( Qualified::Absolute => acc.add_crate_roots(ctx, path_ctx), // only show modules in a fresh UseTree Qualified::No => { - ctx.process_all_names(&mut |name, def| { + ctx.process_all_names(&mut |name, def, doc_aliases| { let mac = match def { ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac)) if !existing_derives.contains(&mac) && mac.is_derive(ctx.db) => @@ -51,7 +51,7 @@ pub(crate) fn complete_derive_path( mac } ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => { - return acc.add_module(ctx, path_ctx, m, name); + return acc.add_module(ctx, path_ctx, m, name, doc_aliases); } _ => return, }; @@ -90,7 +90,7 @@ pub(crate) fn complete_derive_path( item.documentation(docs); } item.lookup_by(lookup); - item.add_to(acc); + item.add_to(acc, ctx.db); } None => acc.add_macro(ctx, path_ctx, mac, name), } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs index 818c3cfd5fe7f..6bc6f34ed41b3 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs @@ -56,6 +56,6 @@ pub(super) fn complete_lint( }; let mut item = CompletionItem::new(SymbolKind::Attribute, ctx.source_range(), label); item.documentation(hir::Documentation::new(description.to_owned())); - item.add_to(acc) + item.add_to(acc, ctx.db) } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/repr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/repr.rs index a29417133e68b..14f464b7753d7 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/repr.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/repr.rs @@ -37,7 +37,7 @@ pub(super) fn complete_repr( if let Some((snippet, cap)) = snippet.zip(ctx.config.snippet_cap) { item.insert_snippet(cap, snippet); } - item.add_to(acc); + item.add_to(acc, ctx.db); } } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs index 77246379e7bd9..57a784c45b614 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs @@ -23,7 +23,7 @@ pub(crate) fn complete_dot( let mut item = CompletionItem::new(CompletionItemKind::Keyword, ctx.source_range(), "await"); item.detail("expr.await"); - item.add_to(acc); + item.add_to(acc, ctx.db); } if let DotAccessKind::Method { .. } = dot_access.kind { @@ -172,6 +172,43 @@ fn foo(s: S) { s.$0 } ); } + #[test] + fn no_unstable_method_on_stable() { + check( + r#" +//- /main.rs crate:main deps:std +fn foo(s: std::S) { s.$0 } +//- /std.rs crate:std +pub struct S; +impl S { + #[unstable] + pub fn bar(&self) {} +} +"#, + expect![""], + ); + } + + #[test] + fn unstable_method_on_nightly() { + check( + r#" +//- toolchain:nightly +//- /main.rs crate:main deps:std +fn foo(s: std::S) { s.$0 } +//- /std.rs crate:std +pub struct S; +impl S { + #[unstable] + pub fn bar(&self) {} +} +"#, + expect![[r#" + me bar() fn(&self) + "#]], + ); + } + #[test] fn test_struct_field_completion_self() { check( diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs index 1002be21131d9..419b864565559 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs @@ -37,10 +37,10 @@ pub(crate) fn complete_cargo_env_vars( guard_env_macro(expanded, &ctx.sema)?; let range = expanded.text_range_between_quotes()?; - CARGO_DEFINED_VARS.iter().for_each(|(var, detail)| { + CARGO_DEFINED_VARS.into_iter().for_each(|&(var, detail)| { let mut item = CompletionItem::new(CompletionItemKind::Keyword, range, var); - item.detail(*detail); - item.add_to(acc); + item.detail(detail); + item.add_to(acc, ctx.db); }); Some(()) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs index cfe4787f734d7..9daa6984c3e38 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs @@ -88,7 +88,13 @@ pub(crate) fn complete_expr_path( let module_scope = module.scope(ctx.db, Some(ctx.module)); for (name, def) in module_scope { if scope_def_applicable(def) { - acc.add_path_resolution(ctx, path_ctx, name, def); + acc.add_path_resolution( + ctx, + path_ctx, + name, + def, + ctx.doc_aliases_in_scope(def), + ); } } } @@ -212,7 +218,7 @@ pub(crate) fn complete_expr_path( } } } - ctx.process_all_names(&mut |name, def| match def { + ctx.process_all_names(&mut |name, def, doc_aliases| match def { ScopeDef::ModuleDef(hir::ModuleDef::Trait(t)) => { let assocs = t.items_with_supertraits(ctx.db); match &*assocs { @@ -220,12 +226,14 @@ pub(crate) fn complete_expr_path( // 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), + &[_item] => acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases), // FIXME: Append `::` to the thing here, since a trait on its own won't work - [..] => acc.add_path_resolution(ctx, path_ctx, name, def), + [..] => acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases), } } - _ if scope_def_applicable(def) => acc.add_path_resolution(ctx, path_ctx, name, def), + _ if scope_def_applicable(def) => { + acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases) + } _ => (), }); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_abi.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_abi.rs index 4e89ef6960821..c717a9cb55b8e 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_abi.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_abi.rs @@ -42,7 +42,7 @@ const SUPPORTED_CALLING_CONVENTIONS: &[&str] = &[ pub(crate) fn complete_extern_abi( acc: &mut Completions, - _ctx: &CompletionContext<'_>, + ctx: &CompletionContext<'_>, expanded: &ast::String, ) -> Option<()> { if !expanded.syntax().parent().map_or(false, |it| ast::Abi::can_cast(it.kind())) { @@ -51,7 +51,7 @@ pub(crate) fn complete_extern_abi( let abi_str = expanded; let source_range = abi_str.text_range_between_quotes()?; for &abi in SUPPORTED_CALLING_CONVENTIONS { - CompletionItem::new(CompletionItemKind::Keyword, source_range, abi).add_to(acc); + CompletionItem::new(CompletionItemKind::Keyword, source_range, abi).add_to(acc, ctx.db); } Some(()) } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs index 0979f6a6dfc7d..39c1b7f7b3fb4 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs @@ -257,30 +257,24 @@ fn import_on_the_fly( }; let user_input_lowercased = potential_import_name.to_lowercase(); - acc.add_all( - import_assets - .search_for_imports( - &ctx.sema, - ctx.config.insert_use.prefix_kind, - ctx.config.prefer_no_std, - ) - .into_iter() - .filter(ns_filter) - .filter(|import| { - !ctx.is_item_hidden(&import.item_to_import) - && !ctx.is_item_hidden(&import.original_item) - }) - .sorted_by_key(|located_import| { - compute_fuzzy_completion_order_key( - &located_import.import_path, - &user_input_lowercased, - ) - }) - .filter_map(|import| { - render_resolution_with_import(RenderContext::new(ctx), path_ctx, import) - }) - .map(|builder| builder.build()), - ); + import_assets + .search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind, ctx.config.prefer_no_std) + .into_iter() + .filter(ns_filter) + .filter(|import| { + let original_item = &import.original_item; + !ctx.is_item_hidden(&import.item_to_import) + && !ctx.is_item_hidden(original_item) + && ctx.check_stability(original_item.attrs(ctx.db).as_deref()) + }) + .sorted_by_key(|located_import| { + compute_fuzzy_completion_order_key(&located_import.import_path, &user_input_lowercased) + }) + .filter_map(|import| { + render_resolution_with_import(RenderContext::new(ctx), path_ctx, import) + }) + .map(|builder| builder.build(ctx.db)) + .for_each(|item| acc.add(item)); Some(()) } @@ -305,30 +299,24 @@ fn import_on_the_fly_pat_( }; let user_input_lowercased = potential_import_name.to_lowercase(); - acc.add_all( - import_assets - .search_for_imports( - &ctx.sema, - ctx.config.insert_use.prefix_kind, - ctx.config.prefer_no_std, - ) - .into_iter() - .filter(ns_filter) - .filter(|import| { - !ctx.is_item_hidden(&import.item_to_import) - && !ctx.is_item_hidden(&import.original_item) - }) - .sorted_by_key(|located_import| { - compute_fuzzy_completion_order_key( - &located_import.import_path, - &user_input_lowercased, - ) - }) - .filter_map(|import| { - render_resolution_with_import_pat(RenderContext::new(ctx), pattern_ctx, import) - }) - .map(|builder| builder.build()), - ); + import_assets + .search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind, ctx.config.prefer_no_std) + .into_iter() + .filter(ns_filter) + .filter(|import| { + let original_item = &import.original_item; + !ctx.is_item_hidden(&import.item_to_import) + && !ctx.is_item_hidden(original_item) + && ctx.check_stability(original_item.attrs(ctx.db).as_deref()) + }) + .sorted_by_key(|located_import| { + compute_fuzzy_completion_order_key(&located_import.import_path, &user_input_lowercased) + }) + .filter_map(|import| { + render_resolution_with_import_pat(RenderContext::new(ctx), pattern_ctx, import) + }) + .map(|builder| builder.build(ctx.db)) + .for_each(|item| acc.add(item)); Some(()) } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs index d8b8a190eb840..8b38d4f01f673 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs @@ -40,7 +40,7 @@ pub(crate) fn complete_fn_param( }; // Completion lookup is omitted intentionally here. // See the full discussion: https://github.com/rust-lang/rust-analyzer/issues/12073 - item.add_to(acc) + item.add_to(acc, ctx.db) }; match kind { @@ -50,7 +50,7 @@ pub(crate) fn complete_fn_param( ParamKind::Closure(closure) => { let stmt_list = closure.syntax().ancestors().find_map(ast::StmtList::cast)?; params_from_stmt_list_scope(ctx, stmt_list, |name, ty| { - add_new_item_to_acc(&format!("{name}: {ty}")); + add_new_item_to_acc(&format!("{}: {ty}", name.display(ctx.db))); }); } } @@ -100,7 +100,9 @@ fn fill_fn_params( if let Some(stmt_list) = function.syntax().parent().and_then(ast::StmtList::cast) { params_from_stmt_list_scope(ctx, stmt_list, |name, ty| { - file_params.entry(format!("{name}: {ty}")).or_insert(name.to_string()); + file_params + .entry(format!("{}: {ty}", name.display(ctx.db))) + .or_insert(name.display(ctx.db).to_string()); }); } remove_duplicated(&mut file_params, param_list.params()); @@ -127,7 +129,7 @@ fn params_from_stmt_list_scope( let module = scope.module().into(); scope.process_all_names(&mut |name, def| { if let hir::ScopeDef::Local(local) = def { - if let Ok(ty) = local.ty(ctx.db).display_source_code(ctx.db, module) { + if let Ok(ty) = local.ty(ctx.db).display_source_code(ctx.db, module, true) { cb(name, ty); } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs index 5c46c5806e65e..8e904fd605a8d 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs @@ -32,7 +32,7 @@ pub(crate) fn format_string( let source_range = TextRange::new(brace_offset, cursor); ctx.locals.iter().for_each(|(name, _)| { CompletionItem::new(CompletionItemKind::Binding, source_range, name.to_smol_str()) - .add_to(acc); + .add_to(acc, ctx.db); }) } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list.rs index 60d05ae46b916..5ea6a49b1ae15 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list.rs @@ -45,7 +45,7 @@ pub(crate) fn complete_item_list( acc.add_macro(ctx, path_ctx, m, name) } hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => { - acc.add_module(ctx, path_ctx, m, name) + acc.add_module(ctx, path_ctx, m, name, vec![]) } _ => (), } @@ -55,12 +55,12 @@ pub(crate) fn complete_item_list( } Qualified::Absolute => acc.add_crate_roots(ctx, path_ctx), Qualified::No if ctx.qualifier_ctx.none() => { - ctx.process_all_names(&mut |name, def| match def { + ctx.process_all_names(&mut |name, def, doc_aliases| match def { hir::ScopeDef::ModuleDef(hir::ModuleDef::Macro(m)) if m.is_fn_like(ctx.db) => { acc.add_macro(ctx, path_ctx, m, name) } hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => { - acc.add_module(ctx, path_ctx, m, name) + acc.add_module(ctx, path_ctx, m, name, doc_aliases) } _ => (), }); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs index 889d90095fab1..7de1bf2dc131f 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs @@ -150,21 +150,24 @@ fn complete_trait_impl( impl_def: &ast::Impl, ) { if let Some(hir_impl) = ctx.sema.to_def(impl_def) { - get_missing_assoc_items(&ctx.sema, impl_def).into_iter().for_each(|item| { - use self::ImplCompletionKind::*; - match (item, kind) { - (hir::AssocItem::Function(func), All | Fn) => { - add_function_impl(acc, ctx, replacement_range, func, hir_impl) + get_missing_assoc_items(&ctx.sema, impl_def) + .into_iter() + .filter(|item| ctx.check_stability(Some(&item.attrs(ctx.db)))) + .for_each(|item| { + use self::ImplCompletionKind::*; + match (item, kind) { + (hir::AssocItem::Function(func), All | Fn) => { + 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, hir_impl) + } + (hir::AssocItem::Const(const_), All | Const) => { + add_const_impl(acc, ctx, replacement_range, const_, hir_impl) + } + _ => {} } - (hir::AssocItem::TypeAlias(type_alias), All | TypeAlias) => { - 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) - } - _ => {} - } - }); + }); } } @@ -179,7 +182,7 @@ fn add_function_impl( let label = format!( "fn {}({})", - fn_name, + fn_name.display(ctx.db), if func.assoc_fn_params(ctx.db).is_empty() { "" } else { ".." } ); @@ -190,7 +193,7 @@ fn add_function_impl( }; let mut item = CompletionItem::new(completion_kind, replacement_range, label); - item.lookup_by(format!("fn {fn_name}")) + item.lookup_by(format!("fn {}", fn_name.display(ctx.db))) .set_documentation(func.docs(ctx.db)) .set_relevance(CompletionRelevance { is_item_from_trait: true, ..Default::default() }); @@ -213,7 +216,7 @@ fn add_function_impl( item.text_edit(TextEdit::replace(replacement_range, header)); } }; - item.add_to(acc); + item.add_to(acc, ctx.db); } } } @@ -297,7 +300,7 @@ fn add_type_alias_impl( item.text_edit(TextEdit::replace(replacement_range, decl)); } }; - item.add_to(acc); + item.add_to(acc, ctx.db); } } } @@ -337,7 +340,7 @@ fn add_const_impl( ), None => item.text_edit(TextEdit::replace(replacement_range, replacement)), }; - item.add_to(acc); + item.add_to(acc, ctx.db); } } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/lifetime.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/lifetime.rs index 3b79def639df4..2c6cbf6146a95 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/lifetime.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/lifetime.rs @@ -329,6 +329,7 @@ fn foo() { fn complete_label_in_for_iterable() { check( r#" +//- minicore: iterator fn foo() { 'outer: for _ in [{ 'inner: loop { break '$0 } }] {} } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs index 950731eb4ca8c..d3e75c6da4761 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs @@ -52,7 +52,7 @@ pub(crate) fn complete_mod( let existing_mod_declarations = current_module .children(ctx.db) - .filter_map(|module| Some(module.name(ctx.db)?.to_string())) + .filter_map(|module| Some(module.name(ctx.db)?.display(ctx.db).to_string())) .filter(|module| module != ctx.original_token.text()) .collect::>(); @@ -99,7 +99,7 @@ pub(crate) fn complete_mod( label.push(';'); } let item = CompletionItem::new(SymbolKind::Module, ctx.source_range(), &label); - item.add_to(acc) + item.add_to(acc, ctx.db) }); Some(()) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs index 58d5bf114cca4..40b2c831a5d01 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs @@ -64,7 +64,7 @@ pub(crate) fn complete_pattern( // FIXME: ideally, we should look at the type we are matching against and // suggest variants + auto-imports - ctx.process_all_names(&mut |name, res| { + ctx.process_all_names(&mut |name, res, _| { let add_simple_path = match res { hir::ScopeDef::ModuleDef(def) => match def { hir::ModuleDef::Adt(hir::Adt::Struct(strukt)) => { @@ -127,7 +127,7 @@ pub(crate) fn complete_pattern_path( }; if add_resolution { - acc.add_path_resolution(ctx, path_ctx, name, def); + acc.add_path_resolution(ctx, path_ctx, name, def, vec![]); } } } @@ -164,7 +164,7 @@ pub(crate) fn complete_pattern_path( Qualified::Absolute => acc.add_crate_roots(ctx, path_ctx), Qualified::No => { // this will only be hit if there are brackets or braces, otherwise this will be parsed as an ident pattern - ctx.process_all_names(&mut |name, res| { + ctx.process_all_names(&mut |name, res, doc_aliases| { // FIXME: we should check what kind of pattern we are in and filter accordingly let add_completion = match res { ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac)) => mac.is_fn_like(ctx.db), @@ -175,7 +175,7 @@ pub(crate) fn complete_pattern_path( _ => false, }; if add_completion { - acc.add_path_resolution(ctx, path_ctx, name, res); + acc.add_path_resolution(ctx, path_ctx, name, res, doc_aliases); } }); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs index c55bd9aaae521..2ffe123374471 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs @@ -64,7 +64,7 @@ pub(crate) fn complete_postfix( &format!("drop($0{receiver_text})"), ); item.set_documentation(drop_fn.docs(ctx.db)); - item.add_to(acc); + item.add_to(acc, ctx.db); } } } @@ -78,14 +78,14 @@ pub(crate) fn complete_postfix( "if let Ok {}", &format!("if let Ok($1) = {receiver_text} {{\n $0\n}}"), ) - .add_to(acc); + .add_to(acc, ctx.db); postfix_snippet( "while", "while let Ok {}", &format!("while let Ok($1) = {receiver_text} {{\n $0\n}}"), ) - .add_to(acc); + .add_to(acc, ctx.db); } TryEnum::Option => { postfix_snippet( @@ -93,22 +93,22 @@ pub(crate) fn complete_postfix( "if let Some {}", &format!("if let Some($1) = {receiver_text} {{\n $0\n}}"), ) - .add_to(acc); + .add_to(acc, ctx.db); postfix_snippet( "while", "while let Some {}", &format!("while let Some($1) = {receiver_text} {{\n $0\n}}"), ) - .add_to(acc); + .add_to(acc, ctx.db); } } } else if receiver_ty.is_bool() || receiver_ty.is_unknown() { postfix_snippet("if", "if expr {}", &format!("if {receiver_text} {{\n $0\n}}")) - .add_to(acc); + .add_to(acc, ctx.db); postfix_snippet("while", "while expr {}", &format!("while {receiver_text} {{\n $0\n}}")) - .add_to(acc); - postfix_snippet("not", "!expr", &format!("!{receiver_text}")).add_to(acc); + .add_to(acc, ctx.db); + postfix_snippet("not", "!expr", &format!("!{receiver_text}")).add_to(acc, ctx.db); } else if let Some(trait_) = ctx.famous_defs().core_iter_IntoIterator() { if receiver_ty.impls_trait(ctx.db, trait_, &[]) { postfix_snippet( @@ -116,12 +116,12 @@ pub(crate) fn complete_postfix( "for ele in expr {}", &format!("for ele in {receiver_text} {{\n $0\n}}"), ) - .add_to(acc); + .add_to(acc, ctx.db); } } - postfix_snippet("ref", "&expr", &format!("&{receiver_text}")).add_to(acc); - postfix_snippet("refm", "&mut expr", &format!("&mut {receiver_text}")).add_to(acc); + postfix_snippet("ref", "&expr", &format!("&{receiver_text}")).add_to(acc, ctx.db); + postfix_snippet("refm", "&mut expr", &format!("&mut {receiver_text}")).add_to(acc, ctx.db); let mut unsafe_should_be_wrapped = true; if dot_receiver.syntax().kind() == BLOCK_EXPR { @@ -137,7 +137,7 @@ pub(crate) fn complete_postfix( } else { format!("unsafe {receiver_text}") }; - postfix_snippet("unsafe", "unsafe {}", &unsafe_completion_string).add_to(acc); + postfix_snippet("unsafe", "unsafe {}", &unsafe_completion_string).add_to(acc, ctx.db); // The rest of the postfix completions create an expression that moves an argument, // so it's better to consider references now to avoid breaking the compilation @@ -162,7 +162,7 @@ pub(crate) fn complete_postfix( "match expr {}", &format!("match {receiver_text} {{\n Ok(${{1:_}}) => {{$2}},\n Err(${{3:_}}) => {{$0}},\n}}"), ) - .add_to(acc); + .add_to(acc, ctx.db); } TryEnum::Option => { postfix_snippet( @@ -172,7 +172,7 @@ pub(crate) fn complete_postfix( "match {receiver_text} {{\n Some(${{1:_}}) => {{$2}},\n None => {{$0}},\n}}" ), ) - .add_to(acc); + .add_to(acc, ctx.db); } }, None => { @@ -181,20 +181,23 @@ pub(crate) fn complete_postfix( "match expr {}", &format!("match {receiver_text} {{\n ${{1:_}} => {{$0}},\n}}"), ) - .add_to(acc); + .add_to(acc, ctx.db); } } - postfix_snippet("box", "Box::new(expr)", &format!("Box::new({receiver_text})")).add_to(acc); - postfix_snippet("dbg", "dbg!(expr)", &format!("dbg!({receiver_text})")).add_to(acc); // fixme - postfix_snippet("dbgr", "dbg!(&expr)", &format!("dbg!(&{receiver_text})")).add_to(acc); - postfix_snippet("call", "function(expr)", &format!("${{1}}({receiver_text})")).add_to(acc); + postfix_snippet("box", "Box::new(expr)", &format!("Box::new({receiver_text})")) + .add_to(acc, ctx.db); + postfix_snippet("dbg", "dbg!(expr)", &format!("dbg!({receiver_text})")).add_to(acc, ctx.db); // fixme + postfix_snippet("dbgr", "dbg!(&expr)", &format!("dbg!(&{receiver_text})")).add_to(acc, ctx.db); + postfix_snippet("call", "function(expr)", &format!("${{1}}({receiver_text})")) + .add_to(acc, ctx.db); if let Some(parent) = dot_receiver.syntax().parent().and_then(|p| p.parent()) { if matches!(parent.kind(), STMT_LIST | EXPR_STMT) { - postfix_snippet("let", "let", &format!("let $0 = {receiver_text};")).add_to(acc); + postfix_snippet("let", "let", &format!("let $0 = {receiver_text};")) + .add_to(acc, ctx.db); postfix_snippet("letm", "let mut", &format!("let mut $0 = {receiver_text};")) - .add_to(acc); + .add_to(acc, ctx.db); } } @@ -315,7 +318,7 @@ fn add_custom_postfix_completions( for import in imports.into_iter() { builder.add_import(import); } - builder.add_to(acc); + builder.add_to(acc, ctx.db); }, ); None diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs index dfcc78e92308d..cb242e4aa6869 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs @@ -60,7 +60,7 @@ pub(crate) fn add_format_like_completions( format!(r#"{}({}, {})"#, macro_name, out, exprs.join(", ")) }; - postfix_snippet(label, macro_name, &snippet).add_to(acc); + postfix_snippet(label, macro_name, &snippet).add_to(acc, ctx.db); } } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs index 0521e735dedf1..945c3945bfa39 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs @@ -69,7 +69,7 @@ pub(crate) fn complete_record_expr_fields( let mut item = CompletionItem::new(CompletionItemKind::Snippet, ctx.source_range(), ".."); item.insert_text("."); - item.add_to(acc); + item.add_to(acc, ctx.db); return; } missing_fields @@ -98,7 +98,7 @@ pub(crate) fn add_default_update( postfix_match: Some(CompletionRelevancePostfixMatch::Exact), ..Default::default() }); - item.add_to(acc); + item.add_to(acc, ctx.db); } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs index da1f0542d286f..e9831a5b2a121 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs @@ -32,8 +32,8 @@ pub(crate) fn complete_expr_snippet( } if in_block_expr { - snippet(ctx, cap, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc); - snippet(ctx, cap, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc); + snippet(ctx, cap, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc, ctx.db); + snippet(ctx, cap, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc, ctx.db); let item = snippet( ctx, cap, @@ -45,7 +45,7 @@ macro_rules! $1 { }; }", ); - item.add_to(acc); + item.add_to(acc, ctx.db); } } @@ -88,7 +88,7 @@ mod tests { }", ); item.lookup_by("tmod"); - item.add_to(acc); + item.add_to(acc, ctx.db); let mut item = snippet( ctx, @@ -101,7 +101,7 @@ fn ${1:feature}() { }", ); item.lookup_by("tfn"); - item.add_to(acc); + item.add_to(acc, ctx.db); let item = snippet( ctx, @@ -114,7 +114,7 @@ macro_rules! $1 { }; }", ); - item.add_to(acc); + item.add_to(acc, ctx.db); } } @@ -146,7 +146,7 @@ fn add_custom_completions( builder.add_import(import); } builder.set_detail(snip.description.clone()); - builder.add_to(acc); + builder.add_to(acc, ctx.db); }, ); None diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs index 69c05a76df4ce..e47054756389b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs @@ -85,7 +85,7 @@ pub(crate) fn complete_type_path( let module_scope = module.scope(ctx.db, Some(ctx.module)); for (name, def) in module_scope { if scope_def_applicable(def) { - acc.add_path_resolution(ctx, path_ctx, name, def); + acc.add_path_resolution(ctx, path_ctx, name, def, vec![]); } } } @@ -141,7 +141,7 @@ pub(crate) fn complete_type_path( match location { TypeLocation::TypeBound => { acc.add_nameref_keywords_with_colon(ctx); - ctx.process_all_names(&mut |name, res| { + ctx.process_all_names(&mut |name, res, doc_aliases| { let add_resolution = match res { ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac)) => { mac.is_fn_like(ctx.db) @@ -152,7 +152,7 @@ pub(crate) fn complete_type_path( _ => false, }; if add_resolution { - acc.add_path_resolution(ctx, path_ctx, name, res); + acc.add_path_resolution(ctx, path_ctx, name, res, doc_aliases); } }); return; @@ -215,9 +215,9 @@ pub(crate) fn complete_type_path( }; acc.add_nameref_keywords_with_colon(ctx); - ctx.process_all_names(&mut |name, def| { + ctx.process_all_names(&mut |name, def, doc_aliases| { if scope_def_applicable(def) { - acc.add_path_resolution(ctx, path_ctx, name, def); + acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases); } }); } @@ -242,7 +242,7 @@ pub(crate) fn complete_ascribed_type( } }? .adjusted(); - let ty_string = x.display_source_code(ctx.db, ctx.module.into()).ok()?; + let ty_string = x.display_source_code(ctx.db, ctx.module.into(), true).ok()?; acc.add(render_type_inference(ty_string, ctx)); None } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs index 2555c34aa7477..7a60030e9ef72 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs @@ -52,6 +52,9 @@ pub(crate) fn complete_use_path( ) }; for (name, def) in module_scope { + if !ctx.check_stability(def.attrs(ctx.db).as_deref()) { + continue; + } let is_name_already_imported = name .as_text() .map_or(false, |text| already_imported_names.contains(text.as_str())); @@ -72,7 +75,7 @@ pub(crate) fn complete_use_path( is_name_already_imported, ..Default::default() }); - acc.add(builder.build()); + acc.add(builder.build(ctx.db)); } } } @@ -91,10 +94,10 @@ pub(crate) fn complete_use_path( // only show modules and non-std enum in a fresh UseTree Qualified::No => { cov_mark::hit!(unqualified_path_selected_only); - ctx.process_all_names(&mut |name, res| { + ctx.process_all_names(&mut |name, res, doc_aliases| { match res { ScopeDef::ModuleDef(hir::ModuleDef::Module(module)) => { - acc.add_module(ctx, path_ctx, module, name); + acc.add_module(ctx, path_ctx, module, name, doc_aliases); } ScopeDef::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Enum(e))) => { // exclude prelude enum @@ -105,9 +108,9 @@ pub(crate) fn complete_use_path( let item = CompletionItem::new( CompletionItemKind::SymbolKind(SymbolKind::Enum), ctx.source_range(), - format!("{}::", e.name(ctx.db)), + format!("{}::", e.name(ctx.db).display(ctx.db)), ); - acc.add(item.build()); + acc.add(item.build(ctx.db)); } } _ => {} diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/vis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/vis.rs index 5e6cf4bf9a521..e0a959ad0b340 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/vis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/vis.rs @@ -23,7 +23,7 @@ pub(crate) fn complete_vis_path( if let Some(next) = next_towards_current { if let Some(name) = next.name(ctx.db) { cov_mark::hit!(visibility_qualified); - acc.add_module(ctx, path_ctx, next, name); + acc.add_module(ctx, path_ctx, next, name, vec![]); } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs index 8cbf89e9c3019..7b145f3c14e32 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -17,7 +17,7 @@ use ide_db::{ }; use syntax::{ ast::{self, AttrKind, NameOrNameRef}, - AstNode, + AstNode, SmolStr, SyntaxKind::{self, *}, SyntaxToken, TextRange, TextSize, T, }; @@ -367,6 +367,8 @@ pub(crate) struct CompletionContext<'a> { pub(super) krate: hir::Crate, /// The module of the `scope`. pub(super) module: hir::Module, + /// Whether nightly toolchain is used. Cached since this is looked up a lot. + is_nightly: bool, /// The expected name of what we are completing. /// This is usually the parameter name of the function argument we are completing. @@ -386,7 +388,7 @@ pub(crate) struct CompletionContext<'a> { pub(super) depth_from_crate_root: usize, } -impl<'a> CompletionContext<'a> { +impl CompletionContext<'_> { /// The range of the identifier that is being completed. pub(crate) fn source_range(&self) -> TextRange { let kind = self.original_token.kind(); @@ -441,6 +443,14 @@ impl<'a> CompletionContext<'a> { self.is_visible_impl(&vis, &attrs, item.krate(self.db)) } + pub(crate) fn doc_aliases(&self, item: &I) -> Vec + where + I: hir::HasAttrs + Copy, + { + let attrs = item.attrs(self.db); + attrs.doc_aliases().collect() + } + /// Check if an item is `#[doc(hidden)]`. pub(crate) fn is_item_hidden(&self, item: &hir::ItemInNs) -> bool { let attrs = item.attrs(self.db); @@ -451,6 +461,12 @@ impl<'a> CompletionContext<'a> { } } + /// Checks whether this item should be listed in regards to stability. Returns `true` if we should. + pub(crate) fn check_stability(&self, attrs: Option<&hir::Attrs>) -> bool { + let Some(attrs) = attrs else { return true; }; + !attrs.is_unstable() || self.is_nightly + } + /// Whether the given trait is an operator trait or not. pub(crate) fn is_ops_trait(&self, trait_: hir::Trait) -> bool { match trait_.attrs(self.db).lang() { @@ -491,21 +507,22 @@ impl<'a> CompletionContext<'a> { ); } - /// A version of [`SemanticsScope::process_all_names`] that filters out `#[doc(hidden)]` items. - pub(crate) fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef)) { + /// A version of [`SemanticsScope::process_all_names`] that filters out `#[doc(hidden)]` items and + /// passes all doc-aliases along, to funnel it into [`Completions::add_path_resolution`]. + pub(crate) fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef, Vec)) { let _p = profile::span("CompletionContext::process_all_names"); self.scope.process_all_names(&mut |name, def| { if self.is_scope_def_hidden(def) { return; } - - f(name, def); + let doc_aliases = self.doc_aliases_in_scope(def); + f(name, def, doc_aliases); }); } pub(crate) fn process_all_names_raw(&self, f: &mut dyn FnMut(Name, ScopeDef)) { let _p = profile::span("CompletionContext::process_all_names_raw"); - self.scope.process_all_names(&mut |name, def| f(name, def)); + self.scope.process_all_names(f); } fn is_scope_def_hidden(&self, scope_def: ScopeDef) -> bool { @@ -545,6 +562,14 @@ impl<'a> CompletionContext<'a> { // `doc(hidden)` items are only completed within the defining crate. self.krate != defining_crate && attrs.has_doc_hidden() } + + pub(crate) fn doc_aliases_in_scope(&self, scope_def: ScopeDef) -> Vec { + if let Some(attrs) = scope_def.attrs(self.db) { + attrs.doc_aliases().collect() + } else { + vec![] + } + } } // CompletionContext construction @@ -615,6 +640,11 @@ impl<'a> CompletionContext<'a> { let krate = scope.krate(); let module = scope.module(); + let toolchain = db.crate_graph()[krate.into()].channel; + // `toolchain == None` means we're in some detached files. Since we have no information on + // the toolchain being used, let's just allow unstable items to be listed. + let is_nightly = matches!(toolchain, Some(base_db::ReleaseChannel::Nightly) | None); + let mut locals = FxHashMap::default(); scope.process_all_names(&mut |name, scope| { if let ScopeDef::Local(local) = scope { @@ -634,6 +664,7 @@ impl<'a> CompletionContext<'a> { token, krate, module, + is_nightly, expected_name, expected_type, qualifier_ctx, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index a94c404586b11..cc5221cfccbdc 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -609,14 +609,14 @@ fn classify_name_ref( _ => false, }; - let reciever_is_part_of_indivisible_expression = match &receiver { + let receiver_is_part_of_indivisible_expression = match &receiver { Some(ast::Expr::IfExpr(_)) => { let next_token_kind = next_non_trivia_token(name_ref.syntax().clone()).map(|t| t.kind()); next_token_kind == Some(SyntaxKind::ELSE_KW) }, _ => false }; - if reciever_is_part_of_indivisible_expression { + if receiver_is_part_of_indivisible_expression { return None; } @@ -1190,7 +1190,7 @@ fn pattern_context_for( }) }).and_then(|variants| { Some(variants.iter().filter_map(|variant| { - let variant_name = variant.name(sema.db).to_string(); + let variant_name = variant.name(sema.db).display(sema.db).to_string(); let variant_already_present = match_arm_list.arms().any(|arm| { arm.pat().and_then(|pat| { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs index bb9fa7ccaccc6..e850f7bfdf3f9 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs @@ -3,7 +3,8 @@ use std::fmt; use hir::{Documentation, Mutability}; -use ide_db::{imports::import_assets::LocatedImport, SnippetCap, SymbolKind}; +use ide_db::{imports::import_assets::LocatedImport, RootDatabase, SnippetCap, SymbolKind}; +use itertools::Itertools; use smallvec::SmallVec; use stdx::{impl_from, never}; use syntax::{SmolStr, TextRange, TextSize}; @@ -45,7 +46,7 @@ pub struct CompletionItem { /// /// That is, in `foo.bar$0` lookup of `abracadabra` will be accepted (it /// contains `bar` sub sequence), and `quux` will rejected. - pub lookup: Option, + pub lookup: SmolStr, /// Additional info to show in the UI pop up. pub detail: Option, @@ -75,7 +76,8 @@ pub struct CompletionItem { pub ref_match: Option<(Mutability, TextSize)>, /// The import data to add to completion's edits. - pub import_to_add: SmallVec<[LocatedImport; 1]>, + /// (ImportPath, LastSegment) + pub import_to_add: SmallVec<[(String, String); 1]>, } // We use custom debug for CompletionItem to make snapshot tests more readable. @@ -353,12 +355,13 @@ impl CompletionItem { relevance: CompletionRelevance::default(), ref_match: None, imports_to_add: Default::default(), + doc_aliases: vec![], } } /// What string is used for filtering. pub fn lookup(&self) -> &str { - self.lookup.as_deref().unwrap_or(&self.label) + self.lookup.as_str() } pub fn ref_match(&self) -> Option<(String, text_edit::Indel, CompletionRelevance)> { @@ -385,6 +388,7 @@ pub(crate) struct Builder { source_range: TextRange, imports_to_add: SmallVec<[LocatedImport; 1]>, trait_name: Option, + doc_aliases: Vec, label: SmolStr, insert_text: Option, is_snippet: bool, @@ -406,21 +410,31 @@ impl Builder { local_name: hir::Name, resolution: hir::ScopeDef, ) -> Self { - render_path_resolution(RenderContext::new(ctx), path_ctx, local_name, resolution) + let doc_aliases = ctx.doc_aliases_in_scope(resolution); + render_path_resolution( + RenderContext::new(ctx).doc_aliases(doc_aliases), + path_ctx, + local_name, + resolution, + ) } - pub(crate) fn build(self) -> CompletionItem { + pub(crate) fn build(self, db: &RootDatabase) -> CompletionItem { let _p = profile::span("item::Builder::build"); let mut label = self.label; - let mut lookup = self.lookup; + let mut lookup = self.lookup.unwrap_or_else(|| label.clone()); let insert_text = self.insert_text.unwrap_or_else(|| label.to_string()); + if !self.doc_aliases.is_empty() { + let doc_aliases = self.doc_aliases.into_iter().join(", "); + label = SmolStr::from(format!("{label} (alias {doc_aliases})")); + lookup = SmolStr::from(format!("{lookup} {doc_aliases}")); + } if let [import_edit] = &*self.imports_to_add { // snippets can have multiple imports, but normal completions only have up to one if let Some(original_path) = import_edit.original_path.as_ref() { - lookup = lookup.or_else(|| Some(label.clone())); - label = SmolStr::from(format!("{label} (use {original_path})")); + label = SmolStr::from(format!("{label} (use {})", original_path.display(db))); } } else if let Some(trait_name) = self.trait_name { label = SmolStr::from(format!("{label} (as {trait_name})")); @@ -431,6 +445,17 @@ impl Builder { None => TextEdit::replace(self.source_range, insert_text), }; + let import_to_add = self + .imports_to_add + .into_iter() + .filter_map(|import| { + Some(( + import.import_path.display(db).to_string(), + import.import_path.segments().last()?.display(db).to_string(), + )) + }) + .collect(); + CompletionItem { source_range: self.source_range, label, @@ -444,7 +469,7 @@ impl Builder { trigger_call_info: self.trigger_call_info, relevance: self.relevance, ref_match: self.ref_match, - import_to_add: self.imports_to_add, + import_to_add, } } pub(crate) fn lookup_by(&mut self, lookup: impl Into) -> &mut Builder { @@ -459,6 +484,10 @@ impl Builder { self.trait_name = Some(trait_name); self } + pub(crate) fn doc_aliases(&mut self, doc_aliases: Vec) -> &mut Builder { + self.doc_aliases = doc_aliases; + self + } pub(crate) fn insert_text(&mut self, insert_text: impl Into) -> &mut Builder { self.insert_text = Some(insert_text.into()); self diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs index 6fe7811140399..106d4e1e52f9a 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs @@ -97,7 +97,7 @@ pub use crate::{ /// Main entry point for completion. We run completion as a two-phase process. /// -/// First, we look at the position and collect a so-called `CompletionContext. +/// First, we look at the position and collect a so-called `CompletionContext`. /// This is a somewhat messy process, because, during completion, syntax tree is /// incomplete and can look really weird. /// @@ -133,7 +133,7 @@ pub use crate::{ /// /// Another case where this would be instrumental is macro expansion. We want to /// insert a fake ident and re-expand code. There's `expand_speculative` as a -/// work-around for this. +/// workaround for this. /// /// A different use-case is completion of injection (examples and links in doc /// comments). When computing completion for a path in a doc-comment, you want @@ -243,7 +243,7 @@ pub fn resolve_completion_edits( config.prefer_no_std, ) }) - .find(|mod_path| mod_path.to_string() == full_import_path); + .find(|mod_path| mod_path.display(db).to_string() == full_import_path); if let Some(import_path) = import { insert_use::insert_use(&new_ast, mod_path_to_ast(&import_path), &config.insert_use); } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index c1f51aabb9676..1953eb4795772 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -32,11 +32,17 @@ pub(crate) struct RenderContext<'a> { completion: &'a CompletionContext<'a>, is_private_editable: bool, import_to_add: Option, + doc_aliases: Vec, } impl<'a> RenderContext<'a> { pub(crate) fn new(completion: &'a CompletionContext<'a>) -> RenderContext<'a> { - RenderContext { completion, is_private_editable: false, import_to_add: None } + RenderContext { + completion, + is_private_editable: false, + import_to_add: None, + doc_aliases: vec![], + } } pub(crate) fn private_editable(mut self, private_editable: bool) -> Self { @@ -49,6 +55,11 @@ impl<'a> RenderContext<'a> { self } + pub(crate) fn doc_aliases(mut self, doc_aliases: Vec) -> Self { + self.doc_aliases = doc_aliases; + self + } + fn snippet_cap(&self) -> Option { self.completion.config.snippet_cap } @@ -115,24 +126,25 @@ pub(crate) fn render_field( field: hir::Field, ty: &hir::Type, ) -> CompletionItem { + let db = ctx.db(); let is_deprecated = ctx.is_deprecated(field); - let name = field.name(ctx.db()); + let name = field.name(db); let (name, escaped_name) = (name.unescaped().to_smol_str(), name.to_smol_str()); let mut item = CompletionItem::new( SymbolKind::Field, ctx.source_range(), - field_with_receiver(receiver.as_ref(), &name), + field_with_receiver(db, receiver.as_ref(), &name), ); item.set_relevance(CompletionRelevance { type_match: compute_type_match(ctx.completion, ty), exact_name_match: compute_exact_name_match(ctx.completion, name.as_str()), ..CompletionRelevance::default() }); - item.detail(ty.display(ctx.db()).to_string()) - .set_documentation(field.docs(ctx.db())) + item.detail(ty.display(db).to_string()) + .set_documentation(field.docs(db)) .set_deprecated(is_deprecated) .lookup_by(name); - item.insert_text(field_with_receiver(receiver.as_ref(), &escaped_name)); + item.insert_text(field_with_receiver(db, 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) { @@ -140,11 +152,19 @@ pub(crate) fn render_field( } } } - item.build() + item.doc_aliases(ctx.doc_aliases); + item.build(db) } -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()) +fn field_with_receiver( + db: &RootDatabase, + receiver: Option<&hir::Name>, + field_name: &str, +) -> SmolStr { + receiver.map_or_else( + || field_name.into(), + |receiver| format!("{}.{field_name}", receiver.display(db)).into(), + ) } pub(crate) fn render_tuple_field( @@ -156,10 +176,10 @@ pub(crate) fn render_tuple_field( let mut item = CompletionItem::new( SymbolKind::Field, ctx.source_range(), - field_with_receiver(receiver.as_ref(), &field.to_string()), + field_with_receiver(ctx.db(), receiver.as_ref(), &field.to_string()), ); item.detail(ty.display(ctx.db()).to_string()).lookup_by(field.to_string()); - item.build() + item.build(ctx.db()) } pub(crate) fn render_type_inference( @@ -169,7 +189,7 @@ pub(crate) fn render_type_inference( let mut builder = CompletionItem::new(CompletionItemKind::InferredType, ctx.source_range(), ty_string); builder.set_relevance(CompletionRelevance { is_definite: true, ..Default::default() }); - builder.build() + builder.build(ctx.db) } pub(crate) fn render_path_resolution( @@ -197,7 +217,9 @@ pub(crate) fn render_resolution_with_import( ) -> Option { let resolution = ScopeDef::from(import_edit.original_item); let local_name = scope_def_to_name(resolution, &ctx, &import_edit)?; - + //this now just renders the alias text, but we need to find the aliases earlier and call this with the alias instead + let doc_aliases = ctx.completion.doc_aliases_in_scope(resolution); + let ctx = ctx.doc_aliases(doc_aliases); Some(render_resolution_path(ctx, path_ctx, local_name, Some(import_edit), resolution)) } @@ -305,7 +327,7 @@ fn render_resolution_path( item.lookup_by(name.clone()) .label(SmolStr::from_iter([&name, "<…>"])) .trigger_call_info() - .insert_snippet(cap, format!("{local_name}<$0>")); + .insert_snippet(cap, format!("{}<$0>", local_name.display(db))); } } } @@ -348,6 +370,8 @@ fn render_resolution_simple_( if let Some(import_to_add) = ctx.import_to_add { item.add_import(import_to_add); } + + item.doc_aliases(ctx.doc_aliases); item } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs index 70b19988ca733..3c73983c39a2b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs @@ -29,5 +29,5 @@ fn render(ctx: RenderContext<'_>, const_: hir::Const) -> Option } item.insert_text(escaped_name); - Some(item.build()) + Some(item.build(ctx.db())) } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs index 197592e78ce26..8afce8db5ea86 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs @@ -52,8 +52,13 @@ fn render( let (call, escaped_call) = match &func_kind { FuncKind::Method(_, Some(receiver)) => ( - format!("{}.{}", receiver.unescaped(), name.unescaped()).into(), - format!("{receiver}.{name}").into(), + format!( + "{}.{}", + receiver.unescaped().display(ctx.db()), + name.unescaped().display(ctx.db()) + ) + .into(), + format!("{}.{}", receiver.display(ctx.db()), name.display(ctx.db())).into(), ), _ => (name.unescaped().to_smol_str(), name.to_smol_str()), }; @@ -147,6 +152,8 @@ fn render( } } } + + item.doc_aliases(ctx.doc_aliases); item } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs index ed78fcd8e6529..728d236dff476 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs @@ -71,8 +71,10 @@ fn render( } None => (name.clone().into(), name.into(), false), }; - let (qualified_name, escaped_qualified_name) = - (qualified_name.unescaped().to_string(), qualified_name.to_string()); + let (qualified_name, escaped_qualified_name) = ( + qualified_name.unescaped().display(ctx.db()).to_string(), + qualified_name.display(ctx.db()).to_string(), + ); let snippet_cap = ctx.snippet_cap(); let mut rendered = match kind { @@ -98,7 +100,7 @@ fn render( } let label = format_literal_label(&qualified_name, kind, snippet_cap); let lookup = if qualified { - format_literal_lookup(&short_qualified_name.to_string(), kind) + format_literal_lookup(&short_qualified_name.display(ctx.db()).to_string(), kind) } else { format_literal_lookup(&qualified_name, kind) }; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs index 44e8860763329..ce7af1d3400e7 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs @@ -74,7 +74,7 @@ fn render( item.insert_text(banged_name(&escaped_name)).lookup_by(banged_name(&name)); } _ => { - cov_mark::hit!(dont_insert_macro_call_parens_unncessary); + cov_mark::hit!(dont_insert_macro_call_parens_unnecessary); item.insert_text(escaped_name); } }; @@ -140,8 +140,8 @@ mod tests { use crate::tests::check_edit; #[test] - fn dont_insert_macro_call_parens_unncessary() { - cov_mark::check!(dont_insert_macro_call_parens_unncessary); + fn dont_insert_macro_call_parens_unnecessary() { + cov_mark::check!(dont_insert_macro_call_parens_unnecessary); check_edit( "frobnicate", r#" diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs index 9225c91bebf51..d06abc5e91e0b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs @@ -57,7 +57,10 @@ pub(crate) fn render_variant_pat( let enum_ty = variant.parent_enum(ctx.db()).ty(ctx.db()); let (name, escaped_name) = match path { - Some(path) => (path.unescaped().to_string().into(), path.to_string().into()), + Some(path) => ( + path.unescaped().display(ctx.db()).to_string().into(), + path.display(ctx.db()).to_string().into(), + ), None => { let name = local_name.unwrap_or_else(|| variant.name(ctx.db())); (name.unescaped().to_smol_str(), name.to_smol_str()) @@ -121,7 +124,7 @@ fn build_completion( Some(snippet_cap) => item.insert_snippet(snippet_cap, pat), None => item.insert_text(pat), }; - item.build() + item.build(ctx.db()) } fn render_pat( @@ -172,7 +175,7 @@ fn render_record_as_pat( format!( "{name} {{ {}{} }}", fields.enumerate().format_with(", ", |(idx, field), f| { - f(&format_args!("{}${}", field.name(db), idx + 1)) + f(&format_args!("{}${}", field.name(db).display(db.upcast()), idx + 1)) }), if fields_omitted { ", .." } else { "" }, name = name diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs index fbe120d2ac949..343ba7e28d8e0 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs @@ -53,5 +53,5 @@ fn render( } item.insert_text(escaped_name); - Some(item.build()) + Some(item.build(ctx.db())) } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs index 6e0c53ec94c43..93e943dbed98e 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs @@ -21,8 +21,10 @@ 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.unescaped().to_string(), p.to_string()), - None => (name.unescaped().to_string(), name.to_string()), + Some(p) => (p.unescaped().display(ctx.db()).to_string(), p.display(ctx.db()).to_string()), + None => { + (name.unescaped().display(ctx.db()).to_string(), name.display(ctx.db()).to_string()) + } }; let label = format_literal_label(&name.to_smol_str(), StructKind::Record, ctx.snippet_cap()); let lookup = format_literal_lookup(&name.to_smol_str(), StructKind::Record); @@ -51,9 +53,9 @@ pub(crate) fn render_union_literal( format!( "{} {{ {} }}", escaped_qualified_name, - fields - .iter() - .format_with(", ", |field, f| { f(&format_args!("{}: ()", field.name(ctx.db()))) }) + fields.iter().format_with(", ", |field, f| { + f(&format_args!("{}: ()", field.name(ctx.db()).display(ctx.db()))) + }) ) }; @@ -61,7 +63,11 @@ pub(crate) fn render_union_literal( "{} {{ {}{} }}", qualified_name, fields.iter().format_with(", ", |field, f| { - f(&format_args!("{}: {}", field.name(ctx.db()), field.ty(ctx.db()).display(ctx.db()))) + f(&format_args!( + "{}: {}", + field.name(ctx.db()).display(ctx.db()), + field.ty(ctx.db()).display(ctx.db()) + )) }), if fields_omitted { ", .." } else { "" } ); @@ -76,5 +82,5 @@ pub(crate) fn render_union_literal( None => item.insert_text(literal), }; - Some(item.build()) + Some(item.build(ctx.db())) } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs index 55c55725be4f9..a9a01a3a30f5b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs @@ -27,14 +27,14 @@ pub(crate) fn render_record_lit( } let completions = fields.iter().enumerate().format_with(", ", |(idx, field), f| { if snippet_cap.is_some() { - f(&format_args!("{}: ${{{}:()}}", field.name(db), idx + 1)) + f(&format_args!("{}: ${{{}:()}}", field.name(db).display(db.upcast()), idx + 1)) } else { - f(&format_args!("{}: ()", field.name(db))) + f(&format_args!("{}: ()", field.name(db).display(db.upcast()))) } }); let types = fields.iter().format_with(", ", |field, f| { - f(&format_args!("{}: {}", field.name(db), field.ty(db).display(db))) + f(&format_args!("{}: {}", field.name(db).display(db.upcast()), field.ty(db).display(db))) }); RenderedLiteral { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs index 1fe48b9e96f9d..2464e8d5f8175 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs @@ -23,7 +23,8 @@ mod type_pos; mod use_tree; mod visibility; -use hir::{db::DefDatabase, PrefixKind}; +use expect_test::Expect; +use hir::PrefixKind; use ide_db::{ base_db::{fixture::ChangeFixture, FileLoader, FilePosition}, imports::insert_use::{ImportGranularity, InsertUseConfig}, @@ -104,7 +105,7 @@ fn completion_list_with_config( include_keywords: bool, trigger_character: Option, ) -> String { - // filter out all but one builtintype completion for smaller test outputs + // filter out all but one built-in type completion for smaller test outputs let items = get_all_items(config, ra_fixture, trigger_character); let items = items .into_iter() @@ -120,7 +121,7 @@ fn completion_list_with_config( pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) { let change_fixture = ChangeFixture::parse(ra_fixture); let mut database = RootDatabase::default(); - database.set_enable_proc_attr_macros(true); + database.enable_proc_attr_macros(); database.apply_change(change_fixture.change); let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); let offset = range_or_offset.expect_offset(); @@ -197,11 +198,11 @@ pub(crate) fn check_edit_with_config( &db, &config, position, - completion.import_to_add.iter().filter_map(|import_edit| { - let import_path = &import_edit.import_path; - let import_name = import_path.segments().last()?; - Some((import_path.to_string(), import_name.to_string())) - }), + completion + .import_to_add + .iter() + .cloned() + .filter_map(|(import_path, import_name)| Some((import_path, import_name))), ) .into_iter() .flatten() @@ -215,6 +216,11 @@ pub(crate) fn check_edit_with_config( assert_eq_text!(&ra_fixture_after, &actual) } +fn check_empty(ra_fixture: &str, expect: Expect) { + let actual = completion_list(ra_fixture); + expect.assert_eq(&actual); +} + pub(crate) fn get_all_items( config: CompletionConfig, code: &str, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs index c1c6a689eb18a..be5b7f8a3404e 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs @@ -1,18 +1,13 @@ //! Completion tests for expressions. use expect_test::{expect, Expect}; -use crate::tests::{check_edit, completion_list, BASE_ITEMS_FIXTURE}; +use crate::tests::{check_edit, check_empty, completion_list, BASE_ITEMS_FIXTURE}; fn check(ra_fixture: &str, expect: Expect) { let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}{ra_fixture}")); expect.assert_eq(&actual) } -fn check_empty(ra_fixture: &str, expect: Expect) { - let actual = completion_list(ra_fixture); - expect.assert_eq(&actual); -} - #[test] fn complete_literal_struct_with_a_private_field() { // `FooDesc.bar` is private, the completion should not be triggered. @@ -672,7 +667,7 @@ fn main() { } #[test] -fn varaiant_with_struct() { +fn variant_with_struct() { check_empty( r#" pub struct YoloVariant { @@ -997,3 +992,105 @@ fn foo() { if foo {} el$0 { let x = 92; } } "#]], ); } + +#[test] +fn expr_no_unstable_item_on_stable() { + check_empty( + r#" +//- /main.rs crate:main deps:std +use std::*; +fn main() { + $0 +} +//- /std.rs crate:std +#[unstable] +pub struct UnstableThisShouldNotBeListed; +"#, + expect![[r#" + fn main() fn() + md std + bt u32 + kw const + kw crate:: + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw let + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); +} + +#[test] +fn expr_unstable_item_on_nightly() { + check_empty( + r#" +//- toolchain:nightly +//- /main.rs crate:main deps:std +use std::*; +fn main() { + $0 +} +//- /std.rs crate:std +#[unstable] +pub struct UnstableButWeAreOnNightlyAnyway; +"#, + expect![[r#" + fn main() fn() + md std + st UnstableButWeAreOnNightlyAnyway + bt u32 + kw const + kw crate:: + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw let + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs index 0b485eb776d8a..8c038c0fbaa1c 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs @@ -1107,6 +1107,41 @@ fn function() { ); } +#[test] +fn flyimport_pattern_no_unstable_item_on_stable() { + check( + r#" +//- /main.rs crate:main deps:std +fn function() { + let foo$0 +} +//- /std.rs crate:std +#[unstable] +pub struct FooStruct {} +"#, + expect![""], + ); +} + +#[test] +fn flyimport_pattern_unstable_item_on_nightly() { + check( + r#" +//- toolchain:nightly +//- /main.rs crate:main deps:std +fn function() { + let foo$0 +} +//- /std.rs crate:std +#[unstable] +pub struct FooStruct {} +"#, + expect![[r#" + st FooStruct (use std::FooStruct) + "#]], + ); +} + #[test] fn flyimport_item_name() { check( @@ -1230,3 +1265,24 @@ macro_rules! define_struct { "#]], ); } + +#[test] +fn macro_use_prelude_is_in_scope() { + check( + r#" +//- /main.rs crate:main deps:dep +#[macro_use] +extern crate dep; + +fn main() { + print$0 +} +//- /lib.rs crate:dep +#[macro_export] +macro_rules! println { + () => {} +} +"#, + expect![""], + ) +} diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs index 9fc731bb11d57..2b5b4dd773c87 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs @@ -1,7 +1,7 @@ //! Completion tests for item list position. use expect_test::{expect, Expect}; -use crate::tests::{check_edit, completion_list, BASE_ITEMS_FIXTURE}; +use crate::tests::{check_edit, check_empty, completion_list, BASE_ITEMS_FIXTURE}; fn check(ra_fixture: &str, expect: Expect) { let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}{ra_fixture}")); @@ -297,6 +297,58 @@ impl Test for () { ); } +#[test] +fn in_trait_impl_no_unstable_item_on_stable() { + check_empty( + r#" +trait Test { + #[unstable] + type Type; + #[unstable] + const CONST: (); + #[unstable] + fn function(); +} + +impl Test for () { + $0 +} +"#, + expect![[r#" + kw crate:: + kw self:: + "#]], + ); +} + +#[test] +fn in_trait_impl_unstable_item_on_nightly() { + check_empty( + r#" +//- toolchain:nightly +trait Test { + #[unstable] + type Type; + #[unstable] + const CONST: (); + #[unstable] + fn function(); +} + +impl Test for () { + $0 +} +"#, + expect![[r#" + ct const CONST: () = + fn fn function() + ta type Type = + kw crate:: + kw self:: + "#]], + ); +} + #[test] fn after_unit_struct() { check( diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs index c0e485c36fdd1..8af6cce98f635 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs @@ -1,12 +1,7 @@ //! Completion tests for pattern position. use expect_test::{expect, Expect}; -use crate::tests::{check_edit, completion_list, BASE_ITEMS_FIXTURE}; - -fn check_empty(ra_fixture: &str, expect: Expect) { - let actual = completion_list(ra_fixture); - expect.assert_eq(&actual) -} +use crate::tests::{check_edit, check_empty, completion_list, BASE_ITEMS_FIXTURE}; fn check(ra_fixture: &str, expect: Expect) { let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}\n{ra_fixture}")); @@ -742,3 +737,56 @@ fn f(x: EnumAlias) { "#]], ); } + +#[test] +fn pat_no_unstable_item_on_stable() { + check_empty( + r#" +//- /main.rs crate:main deps:std +use std::*; +fn foo() { + let a$0 +} +//- /std.rs crate:std +#[unstable] +pub struct S; +#[unstable] +pub enum Enum { + Variant +} +"#, + expect![[r#" + md std + kw mut + kw ref + "#]], + ); +} + +#[test] +fn pat_unstable_item_on_nightly() { + check_empty( + r#" +//- toolchain:nightly +//- /main.rs crate:main deps:std +use std::*; +fn foo() { + let a$0 +} +//- /std.rs crate:std +#[unstable] +pub struct S; +#[unstable] +pub enum Enum { + Variant +} +"#, + expect![[r#" + en Enum + md std + st S + kw mut + kw ref + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs index 2656a4d545e66..789ad66345b12 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs @@ -1,7 +1,7 @@ //! Completion tests for predicates and bounds. use expect_test::{expect, Expect}; -use crate::tests::{completion_list, BASE_ITEMS_FIXTURE}; +use crate::tests::{check_empty, completion_list, BASE_ITEMS_FIXTURE}; fn check(ra_fixture: &str, expect: Expect) { let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}\n{ra_fixture}")); @@ -129,3 +129,43 @@ impl Record { "#]], ); } + +#[test] +fn pred_no_unstable_item_on_stable() { + check_empty( + r#" +//- /main.rs crate:main deps:std +use std::*; +struct Foo where T: $0 {} +//- /std.rs crate:std +#[unstable] +pub trait Trait {} +"#, + expect![[r#" + md std + kw crate:: + kw self:: + "#]], + ); +} + +#[test] +fn pred_unstable_item_on_nightly() { + check_empty( + r#" +//- toolchain:nightly +//- /main.rs crate:main deps:std +use std::*; +struct Foo where T: $0 {} +//- /std.rs crate:std +#[unstable] +pub trait Trait {} +"#, + expect![[r#" + md std + tt Trait + kw crate:: + kw self:: + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/proc_macros.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/proc_macros.rs index 92ea4d15b8512..2d6234e310c60 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/proc_macros.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/proc_macros.rs @@ -81,7 +81,7 @@ impl Foo { } #[proc_macros::input_replace( - fn suprise() { + fn surprise() { Foo.$0 } )] @@ -114,7 +114,7 @@ impl Foo { } #[proc_macros::input_replace( - fn suprise() { + fn surprise() { Foo.f$0 } )] diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs index f8a6f6cd3ed06..3824720839eb9 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs @@ -989,3 +989,294 @@ fn foo { crate::::$0 } expect![""], ) } + +#[test] +fn completes_struct_via_doc_alias_in_fn_body() { + check( + r#" +#[doc(alias = "Bar")] +struct Foo; + +fn here_we_go() { + $0 +} +"#, + expect![[r#" + fn here_we_go() fn() + st Foo (alias Bar) + bt u32 + kw const + kw crate:: + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw let + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); +} + +#[test] +fn completes_struct_via_multiple_doc_aliases_in_fn_body() { + check( + r#" +#[doc(alias("Bar", "Qux"))] +#[doc(alias = "Baz")] +struct Foo; + +fn here_we_go() { + B$0 +} +"#, + expect![[r#" + fn here_we_go() fn() + st Foo (alias Bar, Qux, Baz) + bt u32 + kw const + kw crate:: + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw let + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); +} + +#[test] +fn completes_field_name_via_doc_alias_in_fn_body() { + check( + r#" +struct Foo { + #[doc(alias = "qux")] + bar: u8 +}; + +fn here_we_go() { + let foo = Foo { q$0 } +} +"#, + expect![[r#" + fd bar (alias qux) u8 + "#]], + ); +} + +#[test] +fn completes_struct_fn_name_via_doc_alias_in_fn_body() { + check( + r#" +struct Foo; +impl Foo { + #[doc(alias = "qux")] + fn bar() -> u8 { 1 } +} + +fn here_we_go() { + Foo::q$0 +} +"#, + expect![[r#" + fn bar() (alias qux) fn() -> u8 + "#]], + ); +} + +#[test] +fn completes_method_name_via_doc_alias_in_fn_body() { + check( + r#" +struct Foo { + bar: u8 +} +impl Foo { + #[doc(alias = "qux")] + fn baz(&self) -> u8 { + self.bar + } +} + +fn here_we_go() { + let foo = Foo { field: 42 }; + foo.q$0 +} +"#, + expect![[r#" + fd bar u8 + me baz() (alias qux) fn(&self) -> u8 + 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 + sn unsafe unsafe {} + "#]], + ); +} + +#[test] +fn completes_fn_name_via_doc_alias_in_fn_body() { + check( + r#" +#[doc(alias = "qux")] +fn foo() {} +fn bar() { qu$0 } +"#, + expect![[r#" + fn bar() fn() + fn foo() (alias qux) fn() + bt u32 + kw const + kw crate:: + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw let + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); +} + +#[test] +fn completes_struct_name_via_doc_alias_in_another_mod() { + check( + r#" +mod foo { + #[doc(alias = "Qux")] + pub struct Bar(u8); +} + +fn here_we_go() { + use foo; + let foo = foo::Q$0 +} +"#, + expect![[r#" + st Bar (alias Qux) + "#]], + ); +} + +#[test] +fn completes_use_via_doc_alias_in_another_mod() { + check( + r#" +mod foo { + #[doc(alias = "Qux")] + pub struct Bar(u8); +} + +fn here_we_go() { + use foo::Q$0; +} +"#, + expect![[r#" + st Bar (alias Qux) + "#]], + ); +} + +#[test] +fn completes_flyimport_with_doc_alias_in_another_mod() { + check( + r#" +mod foo { + #[doc(alias = "Qux")] + pub struct Bar(); +} + +fn here_we_go() { + let foo = Bar$0 +} +"#, + expect![[r#" + fn here_we_go() fn() + md foo + st Bar (alias Qux) (use foo::Bar) + bt u32 + kw crate:: + kw false + kw for + kw if + kw if let + kw loop + kw match + kw return + kw self:: + kw true + kw unsafe + kw while + kw while let + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs index c3f4fb4d1817f..8cb1ff4a125f7 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs @@ -1,7 +1,7 @@ //! Completion tests for type position. use expect_test::{expect, Expect}; -use crate::tests::{completion_list, BASE_ITEMS_FIXTURE}; +use crate::tests::{check_empty, completion_list, BASE_ITEMS_FIXTURE}; fn check(ra_fixture: &str, expect: Expect) { let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}\n{ra_fixture}")); @@ -669,3 +669,53 @@ fn f(t: impl MyTrait {} } +"#, + expect![""], + ); +} + +#[test] +fn use_tree_unstable_items_on_nightly() { + check( + r#" +//- toolchain:nightly +//- /lib.rs crate:main deps:std +use std::$0 +//- /std.rs crate:std +#[unstable] +pub mod simd {} +#[unstable] +pub struct S; +#[unstable] +pub fn foo() {} +#[unstable] +#[macro_export] +marco_rules! m { () => {} } +"#, + expect![[r#" + fn foo fn() + md simd + st S + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/ide-db/Cargo.toml b/src/tools/rust-analyzer/crates/ide-db/Cargo.toml index 57daaf623df29..4e75dc4dba541 100644 --- a/src/tools/rust-analyzer/crates/ide-db/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide-db/Cargo.toml @@ -23,6 +23,8 @@ itertools = "0.10.5" arrayvec = "0.7.2" indexmap = "1.9.1" memchr = "2.5.0" +triomphe.workspace = true +nohash-hasher.workspace = true # local deps base-db.workspace = true @@ -36,6 +38,8 @@ text-edit.workspace = true # something from some `hir-xxx` subpackage, reexport the API via `hir`. hir.workspace = true +line-index.workspace = true + [dev-dependencies] expect-test = "1.4.0" oorandom = "11.1.3" diff --git a/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs b/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs index ea1d9cc4919d3..8edda432ce398 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs @@ -1,13 +1,12 @@ //! Applies changes to the IDE state transactionally. -use std::sync::Arc; - use base_db::{ salsa::{Database, Durability}, Change, SourceRootId, }; use profile::{memory_usage, Bytes}; use rustc_hash::FxHashSet; +use triomphe::Arc; use crate::{symbol_index::SymbolsDatabase, RootDatabase}; @@ -64,6 +63,7 @@ impl RootDatabase { // SourceDatabase base_db::ParseQuery base_db::CrateGraphQuery + base_db::ProcMacrosQuery // SourceDatabaseExt base_db::FileTextQuery @@ -79,7 +79,6 @@ impl RootDatabase { hir::db::MacroDefQuery hir::db::MacroExpandQuery hir::db::ExpandProcMacroQuery - hir::db::MacroExpandErrorQuery hir::db::HygieneFrameQuery // DefDatabase diff --git a/src/tools/rust-analyzer/crates/ide-db/src/assists.rs b/src/tools/rust-analyzer/crates/ide-db/src/assists.rs index 8c6c1c44aa706..7a7328f312d20 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/assists.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/assists.rs @@ -98,7 +98,7 @@ impl FromStr for AssistKind { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct AssistId(pub &'static str, pub AssistKind); -/// A way to control how many asssist to resolve during the assist resolution. +/// A way to control how many assist to resolve during the assist resolution. /// When an assist is resolved, its edits are calculated that might be costly to always do by default. #[derive(Debug)] pub enum AssistResolveStrategy { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs index 4071c490b7fc3..760834bfafc7b 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs @@ -8,8 +8,8 @@ use arrayvec::ArrayVec; use hir::{ 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, TraitAlias, TypeAlias, Variant, + Function, GenericParam, HasVisibility, Impl, Label, Local, Macro, Module, ModuleDef, Name, + PathResolution, Semantics, Static, ToolModule, Trait, TraitAlias, TypeAlias, Variant, Visibility, }; use stdx::impl_from; @@ -622,22 +622,3 @@ impl From for Definition { } } } - -impl From for Option { - fn from(def: Definition) -> Self { - let item = match def { - Definition::Module(it) => ModuleDef::Module(it), - Definition::Function(it) => ModuleDef::Function(it), - Definition::Adt(it) => ModuleDef::Adt(it), - Definition::Variant(it) => ModuleDef::Variant(it), - Definition::Const(it) => ModuleDef::Const(it), - Definition::Static(it) => ModuleDef::Static(it), - Definition::Trait(it) => ModuleDef::Trait(it), - Definition::TraitAlias(it) => ModuleDef::TraitAlias(it), - Definition::TypeAlias(it) => ModuleDef::TypeAlias(it), - Definition::BuiltinType(it) => ModuleDef::BuiltinType(it), - _ => return None, - }; - Some(ItemInNs::from(item)) - } -} diff --git a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs index f0c3690962aff..e488300b41caa 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs @@ -4230,7 +4230,7 @@ pub union GenericUnion { // Unions with non-`Copy` fields are unstable. pub const THIS_IS_OKAY: GenericUnion<()> = GenericUnion { field: () }; ``` -Like transarent `struct`s, a transparent `union` of type `U` has the same +Like transparent `struct`s, a transparent `union` of type `U` has the same layout, size, and ABI as its single non-ZST field. If it is generic over a type `T`, and all its fields are ZSTs except for exactly one field of type `T`, then it has the same layout and ABI as `T` (even if `T` is a ZST when monomorphized). @@ -6548,7 +6548,7 @@ subtracting elements in an Add impl."##, }, Lint { label: "clippy::suspicious_assignment_formatting", - description: r##"Checks for use of the non-existent `=*`, `=!` and `=-` + description: r##"Checks for use of the nonexistent `=*`, `=!` and `=-` operators."##, }, Lint { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs b/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs index 8e3b1eef15b2d..eba9d8afc40ab 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs @@ -77,7 +77,7 @@ pub fn visit_file_defs( } module.impl_defs(db).into_iter().for_each(|impl_| cb(impl_.into())); - let is_root = module.is_crate_root(db); + let is_root = module.is_crate_root(); module .legacy_macros(db) .into_iter() diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs index b26b0a9087ea2..901d592c69104 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs @@ -362,12 +362,12 @@ fn import_for_item( let original_item_candidate = item_for_path_search(db, original_item)?; let import_path_candidate = mod_path(original_item_candidate)?; - let import_path_string = import_path_candidate.to_string(); + let import_path_string = import_path_candidate.display(db).to_string(); let expected_import_end = if item_as_assoc(db, original_item).is_some() { unresolved_qualifier.to_string() } else { - format!("{unresolved_qualifier}::{}", item_name(db, original_item)?) + format!("{unresolved_qualifier}::{}", item_name(db, original_item)?.display(db)) }; if !import_path_string.contains(unresolved_first_segment) || !import_path_string.ends_with(&expected_import_end) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs b/src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs index 07a57c883b2d9..46f1353e2e1b0 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs @@ -5,17 +5,11 @@ use either::Either; use hir::{ import_map::{self, ImportKind}, - symbols::FileSymbol, AsAssocItem, Crate, ItemInNs, Semantics, }; use limit::Limit; -use syntax::{ast, AstNode, SyntaxKind::NAME}; -use crate::{ - defs::{Definition, NameClass}, - imports::import_assets::NameToImport, - symbol_index, RootDatabase, -}; +use crate::{imports::import_assets::NameToImport, symbol_index, RootDatabase}; /// A value to use, when uncertain which limit to pick. pub static DEFAULT_QUERY_SEARCH_LIMIT: Limit = Limit::new(40); @@ -115,12 +109,12 @@ fn find_items<'a>( }); // Query the local crate using the symbol index. - let local_results = symbol_index::crate_symbols(db, krate, local_query) + let local_results = local_query + .search(&symbol_index::crate_symbols(db, krate)) .into_iter() - .filter_map(move |local_candidate| get_name_definition(sema, &local_candidate)) - .filter_map(|name_definition_to_import| match name_definition_to_import { - Definition::Macro(macro_def) => Some(ItemInNs::from(macro_def)), - def => >::from(def), + .filter_map(|local_candidate| match local_candidate.def { + hir::ModuleDef::Macro(macro_def) => Some(ItemInNs::Macros(macro_def)), + def => Some(ItemInNs::from(def)), }); external_importables.chain(local_results).filter(move |&item| match assoc_item_search { @@ -130,22 +124,6 @@ fn find_items<'a>( }) } -fn get_name_definition( - sema: &Semantics<'_, RootDatabase>, - import_candidate: &FileSymbol, -) -> Option { - let _p = profile::span("get_name_definition"); - - let candidate_node = import_candidate.loc.syntax(sema)?; - let candidate_name_node = if candidate_node.kind() != NAME { - candidate_node.children().find(|it| it.kind() == NAME)? - } else { - candidate_node - }; - let name = ast::Name::cast(candidate_name_node)?; - NameClass::classify(sema, &name)?.defined() -} - fn is_assoc_item(item: ItemInNs, db: &RootDatabase) -> bool { item.as_module_def().and_then(|module_def| module_def.as_assoc_item(db)).is_some() } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs index b1df11bf91172..ff1a20f03f44c 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs @@ -13,7 +13,6 @@ pub mod famous_defs; pub mod helpers; pub mod items_locator; pub mod label; -pub mod line_index; pub mod path_transform; pub mod rename; pub mod rust_doc; @@ -43,21 +42,20 @@ pub mod syntax_helpers { pub use parser::LexedStr; } -use std::{fmt, mem::ManuallyDrop, sync::Arc}; +use std::{fmt, mem::ManuallyDrop}; use base_db::{ salsa::{self, Durability}, AnchoredPath, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast, }; -use hir::{ - db::{DefDatabase, ExpandDatabase, HirDatabase}, - symbols::FileSymbolKind, -}; -use stdx::hash::NoHashHashSet; +use hir::db::{DefDatabase, ExpandDatabase, HirDatabase}; +use triomphe::Arc; use crate::{line_index::LineIndex, symbol_index::SymbolsDatabase}; pub use rustc_hash::{FxHashMap, FxHashSet, FxHasher}; +pub use ::line_index; + /// `base_db` is normally also needed in places where `ide_db` is used, so this re-export is for convenience. pub use base_db; @@ -114,13 +112,13 @@ impl Upcast for RootDatabase { } impl FileLoader for RootDatabase { - fn file_text(&self, file_id: FileId) -> Arc { + fn file_text(&self, file_id: FileId) -> Arc { FileLoaderDelegate(self).file_text(file_id) } fn resolve_path(&self, path: AnchoredPath<'_>) -> Option { FileLoaderDelegate(self).resolve_path(path) } - fn relevant_crates(&self, file_id: FileId) -> Arc> { + fn relevant_crates(&self, file_id: FileId) -> Arc> { FileLoaderDelegate(self).relevant_crates(file_id) } } @@ -137,18 +135,186 @@ impl RootDatabase { pub fn new(lru_capacity: Option) -> RootDatabase { let mut db = RootDatabase { storage: ManuallyDrop::new(salsa::Storage::default()) }; db.set_crate_graph_with_durability(Default::default(), Durability::HIGH); + db.set_proc_macros_with_durability(Default::default(), Durability::HIGH); db.set_local_roots_with_durability(Default::default(), Durability::HIGH); db.set_library_roots_with_durability(Default::default(), Durability::HIGH); - db.set_enable_proc_attr_macros(false); - db.update_lru_capacity(lru_capacity); + db.set_expand_proc_attr_macros_with_durability(false, Durability::HIGH); + db.update_parse_query_lru_capacity(lru_capacity); db } - pub fn update_lru_capacity(&mut self, lru_capacity: Option) { - let lru_capacity = lru_capacity.unwrap_or(base_db::DEFAULT_LRU_CAP); + pub fn enable_proc_attr_macros(&mut self) { + self.set_expand_proc_attr_macros_with_durability(true, Durability::HIGH); + } + + pub fn update_parse_query_lru_capacity(&mut self, lru_capacity: Option) { + let lru_capacity = lru_capacity.unwrap_or(base_db::DEFAULT_PARSE_LRU_CAP); base_db::ParseQuery.in_db_mut(self).set_lru_capacity(lru_capacity); - hir::db::ParseMacroExpansionQuery.in_db_mut(self).set_lru_capacity(lru_capacity); - hir::db::MacroExpandQuery.in_db_mut(self).set_lru_capacity(lru_capacity); + // macro expansions are usually rather small, so we can afford to keep more of them alive + hir::db::ParseMacroExpansionQuery.in_db_mut(self).set_lru_capacity(4 * lru_capacity); + hir::db::MacroExpandQuery.in_db_mut(self).set_lru_capacity(4 * lru_capacity); + } + + pub fn update_lru_capacities(&mut self, lru_capacities: &FxHashMap, usize>) { + use hir::db as hir_db; + + base_db::ParseQuery.in_db_mut(self).set_lru_capacity( + lru_capacities + .get(stringify!(ParseQuery)) + .copied() + .unwrap_or(base_db::DEFAULT_PARSE_LRU_CAP), + ); + hir_db::ParseMacroExpansionQuery.in_db_mut(self).set_lru_capacity( + lru_capacities + .get(stringify!(ParseMacroExpansionQuery)) + .copied() + .unwrap_or(4 * base_db::DEFAULT_PARSE_LRU_CAP), + ); + hir_db::MacroExpandQuery.in_db_mut(self).set_lru_capacity( + lru_capacities + .get(stringify!(MacroExpandQuery)) + .copied() + .unwrap_or(4 * base_db::DEFAULT_PARSE_LRU_CAP), + ); + + macro_rules! update_lru_capacity_per_query { + ($( $module:ident :: $query:ident )*) => {$( + if let Some(&cap) = lru_capacities.get(stringify!($query)) { + $module::$query.in_db_mut(self).set_lru_capacity(cap); + } + )*} + } + update_lru_capacity_per_query![ + // SourceDatabase + // base_db::ParseQuery + // base_db::CrateGraphQuery + // base_db::ProcMacrosQuery + + // SourceDatabaseExt + // base_db::FileTextQuery + // base_db::FileSourceRootQuery + // base_db::SourceRootQuery + base_db::SourceRootCratesQuery + + // ExpandDatabase + hir_db::AstIdMapQuery + // hir_db::ParseMacroExpansionQuery + // hir_db::InternMacroCallQuery + hir_db::MacroArgTextQuery + hir_db::MacroDefQuery + // hir_db::MacroExpandQuery + hir_db::ExpandProcMacroQuery + hir_db::HygieneFrameQuery + hir_db::ParseMacroExpansionErrorQuery + + // DefDatabase + hir_db::FileItemTreeQuery + hir_db::CrateDefMapQueryQuery + hir_db::BlockDefMapQuery + hir_db::StructDataQuery + hir_db::StructDataWithDiagnosticsQuery + hir_db::UnionDataQuery + hir_db::UnionDataWithDiagnosticsQuery + hir_db::EnumDataQuery + hir_db::EnumDataWithDiagnosticsQuery + hir_db::ImplDataQuery + hir_db::ImplDataWithDiagnosticsQuery + hir_db::TraitDataQuery + hir_db::TraitDataWithDiagnosticsQuery + hir_db::TraitAliasDataQuery + hir_db::TypeAliasDataQuery + hir_db::FunctionDataQuery + hir_db::ConstDataQuery + hir_db::StaticDataQuery + hir_db::Macro2DataQuery + hir_db::MacroRulesDataQuery + hir_db::ProcMacroDataQuery + hir_db::BodyWithSourceMapQuery + hir_db::BodyQuery + hir_db::ExprScopesQuery + hir_db::GenericParamsQuery + hir_db::VariantsAttrsQuery + hir_db::FieldsAttrsQuery + hir_db::VariantsAttrsSourceMapQuery + hir_db::FieldsAttrsSourceMapQuery + hir_db::AttrsQuery + hir_db::CrateLangItemsQuery + hir_db::LangItemQuery + hir_db::ImportMapQuery + hir_db::FieldVisibilitiesQuery + hir_db::FunctionVisibilityQuery + hir_db::ConstVisibilityQuery + hir_db::CrateSupportsNoStdQuery + + // HirDatabase + hir_db::InferQueryQuery + hir_db::MirBodyQuery + hir_db::BorrowckQuery + hir_db::TyQuery + hir_db::ValueTyQuery + hir_db::ImplSelfTyQuery + hir_db::ConstParamTyQuery + hir_db::ConstEvalQuery + hir_db::ConstEvalDiscriminantQuery + hir_db::ImplTraitQuery + hir_db::FieldTypesQuery + hir_db::LayoutOfAdtQuery + hir_db::TargetDataLayoutQuery + hir_db::CallableItemSignatureQuery + hir_db::ReturnTypeImplTraitsQuery + hir_db::GenericPredicatesForParamQuery + hir_db::GenericPredicatesQuery + hir_db::TraitEnvironmentQuery + hir_db::GenericDefaultsQuery + hir_db::InherentImplsInCrateQuery + hir_db::InherentImplsInBlockQuery + hir_db::IncoherentInherentImplCratesQuery + hir_db::TraitImplsInCrateQuery + hir_db::TraitImplsInBlockQuery + hir_db::TraitImplsInDepsQuery + // hir_db::InternCallableDefQuery + // hir_db::InternLifetimeParamIdQuery + // hir_db::InternImplTraitIdQuery + // hir_db::InternTypeOrConstParamIdQuery + // hir_db::InternClosureQuery + // hir_db::InternGeneratorQuery + hir_db::AssociatedTyDataQuery + hir_db::TraitDatumQuery + hir_db::StructDatumQuery + hir_db::ImplDatumQuery + hir_db::FnDefDatumQuery + hir_db::FnDefVarianceQuery + hir_db::AdtVarianceQuery + hir_db::AssociatedTyValueQuery + hir_db::TraitSolveQueryQuery + hir_db::ProgramClausesForChalkEnvQuery + + // SymbolsDatabase + symbol_index::ModuleSymbolsQuery + symbol_index::LibrarySymbolsQuery + // symbol_index::LocalRootsQuery + // symbol_index::LibraryRootsQuery + + // LineIndexDatabase + crate::LineIndexQuery + + // InternDatabase + // hir_db::InternFunctionQuery + // hir_db::InternStructQuery + // hir_db::InternUnionQuery + // hir_db::InternEnumQuery + // hir_db::InternConstQuery + // hir_db::InternStaticQuery + // hir_db::InternTraitQuery + // hir_db::InternTraitAliasQuery + // hir_db::InternTypeAliasQuery + // hir_db::InternImplQuery + // hir_db::InternExternBlockQuery + // hir_db::InternBlockQuery + // hir_db::InternMacro2Query + // hir_db::InternProcMacroQuery + // hir_db::InternMacroRulesQuery + ]; } } @@ -211,20 +377,22 @@ impl From for SymbolKind { } } -impl From for SymbolKind { - fn from(it: FileSymbolKind) -> Self { +impl From for SymbolKind { + fn from(it: hir::ModuleDefId) -> Self { match it { - FileSymbolKind::Const => SymbolKind::Const, - FileSymbolKind::Enum => SymbolKind::Enum, - FileSymbolKind::Function => SymbolKind::Function, - FileSymbolKind::Macro => SymbolKind::Macro, - FileSymbolKind::Module => SymbolKind::Module, - FileSymbolKind::Static => SymbolKind::Static, - FileSymbolKind::Struct => SymbolKind::Struct, - FileSymbolKind::Trait => SymbolKind::Trait, - FileSymbolKind::TraitAlias => SymbolKind::TraitAlias, - FileSymbolKind::TypeAlias => SymbolKind::TypeAlias, - FileSymbolKind::Union => SymbolKind::Union, + hir::ModuleDefId::ConstId(..) => SymbolKind::Const, + hir::ModuleDefId::EnumVariantId(..) => SymbolKind::Variant, + hir::ModuleDefId::FunctionId(..) => SymbolKind::Function, + hir::ModuleDefId::MacroId(..) => SymbolKind::Macro, + hir::ModuleDefId::ModuleId(..) => SymbolKind::Module, + hir::ModuleDefId::StaticId(..) => SymbolKind::Static, + hir::ModuleDefId::AdtId(hir::AdtId::StructId(..)) => SymbolKind::Struct, + hir::ModuleDefId::AdtId(hir::AdtId::EnumId(..)) => SymbolKind::Enum, + hir::ModuleDefId::AdtId(hir::AdtId::UnionId(..)) => SymbolKind::Union, + hir::ModuleDefId::TraitId(..) => SymbolKind::Trait, + hir::ModuleDefId::TraitAliasId(..) => SymbolKind::TraitAlias, + hir::ModuleDefId::TypeAliasId(..) => SymbolKind::TypeAlias, + hir::ModuleDefId::BuiltinType(..) => SymbolKind::TypeAlias, } } } @@ -247,4 +415,5 @@ impl SnippetCap { #[cfg(test)] mod tests { mod sourcegen_lints; + mod line_index; } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/line_index.rs b/src/tools/rust-analyzer/crates/ide-db/src/line_index.rs deleted file mode 100644 index 16814a1e636fa..0000000000000 --- a/src/tools/rust-analyzer/crates/ide-db/src/line_index.rs +++ /dev/null @@ -1,314 +0,0 @@ -//! `LineIndex` maps flat `TextSize` offsets into `(Line, Column)` -//! representation. -use std::{iter, mem}; - -use stdx::hash::NoHashHashMap; -use syntax::{TextRange, TextSize}; - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct LineIndex { - /// Offset the beginning of each line, zero-based. - pub(crate) newlines: Vec, - /// List of non-ASCII characters on each line. - pub(crate) line_wide_chars: NoHashHashMap>, -} - -/// Line/Column information in native, utf8 format. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub struct LineCol { - /// Zero-based - pub line: u32, - /// Zero-based utf8 offset - pub col: u32, -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub enum WideEncoding { - Utf16, - Utf32, -} - -/// Line/Column information in legacy encodings. -/// -/// Deliberately not a generic type and different from `LineCol`. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub struct WideLineCol { - /// Zero-based - pub line: u32, - /// Zero-based - pub col: u32, -} - -#[derive(Clone, Debug, Hash, PartialEq, Eq)] -pub(crate) struct WideChar { - /// Start offset of a character inside a line, zero-based - pub(crate) start: TextSize, - /// End offset of a character inside a line, zero-based - pub(crate) end: TextSize, -} - -impl WideChar { - /// Returns the length in 8-bit UTF-8 code units. - fn len(&self) -> TextSize { - self.end - self.start - } - - /// Returns the length in UTF-16 or UTF-32 code units. - fn wide_len(&self, enc: WideEncoding) -> usize { - match enc { - WideEncoding::Utf16 => { - if self.len() == TextSize::from(4) { - 2 - } else { - 1 - } - } - - WideEncoding::Utf32 => 1, - } - } -} - -impl LineIndex { - pub fn new(text: &str) -> LineIndex { - let mut line_wide_chars = NoHashHashMap::default(); - let mut wide_chars = Vec::new(); - - 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); - curr_row += c_len; - if c == '\n' { - newlines.push(curr_row); - - // Save any utf-16 characters seen in the previous line - if !wide_chars.is_empty() { - line_wide_chars.insert(line, mem::take(&mut wide_chars)); - } - - // Prepare for processing the next line - curr_col = 0.into(); - line += 1; - continue; - } - - if !c.is_ascii() { - wide_chars.push(WideChar { start: curr_col, end: curr_col + c_len }); - } - - curr_col += c_len; - } - - // Save any utf-16 characters seen in the last line - if !wide_chars.is_empty() { - line_wide_chars.insert(line, wide_chars); - } - - LineIndex { newlines, line_wide_chars } - } - - pub fn line_col(&self, offset: TextSize) -> LineCol { - let line = self.newlines.partition_point(|&it| it <= offset) - 1; - let line_start_offset = self.newlines[line]; - let col = offset - line_start_offset; - LineCol { line: line as u32, col: col.into() } - } - - pub fn offset(&self, line_col: LineCol) -> Option { - self.newlines - .get(line_col.line as usize) - .map(|offset| offset + TextSize::from(line_col.col)) - } - - pub fn to_wide(&self, enc: WideEncoding, line_col: LineCol) -> WideLineCol { - let col = self.utf8_to_wide_col(enc, line_col.line, line_col.col.into()); - WideLineCol { line: line_col.line, col: col as u32 } - } - - pub fn to_utf8(&self, enc: WideEncoding, line_col: WideLineCol) -> LineCol { - let col = self.wide_to_utf8_col(enc, line_col.line, line_col.col); - LineCol { line: line_col.line, col: col.into() } - } - - pub fn lines(&self, range: TextRange) -> impl Iterator + '_ { - let lo = self.newlines.partition_point(|&it| it < range.start()); - let hi = self.newlines.partition_point(|&it| it <= range.end()); - let all = iter::once(range.start()) - .chain(self.newlines[lo..hi].iter().copied()) - .chain(iter::once(range.end())); - - all.clone() - .zip(all.skip(1)) - .map(|(lo, hi)| TextRange::new(lo, hi)) - .filter(|it| !it.is_empty()) - } - - fn utf8_to_wide_col(&self, enc: WideEncoding, line: u32, col: TextSize) -> usize { - let mut res: usize = col.into(); - if let Some(wide_chars) = self.line_wide_chars.get(&line) { - for c in wide_chars { - if c.end <= col { - res -= usize::from(c.len()) - c.wide_len(enc); - } else { - // From here on, all utf16 characters come *after* the character we are mapping, - // so we don't need to take them into account - break; - } - } - } - res - } - - fn wide_to_utf8_col(&self, enc: WideEncoding, line: u32, mut col: u32) -> TextSize { - if let Some(wide_chars) = self.line_wide_chars.get(&line) { - for c in wide_chars { - if col > u32::from(c.start) { - col += u32::from(c.len()) - c.wide_len(enc) as u32; - } else { - // From here on, all utf16 characters come *after* the character we are mapping, - // so we don't need to take them into account - break; - } - } - } - - col.into() - } -} - -#[cfg(test)] -mod tests { - use test_utils::skip_slow_tests; - - use super::WideEncoding::{Utf16, Utf32}; - use super::*; - - #[test] - fn test_line_index() { - let text = "hello\nworld"; - let table = [ - (00, 0, 0), - (01, 0, 1), - (05, 0, 5), - (06, 1, 0), - (07, 1, 1), - (08, 1, 2), - (10, 1, 4), - (11, 1, 5), - (12, 1, 6), - ]; - - let index = LineIndex::new(text); - for (offset, line, col) in table { - assert_eq!(index.line_col(offset.into()), LineCol { line, col }); - } - - let text = "\nhello\nworld"; - let table = [(0, 0, 0), (1, 1, 0), (2, 1, 1), (6, 1, 5), (7, 2, 0)]; - let index = LineIndex::new(text); - for (offset, line, col) in table { - assert_eq!(index.line_col(offset.into()), LineCol { line, col }); - } - } - - #[test] - fn test_char_len() { - assert_eq!('メ'.len_utf8(), 3); - assert_eq!('メ'.len_utf16(), 1); - } - - #[test] - fn test_empty_index() { - let col_index = LineIndex::new( - " -const C: char = 'x'; -", - ); - assert_eq!(col_index.line_wide_chars.len(), 0); - } - - #[test] - fn test_every_chars() { - if skip_slow_tests() { - return; - } - - let text: String = { - let mut chars: Vec = ((0 as char)..char::MAX).collect(); // Neat! - chars.extend("\n".repeat(chars.len() / 16).chars()); - let mut rng = oorandom::Rand32::new(stdx::rand::seed()); - stdx::rand::shuffle(&mut chars, |i| rng.rand_range(0..i as u32) as usize); - chars.into_iter().collect() - }; - assert!(text.contains('💩')); // Sanity check. - - let line_index = LineIndex::new(&text); - - let mut lin_col = LineCol { line: 0, col: 0 }; - let mut col_utf16 = 0; - let mut col_utf32 = 0; - for (offset, c) in text.char_indices() { - let got_offset = line_index.offset(lin_col).unwrap(); - assert_eq!(usize::from(got_offset), offset); - - let got_lin_col = line_index.line_col(got_offset); - assert_eq!(got_lin_col, lin_col); - - for enc in [Utf16, Utf32] { - let wide_lin_col = line_index.to_wide(enc, lin_col); - let got_lin_col = line_index.to_utf8(enc, wide_lin_col); - assert_eq!(got_lin_col, lin_col); - - let want_col = match enc { - Utf16 => col_utf16, - Utf32 => col_utf32, - }; - assert_eq!(wide_lin_col.col, want_col) - } - - if c == '\n' { - lin_col.line += 1; - lin_col.col = 0; - col_utf16 = 0; - col_utf32 = 0; - } else { - lin_col.col += c.len_utf8() as u32; - col_utf16 += c.len_utf16() as u32; - col_utf32 += 1; - } - } - } - - #[test] - fn test_splitlines() { - fn r(lo: u32, hi: u32) -> TextRange { - TextRange::new(lo.into(), hi.into()) - } - - let text = "a\nbb\nccc\n"; - let line_index = LineIndex::new(text); - - let actual = line_index.lines(r(0, 9)).collect::>(); - let expected = vec![r(0, 2), r(2, 5), r(5, 9)]; - assert_eq!(actual, expected); - - let text = ""; - let line_index = LineIndex::new(text); - - let actual = line_index.lines(r(0, 0)).collect::>(); - let expected = vec![]; - assert_eq!(actual, expected); - - let text = "\n"; - let line_index = LineIndex::new(text); - - let actual = line_index.lines(r(0, 1)).collect::>(); - let expected = vec![r(0, 1)]; - assert_eq!(actual, expected) - } -} diff --git a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs index 6402a84a68bb3..0ee627a44c68e 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs @@ -116,7 +116,9 @@ impl<'a> PathTransform<'a> { Some(( k, ast::make::ty( - &default.display_source_code(db, source_module.into()).ok()?, + &default + .display_source_code(db, source_module.into(), false) + .ok()?, ), )) } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs index f710211c8cb0f..52a23b4b8f3d2 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs @@ -178,7 +178,7 @@ fn rename_mod( let mut source_change = SourceChange::default(); - if module.is_crate_root(sema.db) { + if module.is_crate_root() { return Ok(source_change); } @@ -202,12 +202,13 @@ fn rename_mod( // - Module has submodules defined in separate files let dir_paths = match (is_mod_rs, has_detached_child, module.name(sema.db)) { // Go up one level since the anchor is inside the dir we're trying to rename - (true, _, Some(mod_name)) => { - Some((format!("../{}", mod_name.unescaped()), format!("../{new_name}"))) - } + (true, _, Some(mod_name)) => Some(( + format!("../{}", mod_name.unescaped().display(sema.db)), + format!("../{new_name}"), + )), // The anchor is on the same level as target dir (false, true, Some(mod_name)) => { - Some((mod_name.unescaped().to_string(), new_name.to_string())) + Some((mod_name.unescaped().display(sema.db).to_string(), new_name.to_owned())) } _ => None, }; @@ -232,7 +233,7 @@ fn rename_mod( { source_change.insert_source_edit( file_id, - TextEdit::replace(file_range.range, new_name.to_string()), + TextEdit::replace(file_range.range, new_name.to_owned()), ) }; } @@ -442,7 +443,7 @@ fn source_edit_from_name_ref( let s = field_name.syntax().text_range().start(); let e = pat.syntax().text_range().start(); edit.delete(TextRange::new(s, e)); - edit.replace(name.syntax().text_range(), new_name.to_string()); + edit.replace(name.syntax().text_range(), new_name.to_owned()); return true; } } @@ -462,7 +463,19 @@ fn source_edit_from_def( if let Definition::Local(local) = def { let mut file_id = None; for source in local.sources(sema.db) { - let source = source.source; + let source = match source.source.clone().original_ast_node(sema.db) { + Some(source) => source, + None => match source.source.syntax().original_file_range_opt(sema.db) { + Some(FileRange { file_id: file_id2, range }) => { + file_id = Some(file_id2); + edit.replace(range, new_name.to_owned()); + continue; + } + None => { + bail!("Can't rename local that is defined in a macro declaration") + } + }, + }; file_id = source.file_id.file_id(); if let Either::Left(pat) = source.value { let name_range = pat.name().unwrap().syntax().text_range(); @@ -485,7 +498,7 @@ fn source_edit_from_def( // Foo { field: ref mut local @ local 2} -> Foo { field: ref mut new_name @ local2 } // Foo { field: ref mut local } -> Foo { field: ref mut new_name } // ^^^^^ replace this with `new_name` - edit.replace(name_range, new_name.to_string()); + edit.replace(name_range, new_name.to_owned()); } } else { // Foo { ref mut field } -> Foo { field: ref mut new_name } @@ -495,10 +508,10 @@ fn source_edit_from_def( pat.syntax().text_range().start(), format!("{}: ", pat_field.field_name().unwrap()), ); - edit.replace(name_range, new_name.to_string()); + edit.replace(name_range, new_name.to_owned()); } } else { - edit.replace(name_range, new_name.to_string()); + edit.replace(name_range, new_name.to_owned()); } } } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/search.rs b/src/tools/rust-analyzer/crates/ide-db/src/search.rs index 12f5e4e2a23a8..73cd5dcaf23fc 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs @@ -4,17 +4,18 @@ //! get a super-set of matches. Then, we we confirm each match using precise //! name resolution. -use std::{mem, sync::Arc}; +use std::mem; use base_db::{FileId, FileRange, SourceDatabase, SourceDatabaseExt}; use hir::{ AsAssocItem, DefWithBody, HasAttrs, HasSource, InFile, ModuleSource, Semantics, Visibility, }; use memchr::memmem::Finder; +use nohash_hasher::IntMap; use once_cell::unsync::Lazy; use parser::SyntaxKind; -use stdx::hash::NoHashHashMap; use syntax::{ast, match_ast, AstNode, TextRange, TextSize}; +use triomphe::Arc; use crate::{ defs::{Definition, NameClass, NameRefClass}, @@ -24,7 +25,7 @@ use crate::{ #[derive(Debug, Default, Clone)] pub struct UsageSearchResult { - pub references: NoHashHashMap>, + pub references: IntMap>, } impl UsageSearchResult { @@ -49,7 +50,7 @@ impl UsageSearchResult { impl IntoIterator for UsageSearchResult { type Item = (FileId, Vec); - type IntoIter = > as IntoIterator>::IntoIter; + type IntoIter = > as IntoIterator>::IntoIter; fn into_iter(self) -> Self::IntoIter { self.references.into_iter() @@ -83,17 +84,17 @@ pub enum ReferenceCategory { /// e.g. for things like local variables. #[derive(Clone, Debug)] pub struct SearchScope { - entries: NoHashHashMap>, + entries: IntMap>, } impl SearchScope { - fn new(entries: NoHashHashMap>) -> SearchScope { + fn new(entries: IntMap>) -> SearchScope { SearchScope { entries } } /// Build a search scope spanning the entire crate graph of files. fn crate_graph(db: &RootDatabase) -> SearchScope { - let mut entries = NoHashHashMap::default(); + let mut entries = IntMap::default(); let graph = db.crate_graph(); for krate in graph.iter() { @@ -107,7 +108,7 @@ impl SearchScope { /// Build a search scope spanning all the reverse dependencies of the given crate. fn reverse_dependencies(db: &RootDatabase, of: hir::Crate) -> SearchScope { - let mut entries = NoHashHashMap::default(); + let mut entries = IntMap::default(); for rev_dep in of.transitive_reverse_dependencies(db) { let root_file = rev_dep.root_file(db); let source_root_id = db.file_source_root(root_file); @@ -127,7 +128,7 @@ impl SearchScope { /// Build a search scope spanning the given module and all its submodules. fn module_and_children(db: &RootDatabase, module: hir::Module) -> SearchScope { - let mut entries = NoHashHashMap::default(); + let mut entries = IntMap::default(); let (file_id, range) = { let InFile { file_id, value } = module.definition_source(db); @@ -160,7 +161,7 @@ impl SearchScope { /// Build an empty search scope. pub fn empty() -> SearchScope { - SearchScope::new(NoHashHashMap::default()) + SearchScope::new(IntMap::default()) } /// Build a empty search scope spanning the given file. @@ -224,7 +225,7 @@ impl Definition { // def is crate root // FIXME: We don't do searches for crates currently, as a crate does not actually have a single name if let &Definition::Module(module) = self { - if module.is_crate_root(db) { + if module.is_crate_root() { return SearchScope::reverse_dependencies(db, module.krate()); } } @@ -391,7 +392,7 @@ impl<'a> FindUsages<'a> { let name = match self.def { // special case crate modules as these do not have a proper name - Definition::Module(module) if module.is_crate_root(self.sema.db) => { + Definition::Module(module) if module.is_crate_root() => { // FIXME: This assumes the crate name is always equal to its display name when it really isn't module .krate() @@ -438,11 +439,11 @@ impl<'a> FindUsages<'a> { fn scope_files<'a>( sema: &'a Semantics<'_, RootDatabase>, scope: &'a SearchScope, - ) -> impl Iterator, FileId, TextRange)> + 'a { + ) -> impl Iterator, FileId, TextRange)> + 'a { scope.entries.iter().map(|(&file_id, &search_range)| { let text = sema.db.file_text(file_id); let search_range = - search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(text.as_str()))); + search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(&*text))); (text, file_id, search_range) }) @@ -499,7 +500,7 @@ impl<'a> FindUsages<'a> { let scope = search_scope.intersection(&SearchScope::module_and_children(self.sema.db, module)); - let is_crate_root = module.is_crate_root(self.sema.db).then(|| Finder::new("crate")); + let is_crate_root = module.is_crate_root().then(|| Finder::new("crate")); let finder = &Finder::new("super"); for (text, file_id, search_range) in scope_files(sema, &scope) { @@ -553,7 +554,7 @@ impl<'a> FindUsages<'a> { let text = sema.db.file_text(file_id); let search_range = - search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(text.as_str()))); + search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(&*text))); let tree = Lazy::new(|| sema.parse(file_id).syntax().clone()); let finder = &Finder::new("self"); diff --git a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs index 936354f296134..061fb0f05cfb9 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs @@ -5,16 +5,16 @@ use std::{collections::hash_map::Entry, iter, mem}; +use crate::SnippetCap; use base_db::{AnchoredPathBuf, FileId}; -use stdx::{hash::NoHashHashMap, never}; -use syntax::{algo, AstNode, SyntaxNode, SyntaxNodePtr, TextRange, TextSize}; +use nohash_hasher::IntMap; +use stdx::never; +use syntax::{algo, ast, ted, AstNode, SyntaxNode, SyntaxNodePtr, TextRange, TextSize}; use text_edit::{TextEdit, TextEditBuilder}; -use crate::SnippetCap; - #[derive(Default, Debug, Clone)] pub struct SourceChange { - pub source_file_edits: NoHashHashMap, + pub source_file_edits: IntMap, pub file_system_edits: Vec, pub is_snippet: bool, } @@ -23,7 +23,7 @@ impl SourceChange { /// Creates a new SourceChange with the given label /// from the edits. pub fn from_edits( - source_file_edits: NoHashHashMap, + source_file_edits: IntMap, file_system_edits: Vec, ) -> Self { SourceChange { source_file_edits, file_system_edits, is_snippet: false } @@ -77,8 +77,8 @@ impl Extend for SourceChange { } } -impl From> for SourceChange { - fn from(source_file_edits: NoHashHashMap) -> SourceChange { +impl From> for SourceChange { + fn from(source_file_edits: IntMap) -> SourceChange { SourceChange { source_file_edits, file_system_edits: Vec::new(), is_snippet: false } } } @@ -99,6 +99,8 @@ pub struct SourceChangeBuilder { /// Maps the original, immutable `SyntaxNode` to a `clone_for_update` twin. pub mutated_tree: Option, + /// Keeps track of where to place snippets + pub snippet_builder: Option, } pub struct TreeMutator { @@ -106,6 +108,12 @@ pub struct TreeMutator { mutable_clone: SyntaxNode, } +#[derive(Default)] +pub struct SnippetBuilder { + /// Where to place snippets at + places: Vec, +} + impl TreeMutator { pub fn new(immutable: &SyntaxNode) -> TreeMutator { let immutable = immutable.ancestors().last().unwrap(); @@ -131,6 +139,7 @@ impl SourceChangeBuilder { source_change: SourceChange::default(), trigger_signature_help: false, mutated_tree: None, + snippet_builder: None, } } @@ -140,6 +149,17 @@ impl SourceChangeBuilder { } fn commit(&mut self) { + // Render snippets first so that they get bundled into the tree diff + if let Some(mut snippets) = self.snippet_builder.take() { + // Last snippet always has stop index 0 + let last_stop = snippets.places.pop().unwrap(); + last_stop.place(0); + + for (index, stop) in snippets.places.into_iter().enumerate() { + stop.place(index + 1) + } + } + if let Some(tm) = self.mutated_tree.take() { algo::diff(&tm.immutable, &tm.mutable_clone).into_text_edit(&mut self.edit) } @@ -161,7 +181,7 @@ impl SourceChangeBuilder { /// 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 + /// phase, and then get their mutable counterparts 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) @@ -214,6 +234,30 @@ impl SourceChangeBuilder { self.trigger_signature_help = true; } + /// Adds a tabstop snippet to place the cursor before `node` + pub fn add_tabstop_before(&mut self, _cap: SnippetCap, node: impl AstNode) { + assert!(node.syntax().parent().is_some()); + self.add_snippet(PlaceSnippet::Before(node.syntax().clone())); + } + + /// Adds a tabstop snippet to place the cursor after `node` + pub fn add_tabstop_after(&mut self, _cap: SnippetCap, node: impl AstNode) { + assert!(node.syntax().parent().is_some()); + self.add_snippet(PlaceSnippet::After(node.syntax().clone())); + } + + /// Adds a snippet to move the cursor selected over `node` + pub fn add_placeholder_snippet(&mut self, _cap: SnippetCap, node: impl AstNode) { + assert!(node.syntax().parent().is_some()); + self.add_snippet(PlaceSnippet::Over(node.syntax().clone())) + } + + fn add_snippet(&mut self, snippet: PlaceSnippet) { + let snippet_builder = self.snippet_builder.get_or_insert(SnippetBuilder { places: vec![] }); + snippet_builder.places.push(snippet); + self.source_change.is_snippet = true; + } + pub fn finish(mut self) -> SourceChange { self.commit(); mem::take(&mut self.source_change) @@ -236,3 +280,66 @@ impl From for SourceChange { } } } + +enum PlaceSnippet { + /// Place a tabstop before a node + Before(SyntaxNode), + /// Place a tabstop before a node + After(SyntaxNode), + /// Place a placeholder snippet in place of the node + Over(SyntaxNode), +} + +impl PlaceSnippet { + /// Places the snippet before or over a node with the given tab stop index + fn place(self, order: usize) { + // ensure the target node is still attached + match &self { + PlaceSnippet::Before(node) | PlaceSnippet::After(node) | PlaceSnippet::Over(node) => { + // node should still be in the tree, but if it isn't + // then it's okay to just ignore this place + if stdx::never!(node.parent().is_none()) { + return; + } + } + } + + match self { + PlaceSnippet::Before(node) => { + ted::insert_raw(ted::Position::before(&node), Self::make_tab_stop(order)); + } + PlaceSnippet::After(node) => { + ted::insert_raw(ted::Position::after(&node), Self::make_tab_stop(order)); + } + PlaceSnippet::Over(node) => { + let position = ted::Position::before(&node); + node.detach(); + + let snippet = ast::SourceFile::parse(&format!("${{{order}:_}}")) + .syntax_node() + .clone_for_update(); + + let placeholder = + snippet.descendants().find_map(ast::UnderscoreExpr::cast).unwrap(); + ted::replace(placeholder.syntax(), node); + + ted::insert_raw(position, snippet); + } + } + } + + fn make_tab_stop(order: usize) -> SyntaxNode { + let stop = ast::SourceFile::parse(&format!("stop!(${order})")) + .syntax_node() + .descendants() + .find_map(ast::TokenTree::cast) + .unwrap() + .syntax() + .clone_for_update(); + + stop.first_token().unwrap().detach(); + stop.last_token().unwrap().detach(); + + stop + } +} diff --git a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs index a91ffd1ec4fd1..b54c43b296b2a 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs @@ -25,7 +25,6 @@ use std::{ fmt, hash::{Hash, Hasher}, mem, - sync::Arc, }; use base_db::{ @@ -40,6 +39,7 @@ use hir::{ }; use rayon::prelude::*; use rustc_hash::FxHashSet; +use triomphe::Arc; use crate::RootDatabase; @@ -98,6 +98,10 @@ pub trait SymbolsDatabase: HirDatabase + SourceDatabaseExt + Upcast Arc; + #[salsa::transparent] + /// The symbol indices of modules that make up a given crate. + fn crate_symbols(&self, krate: Crate) -> Box<[Arc]>; + /// The set of "local" (that is, from the current workspace) roots. /// Files in local roots are assumed to change frequently. #[salsa::input] @@ -112,26 +116,33 @@ pub trait SymbolsDatabase: HirDatabase + SourceDatabaseExt + Upcast Arc { let _p = profile::span("library_symbols"); - // todo: this could be parallelized, once I figure out how to do that... - let symbols = db - .source_root_crates(source_root_id) + let mut symbol_collector = SymbolCollector::new(db.upcast()); + + db.source_root_crates(source_root_id) .iter() .flat_map(|&krate| Crate::from(krate).modules(db.upcast())) - // we specifically avoid calling SymbolsDatabase::module_symbols here, even they do the same thing, + // we specifically avoid calling other SymbolsDatabase queries here, even though they do the same thing, // as the index for a library is not going to really ever change, and we do not want to store each - // module's index in salsa. - .flat_map(|module| SymbolCollector::collect(db.upcast(), module)) - .collect(); + // the module or crate indices for those in salsa unless we need to. + .for_each(|module| symbol_collector.collect(module)); + let mut symbols = symbol_collector.finish(); + symbols.shrink_to_fit(); Arc::new(SymbolIndex::new(symbols)) } fn module_symbols(db: &dyn SymbolsDatabase, module: Module) -> Arc { let _p = profile::span("module_symbols"); - let symbols = SymbolCollector::collect(db.upcast(), module); + + let symbols = SymbolCollector::collect_module(db.upcast(), module); Arc::new(SymbolIndex::new(symbols)) } +pub fn crate_symbols(db: &dyn SymbolsDatabase, krate: Crate) -> Box<[Arc]> { + let _p = profile::span("crate_symbols"); + krate.modules(db.upcast()).into_iter().map(|module| db.module_symbols(module)).collect() +} + /// Need to wrap Snapshot to provide `Clone` impl for `map_with` struct Snap(DB); impl Snap> { @@ -187,36 +198,21 @@ pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec { .map_with(Snap::new(db), |snap, &root| snap.library_symbols(root)) .collect() } else { - let mut modules = Vec::new(); + let mut crates = Vec::new(); for &root in db.local_roots().iter() { - let crates = db.source_root_crates(root); - for &krate in crates.iter() { - modules.extend(Crate::from(krate).modules(db)); - } + crates.extend(db.source_root_crates(root).iter().copied()) } - - modules - .par_iter() - .map_with(Snap::new(db), |snap, &module| snap.module_symbols(module)) - .collect() + let indices: Vec<_> = crates + .into_par_iter() + .map_with(Snap::new(db), |snap, krate| snap.crate_symbols(krate.into())) + .collect(); + indices.iter().flat_map(|indices| indices.iter().cloned()).collect() }; query.search(&indices) } -pub fn crate_symbols(db: &RootDatabase, krate: Crate, query: Query) -> Vec { - let _p = profile::span("crate_symbols").detail(|| format!("{query:?}")); - - let modules = krate.modules(db); - let indices: Vec<_> = modules - .par_iter() - .map_with(Snap::new(db), |snap, &module| snap.module_symbols(module)) - .collect(); - - query.search(&indices) -} - #[derive(Default)] pub struct SymbolIndex { symbols: Vec, @@ -274,7 +270,12 @@ impl SymbolIndex { builder.insert(key, value).unwrap(); } - let map = fst::Map::new(builder.into_inner().unwrap()).unwrap(); + let map = fst::Map::new({ + let mut buf = builder.into_inner().unwrap(); + buf.shrink_to_fit(); + buf + }) + .unwrap(); SymbolIndex { symbols, map } } @@ -316,7 +317,14 @@ impl Query { let (start, end) = SymbolIndex::map_value_to_range(indexed_value.value); for symbol in &symbol_index.symbols[start..end] { - if self.only_types && !symbol.kind.is_type() { + if self.only_types + && !matches!( + symbol.def, + hir::ModuleDef::Adt(..) + | hir::ModuleDef::TypeAlias(..) + | hir::ModuleDef::BuiltinType(..) + ) + { continue; } if self.exact { @@ -418,7 +426,7 @@ struct StructInModB; .modules(&db) .into_iter() .map(|module_id| { - let mut symbols = SymbolCollector::collect(&db, module_id); + let mut symbols = SymbolCollector::collect_module(&db, module_id); symbols.sort_by_key(|it| it.name.clone()); (module_id, symbols) }) @@ -426,4 +434,31 @@ struct StructInModB; expect_file!["./test_data/test_symbol_index_collection.txt"].assert_debug_eq(&symbols); } + + #[test] + fn test_doc_alias() { + let (db, _) = RootDatabase::with_single_file( + r#" +#[doc(alias="s1")] +#[doc(alias="s2")] +#[doc(alias("mul1","mul2"))] +struct Struct; + +#[doc(alias="s1")] +struct Duplicate; + "#, + ); + + let symbols: Vec<_> = Crate::from(db.test_crate()) + .modules(&db) + .into_iter() + .map(|module_id| { + let mut symbols = SymbolCollector::collect_module(&db, module_id); + symbols.sort_by_key(|it| it.name.clone()); + (module_id, symbols) + }) + .collect(); + + expect_file!["./test_data/test_doc_alias.txt"].assert_debug_eq(&symbols); + } } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs index 2d6927cee9953..acf0a67de4a12 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs @@ -92,7 +92,7 @@ pub fn lex_format_specifiers( let (_, second) = cloned.next().unwrap_or_default(); match second { '<' | '^' | '>' => { - // alignment specifier, first char specifies fillment + // alignment specifier, first char specifies fill skip_char_and_emit(&mut chars, FormatSpecifier::Fill, &mut callback); skip_char_and_emit(&mut chars, FormatSpecifier::Align, &mut callback); } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string_exprs.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string_exprs.rs index fcef71fb74e7b..fc230818193dc 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string_exprs.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string_exprs.rs @@ -1,7 +1,7 @@ //! Tools to work with expressions present in format string literals for the `format_args!` family of macros. //! Primarily meant for assists and completions. -/// Enum for represenging extraced format string args. +/// Enum for representing extracted format string args. /// Can either be extracted expressions (which includes identifiers), /// or placeholders `{}`. #[derive(Debug, PartialEq, Eq)] diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/insert_whitespace_into_node.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/insert_whitespace_into_node.rs index 8bc093a85a21b..0b0fc6693526f 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/insert_whitespace_into_node.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/insert_whitespace_into_node.rs @@ -60,7 +60,9 @@ pub fn insert_ws_into(syn: SyntaxNode) -> SyntaxNode { |f: fn(SyntaxKind) -> bool, default| -> bool { last.map(f).unwrap_or(default) }; match tok.kind() { - k if is_text(k) && is_next(|it| !it.is_punct() || it == UNDERSCORE, false) => { + k if is_text(k) + && is_next(|it| !it.is_punct() || matches!(it, T![_] | T![#]), false) => + { mods.push(do_ws(after, tok)); } L_CURLY if is_next(|it| it != R_CURLY, true) => { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs index a34dc1b69507e..22ced69d81e4a 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs @@ -52,7 +52,9 @@ pub fn preorder_expr(start: &ast::Expr, cb: &mut dyn FnMut(WalkEvent) } }; if let Some(let_stmt) = node.parent().and_then(ast::LetStmt::cast) { - if Some(node.clone()) != let_stmt.initializer().map(|it| it.syntax().clone()) { + if let_stmt.initializer().map(|it| it.syntax() != &node).unwrap_or(true) + && let_stmt.let_else().map(|it| it.syntax() != &node).unwrap_or(true) + { // skipping potential const pat expressions in let statements preorder.skip_subtree(); continue; diff --git a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt new file mode 100644 index 0000000000000..77714efa35040 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt @@ -0,0 +1,202 @@ +[ + ( + Module { + id: ModuleId { + krate: Idx::(0), + block: None, + local_id: Idx::(0), + }, + }, + [ + FileSymbol { + name: "Duplicate", + def: Adt( + Struct( + Struct { + id: StructId( + 1, + ), + }, + ), + ), + loc: DeclarationLocation { + hir_file_id: HirFileId( + 0, + ), + ptr: SyntaxNodePtr { + kind: STRUCT, + range: 83..119, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 109..118, + }, + }, + container_name: None, + is_alias: false, + }, + FileSymbol { + name: "Struct", + def: Adt( + Struct( + Struct { + id: StructId( + 0, + ), + }, + ), + ), + loc: DeclarationLocation { + hir_file_id: HirFileId( + 0, + ), + ptr: SyntaxNodePtr { + kind: STRUCT, + range: 0..81, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 74..80, + }, + }, + container_name: None, + is_alias: false, + }, + FileSymbol { + name: "mul1", + def: Adt( + Struct( + Struct { + id: StructId( + 0, + ), + }, + ), + ), + loc: DeclarationLocation { + hir_file_id: HirFileId( + 0, + ), + ptr: SyntaxNodePtr { + kind: STRUCT, + range: 0..81, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 74..80, + }, + }, + container_name: None, + is_alias: true, + }, + FileSymbol { + name: "mul2", + def: Adt( + Struct( + Struct { + id: StructId( + 0, + ), + }, + ), + ), + loc: DeclarationLocation { + hir_file_id: HirFileId( + 0, + ), + ptr: SyntaxNodePtr { + kind: STRUCT, + range: 0..81, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 74..80, + }, + }, + container_name: None, + is_alias: true, + }, + FileSymbol { + name: "s1", + def: Adt( + Struct( + Struct { + id: StructId( + 0, + ), + }, + ), + ), + loc: DeclarationLocation { + hir_file_id: HirFileId( + 0, + ), + ptr: SyntaxNodePtr { + kind: STRUCT, + range: 0..81, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 74..80, + }, + }, + container_name: None, + is_alias: true, + }, + FileSymbol { + name: "s1", + def: Adt( + Struct( + Struct { + id: StructId( + 1, + ), + }, + ), + ), + loc: DeclarationLocation { + hir_file_id: HirFileId( + 0, + ), + ptr: SyntaxNodePtr { + kind: STRUCT, + range: 83..119, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 109..118, + }, + }, + container_name: None, + is_alias: true, + }, + FileSymbol { + name: "s2", + def: Adt( + Struct( + Struct { + id: StructId( + 0, + ), + }, + ), + ), + loc: DeclarationLocation { + hir_file_id: HirFileId( + 0, + ), + ptr: SyntaxNodePtr { + kind: STRUCT, + range: 0..81, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 74..80, + }, + }, + container_name: None, + is_alias: true, + }, + ], + ), +] diff --git a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt index 8c11408dec5d7..b5adfc13d9644 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt +++ b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt @@ -2,9 +2,7 @@ ( Module { id: ModuleId { - krate: CrateId( - 0, - ), + krate: Idx::(0), block: None, local_id: Idx::(0), }, @@ -12,6 +10,13 @@ [ FileSymbol { name: "Alias", + def: TypeAlias( + TypeAlias { + id: TypeAliasId( + 0, + ), + }, + ), loc: DeclarationLocation { hir_file_id: HirFileId( 0, @@ -25,11 +30,18 @@ range: 402..407, }, }, - kind: TypeAlias, container_name: None, + is_alias: false, }, FileSymbol { name: "CONST", + def: Const( + Const { + id: ConstId( + 0, + ), + }, + ), loc: DeclarationLocation { hir_file_id: HirFileId( 0, @@ -43,11 +55,18 @@ range: 346..351, }, }, - kind: Const, container_name: None, + is_alias: false, }, FileSymbol { name: "CONST_WITH_INNER", + def: Const( + Const { + id: ConstId( + 2, + ), + }, + ), loc: DeclarationLocation { hir_file_id: HirFileId( 0, @@ -61,11 +80,20 @@ range: 526..542, }, }, - kind: Const, container_name: None, + is_alias: false, }, FileSymbol { name: "Enum", + def: Adt( + Enum( + Enum { + id: EnumId( + 0, + ), + }, + ), + ), loc: DeclarationLocation { hir_file_id: HirFileId( 0, @@ -79,11 +107,20 @@ range: 190..194, }, }, - kind: Enum, container_name: None, + is_alias: false, }, FileSymbol { name: "Macro", + def: Macro( + Macro { + id: Macro2Id( + Macro2Id( + 0, + ), + ), + }, + ), loc: DeclarationLocation { hir_file_id: HirFileId( 0, @@ -97,11 +134,18 @@ range: 159..164, }, }, - kind: Macro, container_name: None, + is_alias: false, }, FileSymbol { name: "STATIC", + def: Static( + Static { + id: StaticId( + 0, + ), + }, + ), loc: DeclarationLocation { hir_file_id: HirFileId( 0, @@ -115,11 +159,20 @@ range: 369..375, }, }, - kind: Static, container_name: None, + is_alias: false, }, FileSymbol { name: "Struct", + def: Adt( + Struct( + Struct { + id: StructId( + 1, + ), + }, + ), + ), loc: DeclarationLocation { hir_file_id: HirFileId( 0, @@ -133,11 +186,20 @@ range: 177..183, }, }, - kind: Struct, container_name: None, + is_alias: false, }, FileSymbol { name: "StructFromMacro", + def: Adt( + Struct( + Struct { + id: StructId( + 0, + ), + }, + ), + ), loc: DeclarationLocation { hir_file_id: HirFileId( 2147483648, @@ -151,11 +213,20 @@ range: 6..21, }, }, - kind: Struct, container_name: None, + is_alias: false, }, FileSymbol { name: "StructInFn", + def: Adt( + Struct( + Struct { + id: StructId( + 4, + ), + }, + ), + ), loc: DeclarationLocation { hir_file_id: HirFileId( 0, @@ -169,13 +240,22 @@ range: 325..335, }, }, - kind: Struct, container_name: Some( "main", ), + is_alias: false, }, FileSymbol { name: "StructInNamedConst", + def: Adt( + Struct( + Struct { + id: StructId( + 5, + ), + }, + ), + ), loc: DeclarationLocation { hir_file_id: HirFileId( 0, @@ -189,13 +269,22 @@ range: 562..580, }, }, - kind: Struct, container_name: Some( "CONST_WITH_INNER", ), + is_alias: false, }, FileSymbol { name: "StructInUnnamedConst", + def: Adt( + Struct( + Struct { + id: StructId( + 6, + ), + }, + ), + ), loc: DeclarationLocation { hir_file_id: HirFileId( 0, @@ -209,11 +298,18 @@ range: 486..506, }, }, - kind: Struct, container_name: None, + is_alias: false, }, FileSymbol { name: "Trait", + def: Trait( + Trait { + id: TraitId( + 0, + ), + }, + ), loc: DeclarationLocation { hir_file_id: HirFileId( 0, @@ -227,11 +323,20 @@ range: 267..272, }, }, - kind: Trait, container_name: None, + is_alias: false, }, FileSymbol { name: "Union", + def: Adt( + Union( + Union { + id: UnionId( + 0, + ), + }, + ), + ), loc: DeclarationLocation { hir_file_id: HirFileId( 0, @@ -245,11 +350,20 @@ range: 214..219, }, }, - kind: Union, container_name: None, + is_alias: false, }, FileSymbol { name: "a_mod", + def: Module( + Module { + id: ModuleId { + krate: Idx::(0), + block: None, + local_id: Idx::(1), + }, + }, + ), loc: DeclarationLocation { hir_file_id: HirFileId( 0, @@ -263,11 +377,20 @@ range: 423..428, }, }, - kind: Module, container_name: None, + is_alias: false, }, FileSymbol { name: "b_mod", + def: Module( + Module { + id: ModuleId { + krate: Idx::(0), + block: None, + local_id: Idx::(2), + }, + }, + ), loc: DeclarationLocation { hir_file_id: HirFileId( 0, @@ -281,11 +404,20 @@ range: 598..603, }, }, - kind: Module, container_name: None, + is_alias: false, }, FileSymbol { name: "define_struct", + def: Macro( + Macro { + id: MacroRulesId( + MacroRulesId( + 1, + ), + ), + }, + ), loc: DeclarationLocation { hir_file_id: HirFileId( 0, @@ -299,11 +431,18 @@ range: 64..77, }, }, - kind: Macro, container_name: None, + is_alias: false, }, FileSymbol { name: "impl_fn", + def: Function( + Function { + id: FunctionId( + 2, + ), + }, + ), loc: DeclarationLocation { hir_file_id: HirFileId( 0, @@ -317,11 +456,20 @@ range: 245..252, }, }, - kind: Function, container_name: None, + is_alias: false, }, FileSymbol { name: "macro_rules_macro", + def: Macro( + Macro { + id: MacroRulesId( + MacroRulesId( + 0, + ), + ), + }, + ), loc: DeclarationLocation { hir_file_id: HirFileId( 0, @@ -335,11 +483,18 @@ range: 14..31, }, }, - kind: Macro, container_name: None, + is_alias: false, }, FileSymbol { name: "main", + def: Function( + Function { + id: FunctionId( + 0, + ), + }, + ), loc: DeclarationLocation { hir_file_id: HirFileId( 0, @@ -353,11 +508,18 @@ range: 305..309, }, }, - kind: Function, container_name: None, + is_alias: false, }, FileSymbol { name: "trait_fn", + def: Function( + Function { + id: FunctionId( + 1, + ), + }, + ), loc: DeclarationLocation { hir_file_id: HirFileId( 0, @@ -371,19 +533,17 @@ range: 282..290, }, }, - kind: Function, container_name: Some( "Trait", ), + is_alias: false, }, ], ), ( Module { id: ModuleId { - krate: CrateId( - 0, - ), + krate: Idx::(0), block: None, local_id: Idx::(1), }, @@ -391,6 +551,15 @@ [ FileSymbol { name: "StructInModA", + def: Adt( + Struct( + Struct { + id: StructId( + 2, + ), + }, + ), + ), loc: DeclarationLocation { hir_file_id: HirFileId( 0, @@ -404,17 +573,15 @@ range: 442..454, }, }, - kind: Struct, container_name: None, + is_alias: false, }, ], ), ( Module { id: ModuleId { - krate: CrateId( - 0, - ), + krate: Idx::(0), block: None, local_id: Idx::(2), }, @@ -422,6 +589,15 @@ [ FileSymbol { name: "StructInModB", + def: Adt( + Struct( + Struct { + id: StructId( + 3, + ), + }, + ), + ), loc: DeclarationLocation { hir_file_id: HirFileId( 1, @@ -435,8 +611,8 @@ range: 7..19, }, }, - kind: Struct, container_name: None, + is_alias: false, }, ], ), diff --git a/src/tools/rust-analyzer/crates/ide-db/src/tests/line_index.rs b/src/tools/rust-analyzer/crates/ide-db/src/tests/line_index.rs new file mode 100644 index 0000000000000..6b49bb2631c19 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-db/src/tests/line_index.rs @@ -0,0 +1,49 @@ +use line_index::{LineCol, LineIndex, WideEncoding}; +use test_utils::skip_slow_tests; + +#[test] +fn test_every_chars() { + if skip_slow_tests() { + return; + } + + let text: String = { + let mut chars: Vec = ((0 as char)..char::MAX).collect(); // Neat! + chars.extend("\n".repeat(chars.len() / 16).chars()); + let mut rng = oorandom::Rand32::new(stdx::rand::seed()); + stdx::rand::shuffle(&mut chars, |i| rng.rand_range(0..i as u32) as usize); + chars.into_iter().collect() + }; + assert!(text.contains('💩')); // Sanity check. + + let line_index = LineIndex::new(&text); + + let mut lin_col = LineCol { line: 0, col: 0 }; + let mut col_utf16 = 0; + let mut col_utf32 = 0; + for (offset, c) in text.char_indices() { + let got_offset = line_index.offset(lin_col).unwrap(); + assert_eq!(usize::from(got_offset), offset); + + let got_lin_col = line_index.line_col(got_offset); + assert_eq!(got_lin_col, lin_col); + + for (enc, col) in [(WideEncoding::Utf16, col_utf16), (WideEncoding::Utf32, col_utf32)] { + let wide_lin_col = line_index.to_wide(enc, lin_col).unwrap(); + let got_lin_col = line_index.to_utf8(enc, wide_lin_col).unwrap(); + assert_eq!(got_lin_col, lin_col); + assert_eq!(wide_lin_col.col, col) + } + + if c == '\n' { + lin_col.line += 1; + lin_col.col = 0; + col_utf16 = 0; + col_utf32 = 0; + } else { + lin_col.col += c.len_utf8() as u32; + col_utf16 += c.len_utf16() as u32; + col_utf32 += 1; + } + } +} diff --git a/src/tools/rust-analyzer/crates/ide-db/src/traits.rs b/src/tools/rust-analyzer/crates/ide-db/src/traits.rs index 6a7ea7c19f228..9abbc34414291 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/traits.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/traits.rs @@ -38,15 +38,15 @@ pub fn get_missing_assoc_items( for item in imp.items(sema.db) { match item { hir::AssocItem::Function(it) => { - impl_fns_consts.insert(it.name(sema.db).to_string()); + impl_fns_consts.insert(it.name(sema.db).display(sema.db).to_string()); } hir::AssocItem::Const(it) => { if let Some(name) = it.name(sema.db) { - impl_fns_consts.insert(name.to_string()); + impl_fns_consts.insert(name.display(sema.db).to_string()); } } hir::AssocItem::TypeAlias(it) => { - impl_type.insert(it.name(sema.db).to_string()); + impl_type.insert(it.name(sema.db).display(sema.db).to_string()); } } } @@ -57,12 +57,14 @@ pub fn get_missing_assoc_items( .into_iter() .filter(|i| match i { hir::AssocItem::Function(f) => { - !impl_fns_consts.contains(&f.name(sema.db).to_string()) + !impl_fns_consts.contains(&f.name(sema.db).display(sema.db).to_string()) + } + hir::AssocItem::TypeAlias(t) => { + !impl_type.contains(&t.name(sema.db).display(sema.db).to_string()) } - hir::AssocItem::TypeAlias(t) => !impl_type.contains(&t.name(sema.db).to_string()), hir::AssocItem::Const(c) => c .name(sema.db) - .map(|n| !impl_fns_consts.contains(&n.to_string())) + .map(|n| !impl_fns_consts.contains(&n.display(sema.db).to_string())) .unwrap_or_default(), }) .collect() @@ -137,7 +139,7 @@ mod tests { sema.find_node_at_offset_with_descend(file.syntax(), position.offset).unwrap(); let trait_ = crate::traits::resolve_target_trait(&sema, &impl_block); let actual = match trait_ { - Some(trait_) => trait_.name(&db).to_string(), + Some(trait_) => trait_.name(&db).display(&db).to_string(), None => String::new(), }; expect.assert_eq(&actual); @@ -152,7 +154,7 @@ mod tests { let items = crate::traits::get_missing_assoc_items(&sema, &impl_block); let actual = items .into_iter() - .map(|item| item.name(&db).unwrap().to_string()) + .map(|item| item.name(&db).unwrap().display(&db).to_string()) .collect::>() .join("\n"); expect.assert_eq(&actual); diff --git a/src/tools/rust-analyzer/crates/ide-db/src/use_trivial_constructor.rs b/src/tools/rust-analyzer/crates/ide-db/src/use_trivial_constructor.rs index 39431bed3821c..f96ea29ae2f9d 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/use_trivial_constructor.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/use_trivial_constructor.rs @@ -1,9 +1,9 @@ -//! Functionality for generating trivial contructors +//! Functionality for generating trivial constructors use hir::StructKind; use syntax::ast; -/// given a type return the trivial contructor (if one exists) +/// given a type return the trivial constructor (if one exists) pub fn use_trivial_constructor( db: &crate::RootDatabase, path: ast::Path, diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs index 114face2dca85..30576c71fb7c2 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs @@ -31,12 +31,8 @@ mod tests { fn foo() { break; //^^^^^ error: break outside of loop - break 'a; - //^^^^^^^^ error: break outside of loop continue; //^^^^^^^^ error: continue outside of loop - continue 'a; - //^^^^^^^^^^^ error: continue outside of loop } "#, ); @@ -51,12 +47,8 @@ fn foo() { async { break; //^^^^^ error: break outside of loop - break 'a; - //^^^^^^^^ error: break outside of loop continue; //^^^^^^^^ error: continue outside of loop - continue 'a; - //^^^^^^^^^^^ error: continue outside of loop }; } } @@ -73,12 +65,8 @@ fn foo() { || { break; //^^^^^ error: break outside of loop - break 'a; - //^^^^^^^^ error: break outside of loop continue; //^^^^^^^^ error: continue outside of loop - continue 'a; - //^^^^^^^^^^^ error: continue outside of loop }; } } @@ -94,9 +82,7 @@ fn foo() { 'a: loop { { break; - break 'a; continue; - continue 'a; } } } @@ -112,9 +98,7 @@ fn foo() { 'a: loop { try { break; - break 'a; continue; - continue 'a; }; } } @@ -130,11 +114,8 @@ fn foo() { 'a: { break; //^^^^^ error: break outside of loop - break 'a; continue; //^^^^^^^^ error: continue outside of loop - continue 'a; - //^^^^^^^^^^^ error: continue outside of loop } } "#, @@ -143,14 +124,34 @@ fn foo() { #[test] fn value_break_in_for_loop() { + // FIXME: the error is correct, but the message is terrible check_diagnostics( r#" +//- minicore: iterator fn test() { for _ in [()] { break 3; - // ^^^^^^^ error: can't break with a value in this position + // ^ error: expected (), found i32 } } +"#, + ); + } + + #[test] + fn try_block_desugaring_inside_closure() { + // regression test for #14701 + check_diagnostics( + r#" +//- minicore: option, try +fn test() { + try { + || { + let x = Some(2); + Some(x?) + }; + }; +} "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs index db88bf7b9313d..90279e14536be 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs @@ -27,7 +27,7 @@ pub(crate) fn incorrect_case(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCas } fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCase) -> Option> { - let root = ctx.sema.db.parse_or_expand(d.file)?; + let root = ctx.sema.db.parse_or_expand(d.file); let name_node = d.ident.to_node(&root); let def = NameClass::classify(&ctx.sema, &name_node)?.defined()?; @@ -295,7 +295,7 @@ impl someStruct { } #[test] - fn no_diagnostic_for_enum_varinats() { + fn no_diagnostic_for_enum_variants() { check_diagnostics( r#" enum Option { Some, None } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs index 870c78d1f1eb7..7547779a95c72 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs @@ -9,6 +9,16 @@ pub(crate) fn macro_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroError) -> Diagnostic::new("macro-error", d.message.clone(), display_range).experimental() } +// Diagnostic: macro-error +// +// This diagnostic is shown for macro expansion errors. +pub(crate) fn macro_def_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroDefError) -> Diagnostic { + // Use more accurate position if available. + let display_range = + ctx.resolve_precise_location(&d.node.clone().map(|it| it.syntax_node_ptr()), d.name); + Diagnostic::new("macro-def-error", d.message.clone(), display_range).experimental() +} + #[cfg(test)] mod tests { use crate::{ @@ -188,6 +198,7 @@ fn f() { "#, ); } + #[test] fn dollar_crate_in_builtin_macro() { check_diagnostics( @@ -209,6 +220,40 @@ macro_rules! outer { fn f() { outer!(); } //^^^^^^^^ error: leftover tokens +"#, + ) + } + + #[test] + fn def_diagnostic() { + check_diagnostics( + r#" +macro_rules! foo { + //^^^ error: expected subtree + f => {}; +} + +fn f() { + foo!(); + //^^^ error: invalid macro definition: expected subtree + +} +"#, + ) + } + + #[test] + fn expansion_syntax_diagnostic() { + check_diagnostics( + r#" +macro_rules! foo { + () => { struct; }; +} + +fn f() { + foo!(); + //^^^ error: Syntax Error in Expansion: expected a name +} "#, ) } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs index 5c4327ff93413..60ccc41df01cc 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs @@ -31,7 +31,7 @@ use crate::{fix, Diagnostic, DiagnosticsContext}; pub(crate) fn missing_fields(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Diagnostic { let mut message = String::from("missing structure fields:\n"); for field in &d.missed_fields { - format_to!(message, "- {}\n", field); + format_to!(message, "- {}\n", field.display(ctx.sema.db)); } let ptr = InFile::new( @@ -56,7 +56,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option ctx.sema.scope(ptr.to_node(&root).syntax()).map(|it| it.module()), @@ -175,8 +175,10 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option ast::Type { let ty_str = match ty.as_adt() { - Some(adt) => adt.name(db).to_string(), - None => ty.display_source_code(db, module.into()).ok().unwrap_or_else(|| "_".to_string()), + Some(adt) => adt.name(db).display(db.upcast()).to_string(), + None => { + ty.display_source_code(db, module.into(), false).ok().unwrap_or_else(|| "_".to_string()) + } }; make::ty(&ty_str) diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs index ac4463331f2d2..3f13b97a44734 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs @@ -271,15 +271,20 @@ enum Either2 { C, D } fn main() { match Either::A { Either2::C => (), + //^^^^^^^^^^ error: expected Either, found Either2 Either2::D => (), + //^^^^^^^^^^ error: expected Either, found Either2 } match (true, false) { (true, false, true) => (), + //^^^^^^^^^^^^^^^^^^^ error: expected (bool, bool), found (bool, bool, bool) (true) => (), // ^^^^ error: expected (bool, bool), found bool } match (true, false) { (true,) => {} } + //^^^^^^^ error: expected (bool, bool), found (bool,) match (0) { () => () } + //^^ error: expected i32, found () match Unresolved::Bar { Unresolved::Baz => () } } "#, @@ -293,7 +298,9 @@ fn main() { r#" fn main() { match false { true | () => {} } + //^^ error: expected bool, found () match (false,) { (true | (),) => {} } + //^^ error: expected bool, found () } "#, ); @@ -738,17 +745,13 @@ fn main() { #[test] fn binding_ref_has_correct_type() { - cov_mark::check_count!(validate_match_bailed_out, 1); - // Asserts `PatKind::Binding(ref _x): bool`, not &bool. // If that's not true match checking will panic with "incompatible constructors" // FIXME: make facilities to test this directly like `tests::check_infer(..)` - check_diagnostics( + check_diagnostics_no_bails( r#" 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 incorrectly. match Foo::A { ref _x => {} Foo::A => {} @@ -1024,6 +1027,7 @@ fn main() { check_diagnostics( r#" +//- minicore: copy fn main() { match &false { &true => {} @@ -1035,11 +1039,13 @@ fn main() { #[test] fn reference_patterns_in_fields() { - cov_mark::check_count!(validate_match_bailed_out, 2); + cov_mark::check_count!(validate_match_bailed_out, 1); check_diagnostics( r#" +//- minicore: copy fn main() { match (&false,) { + //^^^^^^^^^ error: missing match arm: `(&false,)` not covered (true,) => {} } match (&false,) { diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs index eb32db250656c..2026b6fcef537 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs @@ -24,7 +24,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingUnsafe) -> Option, d: &hir::MovedOutOfRef) -> Diagnostic { + Diagnostic::new( + "moved-out-of-ref", + format!("cannot move `{}` out of reference", d.ty.display(ctx.sema.db)), + ctx.sema.diagnostics_display_range(d.span.clone()).range, + ) + .experimental() // spans are broken, and I'm not sure how precise we can detect copy types +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + // FIXME: spans are broken + + #[test] + fn move_by_explicit_deref() { + check_diagnostics( + r#" +struct X; +fn main() { + let a = &X; + let b = *a; + //^ error: cannot move `X` out of reference +} +"#, + ); + } + + #[test] + fn move_out_of_field() { + check_diagnostics( + r#" +//- minicore: copy +struct X; +struct Y(X, i32); +fn main() { + let a = &Y(X, 5); + let b = a.0; + //^ error: cannot move `X` out of reference + let y = a.1; +} +"#, + ); + } + + #[test] + fn move_out_of_static() { + check_diagnostics( + r#" +//- minicore: copy +struct X; +fn main() { + static S: X = X; + let s = S; + //^ error: cannot move `X` out of reference +} +"#, + ); + } + + #[test] + fn generic_types() { + check_diagnostics( + r#" +//- minicore: derive, copy + +#[derive(Copy)] +struct X(T); +struct Y; + +fn consume(_: X) { + +} + +fn main() { + let a = &X(Y); + consume(*a); + //^^^^^^^^^^^ error: cannot move `X` out of reference + let a = &X(5); + consume(*a); +} +"#, + ); + } + + #[test] + fn no_false_positive_simple() { + check_diagnostics( + r#" +//- minicore: copy +fn f(_: i32) {} +fn main() { + let x = &2; + f(*x); +} +"#, + ); + } + + #[test] + fn no_false_positive_unknown_type() { + check_diagnostics( + r#" +//- minicore: derive, copy +fn f(x: &Unknown) -> Unknown { + *x +} + +#[derive(Copy)] +struct X(T); + +struct Y(T); + +fn g(x: &X) -> X { + *x +} + +fn h(x: &Y) -> Y { + // FIXME: we should show error for this, as `Y` is not copy + // regardless of its generic parameter. + *x +} + +"#, + ); + } + + #[test] + fn no_false_positive_dyn_fn() { + check_diagnostics( + r#" +//- minicore: copy, fn +fn f(x: &mut &mut dyn Fn()) { + x(); +} + +struct X<'a> { + field: &'a mut dyn Fn(), +} + +fn f(x: &mut X<'_>) { + (x.field)(); +} +"#, + ); + } + + #[test] + fn no_false_positive_match_and_closure_capture() { + check_diagnostics( + r#" +//- minicore: copy, fn +enum X { + Foo(u16), + Bar, +} + +fn main() { + let x = &X::Bar; + let c = || match *x { + X::Foo(t) => t, + _ => 5, + }; +} + "#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs index 96470265d11d1..f61460e317f78 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs @@ -18,7 +18,8 @@ pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Diagno let use_range = d.span.value.text_range(); for source in d.local.sources(ctx.sema.db) { let Some(ast) = source.name() else { continue }; - edit_builder.insert(ast.syntax().text_range().start(), "mut ".to_string()); + // FIXME: macros + edit_builder.insert(ast.value.syntax().text_range().start(), "mut ".to_string()); } let edit = edit_builder.finish(); Some(vec![fix( @@ -30,7 +31,10 @@ pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Diagno })(); Diagnostic::new( "need-mut", - format!("cannot mutate immutable variable `{}`", d.local.name(ctx.sema.db)), + format!( + "cannot mutate immutable variable `{}`", + d.local.name(ctx.sema.db).display(ctx.sema.db) + ), ctx.sema.diagnostics_display_range(d.span.clone()).range, ) .with_fixes(fixes) @@ -340,6 +344,7 @@ fn main() { fn regression_14310() { check_diagnostics( r#" + //- minicore: copy, builtin_impls fn clone(mut i: &!) -> ! { //^^^^^ 💡 weak: variable does not need to be mutable *i @@ -348,6 +353,32 @@ fn main() { ); } + #[test] + fn match_closure_capture() { + check_diagnostics( + r#" +//- minicore: option +fn main() { + let mut v = &mut Some(2); + //^^^^^ 💡 weak: variable does not need to be mutable + let _ = || match v { + Some(k) => { + *k = 5; + } + None => {} + }; + let v = &mut Some(2); + let _ = || match v { + //^ 💡 error: cannot mutate immutable variable `v` + ref mut k => { + *k = &mut Some(5); + } + }; +} +"#, + ); + } + #[test] fn match_bindings() { check_diagnostics( @@ -368,7 +399,7 @@ fn main() { #[test] fn mutation_in_dead_code() { // This one is interesting. Dead code is not represented at all in the MIR, so - // there would be no mutablility error for locals in dead code. Rustc tries to + // there would be no mutability error for locals in dead code. Rustc tries to // not emit `unused_mut` in this case, but since it works without `mut`, and // special casing it is not trivial, we emit it. check_diagnostics( @@ -481,6 +512,38 @@ fn main() { f(x); } } +"#, + ); + check_diagnostics( + r#" +fn check(_: i32) -> bool { + false +} +fn main() { + loop { + let x = 1; + if check(x) { + break; + } + let y = (1, 2); + if check(y.1) { + return; + } + let z = (1, 2); + match z { + (k @ 5, ref mut t) if { continue; } => { + //^^^^^^^^^ 💡 error: cannot mutate immutable variable `z` + *t = 5; + } + _ => { + let y = (1, 2); + if check(y.1) { + return; + } + } + } + } +} "#, ); check_diagnostics( @@ -544,6 +607,28 @@ fn f(x: i32) { x = 5; //^^^^^ 💡 error: cannot mutate immutable variable `x` } +"#, + ); + check_diagnostics( + r#" +fn f((x, y): (i32, i32)) { + let t = [0; 2]; + x = 5; + //^^^^^ 💡 error: cannot mutate immutable variable `x` +} +"#, + ); + } + + #[test] + fn no_diagnostics_in_case_of_multiple_bounds() { + check_diagnostics( + r#" +fn f() { + let (b, a, b) = (2, 3, 5); + a = 8; + //^^^^^ 💡 error: cannot mutate immutable variable `a` +} "#, ); } @@ -552,7 +637,7 @@ fn f(x: i32) { fn for_loop() { check_diagnostics( r#" -//- minicore: iterators +//- minicore: iterators, copy fn f(x: [(i32, u8); 10]) { for (a, mut b) in x { //^^^^^ 💡 weak: variable does not need to be mutable @@ -564,9 +649,97 @@ fn f(x: [(i32, u8); 10]) { ); } + #[test] + fn while_let() { + check_diagnostics( + r#" +//- minicore: iterators, copy +fn f(x: [(i32, u8); 10]) { + let mut it = x.into_iter(); + while let Some((a, mut b)) = it.next() { + //^^^^^ 💡 weak: variable does not need to be mutable + while let Some((c, mut d)) = it.next() { + //^^^^^ 💡 weak: variable does not need to be mutable + a = 2; + //^^^^^ 💡 error: cannot mutate immutable variable `a` + c = 2; + //^^^^^ 💡 error: cannot mutate immutable variable `c` + } + } +} +"#, + ); + } + + #[test] + fn index() { + check_diagnostics( + r#" +//- minicore: coerce_unsized, index, slice +fn f() { + let x = [1, 2, 3]; + x[2] = 5; + //^^^^^^^^ 💡 error: cannot mutate immutable variable `x` + let x = &mut x; + //^^^^^^ 💡 error: cannot mutate immutable variable `x` + let mut x = x; + //^^^^^ 💡 weak: variable does not need to be mutable + x[2] = 5; +} +"#, + ); + } + + #[test] + fn overloaded_index() { + check_diagnostics( + r#" +//- minicore: index +use core::ops::{Index, IndexMut}; + +struct Foo; +impl Index for Foo { + type Output = (i32, u8); + fn index(&self, index: usize) -> &(i32, u8) { + &(5, 2) + } +} +impl IndexMut for Foo { + fn index_mut(&mut self, index: usize) -> &mut (i32, u8) { + &mut (5, 2) + } +} +fn f() { + let mut x = Foo; + //^^^^^ 💡 weak: variable does not need to be mutable + let y = &x[2]; + let x = Foo; + let y = &mut x[2]; + //^💡 error: cannot mutate immutable variable `x` + let mut x = &mut Foo; + //^^^^^ 💡 weak: variable does not need to be mutable + let y: &mut (i32, u8) = &mut x[2]; + let x = Foo; + let ref mut y = x[7]; + //^ 💡 error: cannot mutate immutable variable `x` + let (ref mut y, _) = x[3]; + //^ 💡 error: cannot mutate immutable variable `x` + match x[10] { + //^ 💡 error: cannot mutate immutable variable `x` + (ref y, _) => (), + (_, ref mut y) => (), + } + let mut x = Foo; + let mut i = 5; + //^^^^^ 💡 weak: variable does not need to be mutable + let y = &mut x[i]; +} +"#, + ); + } + #[test] fn overloaded_deref() { - // FIXME: check for false negative check_diagnostics( r#" //- minicore: deref_mut @@ -574,22 +747,36 @@ use core::ops::{Deref, DerefMut}; struct Foo; impl Deref for Foo { - type Target = i32; - fn deref(&self) -> &i32 { - &5 + type Target = (i32, u8); + fn deref(&self) -> &(i32, u8) { + &(5, 2) } } impl DerefMut for Foo { - fn deref_mut(&mut self) -> &mut i32 { - &mut 5 + fn deref_mut(&mut self) -> &mut (i32, u8) { + &mut (5, 2) } } fn f() { - let x = Foo; + let mut x = Foo; + //^^^^^ 💡 weak: variable does not need to be mutable let y = &*x; let x = Foo; - let mut x = Foo; - let y: &mut i32 = &mut x; + let y = &mut *x; + //^^ 💡 error: cannot mutate immutable variable `x` + let x = Foo; + let x = Foo; + let y: &mut (i32, u8) = &mut x; + //^^^^^^ 💡 error: cannot mutate immutable variable `x` + let ref mut y = *x; + //^^ 💡 error: cannot mutate immutable variable `x` + let (ref mut y, _) = *x; + //^^ 💡 error: cannot mutate immutable variable `x` + match *x { + //^^ 💡 error: cannot mutate immutable variable `x` + (ref y, _) => (), + (_, ref mut y) => (), + } } "#, ); @@ -631,6 +818,267 @@ fn f(inp: (Foo, Foo, Foo, Foo)) { ); } + #[test] + // FIXME: We should have tests for `is_ty_uninhabited_from` + fn regression_14421() { + check_diagnostics( + r#" +pub enum Tree { + Node(TreeNode), + Leaf(TreeLeaf), +} + +struct Box(&T); + +pub struct TreeNode { + pub depth: usize, + pub children: [Box; 8] +} + +pub struct TreeLeaf { + pub depth: usize, + pub data: u8 +} + +pub fn test() { + let mut tree = Tree::Leaf( + //^^^^^^^^ 💡 weak: variable does not need to be mutable + TreeLeaf { + depth: 0, + data: 0 + } + ); +} +"#, + ); + } + + #[test] + fn fn_traits() { + check_diagnostics( + r#" +//- minicore: fn +fn fn_ref(mut x: impl Fn(u8) -> u8) -> u8 { + //^^^^^ 💡 weak: variable does not need to be mutable + x(2) +} +fn fn_mut(x: impl FnMut(u8) -> u8) -> u8 { + x(2) + //^ 💡 error: cannot mutate immutable variable `x` +} +fn fn_borrow_mut(mut x: &mut impl FnMut(u8) -> u8) -> u8 { + //^^^^^ 💡 weak: variable does not need to be mutable + x(2) +} +fn fn_once(mut x: impl FnOnce(u8) -> u8) -> u8 { + //^^^^^ 💡 weak: variable does not need to be mutable + x(2) +} +"#, + ); + } + + #[test] + fn closure() { + // FIXME: Diagnostic spans are inconsistent inside and outside closure + check_diagnostics( + r#" + //- minicore: copy, fn + struct X; + + impl X { + fn mutate(&mut self) {} + } + + fn f() { + let x = 5; + let closure1 = || { x = 2; }; + //^ 💡 error: cannot mutate immutable variable `x` + let _ = closure1(); + //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1` + let closure2 = || { x = x; }; + //^ 💡 error: cannot mutate immutable variable `x` + let closure3 = || { + let x = 2; + x = 5; + //^^^^^ 💡 error: cannot mutate immutable variable `x` + x + }; + let x = X; + let closure4 = || { x.mutate(); }; + //^ 💡 error: cannot mutate immutable variable `x` + } + "#, + ); + check_diagnostics( + r#" + //- minicore: copy, fn + fn f() { + let mut x = 5; + //^^^^^ 💡 weak: variable does not need to be mutable + let mut y = 2; + y = 7; + let closure = || { + let mut z = 8; + z = 3; + let mut k = z; + //^^^^^ 💡 weak: variable does not need to be mutable + }; + } + "#, + ); + check_diagnostics( + r#" +//- minicore: copy, fn +fn f() { + let closure = || { + || { + || { + let x = 2; + || { || { x = 5; } } + //^ 💡 error: cannot mutate immutable variable `x` + } + } + }; +} + "#, + ); + check_diagnostics( + r#" +//- minicore: copy, fn +fn f() { + struct X; + let mut x = X; + //^^^^^ 💡 weak: variable does not need to be mutable + let c1 = || x; + let mut x = X; + let c2 = || { x = X; x }; + let mut x = X; + let c2 = move || { x = X; }; +} + "#, + ); + check_diagnostics( + r#" + //- minicore: copy, fn, deref_mut + struct X(i32, i64); + + fn f() { + let mut x = &mut 5; + //^^^^^ 💡 weak: variable does not need to be mutable + let closure1 = || { *x = 2; }; + let _ = closure1(); + //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1` + let mut x = &mut 5; + //^^^^^ 💡 weak: variable does not need to be mutable + let closure1 = || { *x = 2; &x; }; + let _ = closure1(); + //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1` + let mut x = &mut 5; + let closure1 = || { *x = 2; &x; x = &mut 3; }; + let _ = closure1(); + //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1` + let mut x = &mut 5; + //^^^^^ 💡 weak: variable does not need to be mutable + let closure1 = move || { *x = 2; }; + let _ = closure1(); + //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1` + let mut x = &mut X(1, 2); + //^^^^^ 💡 weak: variable does not need to be mutable + let closure1 = || { x.0 = 2; }; + let _ = closure1(); + //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1` + } + "#, + ); + } + + #[test] + fn slice_pattern() { + check_diagnostics( + r#" +//- minicore: coerce_unsized, deref_mut, slice, copy +fn x(t: &[u8]) { + match t { + &[a, mut b] | &[a, _, mut b] => { + //^^^^^ 💡 weak: variable does not need to be mutable + + a = 2; + //^^^^^ 💡 error: cannot mutate immutable variable `a` + + } + _ => {} + } +} + "#, + ); + } + + #[test] + fn boxes() { + check_diagnostics( + r#" +//- minicore: coerce_unsized, deref_mut, slice +use core::ops::{Deref, DerefMut}; +use core::{marker::Unsize, ops::CoerceUnsized}; + +#[lang = "owned_box"] +pub struct Box { + inner: *mut T, +} +impl Box { + fn new(t: T) -> Self { + #[rustc_box] + Box::new(t) + } +} + +impl Deref for Box { + type Target = T; + + fn deref(&self) -> &T { + &**self + } +} + +impl DerefMut for Box { + fn deref_mut(&mut self) -> &mut T { + &mut **self + } +} + +fn f() { + let x = Box::new(5); + x = Box::new(7); + //^^^^^^^^^^^^^^^ 💡 error: cannot mutate immutable variable `x` + let x = Box::new(5); + *x = 7; + //^^^^^^ 💡 error: cannot mutate immutable variable `x` + let mut y = Box::new(5); + //^^^^^ 💡 weak: variable does not need to be mutable + *x = *y; + //^^^^^^^ 💡 error: cannot mutate immutable variable `x` + let x = Box::new(5); + let closure = || *x = 2; + //^ 💡 error: cannot mutate immutable variable `x` +} +"#, + ); + } + + #[test] + fn allow_unused_mut_for_identifiers_starting_with_underline() { + check_diagnostics( + r#" +fn f(_: i32) {} +fn main() { + let mut _x = 2; + f(_x); +} +"#, + ); + } + #[test] fn respect_allow_unused_mut() { // FIXME: respect diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs index 24c521ed1a8a4..a39eceab24379 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs @@ -21,7 +21,7 @@ pub(crate) fn no_such_field(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) } fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Option> { - let root = ctx.sema.db.parse_or_expand(d.field.file_id)?; + let root = ctx.sema.db.parse_or_expand(d.field.file_id); missing_record_expr_field_fixes( &ctx.sema, d.field.file_id.original_file(ctx.sema.db), @@ -69,7 +69,7 @@ fn missing_record_expr_field_fixes( let new_field = make::record_field( None, make::name(record_expr_field.field_name()?.ident_token()?.text()), - make::ty(&new_field_type.display_source_code(sema.db, module.into()).ok()?), + make::ty(&new_field_type.display_source_code(sema.db, module.into(), true).ok()?), ); let last_field = record_fields.fields().last()?; diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs index 67da5c7f27d14..4cd85a479a061 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs @@ -11,7 +11,11 @@ pub(crate) fn private_assoc_item( d: &hir::PrivateAssocItem, ) -> Diagnostic { // FIXME: add quickfix - let name = d.item.name(ctx.sema.db).map(|name| format!("`{name}` ")).unwrap_or_default(); + let name = d + .item + .name(ctx.sema.db) + .map(|name| format!("`{}` ", name.display(ctx.sema.db))) + .unwrap_or_default(); Diagnostic::new( "private-assoc-item", format!( diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_field.rs index be83ad6aaadaa..de7f51f693c42 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_field.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_field.rs @@ -9,8 +9,8 @@ pub(crate) fn private_field(ctx: &DiagnosticsContext<'_>, d: &hir::PrivateField) "private-field", format!( "field `{}` of `{}` is private", - d.field.name(ctx.sema.db), - d.field.parent_def(ctx.sema.db).name(ctx.sema.db) + d.field.name(ctx.sema.db).display(ctx.sema.db), + d.field.parent_def(ctx.sema.db).name(ctx.sema.db).display(ctx.sema.db) ), ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range, ) diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs index 9b1c65983e615..d3eda3c5ebce3 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs @@ -28,7 +28,7 @@ fn fixes( ctx: &DiagnosticsContext<'_>, d: &hir::ReplaceFilterMapNextWithFindMap, ) -> Option> { - let root = ctx.sema.db.parse_or_expand(d.file)?; + let root = ctx.sema.db.parse_or_expand(d.file); let next_expr = d.next_expr.to_node(&root); let next_call = ast::MethodCallExpr::cast(next_expr.syntax().clone())?; @@ -115,7 +115,7 @@ fn foo() { r#" //- minicore: iterators fn foo() { - let m = core::iter::repeat(()) + let mut m = core::iter::repeat(()) .filter_map(|()| Some(92)); let n = m.next(); } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs index 4abc25a28fbc0..c28f98d8333e2 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs @@ -1,5 +1,5 @@ use either::Either; -use hir::{db::ExpandDatabase, HirDisplay, InFile, Type}; +use hir::{db::ExpandDatabase, ClosureStyle, HirDisplay, InFile, Type}; use ide_db::{famous_defs::FamousDefs, source_change::SourceChange}; use syntax::{ ast::{self, BlockExpr, ExprStmt}, @@ -15,15 +15,25 @@ use crate::{adjusted_display_range, fix, Assist, Diagnostic, DiagnosticsContext} // the expected type. pub(crate) fn type_mismatch(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch) -> Diagnostic { let display_range = match &d.expr_or_pat { - Either::Left(expr) => adjusted_display_range::( - ctx, - expr.clone().map(|it| it.into()), - &|block| { - let r_curly_range = block.stmt_list()?.r_curly_token()?.text_range(); - cov_mark::hit!(type_mismatch_on_block); - Some(r_curly_range) - }, - ), + Either::Left(expr) => { + adjusted_display_range::(ctx, expr.clone().map(|it| it.into()), &|expr| { + let salient_token_range = match expr { + ast::Expr::IfExpr(it) => it.if_token()?.text_range(), + ast::Expr::LoopExpr(it) => it.loop_token()?.text_range(), + ast::Expr::ForExpr(it) => it.for_token()?.text_range(), + ast::Expr::WhileExpr(it) => it.while_token()?.text_range(), + ast::Expr::BlockExpr(it) => it.stmt_list()?.r_curly_token()?.text_range(), + ast::Expr::MatchExpr(it) => it.match_token()?.text_range(), + ast::Expr::MethodCallExpr(it) => it.name_ref()?.ident_token()?.text_range(), + ast::Expr::FieldExpr(it) => it.name_ref()?.ident_token()?.text_range(), + ast::Expr::AwaitExpr(it) => it.await_token()?.text_range(), + _ => return None, + }; + + cov_mark::hit!(type_mismatch_range_adjustment); + Some(salient_token_range) + }) + } Either::Right(pat) => { ctx.sema.diagnostics_display_range(pat.clone().map(|it| it.into())).range } @@ -32,8 +42,8 @@ pub(crate) fn type_mismatch(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch) "type-mismatch", format!( "expected {}, found {}", - d.expected.display(ctx.sema.db), - d.actual.display(ctx.sema.db) + d.expected.display(ctx.sema.db).with_closure_style(ClosureStyle::ClosureWithId), + d.actual.display(ctx.sema.db).with_closure_style(ClosureStyle::ClosureWithId), ), display_range, ) @@ -93,7 +103,7 @@ fn add_missing_ok_or_some( expr_ptr: &InFile>, acc: &mut Vec, ) -> Option<()> { - let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id)?; + let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id); let expr = expr_ptr.value.to_node(&root); let expr_range = expr.syntax().text_range(); let scope = ctx.sema.scope(expr.syntax())?; @@ -133,7 +143,7 @@ fn remove_semicolon( expr_ptr: &InFile>, acc: &mut Vec, ) -> Option<()> { - let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id)?; + let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id); let expr = expr_ptr.value.to_node(&root); if !d.actual.is_unit() { return None; @@ -169,7 +179,7 @@ fn str_ref_to_owned( return None; } - let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id)?; + let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id); let expr = expr_ptr.value.to_node(&root); let expr_range = expr.syntax().text_range(); @@ -597,8 +607,21 @@ fn test() -> String { } #[test] - fn type_mismatch_on_block() { - cov_mark::check!(type_mismatch_on_block); + fn closure_mismatch_show_different_type() { + check_diagnostics( + r#" +fn f() { + let mut x = (|| 1, 2); + x = (|| 3, 4); + //^^^^ error: expected {closure#0}, found {closure#1} +} + "#, + ); + } + + #[test] + fn type_mismatch_range_adjustment() { + cov_mark::check!(type_mismatch_range_adjustment); check_diagnostics( r#" fn f() -> i32 { @@ -607,6 +630,57 @@ fn f() -> i32 { let _ = x + y; } //^ error: expected i32, found () + +fn g() -> i32 { + while true {} +} //^^^^^ error: expected i32, found () + +struct S; +impl S { fn foo(&self) -> &S { self } } +fn h() { + let _: i32 = S.foo().foo().foo(); +} //^^^ error: expected i32, found &S +"#, + ); + } + + #[test] + fn unknown_type_in_function_signature() { + check_diagnostics( + r#" +struct X(T); + +fn foo(x: X) {} +fn test1() { + // Unknown might be `i32`, so we should not emit type mismatch here. + foo(X(42)); +} +fn test2() { + foo(42); + //^^ error: expected X<{unknown}>, found i32 +} +"#, + ); + } + + #[test] + fn evaluate_const_generics_in_types() { + check_diagnostics( + r#" +pub const ONE: usize = 1; + +pub struct Inner(); + +pub struct Outer { + pub inner: Inner, +} + +fn main() { + _ = Outer { + inner: Inner::<2>(), + //^^^^^^^^^^^^ error: expected Inner<1>, found Inner<2> + }; +} "#, ); } @@ -617,11 +691,48 @@ fn f() -> i32 { r#" fn f() { let &() = &mut (); + //^^^ error: expected &mut (), found &() match &() { + // FIXME: we should only show the deep one. &9 => () + //^^ error: expected &(), found &i32 //^ error: expected (), found i32 } } +"#, + ); + } + + #[test] + fn regression_14768() { + check_diagnostics( + r#" +//- minicore: derive, fmt, slice, coerce_unsized, builtin_impls +use core::fmt::Debug; + +#[derive(Debug)] +struct Foo(u8, u16, [u8]); + +#[derive(Debug)] +struct Bar { + f1: u8, + f2: &[u16], + f3: dyn Debug, +} +"#, + ); + } + + #[test] + fn return_no_value() { + check_diagnostics( + r#" +fn f() -> i32 { + return; + // ^^^^^^ error: expected i32, found () + 0 +} +fn g() { return; } "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs new file mode 100644 index 0000000000000..e12bbcf682068 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs @@ -0,0 +1,232 @@ +use hir::{db::ExpandDatabase, ClosureStyle, HirDisplay, StructKind}; +use ide_db::{ + assists::{Assist, AssistId, AssistKind, GroupLabel}, + label::Label, + source_change::SourceChange, +}; +use syntax::AstNode; +use text_edit::TextEdit; + +use crate::{Diagnostic, DiagnosticsContext}; + +// Diagnostic: typed-hole +// +// This diagnostic is triggered when an underscore expression is used in an invalid position. +pub(crate) fn typed_hole(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole) -> Diagnostic { + let display_range = ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())); + let (message, fixes) = if d.expected.is_unknown() { + ("`_` expressions may only appear on the left-hand side of an assignment".to_owned(), None) + } else { + ( + format!( + "invalid `_` expression, expected type `{}`", + d.expected.display(ctx.sema.db).with_closure_style(ClosureStyle::ClosureWithId), + ), + fixes(ctx, d), + ) + }; + + Diagnostic::new("typed-hole", message, display_range.range).with_fixes(fixes) +} + +fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole) -> Option> { + let db = ctx.sema.db; + let root = db.parse_or_expand(d.expr.file_id); + let original_range = + d.expr.as_ref().map(|it| it.to_node(&root)).syntax().original_file_range_opt(db)?; + let scope = ctx.sema.scope(d.expr.value.to_node(&root).syntax())?; + let mut assists = vec![]; + scope.process_all_names(&mut |name, def| { + let ty = match def { + hir::ScopeDef::ModuleDef(it) => match it { + hir::ModuleDef::Function(it) => it.ty(db), + hir::ModuleDef::Adt(hir::Adt::Struct(it)) if it.kind(db) != StructKind::Record => { + it.constructor_ty(db) + } + hir::ModuleDef::Variant(it) if it.kind(db) != StructKind::Record => { + it.constructor_ty(db) + } + hir::ModuleDef::Const(it) => it.ty(db), + hir::ModuleDef::Static(it) => it.ty(db), + _ => return, + }, + hir::ScopeDef::GenericParam(hir::GenericParam::ConstParam(it)) => it.ty(db), + hir::ScopeDef::Local(it) => it.ty(db), + _ => return, + }; + // FIXME: should also check coercions if it is at a coercion site + if !ty.contains_unknown() && ty.could_unify_with(db, &d.expected) { + assists.push(Assist { + id: AssistId("typed-hole", AssistKind::QuickFix), + label: Label::new(format!("Replace `_` with `{}`", name.display(db))), + group: Some(GroupLabel("Replace `_` with a matching entity in scope".to_owned())), + target: original_range.range, + source_change: Some(SourceChange::from_text_edit( + original_range.file_id, + TextEdit::replace(original_range.range, name.display(db).to_string()), + )), + trigger_signature_help: false, + }); + } + }); + if assists.is_empty() { + None + } else { + Some(assists) + } +} + +#[cfg(test)] +mod tests { + use crate::tests::{check_diagnostics, check_fixes}; + + #[test] + fn unknown() { + check_diagnostics( + r#" +fn main() { + _; + //^ error: `_` expressions may only appear on the left-hand side of an assignment +} +"#, + ); + } + + #[test] + fn concrete_expectation() { + check_diagnostics( + r#" +fn main() { + if _ {} + //^ error: invalid `_` expression, expected type `bool` + let _: fn() -> i32 = _; + //^ error: invalid `_` expression, expected type `fn() -> i32` + let _: fn() -> () = _; // FIXME: This should trigger an assist because `main` matches via *coercion* + //^ error: invalid `_` expression, expected type `fn()` +} +"#, + ); + } + + #[test] + fn integer_ty_var() { + check_diagnostics( + r#" +fn main() { + let mut x = 3; + x = _; + //^ 💡 error: invalid `_` expression, expected type `i32` +} +"#, + ); + } + + #[test] + fn ty_var_resolved() { + check_diagnostics( + r#" +fn main() { + let mut x = t(); + x = _; + //^ 💡 error: invalid `_` expression, expected type `&str` + x = ""; +} +fn t() -> T { loop {} } +"#, + ); + } + + #[test] + fn valid_positions() { + check_diagnostics( + r#" +fn main() { + let x = [(); _]; + let y: [(); 10] = [(); _]; + _ = 0; + (_,) = (1,); +} +"#, + ); + } + + #[test] + fn check_quick_fix() { + check_fixes( + r#" +enum Foo { + Bar +} +use Foo::Bar; +const C: Foo = Foo::Bar; +fn main(param: Foo) { + let local = Foo::Bar; + let _: Foo = _$0; + //^ error: invalid `_` expression, expected type `fn()` +} +"#, + vec![ + r#" +enum Foo { + Bar +} +use Foo::Bar; +const C: Foo = Foo::Bar; +fn main(param: Foo) { + let local = Foo::Bar; + let _: Foo = local; + //^ error: invalid `_` expression, expected type `fn()` +} +"#, + r#" +enum Foo { + Bar +} +use Foo::Bar; +const C: Foo = Foo::Bar; +fn main(param: Foo) { + let local = Foo::Bar; + let _: Foo = param; + //^ error: invalid `_` expression, expected type `fn()` +} +"#, + r#" +enum Foo { + Bar +} +use Foo::Bar; +const C: Foo = Foo::Bar; +fn main(param: Foo) { + let local = Foo::Bar; + let _: Foo = CP; + //^ error: invalid `_` expression, expected type `fn()` +} +"#, + r#" +enum Foo { + Bar +} +use Foo::Bar; +const C: Foo = Foo::Bar; +fn main(param: Foo) { + let local = Foo::Bar; + let _: Foo = Bar; + //^ error: invalid `_` expression, expected type `fn()` +} +"#, + r#" +enum Foo { + Bar +} +use Foo::Bar; +const C: Foo = Foo::Bar; +fn main(param: Foo) { + let local = Foo::Bar; + let _: Foo = C; + //^ error: invalid `_` expression, expected type `fn()` +} +"#, + ], + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs new file mode 100644 index 0000000000000..034e4fcfb85b2 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs @@ -0,0 +1,88 @@ +use crate::{Diagnostic, DiagnosticsContext}; + +// Diagnostic: undeclared-label +pub(crate) fn undeclared_label( + ctx: &DiagnosticsContext<'_>, + d: &hir::UndeclaredLabel, +) -> Diagnostic { + let name = &d.name; + Diagnostic::new( + "undeclared-label", + format!("use of undeclared label `{}`", name.display(ctx.sema.db)), + ctx.sema.diagnostics_display_range(d.node.clone().map(|it| it.into())).range, + ) +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn smoke_test() { + check_diagnostics( + r#" +fn foo() { + break 'a; + //^^^^^^^^ error: break outside of loop + //^^ error: use of undeclared label `'a` + continue 'a; + //^^^^^^^^^^^ error: continue outside of loop + //^^ error: use of undeclared label `'a` +} +"#, + ); + } + + #[test] + fn for_loop() { + check_diagnostics( + r#" +//- minicore: iterator +fn foo() { + 'xxx: for _ in unknown { + 'yyy: for _ in unknown { + break 'xxx; + continue 'yyy; + break 'zzz; + //^^^^ error: use of undeclared label `'zzz` + } + continue 'xxx; + continue 'yyy; + //^^^^ error: use of undeclared label `'yyy` + break 'xxx; + break 'yyy; + //^^^^ error: use of undeclared label `'yyy` + } +} +"#, + ); + } + + #[test] + fn try_operator_desugar_works() { + check_diagnostics( + r#" +//- minicore: option, try +fn foo() { + None?; +} +"#, + ); + check_diagnostics( + r#" +//- minicore: option, try, future +async fn foo() { + None?; +} +"#, + ); + check_diagnostics( + r#" +//- minicore: option, try, future, fn +async fn foo() { + || None?; +} +"#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs index 3d45a75913ad8..271e7ce73bcdd 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs @@ -2,7 +2,7 @@ use std::iter; -use hir::{db::DefDatabase, InFile, ModuleSource}; +use hir::{db::DefDatabase, DefMap, InFile, ModuleSource}; use ide_db::{ base_db::{FileId, FileLoader, SourceDatabase, SourceDatabaseExt}, source_change::SourceChange, @@ -10,7 +10,7 @@ use ide_db::{ }; use syntax::{ ast::{self, edit::IndentLevel, HasModuleItem, HasName}, - AstNode, TextRange, TextSize, + AstNode, TextRange, }; use text_edit::TextEdit; @@ -27,14 +27,28 @@ pub(crate) fn unlinked_file( ) { // Limit diagnostic to the first few characters in the file. This matches how VS Code // renders it with the full span, but on other editors, and is less invasive. + let fixes = fixes(ctx, file_id); + // FIXME: This is a hack for the vscode extension to notice whether there is an autofix or not before having to resolve diagnostics. + // This is to prevent project linking popups from appearing when there is an autofix. https://github.com/rust-lang/rust-analyzer/issues/14523 + let message = if fixes.is_none() { + "file not included in crate hierarchy" + } else { + "file not included in module tree" + }; + let range = ctx.sema.db.parse(file_id).syntax_node().text_range(); - // FIXME: This is wrong if one of the first three characters is not ascii: `//Ы`. - let range = range.intersect(TextRange::up_to(TextSize::of("..."))).unwrap_or(range); + let range = FileLoader::file_text(ctx.sema.db, file_id) + .char_indices() + .take(3) + .last() + .map(|(i, _)| i) + .map(|i| TextRange::up_to(i.try_into().unwrap())) + .unwrap_or(range); acc.push( - Diagnostic::new("unlinked-file", "file not included in module tree", range) + Diagnostic::new("unlinked-file", message, range) .severity(Severity::WeakWarning) - .with_fixes(fixes(ctx, file_id)), + .with_fixes(fixes), ); } @@ -60,7 +74,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, file_id: FileId) -> Option> { 'crates: for &krate in &*ctx.sema.db.relevant_crates(file_id) { let crate_def_map = ctx.sema.db.crate_def_map(krate); - let root_module = &crate_def_map[crate_def_map.root()]; + let root_module = &crate_def_map[DefMap::ROOT]; let Some(root_file_id) = root_module.origin.file_id() else { continue }; let Some(crate_root_path) = source_root.path_for_file(&root_file_id) else { continue }; let Some(rel) = parent.strip_prefix(&crate_root_path.parent()?) else { continue }; @@ -92,7 +106,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, file_id: FileId) -> Option> { // if we aren't adding to a crate root, walk backwards such that we support `#[path = ...]` overrides if possible // build all parent paths of the form `../module_name/mod.rs` and `../module_name.rs` - let paths = iter::successors(Some(parent.clone()), |prev| prev.parent()).filter_map(|path| { + let paths = iter::successors(Some(parent), |prev| prev.parent()).filter_map(|path| { let parent = path.parent()?; let (name, _) = path.name_and_extension()?; Some(([parent.join(&format!("{name}.rs"))?, path.join("mod.rs")?], name.to_owned())) diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unreachable_label.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unreachable_label.rs new file mode 100644 index 0000000000000..9fedadeae0359 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unreachable_label.rs @@ -0,0 +1,91 @@ +use crate::{Diagnostic, DiagnosticsContext}; + +// Diagnostic: unreachable-label +pub(crate) fn unreachable_label( + ctx: &DiagnosticsContext<'_>, + d: &hir::UnreachableLabel, +) -> Diagnostic { + let name = &d.name; + Diagnostic::new( + "unreachable-label", + format!("use of unreachable label `{}`", name.display(ctx.sema.db)), + ctx.sema.diagnostics_display_range(d.node.clone().map(|it| it.into())).range, + ) +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn async_blocks_are_borders() { + check_diagnostics( + r#" +fn foo() { + 'a: loop { + async { + break 'a; + //^^^^^^^^ error: break outside of loop + // ^^ error: use of unreachable label `'a` + continue 'a; + //^^^^^^^^^^^ error: continue outside of loop + // ^^ error: use of unreachable label `'a` + }; + } +} +"#, + ); + } + + #[test] + fn closures_are_borders() { + check_diagnostics( + r#" +fn foo() { + 'a: loop { + || { + break 'a; + //^^^^^^^^ error: break outside of loop + // ^^ error: use of unreachable label `'a` + continue 'a; + //^^^^^^^^^^^ error: continue outside of loop + // ^^ error: use of unreachable label `'a` + }; + } +} +"#, + ); + } + + #[test] + fn blocks_pass_through() { + check_diagnostics( + r#" +fn foo() { + 'a: loop { + { + break 'a; + continue 'a; + } + } +} +"#, + ); + } + + #[test] + fn try_blocks_pass_through() { + check_diagnostics( + r#" +fn foo() { + 'a: loop { + try { + break 'a; + continue 'a; + }; + } +} +"#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs index cefa74e523e8f..5e4efa41fd9c3 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs @@ -26,7 +26,7 @@ pub(crate) fn unresolved_field( "unresolved-field", format!( "no field `{}` on type `{}`{method_suffix}", - d.name, + d.name.display(ctx.sema.db), d.receiver.display(ctx.sema.db) ), ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range, @@ -45,12 +45,12 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedField) -> Option, expr_ptr: &InFile>, ) -> Option> { - let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id)?; + let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id); let expr = expr_ptr.value.to_node(&root); let FileRange { range, file_id } = ctx.sema.original_range_opt(expr.syntax())?; Some(vec![Assist { diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs index 1a5efff2c0c60..3943b51ab422d 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs @@ -13,7 +13,7 @@ pub(crate) fn unresolved_macro_call( let bang = if d.is_bang { "!" } else { "" }; Diagnostic::new( "unresolved-macro-call", - format!("unresolved macro `{}{bang}`", d.path), + format!("unresolved macro `{}{bang}`", d.path.display(ctx.sema.db)), display_range, ) .experimental() diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs index f3ec6efa75215..8bbb837e6709e 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs @@ -26,7 +26,7 @@ pub(crate) fn unresolved_method( "unresolved-method", format!( "no method `{}` on type `{}`{field_suffix}", - d.name, + d.name.display(ctx.sema.db), d.receiver.display(ctx.sema.db) ), ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range, @@ -53,7 +53,7 @@ fn field_fix( return None; } let expr_ptr = &d.expr; - let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id)?; + let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id); let expr = expr_ptr.value.to_node(&root); let (file_id, range) = match expr { ast::Expr::MethodCallExpr(mcall) => { diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_module.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_module.rs index 94614f11c3349..6e3fd3b42b0c3 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_module.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_module.rs @@ -31,7 +31,7 @@ pub(crate) fn unresolved_module( } fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedModule) -> Option> { - let root = ctx.sema.db.parse_or_expand(d.decl.file_id)?; + let root = ctx.sema.db.parse_or_expand(d.decl.file_id); let unresolved_module = d.decl.value.to_node(&root); Some( d.candidates diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_proc_macro.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_proc_macro.rs index 9a984ba6bf07a..ae5cf135839f1 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_proc_macro.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_proc_macro.rs @@ -25,25 +25,21 @@ pub(crate) fn unresolved_proc_macro( _ => proc_macros_enabled, }; - let message = match &d.macro_name { + let not_expanded_message = match &d.macro_name { Some(name) => format!("proc macro `{name}` not expanded"), None => "proc macro not expanded".to_string(), }; let severity = if config_enabled { Severity::Error } else { Severity::WeakWarning }; let def_map = ctx.sema.db.crate_def_map(d.krate); - let message = format!( - "{message}: {}", - if config_enabled { - def_map.proc_macro_loading_error().unwrap_or("proc macro not found in the built dylib") - } else { - match d.kind { - hir::MacroKind::Attr if proc_macros_enabled => { - "attribute macro expansion is disabled" - } - _ => "proc-macro expansion is disabled", - } - }, - ); + let message = if config_enabled { + def_map.proc_macro_loading_error().unwrap_or("proc macro not found in the built dylib") + } else { + match d.kind { + hir::MacroKind::Attr if proc_macros_enabled => "attribute macro expansion is disabled", + _ => "proc-macro expansion is disabled", + } + }; + let message = format!("{not_expanded_message}: {message}"); Diagnostic::new("unresolved-proc-macro", message, display_range).severity(severity) } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs index 71f136b8c9030..55a4a482d3b28 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -38,11 +38,13 @@ mod handlers { pub(crate) mod missing_fields; pub(crate) mod missing_match_arms; pub(crate) mod missing_unsafe; + pub(crate) mod moved_out_of_ref; pub(crate) mod mutability_errors; pub(crate) mod no_such_field; pub(crate) mod private_assoc_item; pub(crate) mod private_field; pub(crate) mod replace_filter_map_next_with_find_map; + pub(crate) mod typed_hole; pub(crate) mod type_mismatch; pub(crate) mod unimplemented_builtin_macro; pub(crate) mod unresolved_extern_crate; @@ -52,6 +54,8 @@ mod handlers { pub(crate) mod unresolved_macro_call; pub(crate) mod unresolved_module; pub(crate) mod unresolved_proc_macro; + pub(crate) mod undeclared_label; + pub(crate) mod unreachable_label; // The handlers below are unusual, the implement the diagnostics as well. pub(crate) mod field_shorthand; @@ -74,6 +78,7 @@ use ide_db::{ }; use syntax::{algo::find_node_at_range, ast::AstNode, SyntaxNodePtr, TextRange}; +// FIXME: Make this an enum #[derive(Copy, Clone, Debug, PartialEq)] pub struct DiagnosticCode(pub &'static str); @@ -198,7 +203,7 @@ impl<'a> DiagnosticsContext<'a> { let sema = &self.sema; (|| { let precise_location = precise_location?; - let root = sema.parse_or_expand(node.file_id)?; + 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) => { @@ -246,42 +251,60 @@ pub fn diagnostics( let mut diags = Vec::new(); if let Some(m) = module { - m.diagnostics(db, &mut diags) + m.diagnostics(db, &mut diags); } for diag in diags { #[rustfmt::skip] let d = match diag { - AnyDiagnostic::BreakOutsideOfLoop(d) => handlers::break_outside_of_loop::break_outside_of_loop(&ctx, &d), AnyDiagnostic::ExpectedFunction(d) => handlers::expected_function::expected_function(&ctx, &d), - AnyDiagnostic::IncorrectCase(d) => handlers::incorrect_case::incorrect_case(&ctx, &d), + AnyDiagnostic::InactiveCode(d) => match handlers::inactive_code::inactive_code(&ctx, &d) { + Some(it) => it, + None => continue, + } AnyDiagnostic::IncoherentImpl(d) => handlers::incoherent_impl::incoherent_impl(&ctx, &d), + AnyDiagnostic::IncorrectCase(d) => handlers::incorrect_case::incorrect_case(&ctx, &d), + AnyDiagnostic::InvalidDeriveTarget(d) => handlers::invalid_derive_target::invalid_derive_target(&ctx, &d), + AnyDiagnostic::MacroDefError(d) => handlers::macro_error::macro_def_error(&ctx, &d), AnyDiagnostic::MacroError(d) => handlers::macro_error::macro_error(&ctx, &d), + AnyDiagnostic::MacroExpansionParseError(d) => { + res.extend(d.errors.iter().take(32).map(|err| { + { + Diagnostic::new( + "syntax-error", + format!("Syntax Error in Expansion: {err}"), + ctx.resolve_precise_location(&d.node.clone(), d.precise_location), + ) + } + .experimental() + })); + continue; + }, AnyDiagnostic::MalformedDerive(d) => handlers::malformed_derive::malformed_derive(&ctx, &d), AnyDiagnostic::MismatchedArgCount(d) => handlers::mismatched_arg_count::mismatched_arg_count(&ctx, &d), AnyDiagnostic::MissingFields(d) => handlers::missing_fields::missing_fields(&ctx, &d), AnyDiagnostic::MissingMatchArms(d) => handlers::missing_match_arms::missing_match_arms(&ctx, &d), AnyDiagnostic::MissingUnsafe(d) => handlers::missing_unsafe::missing_unsafe(&ctx, &d), + AnyDiagnostic::MovedOutOfRef(d) => handlers::moved_out_of_ref::moved_out_of_ref(&ctx, &d), + AnyDiagnostic::NeedMut(d) => handlers::mutability_errors::need_mut(&ctx, &d), AnyDiagnostic::NoSuchField(d) => handlers::no_such_field::no_such_field(&ctx, &d), AnyDiagnostic::PrivateAssocItem(d) => handlers::private_assoc_item::private_assoc_item(&ctx, &d), AnyDiagnostic::PrivateField(d) => handlers::private_field::private_field(&ctx, &d), AnyDiagnostic::ReplaceFilterMapNextWithFindMap(d) => handlers::replace_filter_map_next_with_find_map::replace_filter_map_next_with_find_map(&ctx, &d), + AnyDiagnostic::TypedHole(d) => handlers::typed_hole::typed_hole(&ctx, &d), AnyDiagnostic::TypeMismatch(d) => handlers::type_mismatch::type_mismatch(&ctx, &d), + AnyDiagnostic::UndeclaredLabel(d) => handlers::undeclared_label::undeclared_label(&ctx, &d), AnyDiagnostic::UnimplementedBuiltinMacro(d) => handlers::unimplemented_builtin_macro::unimplemented_builtin_macro(&ctx, &d), + AnyDiagnostic::UnreachableLabel(d) => handlers::unreachable_label:: unreachable_label(&ctx, &d), AnyDiagnostic::UnresolvedExternCrate(d) => handlers::unresolved_extern_crate::unresolved_extern_crate(&ctx, &d), + AnyDiagnostic::UnresolvedField(d) => handlers::unresolved_field::unresolved_field(&ctx, &d), AnyDiagnostic::UnresolvedImport(d) => handlers::unresolved_import::unresolved_import(&ctx, &d), AnyDiagnostic::UnresolvedMacroCall(d) => handlers::unresolved_macro_call::unresolved_macro_call(&ctx, &d), + AnyDiagnostic::UnresolvedMethodCall(d) => handlers::unresolved_method::unresolved_method(&ctx, &d), AnyDiagnostic::UnresolvedModule(d) => handlers::unresolved_module::unresolved_module(&ctx, &d), AnyDiagnostic::UnresolvedProcMacro(d) => handlers::unresolved_proc_macro::unresolved_proc_macro(&ctx, &d, config.proc_macros_enabled, config.proc_attr_macros_enabled), - AnyDiagnostic::InvalidDeriveTarget(d) => handlers::invalid_derive_target::invalid_derive_target(&ctx, &d), - AnyDiagnostic::UnresolvedField(d) => handlers::unresolved_field::unresolved_field(&ctx, &d), - AnyDiagnostic::UnresolvedMethodCall(d) => handlers::unresolved_method::unresolved_method(&ctx, &d), - AnyDiagnostic::NeedMut(d) => handlers::mutability_errors::need_mut(&ctx, &d), AnyDiagnostic::UnusedMut(d) => handlers::mutability_errors::unused_mut(&ctx, &d), - AnyDiagnostic::InactiveCode(d) => match handlers::inactive_code::inactive_code(&ctx, &d) { - Some(it) => it, - None => continue, - } + AnyDiagnostic::BreakOutsideOfLoop(d) => handlers::break_outside_of_loop::break_outside_of_loop(&ctx, &d), }; res.push(d) } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs index afa641c733ebb..b5cd4e0d68911 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs @@ -8,7 +8,7 @@ use ide_db::{ RootDatabase, }; use stdx::trim_indent; -use test_utils::{assert_eq_text, extract_annotations}; +use test_utils::{assert_eq_text, extract_annotations, MiniCore}; use crate::{DiagnosticsConfig, ExprFillDefaultMode, Severity}; @@ -121,6 +121,15 @@ pub(crate) fn check_diagnostics_with_config(config: DiagnosticsConfig, ra_fixtur }) .collect::>(); actual.sort_by_key(|(range, _)| range.start()); + if expected.is_empty() { + // makes minicore smoke test debugable + for (e, _) in &actual { + eprintln!( + "Code in range {e:?} = {}", + &db.file_text(file_id)[usize::from(e.start())..usize::from(e.end())] + ) + } + } assert_eq!(expected, actual); } } @@ -143,3 +152,28 @@ fn test_disabled_diagnostics() { ); assert!(!diagnostics.is_empty()); } + +#[test] +fn minicore_smoke_test() { + fn check(minicore: MiniCore) { + let source = minicore.source_code(); + let mut config = DiagnosticsConfig::test_sample(); + // This should be ignored since we conditionaly remove code which creates single item use with braces + config.disabled.insert("unnecessary-braces".to_string()); + check_diagnostics_with_config(config, &source); + } + + // Checks that there is no diagnostic in minicore for each flag. + for flag in MiniCore::available_flags() { + if flag == "clone" { + // Clone without copy has `moved-out-of-ref`, so ignoring. + // FIXME: Maybe we should merge copy and clone in a single flag? + continue; + } + eprintln!("Checking minicore flag {flag}"); + check(MiniCore::from_flags([flag])); + } + // And one time for all flags, to check codes which are behind multiple flags + prevent name collisions + eprintln!("Checking all minicore flags"); + check(MiniCore::from_flags(MiniCore::available_flags())) +} diff --git a/src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml b/src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml index 04efa7b91d824..70ed6dea5bf28 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml @@ -15,6 +15,8 @@ doctest = false [dependencies] cov-mark = "2.0.0-pre.1" itertools = "0.10.5" +triomphe.workspace = true +nohash-hasher.workspace = true # local deps hir.workspace = true diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs index d9834ee63adcc..66832a0bee496 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs @@ -87,8 +87,8 @@ pub use crate::{errors::SsrError, from_comment::ssr_from_comment, matching::Matc use crate::{errors::bail, matching::MatchFailureReason}; use hir::Semantics; use ide_db::base_db::{FileId, FilePosition, FileRange}; +use nohash_hasher::IntMap; use resolving::ResolvedRule; -use stdx::hash::NoHashHashMap; use syntax::{ast, AstNode, SyntaxNode, TextRange}; use text_edit::TextEdit; @@ -168,9 +168,9 @@ impl<'db> MatchFinder<'db> { } /// Finds matches for all added rules and returns edits for all found matches. - pub fn edits(&self) -> NoHashHashMap { + pub fn edits(&self) -> IntMap { use ide_db::base_db::SourceDatabaseExt; - let mut matches_by_file = NoHashHashMap::default(); + let mut matches_by_file = IntMap::default(); for m in self.matches().matches { matches_by_file .entry(m.range.file_id) @@ -184,6 +184,7 @@ impl<'db> MatchFinder<'db> { ( file_id, replacing::matches_to_edit( + self.sema.db, &matches, &self.sema.db.file_text(file_id), &self.rules, @@ -224,7 +225,7 @@ impl<'db> MatchFinder<'db> { let file = self.sema.parse(file_id); let mut res = Vec::new(); let file_text = self.sema.db.file_text(file_id); - let mut remaining_text = file_text.as_str(); + let mut remaining_text = &*file_text; let mut base = 0; let len = snippet.len() as u32; while let Some(offset) = remaining_text.find(snippet) { diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/replacing.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/replacing.rs index e27ef6e35ef42..b4b83f62da240 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/replacing.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/replacing.rs @@ -14,14 +14,16 @@ use crate::{fragments, resolving::ResolvedRule, Match, SsrMatches}; /// template. Placeholders in the template will have been substituted with whatever they matched to /// in the original code. pub(crate) fn matches_to_edit( + db: &dyn hir::db::ExpandDatabase, matches: &SsrMatches, file_src: &str, rules: &[ResolvedRule], ) -> TextEdit { - matches_to_edit_at_offset(matches, file_src, 0.into(), rules) + matches_to_edit_at_offset(db, matches, file_src, 0.into(), rules) } fn matches_to_edit_at_offset( + db: &dyn hir::db::ExpandDatabase, matches: &SsrMatches, file_src: &str, relative_start: TextSize, @@ -31,13 +33,14 @@ fn matches_to_edit_at_offset( for m in &matches.matches { edit_builder.replace( m.range.range.checked_sub(relative_start).unwrap(), - render_replace(m, file_src, rules), + render_replace(db, m, file_src, rules), ); } edit_builder.finish() } struct ReplacementRenderer<'a> { + db: &'a dyn hir::db::ExpandDatabase, match_info: &'a Match, file_src: &'a str, rules: &'a [ResolvedRule], @@ -53,13 +56,19 @@ struct ReplacementRenderer<'a> { placeholder_tokens_requiring_parenthesis: FxHashSet, } -fn render_replace(match_info: &Match, file_src: &str, rules: &[ResolvedRule]) -> String { +fn render_replace( + db: &dyn hir::db::ExpandDatabase, + match_info: &Match, + file_src: &str, + rules: &[ResolvedRule], +) -> String { let rule = &rules[match_info.rule_index]; let template = rule .template .as_ref() .expect("You called MatchFinder::edits after calling MatchFinder::add_search_pattern"); let mut renderer = ReplacementRenderer { + db, match_info, file_src, rules, @@ -96,7 +105,7 @@ impl ReplacementRenderer<'_> { fn render_node(&mut self, node: &SyntaxNode) { if let Some(mod_path) = self.match_info.rendered_template_paths.get(node) { - self.out.push_str(&mod_path.to_string()); + self.out.push_str(&mod_path.display(self.db).to_string()); // Emit everything except for the segment's name-ref, since we already effectively // emitted that as part of `mod_path`. if let Some(path) = ast::Path::cast(node.clone()) { @@ -144,6 +153,7 @@ impl ReplacementRenderer<'_> { ); } let edit = matches_to_edit_at_offset( + self.db, &placeholder_value.inner_matches, self.file_src, range.start(), diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs index 61698fca80fee..424ba3d7fd506 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs @@ -3,8 +3,8 @@ use ide_db::{ base_db::{salsa::Durability, FileId, FilePosition, FileRange, SourceDatabaseExt}, FxHashSet, }; -use std::sync::Arc; use test_utils::RangeOrOffset; +use triomphe::Arc; use crate::{MatchFinder, SsrRule}; diff --git a/src/tools/rust-analyzer/crates/ide/Cargo.toml b/src/tools/rust-analyzer/crates/ide/Cargo.toml index 30e514e4136a4..2aee203c4ea54 100644 --- a/src/tools/rust-analyzer/crates/ide/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide/Cargo.toml @@ -23,6 +23,8 @@ pulldown-cmark = { version = "0.9.1", default-features = false } url = "2.3.1" dot = "0.1.4" smallvec.workspace = true +triomphe.workspace = true +nohash-hasher.workspace = true # local deps cfg.workspace = true diff --git a/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs b/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs index 48bcd37b62c61..dd1d0d75c63ee 100644 --- a/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs +++ b/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs @@ -263,7 +263,7 @@ mod tests { expect![["callee Function FileId(0) 0..14 3..9"]], expect![[r#" caller1 Function FileId(0) 15..45 18..25 : [34..40] - test_caller Function FileId(0) 95..149 110..121 : [134..140]"#]], + test_caller Function FileId(0) 95..149 110..121 tests : [134..140]"#]], expect![[]], ); } @@ -283,7 +283,7 @@ fn caller() { //- /foo/mod.rs pub fn callee() {} "#, - expect![["callee Function FileId(1) 0..18 7..13"]], + expect!["callee Function FileId(1) 0..18 7..13 foo"], expect![["caller Function FileId(0) 27..56 30..36 : [45..51]"]], expect![[]], ); @@ -323,7 +323,7 @@ pub fn callee() {} "#, expect![["caller Function FileId(0) 27..56 30..36"]], expect![[]], - expect![["callee Function FileId(1) 0..18 7..13 : [45..51]"]], + expect!["callee Function FileId(1) 0..18 7..13 foo : [45..51]"], ); } @@ -477,7 +477,7 @@ fn caller() { S1::callee(); } "#, - expect![["callee Function FileId(0) 15..27 18..24"]], + expect!["callee Function FileId(0) 15..27 18..24 T1"], expect![["caller Function FileId(0) 82..115 85..91 : [104..110]"]], expect![[]], ); diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs index fae25f3102333..8112c4f7259cf 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs @@ -5,6 +5,8 @@ mod tests; mod intra_doc_links; +use std::ffi::OsStr; + use pulldown_cmark::{BrokenLink, CowStr, Event, InlineStr, LinkType, Options, Parser, Tag}; use pulldown_cmark_to_cmark::{cmark_resume_with_options, Options as CMarkOptions}; use stdx::format_to; @@ -12,7 +14,7 @@ use url::Url; use hir::{db::HirDatabase, Adt, AsAssocItem, AssocItem, AssocItemContainer, HasAttrs}; use ide_db::{ - base_db::{CrateOrigin, LangCrateOrigin, SourceDatabase}, + base_db::{CrateOrigin, LangCrateOrigin, ReleaseChannel, SourceDatabase}, defs::{Definition, NameClass, NameRefClass}, helpers::pick_best_token, RootDatabase, @@ -29,8 +31,16 @@ use crate::{ FilePosition, Semantics, }; -/// Weblink to an item's documentation. -pub(crate) type DocumentationLink = String; +/// Web and local links to an item's documentation. +#[derive(Default, Debug, Clone, PartialEq, Eq)] +pub struct DocumentationLinks { + /// The URL to the documentation on docs.rs. + /// May not lead anywhere. + pub web_url: Option, + /// The URL to the documentation in the local file system. + /// May not lead anywhere. + pub local_url: Option, +} const MARKDOWN_OPTIONS: Options = Options::ENABLE_FOOTNOTES.union(Options::ENABLE_TABLES).union(Options::ENABLE_TASKLISTS); @@ -109,7 +119,7 @@ pub(crate) fn remove_links(markdown: &str) -> String { // Feature: Open Docs // -// Retrieve a link to documentation for the given symbol. +// Retrieve a links to documentation for the given symbol. // // The simplest way to use this feature is via the context menu. Right-click on // the selected item. The context menu opens. Select **Open Docs**. @@ -122,7 +132,9 @@ pub(crate) fn remove_links(markdown: &str) -> String { pub(crate) fn external_docs( db: &RootDatabase, position: &FilePosition, -) -> Option { + target_dir: Option<&OsStr>, + sysroot: Option<&OsStr>, +) -> Option { let sema = &Semantics::new(db); let file = sema.parse(position.file_id).syntax().clone(); let token = pick_best_token(file.token_at_offset(position.offset), |kind| match kind { @@ -146,11 +158,11 @@ pub(crate) fn external_docs( NameClass::Definition(it) | NameClass::ConstReference(it) => it, NameClass::PatFieldShorthand { local_def: _, field_ref } => Definition::Field(field_ref), }, - _ => return None, + _ => return None } }; - get_doc_link(db, definition) + Some(get_doc_links(db, definition, target_dir, sysroot)) } /// Extracts all links from a given markdown text returning the definition text range, link-text @@ -308,19 +320,35 @@ fn broken_link_clone_cb(link: BrokenLink<'_>) -> Option<(CowStr<'_>, CowStr<'_>) // // This should cease to be a problem if RFC2988 (Stable Rustdoc URLs) is implemented // https://github.com/rust-lang/rfcs/pull/2988 -fn get_doc_link(db: &RootDatabase, def: Definition) -> Option { - let (target, file, frag) = filename_and_frag_for_def(db, def)?; +fn get_doc_links( + db: &RootDatabase, + def: Definition, + target_dir: Option<&OsStr>, + sysroot: Option<&OsStr>, +) -> DocumentationLinks { + let join_url = |base_url: Option, path: &str| -> Option { + base_url.and_then(|url| url.join(path).ok()) + }; + + let Some((target, file, frag)) = filename_and_frag_for_def(db, def) else { return Default::default(); }; - let mut url = get_doc_base_url(db, target)?; + let (mut web_url, mut local_url) = get_doc_base_urls(db, target, target_dir, sysroot); if let Some(path) = mod_path_of_def(db, target) { - url = url.join(&path).ok()?; + web_url = join_url(web_url, &path); + local_url = join_url(local_url, &path); } - url = url.join(&file).ok()?; - url.set_fragment(frag.as_deref()); + web_url = join_url(web_url, &file); + local_url = join_url(local_url, &file); - Some(url.into()) + web_url.as_mut().map(|url| url.set_fragment(frag.as_deref())); + local_url.as_mut().map(|url| url.set_fragment(frag.as_deref())); + + DocumentationLinks { + web_url: web_url.map(|it| it.into()), + local_url: local_url.map(|it| it.into()), + } } fn rewrite_intra_doc_link( @@ -332,7 +360,7 @@ fn rewrite_intra_doc_link( let (link, ns) = parse_intra_doc_link(target); let resolved = resolve_doc_path_for_def(db, def, link, ns)?; - let mut url = get_doc_base_url(db, resolved)?; + let mut url = get_doc_base_urls(db, resolved, None, None).0?; let (_, file, frag) = filename_and_frag_for_def(db, resolved)?; if let Some(path) = mod_path_of_def(db, resolved) { @@ -351,7 +379,7 @@ fn rewrite_url_link(db: &RootDatabase, def: Definition, target: &str) -> Option< return None; } - let mut url = get_doc_base_url(db, def)?; + let mut url = get_doc_base_urls(db, def, None, None).0?; let (def, file, frag) = filename_and_frag_for_def(db, def)?; if let Some(path) = mod_path_of_def(db, def) { @@ -366,7 +394,7 @@ fn rewrite_url_link(db: &RootDatabase, def: Definition, target: &str) -> Option< fn mod_path_of_def(db: &RootDatabase, def: Definition) -> Option { def.canonical_module_path(db).map(|it| { let mut path = String::new(); - it.flat_map(|it| it.name(db)).for_each(|name| format_to!(path, "{}/", name)); + it.flat_map(|it| it.name(db)).for_each(|name| format_to!(path, "{}/", name.display(db))); path }) } @@ -426,18 +454,38 @@ fn map_links<'e>( /// ```ignore /// https://doc.rust-lang.org/std/iter/trait.Iterator.html#tymethod.next /// ^^^^^^^^^^^^^^^^^^^^^^^^^^ +/// file:///project/root/target/doc/std/iter/trait.Iterator.html#tymethod.next +/// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ /// ``` -fn get_doc_base_url(db: &RootDatabase, def: Definition) -> Option { +fn get_doc_base_urls( + db: &RootDatabase, + def: Definition, + target_dir: Option<&OsStr>, + sysroot: Option<&OsStr>, +) -> (Option, Option) { + let local_doc = target_dir + .and_then(|path| path.to_str()) + .and_then(|path| Url::parse(&format!("file:///{path}/")).ok()) + .and_then(|it| it.join("doc/").ok()); + let system_doc = sysroot + .and_then(|it| it.to_str()) + .map(|sysroot| format!("file:///{sysroot}/share/doc/rust/html/")) + .and_then(|it| Url::parse(&it).ok()); + // special case base url of `BuiltinType` to core // https://github.com/rust-lang/rust-analyzer/issues/12250 if let Definition::BuiltinType(..) = def { - return Url::parse("https://doc.rust-lang.org/nightly/core/").ok(); + let web_link = Url::parse("https://doc.rust-lang.org/nightly/core/").ok(); + let system_link = system_doc.and_then(|it| it.join("core/").ok()); + return (web_link, system_link); }; - let krate = def.krate(db)?; - let display_name = krate.display_name(db)?; + let Some(krate) = def.krate(db) else { return Default::default() }; + let Some(display_name) = krate.display_name(db) else { return Default::default() }; + let crate_data = &db.crate_graph()[krate.into()]; + let channel = crate_data.channel.map_or("nightly", ReleaseChannel::as_str); - let base = match db.crate_graph()[krate.into()].origin { + let (web_base, local_base) = match &crate_data.origin { // std and co do not specify `html_root_url` any longer so we gotta handwrite this ourself. // FIXME: Use the toolchains channel instead of nightly CrateOrigin::Lang( @@ -447,10 +495,17 @@ fn get_doc_base_url(db: &RootDatabase, def: Definition) -> Option { | LangCrateOrigin::Std | LangCrateOrigin::Test), ) => { - format!("https://doc.rust-lang.org/nightly/{origin}") + let system_url = system_doc.and_then(|it| it.join(&format!("{origin}")).ok()); + let web_url = format!("https://doc.rust-lang.org/{channel}/{origin}"); + (Some(web_url), system_url) } - _ => { - krate.get_html_root_url(db).or_else(|| { + CrateOrigin::Lang(_) => return (None, None), + CrateOrigin::Rustc { name: _ } => { + (Some(format!("https://doc.rust-lang.org/{channel}/nightly-rustc/")), None) + } + CrateOrigin::Local { repo: _, name: _ } => { + // FIXME: These should not attempt to link to docs.rs! + let weblink = krate.get_html_root_url(db).or_else(|| { let version = krate.version(db); // Fallback to docs.rs. This uses `display_name` and can never be // correct, but that's what fallbacks are about. @@ -462,10 +517,32 @@ fn get_doc_base_url(db: &RootDatabase, def: Definition) -> Option { krate = display_name, version = version.as_deref().unwrap_or("*") )) - })? + }); + (weblink, local_doc) + } + CrateOrigin::Library { repo: _, name } => { + let weblink = krate.get_html_root_url(db).or_else(|| { + let version = krate.version(db); + // Fallback to docs.rs. This uses `display_name` and can never be + // correct, but that's what fallbacks are about. + // + // FIXME: clicking on the link should just open the file in the editor, + // instead of falling back to external urls. + Some(format!( + "https://docs.rs/{krate}/{version}/", + krate = name, + version = version.as_deref().unwrap_or("*") + )) + }); + (weblink, local_doc) } }; - Url::parse(&base).ok()?.join(&format!("{display_name}/")).ok() + let web_base = web_base + .and_then(|it| Url::parse(&it).ok()) + .and_then(|it| it.join(&format!("{display_name}/")).ok()); + let local_base = local_base.and_then(|it| it.join(&format!("{display_name}/")).ok()); + + (web_base, local_base) } /// Get the filename and extension generated for a symbol by rustdoc. @@ -490,9 +567,9 @@ fn filename_and_frag_for_def( let res = match def { Definition::Adt(adt) => match adt { - Adt::Struct(s) => format!("struct.{}.html", s.name(db)), - Adt::Enum(e) => format!("enum.{}.html", e.name(db)), - Adt::Union(u) => format!("union.{}.html", u.name(db)), + Adt::Struct(s) => format!("struct.{}.html", s.name(db).display(db.upcast())), + Adt::Enum(e) => format!("enum.{}.html", e.name(db).display(db.upcast())), + Adt::Union(u) => format!("union.{}.html", u.name(db).display(db.upcast())), }, Definition::Module(m) => match m.name(db) { // `#[doc(keyword = "...")]` is internal used only by rust compiler @@ -500,21 +577,25 @@ fn filename_and_frag_for_def( Some(kw) => { format!("keyword.{}.html", kw.trim_matches('"')) } - None => format!("{name}/index.html"), + None => format!("{}/index.html", name.display(db.upcast())), }, None => String::from("index.html"), }, - Definition::Trait(t) => format!("trait.{}.html", t.name(db)), - Definition::TraitAlias(t) => format!("traitalias.{}.html", t.name(db)), - Definition::TypeAlias(t) => format!("type.{}.html", t.name(db)), - Definition::BuiltinType(t) => format!("primitive.{}.html", t.name()), - Definition::Function(f) => format!("fn.{}.html", f.name(db)), + Definition::Trait(t) => format!("trait.{}.html", t.name(db).display(db.upcast())), + Definition::TraitAlias(t) => format!("traitalias.{}.html", t.name(db).display(db.upcast())), + Definition::TypeAlias(t) => format!("type.{}.html", t.name(db).display(db.upcast())), + Definition::BuiltinType(t) => format!("primitive.{}.html", t.name().display(db.upcast())), + Definition::Function(f) => format!("fn.{}.html", f.name(db).display(db.upcast())), Definition::Variant(ev) => { - format!("enum.{}.html#variant.{}", ev.parent_enum(db).name(db), ev.name(db)) + format!( + "enum.{}.html#variant.{}", + ev.parent_enum(db).name(db).display(db.upcast()), + ev.name(db).display(db.upcast()) + ) } - Definition::Const(c) => format!("const.{}.html", c.name(db)?), - Definition::Static(s) => format!("static.{}.html", s.name(db)), - Definition::Macro(mac) => format!("macro.{}.html", mac.name(db)), + Definition::Const(c) => format!("const.{}.html", c.name(db)?.display(db.upcast())), + Definition::Static(s) => format!("static.{}.html", s.name(db).display(db.upcast())), + Definition::Macro(mac) => format!("macro.{}.html", mac.name(db).display(db.upcast())), Definition::Field(field) => { let def = match field.parent_def(db) { hir::VariantDef::Struct(it) => Definition::Adt(it.into()), @@ -522,7 +603,11 @@ fn filename_and_frag_for_def( hir::VariantDef::Variant(it) => Definition::Variant(it), }; let (_, file, _) = filename_and_frag_for_def(db, def)?; - return Some((def, file, Some(format!("structfield.{}", field.name(db))))); + return Some(( + def, + file, + Some(format!("structfield.{}", field.name(db).display(db.upcast()))), + )); } Definition::SelfType(impl_) => { let adt = impl_.self_ty(db).as_adt()?.into(); @@ -556,12 +641,14 @@ fn get_assoc_item_fragment(db: &dyn HirDatabase, assoc_item: hir::AssocItem) -> // Rustdoc makes this decision based on whether a method 'has defaultness'. // Currently this is only the case for provided trait methods. if is_trait_method && !function.has_body(db) { - format!("tymethod.{}", function.name(db)) + format!("tymethod.{}", function.name(db).display(db.upcast())) } else { - format!("method.{}", function.name(db)) + format!("method.{}", function.name(db).display(db.upcast())) } } - AssocItem::Const(constant) => format!("associatedconstant.{}", constant.name(db)?), - AssocItem::TypeAlias(ty) => format!("associatedtype.{}", ty.name(db)), + AssocItem::Const(constant) => { + format!("associatedconstant.{}", constant.name(db)?.display(db.upcast())) + } + AssocItem::TypeAlias(ty) => format!("associatedtype.{}", ty.name(db).display(db.upcast())), }) } diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs index 104181a33e68d..05a64b33bfdc9 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs @@ -1,3 +1,5 @@ +use std::ffi::OsStr; + use expect_test::{expect, Expect}; use hir::{HasAttrs, Semantics}; use ide_db::{ @@ -13,11 +15,33 @@ use crate::{ fixture, TryToNav, }; -fn check_external_docs(ra_fixture: &str, expect: Expect) { +fn check_external_docs( + ra_fixture: &str, + target_dir: Option<&OsStr>, + expect_web_url: Option, + expect_local_url: Option, + sysroot: Option<&OsStr>, +) { let (analysis, position) = fixture::position(ra_fixture); - let url = analysis.external_docs(position).unwrap().expect("could not find url for symbol"); + let links = analysis.external_docs(position, target_dir, sysroot).unwrap(); + + let web_url = links.web_url; + let local_url = links.local_url; + + println!("web_url: {:?}", web_url); + println!("local_url: {:?}", local_url); + + match (expect_web_url, web_url) { + (Some(expect), Some(url)) => expect.assert_eq(&url), + (None, None) => (), + _ => panic!("Unexpected web url"), + } - expect.assert_eq(&url) + match (expect_local_url, local_url) { + (Some(expect), Some(url)) => expect.assert_eq(&url), + (None, None) => (), + _ => panic!("Unexpected local url"), + } } fn check_rewrite(ra_fixture: &str, expect: Expect) { @@ -96,6 +120,20 @@ fn node_to_def( }) } +#[test] +fn external_docs_doc_builtin_type() { + check_external_docs( + r#" +//- /main.rs crate:foo +let x: u3$02 = 0; +"#, + Some(&OsStr::new("/home/user/project")), + Some(expect![[r#"https://doc.rust-lang.org/nightly/core/primitive.u32.html"#]]), + Some(expect![[r#"file:///sysroot/share/doc/rust/html/core/primitive.u32.html"#]]), + Some(&OsStr::new("/sysroot")), + ); +} + #[test] fn external_docs_doc_url_crate() { check_external_docs( @@ -105,7 +143,10 @@ use foo$0::Foo; //- /lib.rs crate:foo pub struct Foo; "#, - expect![[r#"https://docs.rs/foo/*/foo/index.html"#]], + Some(&OsStr::new("/home/user/project")), + Some(expect![[r#"https://docs.rs/foo/*/foo/index.html"#]]), + Some(expect![[r#"file:///home/user/project/doc/foo/index.html"#]]), + Some(&OsStr::new("/sysroot")), ); } @@ -116,7 +157,10 @@ fn external_docs_doc_url_std_crate() { //- /main.rs crate:std use self$0; "#, - expect![[r#"https://doc.rust-lang.org/nightly/std/index.html"#]], + Some(&OsStr::new("/home/user/project")), + Some(expect!["https://doc.rust-lang.org/stable/std/index.html"]), + Some(expect!["file:///sysroot/share/doc/rust/html/std/index.html"]), + Some(&OsStr::new("/sysroot")), ); } @@ -127,7 +171,38 @@ fn external_docs_doc_url_struct() { //- /main.rs crate:foo pub struct Fo$0o; "#, - expect![[r#"https://docs.rs/foo/*/foo/struct.Foo.html"#]], + Some(&OsStr::new("/home/user/project")), + Some(expect![[r#"https://docs.rs/foo/*/foo/struct.Foo.html"#]]), + Some(expect![[r#"file:///home/user/project/doc/foo/struct.Foo.html"#]]), + Some(&OsStr::new("/sysroot")), + ); +} + +#[test] +fn external_docs_doc_url_windows_backslash_path() { + check_external_docs( + r#" +//- /main.rs crate:foo +pub struct Fo$0o; +"#, + Some(&OsStr::new(r"C:\Users\user\project")), + Some(expect![[r#"https://docs.rs/foo/*/foo/struct.Foo.html"#]]), + Some(expect![[r#"file:///C:/Users/user/project/doc/foo/struct.Foo.html"#]]), + Some(&OsStr::new("/sysroot")), + ); +} + +#[test] +fn external_docs_doc_url_windows_slash_path() { + check_external_docs( + r#" +//- /main.rs crate:foo +pub struct Fo$0o; +"#, + Some(&OsStr::new(r"C:/Users/user/project")), + Some(expect![[r#"https://docs.rs/foo/*/foo/struct.Foo.html"#]]), + Some(expect![[r#"file:///C:/Users/user/project/doc/foo/struct.Foo.html"#]]), + Some(&OsStr::new("/sysroot")), ); } @@ -140,7 +215,10 @@ pub struct Foo { field$0: () } "#, - expect![[r##"https://docs.rs/foo/*/foo/struct.Foo.html#structfield.field"##]], + None, + Some(expect![[r##"https://docs.rs/foo/*/foo/struct.Foo.html#structfield.field"##]]), + None, + None, ); } @@ -151,7 +229,10 @@ fn external_docs_doc_url_fn() { //- /main.rs crate:foo pub fn fo$0o() {} "#, - expect![[r#"https://docs.rs/foo/*/foo/fn.foo.html"#]], + None, + Some(expect![[r#"https://docs.rs/foo/*/foo/fn.foo.html"#]]), + None, + None, ); } @@ -165,7 +246,10 @@ impl Foo { pub fn method$0() {} } "#, - expect![[r##"https://docs.rs/foo/*/foo/struct.Foo.html#method.method"##]], + None, + Some(expect![[r##"https://docs.rs/foo/*/foo/struct.Foo.html#method.method"##]]), + None, + None, ); check_external_docs( r#" @@ -175,7 +259,10 @@ impl Foo { const CONST$0: () = (); } "#, - expect![[r##"https://docs.rs/foo/*/foo/struct.Foo.html#associatedconstant.CONST"##]], + None, + Some(expect![[r##"https://docs.rs/foo/*/foo/struct.Foo.html#associatedconstant.CONST"##]]), + None, + None, ); } @@ -192,7 +279,10 @@ impl Trait for Foo { pub fn method$0() {} } "#, - expect![[r##"https://docs.rs/foo/*/foo/struct.Foo.html#method.method"##]], + None, + Some(expect![[r##"https://docs.rs/foo/*/foo/struct.Foo.html#method.method"##]]), + None, + None, ); check_external_docs( r#" @@ -205,7 +295,10 @@ impl Trait for Foo { const CONST$0: () = (); } "#, - expect![[r##"https://docs.rs/foo/*/foo/struct.Foo.html#associatedconstant.CONST"##]], + None, + Some(expect![[r##"https://docs.rs/foo/*/foo/struct.Foo.html#associatedconstant.CONST"##]]), + None, + None, ); check_external_docs( r#" @@ -218,7 +311,10 @@ impl Trait for Foo { type Type$0 = (); } "#, - expect![[r##"https://docs.rs/foo/*/foo/struct.Foo.html#associatedtype.Type"##]], + None, + Some(expect![[r##"https://docs.rs/foo/*/foo/struct.Foo.html#associatedtype.Type"##]]), + None, + None, ); } @@ -231,7 +327,10 @@ pub trait Foo { fn method$0(); } "#, - expect![[r##"https://docs.rs/foo/*/foo/trait.Foo.html#tymethod.method"##]], + None, + Some(expect![[r##"https://docs.rs/foo/*/foo/trait.Foo.html#tymethod.method"##]]), + None, + None, ); check_external_docs( r#" @@ -240,7 +339,10 @@ pub trait Foo { const CONST$0: (); } "#, - expect![[r##"https://docs.rs/foo/*/foo/trait.Foo.html#associatedconstant.CONST"##]], + None, + Some(expect![[r##"https://docs.rs/foo/*/foo/trait.Foo.html#associatedconstant.CONST"##]]), + None, + None, ); check_external_docs( r#" @@ -249,7 +351,10 @@ pub trait Foo { type Type$0; } "#, - expect![[r##"https://docs.rs/foo/*/foo/trait.Foo.html#associatedtype.Type"##]], + None, + Some(expect![[r##"https://docs.rs/foo/*/foo/trait.Foo.html#associatedtype.Type"##]]), + None, + None, ); } @@ -260,7 +365,10 @@ fn external_docs_trait() { //- /main.rs crate:foo trait Trait$0 {} "#, - expect![[r#"https://docs.rs/foo/*/foo/trait.Trait.html"#]], + None, + Some(expect![[r#"https://docs.rs/foo/*/foo/trait.Trait.html"#]]), + None, + None, ) } @@ -273,7 +381,10 @@ pub mod foo { pub mod ba$0r {} } "#, - expect![[r#"https://docs.rs/foo/*/foo/foo/bar/index.html"#]], + None, + Some(expect![[r#"https://docs.rs/foo/*/foo/foo/bar/index.html"#]]), + None, + None, ) } @@ -294,7 +405,10 @@ fn foo() { let bar: wrapper::It$0em; } "#, - expect![[r#"https://docs.rs/foo/*/foo/wrapper/module/struct.Item.html"#]], + None, + Some(expect![[r#"https://docs.rs/foo/*/foo/wrapper/module/struct.Item.html"#]]), + None, + None, ) } diff --git a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs index 418043d679811..d6bbf2bf794dc 100644 --- a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs +++ b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs @@ -76,15 +76,17 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< if let Some(item) = ast::Item::cast(node.clone()) { if let Some(def) = sema.resolve_attr_macro_call(&item) { break ( - def.name(db).to_string(), + def.name(db).display(db).to_string(), expand_attr_macro_recur(&sema, &item)?, SyntaxKind::MACRO_ITEMS, ); } } if let Some(mac) = ast::MacroCall::cast(node) { + let mut name = mac.path()?.segment()?.name_ref()?.to_string(); + name.push('!'); break ( - mac.path()?.segment()?.name_ref()?.to_string(), + name, expand_macro_recur(&sema, &mac)?, mac.syntax().parent().map(|it| it.kind()).unwrap_or(SyntaxKind::MACRO_ITEMS), ); @@ -149,9 +151,11 @@ fn _format( _db: &RootDatabase, _kind: SyntaxKind, _file_id: FileId, - _expansion: &str, + expansion: &str, ) -> Option { - None + // remove trailing spaces for test + use itertools::Itertools; + Some(expansion.lines().map(|x| x.trim_end()).join("\n")) } #[cfg(not(any(test, target_arch = "wasm32", target_os = "emscripten")))] @@ -235,7 +239,7 @@ fn main() { } "#, expect![[r#" - bar + bar! 5i64 as _"#]], ); } @@ -252,7 +256,7 @@ fn main() { } "#, expect![[r#" - bar + bar! for _ in 0..42{}"#]], ); } @@ -273,9 +277,8 @@ macro_rules! baz { f$0oo!(); "#, expect![[r#" - foo - fn b(){} - "#]], + foo! + fn b(){}"#]], ); } @@ -294,7 +297,7 @@ macro_rules! foo { f$0oo!(); "#, expect![[r#" - foo + foo! fn some_thing() -> u32 { let a = 0; a+10 @@ -328,16 +331,16 @@ fn main() { } "#, expect![[r#" - match_ast - { - if let Some(it) = ast::TraitDef::cast(container.clone()){} - else if let Some(it) = ast::ImplDef::cast(container.clone()){} - else { - { - continue - } - } - }"#]], + match_ast! + { + if let Some(it) = ast::TraitDef::cast(container.clone()){} + else if let Some(it) = ast::ImplDef::cast(container.clone()){} + else { + { + continue + } + } + }"#]], ); } @@ -358,7 +361,7 @@ fn main() { } "#, expect![[r#" - match_ast + match_ast! {}"#]], ); } @@ -383,7 +386,7 @@ fn main() { } "#, expect![[r#" - foo + foo! { macro_rules! bar { () => { @@ -411,7 +414,7 @@ fn main() { } "#, expect![[r#" - foo + foo! "#]], ); } @@ -433,7 +436,7 @@ fn main() { } "#, expect![[r#" - foo + foo! 0"#]], ); } @@ -451,7 +454,7 @@ fn main() { } "#, expect![[r#" - foo + foo! fn f(_: &dyn ::std::marker::Copy){}"#]], ); } @@ -469,8 +472,17 @@ struct Foo {} "#, expect![[r#" Clone - impl < >core::clone::Clone for Foo< >{} - "#]], + impl < >core::clone::Clone for Foo< >where { + fn clone(&self) -> Self { + match self { + Foo{} + => Foo{} + , + + } + } + + }"#]], ); } @@ -486,8 +498,7 @@ struct Foo {} "#, expect![[r#" Copy - impl < >core::marker::Copy for Foo< >{} - "#]], + impl < >core::marker::Copy for Foo< >where{}"#]], ); } @@ -502,8 +513,7 @@ struct Foo {} "#, expect![[r#" Copy - impl < >core::marker::Copy for Foo< >{} - "#]], + impl < >core::marker::Copy for Foo< >where{}"#]], ); check( r#" @@ -514,8 +524,17 @@ struct Foo {} "#, expect![[r#" Clone - impl < >core::clone::Clone for Foo< >{} - "#]], + impl < >core::clone::Clone for Foo< >where { + fn clone(&self) -> Self { + match self { + Foo{} + => Foo{} + , + + } + } + + }"#]], ); } } diff --git a/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs b/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs index 9f78c75e90aa2..f906182224468 100644 --- a/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs +++ b/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs @@ -39,7 +39,7 @@ fn try_extend_selection( ) -> Option { let range = frange.range; - let string_kinds = [COMMENT, STRING, BYTE_STRING]; + let string_kinds = [COMMENT, STRING, BYTE_STRING, C_STRING]; let list_kinds = [ RECORD_PAT_FIELD_LIST, MATCH_ARM_LIST, diff --git a/src/tools/rust-analyzer/crates/ide/src/fetch_crates.rs b/src/tools/rust-analyzer/crates/ide/src/fetch_crates.rs new file mode 100644 index 0000000000000..46ee671defbc0 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide/src/fetch_crates.rs @@ -0,0 +1,42 @@ +use ide_db::{ + base_db::{CrateOrigin, FileId, SourceDatabase}, + FxIndexSet, RootDatabase, +}; + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct CrateInfo { + pub name: Option, + pub version: Option, + pub root_file_id: FileId, +} + +// Feature: Show Dependency Tree +// +// Shows a view tree with all the dependencies of this project +// +// |=== +// | Editor | Panel Name +// +// | VS Code | **Rust Dependencies** +// |=== +// +// image::https://user-images.githubusercontent.com/5748995/229394139-2625beab-f4c9-484b-84ed-ad5dee0b1e1a.png[] +pub(crate) fn fetch_crates(db: &RootDatabase) -> FxIndexSet { + let crate_graph = db.crate_graph(); + crate_graph + .iter() + .map(|crate_id| &crate_graph[crate_id]) + .filter(|&data| !matches!(data.origin, CrateOrigin::Local { .. })) + .map(|data| crate_info(data)) + .collect() +} + +fn crate_info(data: &ide_db::base_db::CrateData) -> CrateInfo { + let crate_name = crate_name(data); + let version = data.version.clone(); + CrateInfo { name: crate_name, version, root_file_id: data.root_file_id } +} + +fn crate_name(data: &ide_db::base_db::CrateData) -> Option { + data.display_name.as_ref().map(|it| it.canonical_name().to_owned()) +} diff --git a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs index a32ac35496aa4..b278924721c0f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs +++ b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs @@ -219,7 +219,7 @@ mod tests { } #[test] - fn test_nagative_trait_bound() { + fn test_negative_trait_bound() { let txt = r#"impl !Unpin for Test {}"#; check( txt, diff --git a/src/tools/rust-analyzer/crates/ide/src/fixture.rs b/src/tools/rust-analyzer/crates/ide/src/fixture.rs index 2ea6f6a9ab1b6..2e5903c0602e3 100644 --- a/src/tools/rust-analyzer/crates/ide/src/fixture.rs +++ b/src/tools/rust-analyzer/crates/ide/src/fixture.rs @@ -1,5 +1,4 @@ //! Utilities for creating `Analysis` instances for tests. -use hir::db::DefDatabase; use ide_db::base_db::fixture::ChangeFixture; use test_utils::{extract_annotations, RangeOrOffset}; @@ -9,7 +8,7 @@ use crate::{Analysis, AnalysisHost, FileId, FilePosition, FileRange}; pub(crate) fn file(ra_fixture: &str) -> (Analysis, FileId) { let mut host = AnalysisHost::default(); let change_fixture = ChangeFixture::parse(ra_fixture); - host.db.set_enable_proc_attr_macros(true); + host.db.enable_proc_attr_macros(); host.db.apply_change(change_fixture.change); (host.analysis(), change_fixture.files[0]) } @@ -18,7 +17,7 @@ pub(crate) fn file(ra_fixture: &str) -> (Analysis, FileId) { pub(crate) fn position(ra_fixture: &str) -> (Analysis, FilePosition) { let mut host = AnalysisHost::default(); let change_fixture = ChangeFixture::parse(ra_fixture); - host.db.set_enable_proc_attr_macros(true); + host.db.enable_proc_attr_macros(); host.db.apply_change(change_fixture.change); let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); let offset = range_or_offset.expect_offset(); @@ -29,7 +28,7 @@ pub(crate) fn position(ra_fixture: &str) -> (Analysis, FilePosition) { pub(crate) fn range(ra_fixture: &str) -> (Analysis, FileRange) { let mut host = AnalysisHost::default(); let change_fixture = ChangeFixture::parse(ra_fixture); - host.db.set_enable_proc_attr_macros(true); + host.db.enable_proc_attr_macros(); host.db.apply_change(change_fixture.change); let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); let range = range_or_offset.expect_range(); @@ -40,7 +39,7 @@ pub(crate) fn range(ra_fixture: &str) -> (Analysis, FileRange) { pub(crate) fn range_or_position(ra_fixture: &str) -> (Analysis, FileId, RangeOrOffset) { let mut host = AnalysisHost::default(); let change_fixture = ChangeFixture::parse(ra_fixture); - host.db.set_enable_proc_attr_macros(true); + host.db.enable_proc_attr_macros(); host.db.apply_change(change_fixture.change); let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); (host.analysis(), file_id, range_or_offset) @@ -50,7 +49,7 @@ pub(crate) fn range_or_position(ra_fixture: &str) -> (Analysis, FileId, RangeOrO pub(crate) fn annotations(ra_fixture: &str) -> (Analysis, FilePosition, Vec<(FileRange, String)>) { let mut host = AnalysisHost::default(); let change_fixture = ChangeFixture::parse(ra_fixture); - host.db.set_enable_proc_attr_macros(true); + host.db.enable_proc_attr_macros(); host.db.apply_change(change_fixture.change); let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); let offset = range_or_offset.expect_offset(); @@ -67,11 +66,11 @@ pub(crate) fn annotations(ra_fixture: &str) -> (Analysis, FilePosition, Vec<(Fil (host.analysis(), FilePosition { file_id, offset }, annotations) } -/// Creates analysis from a multi-file fixture with annonations without $0 +/// Creates analysis from a multi-file fixture with annotations without $0 pub(crate) fn annotations_without_marker(ra_fixture: &str) -> (Analysis, Vec<(FileRange, String)>) { let mut host = AnalysisHost::default(); let change_fixture = ChangeFixture::parse(ra_fixture); - host.db.set_enable_proc_attr_macros(true); + host.db.enable_proc_attr_macros(); host.db.apply_change(change_fixture.change); let annotations = change_fixture diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs index cf0819a2524b9..4e641357e3728 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -113,6 +113,7 @@ fn try_lookup_include_path( file_id, full_range: TextRange::new(0.into(), size), name: path.into(), + alias: None, focus_range: None, kind: None, container_name: None, @@ -833,8 +834,7 @@ fn test() { #[rustc_builtin_macro] macro_rules! include {} - include!("foo.rs"); -//^^^^^^^^^^^^^^^^^^^ +include!("foo.rs"); fn f() { foo$0(); @@ -846,6 +846,33 @@ mod confuse_index { //- /foo.rs fn foo() {} + //^^^ + "#, + ); + } + + #[test] + fn goto_through_included_file_struct_with_doc_comment() { + check( + r#" +//- /main.rs +#[rustc_builtin_macro] +macro_rules! include {} + +include!("foo.rs"); + +fn f() { + let x = Foo$0; +} + +mod confuse_index { + pub struct Foo; +} + +//- /foo.rs +/// This is a doc comment +pub struct Foo; + //^^^ "#, ); } @@ -1466,6 +1493,29 @@ impl Twait for Stwuct { fn f() { let s = Stwuct; s.a$0(); +} + "#, + ); + } + #[test] + fn method_call_inside_block() { + check( + r#" +trait Twait { + fn a(&self); +} + +fn outer() { + struct Stwuct; + + impl Twait for Stwuct { + fn a(&self){} + //^ + } + fn f() { + let s = Stwuct; + s.a$0(); + } } "#, ); diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs index 6d2d0bd635165..6048990f7492c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs @@ -10,7 +10,7 @@ use crate::{FilePosition, NavigationTarget, RangeInfo, TryToNav}; // |=== // | Editor | Action Name // -// | VS Code | **Go to Type Definition* +// | VS Code | **Go to Type Definition** // |=== // // image::https://user-images.githubusercontent.com/48062697/113020657-b560f500-917a-11eb-9007-0f809733a338.gif[] @@ -38,32 +38,41 @@ pub(crate) fn goto_type_definition( }; let range = token.text_range(); sema.descend_into_macros(token) - .iter() + .into_iter() .filter_map(|token| { - let ty = sema.token_ancestors_with_macros(token.clone()).find_map(|node| { - let ty = match_ast! { - match node { - ast::Expr(it) => sema.type_of_expr(&it)?.original, - ast::Pat(it) => sema.type_of_pat(&it)?.original, - ast::SelfParam(it) => sema.type_of_self(&it)?, - ast::Type(it) => sema.resolve_type(&it)?, - ast::RecordField(it) => sema.to_def(&it).map(|d| d.ty(db.upcast()))?, - // can't match on RecordExprField directly as `ast::Expr` will match an iteration too early otherwise - ast::NameRef(it) => { - if let Some(record_field) = ast::RecordExprField::for_name_ref(&it) { - let (_, _, ty) = sema.resolve_record_field(&record_field)?; - ty - } else { - let record_field = ast::RecordPatField::for_field_name_ref(&it)?; - sema.resolve_record_pat_field(&record_field)?.1 - } - }, - _ => return None, - } - }; + let ty = sema + .token_ancestors_with_macros(token) + // When `token` is within a macro call, we can't determine its type. Don't continue + // this traversal because otherwise we'll end up returning the type of *that* macro + // call, which is not what we want in general. + // + // Macro calls always wrap `TokenTree`s, so it's sufficient and efficient to test + // if the current node is a `TokenTree`. + .take_while(|node| !ast::TokenTree::can_cast(node.kind())) + .find_map(|node| { + let ty = match_ast! { + match node { + ast::Expr(it) => sema.type_of_expr(&it)?.original, + ast::Pat(it) => sema.type_of_pat(&it)?.original, + ast::SelfParam(it) => sema.type_of_self(&it)?, + ast::Type(it) => sema.resolve_type(&it)?, + ast::RecordField(it) => sema.to_def(&it)?.ty(db.upcast()), + // can't match on RecordExprField directly as `ast::Expr` will match an iteration too early otherwise + ast::NameRef(it) => { + if let Some(record_field) = ast::RecordExprField::for_name_ref(&it) { + let (_, _, ty) = sema.resolve_record_field(&record_field)?; + ty + } else { + let record_field = ast::RecordPatField::for_field_name_ref(&it)?; + sema.resolve_record_pat_field(&record_field)?.1 + } + }, + _ => return None, + } + }; - Some(ty) - }); + Some(ty) + }); ty }) .for_each(|ty| { @@ -94,7 +103,7 @@ mod tests { fn check(ra_fixture: &str) { let (analysis, position, expected) = fixture::annotations(ra_fixture); let navs = analysis.goto_type_definition(position).unwrap().unwrap().info; - assert_ne!(navs.len(), 0); + assert!(!navs.is_empty(), "navigation is empty"); let cmp = |&FileRange { file_id, range }: &_| (file_id, range.start()); let navs = navs @@ -104,7 +113,7 @@ mod tests { .collect::>(); let expected = expected .into_iter() - .map(|(FileRange { file_id, range }, _)| FileRange { file_id, range }) + .map(|(file_range, _)| file_range) .sorted_by_key(cmp) .collect::>(); assert_eq!(expected, navs); @@ -198,6 +207,32 @@ id! { ); } + #[test] + fn dont_collect_type_from_token_in_macro_call() { + check( + r#" +struct DontCollectMe; +struct S; + //^ + +macro_rules! inner { + ($t:tt) => { DontCollectMe } +} +macro_rules! m { + ($t:ident) => { + match $t { + _ => inner!($t); + } + } +} + +fn test() { + m!($0S); +} +"#, + ); + } + #[test] fn goto_type_definition_for_param() { check( diff --git a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs index d88ffd25c40fe..7e545491f8e7e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs +++ b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs @@ -1,10 +1,12 @@ use hir::Semantics; use ide_db::{ - base_db::{FileId, FilePosition}, + base_db::{FileId, FilePosition, FileRange}, defs::{Definition, IdentClass}, helpers::pick_best_token, search::{FileReference, ReferenceCategory, SearchScope}, - syntax_helpers::node_ext::{for_each_break_and_continue_expr, for_each_tail_expr, walk_expr}, + syntax_helpers::node_ext::{ + for_each_break_and_continue_expr, for_each_tail_expr, full_path_of_name_ref, walk_expr, + }, FxHashSet, RootDatabase, }; use syntax::{ @@ -30,6 +32,7 @@ pub struct HighlightRelatedConfig { pub references: bool, pub exit_points: bool, pub break_points: bool, + pub closure_captures: bool, pub yield_points: bool, } @@ -38,11 +41,13 @@ pub struct HighlightRelatedConfig { // Highlights constructs related to the thing under the cursor: // // . if on an identifier, highlights all references to that identifier in the current file +// .. additionally, if the identifier is a trait in a where clause, type parameter trait bound or use item, highlights all references to that trait's assoc items in the corresponding scope // . if on an `async` or `await token, highlights all yield points for that async context // . if on a `return` or `fn` keyword, `?` character or `->` return type arrow, highlights all exit points for that context // . if on a `break`, `loop`, `while` or `for` token, highlights all break points for that loop or block context +// . if on a `move` or `|` token that belongs to a closure, highlights all captures of the closure. // -// Note: `?` and `->` do not currently trigger this behavior in the VSCode editor. +// Note: `?`, `|` and `->` do not currently trigger this behavior in the VSCode editor. pub(crate) fn highlight_related( sema: &Semantics<'_, RootDatabase>, config: HighlightRelatedConfig, @@ -53,11 +58,13 @@ pub(crate) fn highlight_related( let token = pick_best_token(syntax.token_at_offset(offset), |kind| match kind { T![?] => 4, // prefer `?` when the cursor is sandwiched like in `await$0?` - T![->] => 3, - kind if kind.is_keyword() => 2, - IDENT | INT_NUMBER => 1, + T![->] => 4, + kind if kind.is_keyword() => 3, + IDENT | INT_NUMBER => 2, + T![|] => 1, _ => 0, })?; + // most if not all of these should be re-implemented with information seeded from hir match token.kind() { T![?] if config.exit_points && token.parent().and_then(ast::TryExpr::cast).is_some() => { highlight_exit_points(sema, token) @@ -70,18 +77,64 @@ pub(crate) fn highlight_related( T![break] | T![loop] | T![while] | T![continue] if config.break_points => { highlight_break_points(token) } + T![|] if config.closure_captures => highlight_closure_captures(sema, token, file_id), + T![move] if config.closure_captures => highlight_closure_captures(sema, token, file_id), _ if config.references => highlight_references(sema, &syntax, token, file_id), _ => None, } } +fn highlight_closure_captures( + sema: &Semantics<'_, RootDatabase>, + token: SyntaxToken, + file_id: FileId, +) -> Option> { + let closure = token.parent_ancestors().take(2).find_map(ast::ClosureExpr::cast)?; + let search_range = closure.body()?.syntax().text_range(); + let ty = &sema.type_of_expr(&closure.into())?.original; + let c = ty.as_closure()?; + Some( + c.captured_items(sema.db) + .into_iter() + .map(|capture| capture.local()) + .flat_map(|local| { + let usages = Definition::Local(local) + .usages(sema) + .set_scope(Some(SearchScope::file_range(FileRange { + file_id, + range: search_range, + }))) + .include_self_refs() + .all() + .references + .remove(&file_id) + .into_iter() + .flatten() + .map(|FileReference { category, range, .. }| HighlightedRange { + range, + category, + }); + let category = local.is_mut(sema.db).then_some(ReferenceCategory::Write); + local + .sources(sema.db) + .into_iter() + .map(|x| x.to_nav(sema.db)) + .filter(|decl| decl.file_id == file_id) + .filter_map(|decl| decl.focus_range) + .map(move |range| HighlightedRange { range, category }) + .chain(usages) + }) + .collect(), + ) +} + fn highlight_references( sema: &Semantics<'_, RootDatabase>, node: &SyntaxNode, token: SyntaxToken, file_id: FileId, ) -> Option> { - let defs = find_defs(sema, token); + let defs = find_defs(sema, token.clone()); let usages = defs .iter() .filter_map(|&d| { @@ -93,12 +146,62 @@ fn highlight_references( .remove(&file_id) }) .flatten() - .map(|FileReference { category: access, range, .. }| HighlightedRange { - range, - category: access, - }); + .map(|FileReference { category, range, .. }| HighlightedRange { range, category }); let mut res = FxHashSet::default(); for &def in &defs { + // highlight trait usages + if let Definition::Trait(t) = def { + let trait_item_use_scope = (|| { + let name_ref = token.parent().and_then(ast::NameRef::cast)?; + let path = full_path_of_name_ref(&name_ref)?; + let parent = path.syntax().parent()?; + match_ast! { + match parent { + ast::UseTree(it) => it.syntax().ancestors().find(|it| { + ast::SourceFile::can_cast(it.kind()) || ast::Module::can_cast(it.kind()) + }), + ast::PathType(it) => it + .syntax() + .ancestors() + .nth(2) + .and_then(ast::TypeBoundList::cast)? + .syntax() + .parent() + .filter(|it| ast::WhereClause::can_cast(it.kind()) || ast::TypeParam::can_cast(it.kind()))? + .ancestors() + .find(|it| { + ast::Item::can_cast(it.kind()) + }), + _ => None, + } + } + })(); + if let Some(trait_item_use_scope) = trait_item_use_scope { + res.extend( + t.items_with_supertraits(sema.db) + .into_iter() + .filter_map(|item| { + Definition::from(item) + .usages(sema) + .set_scope(Some(SearchScope::file_range(FileRange { + file_id, + range: trait_item_use_scope.text_range(), + }))) + .include_self_refs() + .all() + .references + .remove(&file_id) + }) + .flatten() + .map(|FileReference { category, range, .. }| HighlightedRange { + range, + category, + }), + ); + } + } + + // highlight the defs themselves match def { Definition::Local(local) => { let category = local.is_mut(sema.db).then_some(ReferenceCategory::Write); @@ -148,9 +251,16 @@ fn highlight_exit_points( ) -> Option> { fn hl( sema: &Semantics<'_, RootDatabase>, + def_ranges: [Option; 2], body: Option, ) -> Option> { let mut highlights = Vec::new(); + highlights.extend( + def_ranges + .into_iter() + .flatten() + .map(|range| HighlightedRange { category: None, range }), + ); let body = body?; walk_expr(&body, &mut |expr| match expr { ast::Expr::ReturnExpr(expr) => { @@ -194,10 +304,21 @@ fn highlight_exit_points( for anc in token.parent_ancestors() { return match_ast! { match anc { - ast::Fn(fn_) => hl(sema, fn_.body().map(ast::Expr::BlockExpr)), - ast::ClosureExpr(closure) => hl(sema, closure.body()), + ast::Fn(fn_) => hl(sema, [fn_.fn_token().map(|it| it.text_range()), None], fn_.body().map(ast::Expr::BlockExpr)), + ast::ClosureExpr(closure) => hl( + sema, + closure.param_list().map_or([None; 2], |p| [p.l_paren_token().map(|it| it.text_range()), p.r_paren_token().map(|it| it.text_range())]), + closure.body() + ), ast::BlockExpr(block_expr) => if matches!(block_expr.modifier(), Some(ast::BlockModifier::Async(_) | ast::BlockModifier::Try(_)| ast::BlockModifier::Const(_))) { - hl(sema, Some(block_expr.into())) + hl( + sema, + [block_expr.modifier().and_then(|modifier| match modifier { + ast::BlockModifier::Async(t) | ast::BlockModifier::Try(t) | ast::BlockModifier::Const(t) => Some(t.text_range()), + _ => None, + }), None], + Some(block_expr.into()) + ) } else { continue; }, @@ -352,16 +473,17 @@ mod tests { use super::*; + const ENABLED_CONFIG: HighlightRelatedConfig = HighlightRelatedConfig { + break_points: true, + exit_points: true, + references: true, + closure_captures: true, + yield_points: true, + }; + #[track_caller] fn check(ra_fixture: &str) { - let config = HighlightRelatedConfig { - break_points: true, - exit_points: true, - references: true, - yield_points: true, - }; - - check_with_config(ra_fixture, config); + check_with_config(ra_fixture, ENABLED_CONFIG); } #[track_caller] @@ -570,6 +692,29 @@ pub async$0 fn foo() { ); } + #[test] + fn test_hl_let_else_yield_points() { + check( + r#" +pub async fn foo() { + // ^^^^^ + let x = foo() + .await$0 + // ^^^^^ + .await; + // ^^^^^ + || { 0.await }; + let Some(_) = None else { + foo().await + // ^^^^^ + }; + (async { 0.await }).await + // ^^^^^ +} +"#, + ); + } + #[test] fn test_hl_yield_nested_fn() { check( @@ -610,7 +755,8 @@ async fn foo() { fn test_hl_exit_points() { check( r#" -fn foo() -> u32 { + fn foo() -> u32 { +//^^ if true { return$0 0; // ^^^^^^ @@ -629,7 +775,8 @@ fn foo() -> u32 { fn test_hl_exit_points2() { check( r#" -fn foo() ->$0 u32 { + fn foo() ->$0 u32 { +//^^ if true { return 0; // ^^^^^^ @@ -648,7 +795,8 @@ fn foo() ->$0 u32 { fn test_hl_exit_points3() { check( r#" -fn$0 foo() -> u32 { + fn$0 foo() -> u32 { +//^^ if true { return 0; // ^^^^^^ @@ -663,6 +811,26 @@ fn$0 foo() -> u32 { ); } + #[test] + fn test_hl_let_else_exit_points() { + check( + r#" + fn$0 foo() -> u32 { +//^^ + let Some(bar) = None else { + return 0; + // ^^^^^^ + }; + + 0?; + // ^ + 0xDEAD_BEEF + // ^^^^^^^^^^^ +} +"#, + ); + } + #[test] fn test_hl_prefer_ref_over_tail_exit() { check( @@ -694,7 +862,8 @@ macro_rules! never { () => { never() } } fn never() -> ! { loop {} } -fn foo() ->$0 u32 { + fn foo() ->$0 u32 { +//^^ never(); // ^^^^^^^ never!(); @@ -714,7 +883,8 @@ fn foo() ->$0 u32 { fn test_hl_inner_tail_exit_points() { check( r#" -fn foo() ->$0 u32 { + fn foo() ->$0 u32 { +//^^ if true { unsafe { return 5; @@ -755,7 +925,8 @@ fn foo() ->$0 u32 { fn test_hl_inner_tail_exit_points_labeled_block() { check( r#" -fn foo() ->$0 u32 { + fn foo() ->$0 u32 { +//^^ 'foo: { break 'foo 0; // ^^^^^ @@ -776,7 +947,8 @@ fn foo() ->$0 u32 { fn test_hl_inner_tail_exit_points_loops() { check( r#" -fn foo() ->$0 u32 { + fn foo() ->$0 u32 { +//^^ 'foo: while { return 0; true } { // ^^^^^^ break 'foo 0; @@ -1086,12 +1258,7 @@ fn function(field: u32) { #[test] fn test_hl_disabled_ref_local() { - let config = HighlightRelatedConfig { - references: false, - break_points: true, - exit_points: true, - yield_points: true, - }; + let config = HighlightRelatedConfig { references: false, ..ENABLED_CONFIG }; check_with_config( r#" @@ -1106,12 +1273,7 @@ fn foo() { #[test] fn test_hl_disabled_ref_local_preserved_break() { - let config = HighlightRelatedConfig { - references: false, - break_points: true, - exit_points: true, - yield_points: true, - }; + let config = HighlightRelatedConfig { references: false, ..ENABLED_CONFIG }; check_with_config( r#" @@ -1146,12 +1308,7 @@ fn foo() { #[test] fn test_hl_disabled_ref_local_preserved_yield() { - let config = HighlightRelatedConfig { - references: false, - break_points: true, - exit_points: true, - yield_points: true, - }; + let config = HighlightRelatedConfig { references: false, ..ENABLED_CONFIG }; check_with_config( r#" @@ -1182,12 +1339,7 @@ async fn foo() { #[test] fn test_hl_disabled_ref_local_preserved_exit() { - let config = HighlightRelatedConfig { - references: false, - break_points: true, - exit_points: true, - yield_points: true, - }; + let config = HighlightRelatedConfig { references: false, ..ENABLED_CONFIG }; check_with_config( r#" @@ -1207,7 +1359,8 @@ fn foo() -> i32 { check_with_config( r#" -fn foo() ->$0 i32 { + fn foo() ->$0 i32 { +//^^ let x = 5; let y = x * 2; @@ -1225,12 +1378,7 @@ fn foo() ->$0 i32 { #[test] fn test_hl_disabled_break() { - let config = HighlightRelatedConfig { - references: true, - break_points: false, - exit_points: true, - yield_points: true, - }; + let config = HighlightRelatedConfig { break_points: false, ..ENABLED_CONFIG }; check_with_config( r#" @@ -1246,12 +1394,7 @@ fn foo() { #[test] fn test_hl_disabled_yield() { - let config = HighlightRelatedConfig { - references: true, - break_points: true, - exit_points: true, - yield_points: false, - }; + let config = HighlightRelatedConfig { yield_points: false, ..ENABLED_CONFIG }; check_with_config( r#" @@ -1265,12 +1408,7 @@ async$0 fn foo() { #[test] fn test_hl_disabled_exit() { - let config = HighlightRelatedConfig { - references: true, - break_points: true, - exit_points: false, - yield_points: true, - }; + let config = HighlightRelatedConfig { exit_points: false, ..ENABLED_CONFIG }; check_with_config( r#" @@ -1411,6 +1549,75 @@ impl Trait for () { type Output$0 = (); // ^^^^^^ } +"#, + ); + } + + #[test] + fn test_closure_capture_pipe() { + check( + r#" +fn f() { + let x = 1; + // ^ + let c = $0|y| x + y; + // ^ read +} +"#, + ); + } + + #[test] + fn test_closure_capture_move() { + check( + r#" +fn f() { + let x = 1; + // ^ + let c = move$0 |y| x + y; + // ^ read +} +"#, + ); + } + + #[test] + fn test_trait_highlights_assoc_item_uses() { + check( + r#" +trait Foo { + //^^^ + type T; + const C: usize; + fn f() {} + fn m(&self) {} +} +impl Foo for i32 { + //^^^ + type T = i32; + const C: usize = 0; + fn f() {} + fn m(&self) {} +} +fn f(t: T) { + //^^^ + let _: T::T; + //^ + t.m(); + //^ + T::C; + //^ + T::f(); + //^ +} + +fn f2(t: T) { + //^^^ + let _: T::T; + t.m(); + T::C; + T::f(); +} "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs index 64b2221bdeab4..5ef6ac9480721 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs @@ -6,7 +6,7 @@ mod tests; use std::iter; use either::Either; -use hir::{HasSource, Semantics}; +use hir::{db::DefDatabase, HasSource, LangItem, Semantics}; use ide_db::{ base_db::FileRange, defs::{Definition, IdentClass, OperatorClass}, @@ -27,10 +27,25 @@ use crate::{ #[derive(Clone, Debug, PartialEq, Eq)] pub struct HoverConfig { pub links_in_hover: bool, + pub memory_layout: Option, pub documentation: bool, pub keywords: bool, pub format: HoverDocFormat, - pub interpret_tests: bool, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct MemoryLayoutHoverConfig { + pub size: Option, + pub offset: Option, + pub alignment: Option, + pub niches: bool, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum MemoryLayoutHoverRenderKind { + Decimal, + Hexadecimal, + Both, } #[derive(Clone, Debug, PartialEq, Eq)] @@ -56,7 +71,7 @@ impl HoverAction { mod_path: render::path( db, it.module(db)?, - it.name(db).map(|name| name.to_string()), + it.name(db).map(|name| name.display(db).to_string()), ), nav: it.try_to_nav(db)?, }) @@ -119,8 +134,8 @@ fn hover_simple( | T![crate] | T![Self] | T![_] => 4, - // index and prefix ops - T!['['] | T![']'] | T![?] | T![*] | T![-] | T![!] => 3, + // index and prefix ops and closure pipe + T!['['] | T![']'] | T![?] | T![*] | T![-] | T![!] | T![|] => 3, kind if kind.is_keyword() => 2, T!['('] | T![')'] => 2, kind if kind.is_trivia() => 0, @@ -219,6 +234,16 @@ fn hover_simple( }; render::type_info_of(sema, config, &Either::Left(call_expr)) }) + }) + // try closure + .or_else(|| { + descended().find_map(|token| { + if token.kind() != T![|] { + return None; + } + let c = token.parent().and_then(|x| x.parent()).and_then(ast::ClosureExpr::cast)?; + render::closure_expr(sema, config, c) + }) }); result.map(|mut res: HoverResult| { @@ -344,7 +369,14 @@ fn goto_type_action_for_def(db: &RootDatabase, def: Definition) -> Option it.ty(db), diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs index da725ce502b8d..1362146413e0d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -3,8 +3,8 @@ use std::fmt::Display; use either::Either; use hir::{ - db::DefDatabase, Adt, AsAssocItem, AttributeTemplate, HasAttrs, HasSource, HirDisplay, - MirEvalError, Semantics, TypeInfo, + Adt, AsAssocItem, AttributeTemplate, CaptureKind, HasAttrs, HasSource, HirDisplay, Layout, + LayoutError, Semantics, TypeInfo, }; use ide_db::{ base_db::SourceDatabase, @@ -27,7 +27,8 @@ use syntax::{ use crate::{ doc_links::{remove_links, rewrite_links}, hover::walk_and_push_ty, - HoverAction, HoverConfig, HoverResult, Markup, + HoverAction, HoverConfig, HoverResult, Markup, MemoryLayoutHoverConfig, + MemoryLayoutHoverRenderKind, }; pub(super) fn type_info_of( @@ -35,11 +36,20 @@ pub(super) fn type_info_of( _config: &HoverConfig, expr_or_pat: &Either, ) -> Option { - let TypeInfo { original, adjusted } = match expr_or_pat { + let ty_info = match expr_or_pat { Either::Left(expr) => sema.type_of_expr(expr)?, Either::Right(pat) => sema.type_of_pat(pat)?, }; - type_info(sema, _config, original, adjusted) + type_info(sema, _config, ty_info) +} + +pub(super) fn closure_expr( + sema: &Semantics<'_, RootDatabase>, + config: &HoverConfig, + c: ast::ClosureExpr, +) -> Option { + let TypeInfo { original, .. } = sema.type_of_expr(&c.into())?; + closure_ty(sema, config, &TypeInfo { original, adjusted: None }) } pub(super) fn try_expr( @@ -361,7 +371,7 @@ fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option Definition::Variant(e) => Some(e.parent_enum(db).name(db)), _ => None, } - .map(|name| name.to_string()) + .map(|name| name.display(db).to_string()) } pub(super) fn path(db: &RootDatabase, module: hir::Module, item_name: Option) -> String { @@ -371,7 +381,7 @@ pub(super) fn path(db: &RootDatabase, module: hir::Module, item_name: Option label_and_docs(db, it), - Definition::Field(it) => label_and_layout_info_and_docs(db, it, |&it| { - let var_def = it.parent_def(db); - let id = it.index(); - let layout = it.layout(db).ok()?; - let offset = match var_def { - hir::VariantDef::Struct(s) => Adt::from(s) - .layout(db) - .ok() - .map(|layout| format!(", offset = {}", layout.fields.offset(id).bytes())), - _ => None, - }; - Some(format!( - "size = {}, align = {}{}", - layout.size.bytes(), - layout.align.abi.bytes(), - offset.as_deref().unwrap_or_default() - )) - }), - Definition::Module(it) => label_and_docs(db, it), - Definition::Function(it) => label_and_layout_info_and_docs(db, it, |_| { - if !config.interpret_tests { - return None; - } - match it.eval(db) { - Ok(()) => Some("pass".into()), - Err(MirEvalError::Panic) => Some("fail".into()), - Err(MirEvalError::MirLowerError(f, e)) => { - let name = &db.function_data(f).name; - Some(format!("error: fail to lower {name} due {e:?}")) + Definition::Field(it) => label_and_layout_info_and_docs( + db, + it, + config, + |&it| it.layout(db), + |_| { + let var_def = it.parent_def(db); + let id = it.index(); + match var_def { + hir::VariantDef::Struct(s) => { + Adt::from(s).layout(db).ok().and_then(|layout| layout.field_offset(id)) + } + _ => None, } - Err(e) => Some(format!("error: {e:?}")), - } - }), - Definition::Adt(it) => label_and_layout_info_and_docs(db, it, |&it| { - let layout = it.layout(db).ok()?; - Some(format!("size = {}, align = {}", layout.size.bytes(), layout.align.abi.bytes())) - }), - Definition::Variant(it) => label_value_and_docs(db, it, |&it| { - if !it.parent_enum(db).is_data_carrying(db) { - match it.eval(db) { - Ok(x) => Some(if x >= 10 { format!("{x} ({x:#X})") } else { format!("{x}") }), - Err(_) => it.value(db).map(|x| format!("{x:?}")), + }, + ), + Definition::Module(it) => label_and_docs(db, it), + Definition::Function(it) => label_and_docs(db, it), + Definition::Adt(it) => { + label_and_layout_info_and_docs(db, it, config, |&it| it.layout(db), |_| None) + } + Definition::Variant(it) => label_value_and_layout_info_and_docs( + db, + it, + config, + |&it| { + if !it.parent_enum(db).is_data_carrying(db) { + match it.eval(db) { + Ok(x) => { + Some(if x >= 10 { format!("{x} ({x:#X})") } else { format!("{x}") }) + } + Err(_) => it.value(db).map(|x| format!("{x:?}")), + } + } else { + None } - } else { - None - } - }), + }, + |it| it.layout(db), + |layout| layout.enum_tag_size(), + ), Definition::Const(it) => label_value_and_docs(db, it, |it| { let body = it.render_eval(db); match body { @@ -455,22 +458,26 @@ pub(super) fn definition( }), Definition::Trait(it) => label_and_docs(db, it), Definition::TraitAlias(it) => label_and_docs(db, it), - Definition::TypeAlias(it) => label_and_docs(db, it), + Definition::TypeAlias(it) => { + label_and_layout_info_and_docs(db, it, config, |&it| it.ty(db).layout(db), |_| None) + } Definition::BuiltinType(it) => { return famous_defs .and_then(|fd| builtin(fd, it)) - .or_else(|| Some(Markup::fenced_block(&it.name()))) + .or_else(|| Some(Markup::fenced_block(&it.name().display(db)))) } - Definition::Local(it) => return local(db, it), + Definition::Local(it) => return local(db, it, config), Definition::SelfType(impl_def) => { impl_def.self_ty(db).as_adt().map(|adt| label_and_docs(db, adt))? } Definition::GenericParam(it) => label_and_docs(db, it), - Definition::Label(it) => return Some(Markup::fenced_block(&it.name(db))), + Definition::Label(it) => return Some(Markup::fenced_block(&it.name(db).display(db))), // 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), + Definition::DeriveHelper(it) => { + (format!("derive_helper {}", it.name(db).display(db)), None) + } }; let docs = docs @@ -490,10 +497,13 @@ pub(super) fn definition( fn type_info( sema: &Semantics<'_, RootDatabase>, - _config: &HoverConfig, - original: hir::Type, - adjusted: Option, + config: &HoverConfig, + ty: TypeInfo, ) -> Option { + if let Some(res) = closure_ty(sema, config, &ty) { + return Some(res); + }; + let TypeInfo { original, adjusted } = ty; let mut res = HoverResult::default(); let mut targets: Vec = Vec::new(); let mut push_new_def = |item: hir::ModuleDef| { @@ -523,6 +533,67 @@ fn type_info( Some(res) } +fn closure_ty( + sema: &Semantics<'_, RootDatabase>, + config: &HoverConfig, + TypeInfo { original, adjusted }: &TypeInfo, +) -> Option { + let c = original.as_closure()?; + let mut captures_rendered = c.captured_items(sema.db) + .into_iter() + .map(|it| { + let borrow_kind = match it.kind() { + CaptureKind::SharedRef => "immutable borrow", + CaptureKind::UniqueSharedRef => "unique immutable borrow ([read more](https://doc.rust-lang.org/stable/reference/types/closure.html#unique-immutable-borrows-in-captures))", + CaptureKind::MutableRef => "mutable borrow", + CaptureKind::Move => "move", + }; + format!("* `{}` by {}", it.display_place(sema.db), borrow_kind) + }) + .join("\n"); + if captures_rendered.trim().is_empty() { + captures_rendered = "This closure captures nothing".to_string(); + } + let mut targets: Vec = Vec::new(); + let mut push_new_def = |item: hir::ModuleDef| { + if !targets.contains(&item) { + targets.push(item); + } + }; + walk_and_push_ty(sema.db, original, &mut push_new_def); + c.capture_types(sema.db).into_iter().for_each(|ty| { + walk_and_push_ty(sema.db, &ty, &mut push_new_def); + }); + + let adjusted = if let Some(adjusted_ty) = adjusted { + walk_and_push_ty(sema.db, &adjusted_ty, &mut push_new_def); + format!( + "\nCoerced to: {}", + adjusted_ty.display(sema.db).with_closure_style(hir::ClosureStyle::ImplFn) + ) + } else { + String::new() + }; + let mut markup = format!("```rust\n{}", c.display_with_id(sema.db),); + + if let Some(layout) = + render_memory_layout(config.memory_layout, || original.layout(sema.db), |_| None, |_| None) + { + format_to!(markup, "{layout}"); + } + format_to!( + markup, + "\n{}\n```{adjusted}\n\n## Captures\n{}", + c.display_with_impl(sema.db), + captures_rendered, + ); + + let mut res = HoverResult::default(); + res.actions.push(HoverAction::goto_type_from_targets(sema.db, targets)); + res.markup = markup.into(); + Some(res) +} + fn render_builtin_attr(db: &RootDatabase, attr: hir::BuiltinAttr) -> Option { let name = attr.name(db); let desc = format!("#[{name}]"); @@ -553,21 +624,59 @@ where (label, docs) } -fn label_and_layout_info_and_docs( +fn label_and_layout_info_and_docs( + db: &RootDatabase, + def: D, + config: &HoverConfig, + layout_extractor: E, + layout_offset_extractor: E2, +) -> (String, Option) +where + D: HasAttrs + HirDisplay, + E: Fn(&D) -> Result, + E2: Fn(&Layout) -> Option, +{ + let mut label = def.display(db).to_string(); + if let Some(layout) = render_memory_layout( + config.memory_layout, + || layout_extractor(&def), + layout_offset_extractor, + |_| None, + ) { + format_to!(label, "{layout}"); + } + let docs = def.attrs(db).docs(); + (label, docs) +} + +fn label_value_and_layout_info_and_docs( db: &RootDatabase, def: D, + config: &HoverConfig, value_extractor: E, + layout_extractor: E2, + layout_tag_extractor: E3, ) -> (String, Option) where D: HasAttrs + HirDisplay, E: Fn(&D) -> Option, + E2: Fn(&D) -> Result, + E3: Fn(&Layout) -> Option, V: Display, { - let label = if let Some(value) = value_extractor(&def) { - format!("{} // {value}", def.display(db)) - } else { - def.display(db).to_string() + let value = value_extractor(&def); + let mut label = match value { + Some(value) => format!("{} = {value}", def.display(db)), + None => def.display(db).to_string(), }; + if let Some(layout) = render_memory_layout( + config.memory_layout, + || layout_extractor(&def), + |_| None, + layout_tag_extractor, + ) { + format_to!(label, "{layout}"); + } let docs = def.attrs(db).docs(); (label, docs) } @@ -616,26 +725,26 @@ fn markup(docs: Option, desc: String, mod_path: Option) -> Optio fn builtin(famous_defs: &FamousDefs<'_, '_>, builtin: hir::BuiltinType) -> Option { // std exposes prim_{} modules with docstrings on the root to document the builtins - let primitive_mod = format!("prim_{}", builtin.name()); + let primitive_mod = format!("prim_{}", builtin.name().display(famous_defs.0.db)); let doc_owner = find_std_module(famous_defs, &primitive_mod)?; let docs = doc_owner.attrs(famous_defs.0.db).docs()?; - markup(Some(docs.into()), builtin.name().to_string(), None) + markup(Some(docs.into()), builtin.name().display(famous_defs.0.db).to_string(), None) } fn find_std_module(famous_defs: &FamousDefs<'_, '_>, name: &str) -> Option { let db = famous_defs.0.db; let std_crate = famous_defs.std()?; let std_root_module = std_crate.root_module(db); - std_root_module - .children(db) - .find(|module| module.name(db).map_or(false, |module| module.to_string() == name)) + std_root_module.children(db).find(|module| { + module.name(db).map_or(false, |module| module.display(db).to_string() == name) + }) } -fn local(db: &RootDatabase, it: hir::Local) -> Option { +fn local(db: &RootDatabase, it: hir::Local, config: &HoverConfig) -> Option { let ty = it.ty(db); let ty = ty.display_truncated(db, None); let is_mut = if it.is_mut(db) { "mut " } else { "" }; - let desc = match it.primary_source(db).into_ident_pat() { + let mut desc = match it.primary_source(db).into_ident_pat() { Some(ident) => { let name = it.name(db); let let_kw = if ident @@ -647,13 +756,91 @@ fn local(db: &RootDatabase, it: hir::Local) -> Option { } else { "" }; - format!("{let_kw}{is_mut}{name}: {ty}") + format!("{let_kw}{is_mut}{}: {ty}", name.display(db)) } None => format!("{is_mut}self: {ty}"), }; + if let Some(layout) = + render_memory_layout(config.memory_layout, || it.ty(db).layout(db), |_| None, |_| None) + { + format_to!(desc, "{layout}"); + } markup(None, desc, None) } +fn render_memory_layout( + config: Option, + layout: impl FnOnce() -> Result, + offset: impl FnOnce(&Layout) -> Option, + tag: impl FnOnce(&Layout) -> Option, +) -> Option { + // field + + let config = config?; + let layout = layout().ok()?; + + let mut label = String::from(" // "); + + if let Some(render) = config.size { + let size = match tag(&layout) { + Some(tag) => layout.size() as usize - tag, + None => layout.size() as usize, + }; + format_to!(label, "size = "); + match render { + MemoryLayoutHoverRenderKind::Decimal => format_to!(label, "{size}"), + MemoryLayoutHoverRenderKind::Hexadecimal => format_to!(label, "{size:#X}"), + MemoryLayoutHoverRenderKind::Both if size >= 10 => { + format_to!(label, "{size} ({size:#X})") + } + MemoryLayoutHoverRenderKind::Both => format_to!(label, "{size}"), + } + format_to!(label, ", "); + } + + if let Some(render) = config.alignment { + let align = layout.align(); + format_to!(label, "align = "); + match render { + MemoryLayoutHoverRenderKind::Decimal => format_to!(label, "{align}",), + MemoryLayoutHoverRenderKind::Hexadecimal => format_to!(label, "{align:#X}",), + MemoryLayoutHoverRenderKind::Both if align >= 10 => { + format_to!(label, "{align} ({align:#X})") + } + MemoryLayoutHoverRenderKind::Both => { + format_to!(label, "{align}") + } + } + format_to!(label, ", "); + } + + if let Some(render) = config.offset { + if let Some(offset) = offset(&layout) { + format_to!(label, "offset = "); + match render { + MemoryLayoutHoverRenderKind::Decimal => format_to!(label, "{offset}"), + MemoryLayoutHoverRenderKind::Hexadecimal => format_to!(label, "{offset:#X}"), + MemoryLayoutHoverRenderKind::Both if offset >= 10 => { + format_to!(label, "{offset} ({offset:#X})") + } + MemoryLayoutHoverRenderKind::Both => { + format_to!(label, "{offset}") + } + } + format_to!(label, ", "); + } + } + + if config.niches { + if let Some(niches) = layout.niches() { + format_to!(label, "niches = {niches}, "); + } + } + label.pop(); // ' ' + label.pop(); // ',' + Some(label) +} + struct KeywordHint { description: String, keyword_mod: String, diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index 57bf0f9ad5f3c..a2f96977581c2 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -2,14 +2,21 @@ use expect_test::{expect, Expect}; use ide_db::base_db::{FileLoader, FileRange}; use syntax::TextRange; -use crate::{fixture, HoverConfig, HoverDocFormat}; +use crate::{ + fixture, HoverConfig, HoverDocFormat, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, +}; const HOVER_BASE_CONFIG: HoverConfig = HoverConfig { links_in_hover: false, + memory_layout: Some(MemoryLayoutHoverConfig { + size: Some(MemoryLayoutHoverRenderKind::Both), + offset: Some(MemoryLayoutHoverRenderKind::Both), + alignment: Some(MemoryLayoutHoverRenderKind::Both), + niches: true, + }), documentation: true, format: HoverDocFormat::Markdown, keywords: true, - interpret_tests: false, }; fn check_hover_no_result(ra_fixture: &str) { @@ -58,6 +65,23 @@ fn check_hover_no_links(ra_fixture: &str, expect: Expect) { expect.assert_eq(&actual) } +fn check_hover_no_memory_layout(ra_fixture: &str, expect: Expect) { + let (analysis, position) = fixture::position(ra_fixture); + let hover = analysis + .hover( + &HoverConfig { memory_layout: None, ..HOVER_BASE_CONFIG }, + FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) }, + ) + .unwrap() + .unwrap(); + + let content = analysis.db.file_text(position.file_id); + let hovered_element = &content[hover.range]; + + let actual = format!("*{hovered_element}*\n{}\n", hover.info.markup); + expect.assert_eq(&actual) +} + fn check_hover_no_markdown(ra_fixture: &str, expect: Expect) { let (analysis, position) = fixture::position(ra_fixture); let hover = analysis @@ -97,6 +121,15 @@ fn check_hover_range(ra_fixture: &str, expect: Expect) { expect.assert_eq(hover.info.markup.as_str()) } +fn check_hover_range_actions(ra_fixture: &str, expect: Expect) { + let (analysis, range) = fixture::range(ra_fixture); + let hover = analysis + .hover(&HoverConfig { links_in_hover: true, ..HOVER_BASE_CONFIG }, range) + .unwrap() + .unwrap(); + expect.assert_debug_eq(&hover.info.actions); +} + fn check_hover_range_no_results(ra_fixture: &str) { let (analysis, range) = fixture::range(ra_fixture); let hover = analysis.hover(&HOVER_BASE_CONFIG, range).unwrap(); @@ -124,7 +157,7 @@ fn foo() { *local* ```rust - let local: i32 + let local: i32 // size = 4, align = 4 ``` "#]], ); @@ -198,6 +231,181 @@ fn main() { ); } +#[test] +fn hover_closure() { + check( + r#" +//- minicore: copy +fn main() { + let x = 2; + let y = $0|z| x + z; +} +"#, + expect![[r#" + *|* + ```rust + {closure#0} // size = 8, align = 8, niches = 1 + impl Fn(i32) -> i32 + ``` + + ## Captures + * `x` by immutable borrow + "#]], + ); + + check( + r#" +//- minicore: copy +fn foo(x: impl Fn(i32) -> i32) { + +} +fn main() { + foo($0|x: i32| x) +} +"#, + expect![[r#" + *|* + ```rust + {closure#0} // size = 0, align = 1 + impl Fn(i32) -> i32 + ``` + + ## Captures + This closure captures nothing + "#]], + ); + + check( + r#" +//- minicore: copy + +struct Z { f: i32 } + +struct Y(&'static mut Z) + +struct X { + f1: Y, + f2: (Y, Y), +} + +fn main() { + let x: X; + let y = $0|| { + x.f1; + &mut x.f2.0 .0.f; + }; +} +"#, + expect![[r#" + *|* + ```rust + {closure#0} // size = 16 (0x10), align = 8, niches = 1 + impl FnOnce() + ``` + + ## Captures + * `x.f1` by move + * `(*x.f2.0.0).f` by mutable borrow + "#]], + ); + check( + r#" +//- minicore: copy, option + +fn do_char(c: char) {} + +fn main() { + let x = None; + let y = |$0| { + match x { + Some(c) => do_char(c), + None => x = None, + } + }; +} +"#, + expect![[r#" + *|* + ```rust + {closure#0} // size = 8, align = 8, niches = 1 + impl FnMut() + ``` + + ## Captures + * `x` by mutable borrow + "#]], + ); +} + +#[test] +fn hover_ranged_closure() { + check_hover_range( + r#" +//- minicore: fn +struct S; +struct S2; +fn main() { + let x = &S; + let y = ($0|| {x; S2}$0).call(); +} +"#, + expect![[r#" + ```rust + {closure#0} // size = 8, align = 8, niches = 1 + impl FnOnce() -> S2 + ``` + Coerced to: &impl FnOnce() -> S2 + + ## Captures + * `x` by move"#]], + ); + check_hover_range_actions( + r#" +//- minicore: fn +struct S; +struct S2; +fn main() { + let x = &S; + let y = ($0|| {x; S2}$0).call(); +} +"#, + expect![[r#" + [ + GoToType( + [ + HoverGotoTypeData { + mod_path: "test::S2", + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 10..20, + focus_range: 17..19, + name: "S2", + kind: Struct, + description: "struct S2", + }, + }, + HoverGotoTypeData { + mod_path: "test::S", + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 0..9, + focus_range: 7..8, + name: "S", + kind: Struct, + description: "struct S", + }, + }, + ], + ), + ] + "#]], + ); +} + #[test] fn hover_shows_long_type_of_an_expression() { check( @@ -222,12 +430,12 @@ fn main() { } "#, expect![[r#" - *iter* + *iter* - ```rust - let mut iter: Iter>, |&mut u32, &u32, &mut u32| -> Option, u32>> - ``` - "#]], + ```rust + let mut iter: Iter>, impl Fn(&mut u32, &u32, &mut u32) -> Option, u32>> // size = 8, align = 4 + ``` + "#]], ); } @@ -604,12 +812,12 @@ fn main() { let zz$0 = Test { t: 23u8, k: 33 }; }"#, expect![[r#" - *zz* + *zz* - ```rust - let zz: Test - ``` - "#]], + ```rust + let zz: Test // size = 8, align = 4 + ``` + "#]], ); check_hover_range( r#" @@ -655,12 +863,12 @@ use Option::Some; fn main() { let b$0ar = Some(12); } "#, expect![[r#" - *bar* + *bar* - ```rust - let bar: Option - ``` - "#]], + ```rust + let bar: Option // size = 4, align = 4 + ``` + "#]], ); } @@ -724,12 +932,12 @@ fn hover_for_local_variable() { check( r#"fn func(foo: i32) { fo$0o; }"#, expect![[r#" - *foo* + *foo* - ```rust - foo: i32 - ``` - "#]], + ```rust + foo: i32 // size = 4, align = 4 + ``` + "#]], ) } @@ -738,12 +946,12 @@ fn hover_for_local_variable_pat() { check( r#"fn func(fo$0o: i32) {}"#, expect![[r#" - *foo* + *foo* - ```rust - foo: i32 - ``` - "#]], + ```rust + foo: i32 // size = 4, align = 4 + ``` + "#]], ) } @@ -752,12 +960,12 @@ fn hover_local_var_edge() { check( r#"fn func(foo: i32) { if true { $0foo; }; }"#, expect![[r#" - *foo* + *foo* - ```rust - foo: i32 - ``` - "#]], + ```rust + foo: i32 // size = 4, align = 4 + ``` + "#]], ) } @@ -766,12 +974,12 @@ fn hover_for_param_edge() { check( r#"fn func($0foo: i32) {}"#, expect![[r#" - *foo* + *foo* - ```rust - foo: i32 - ``` - "#]], + ```rust + foo: i32 // size = 4, align = 4 + ``` + "#]], ) } @@ -810,12 +1018,12 @@ impl Thing { fn main() { let foo_$0test = Thing::new(); } "#, expect![[r#" - *foo_test* + *foo_test* - ```rust - let foo_test: Thing - ``` - "#]], + ```rust + let foo_test: Thing // size = 4, align = 4 + ``` + "#]], ) } @@ -970,12 +1178,12 @@ fn y() { } "#, expect![[r#" - *x* + *x* - ```rust - let x: i32 - ``` - "#]], + ```rust + let x: i32 // size = 4, align = 4 + ``` + "#]], ) } @@ -1100,12 +1308,12 @@ macro_rules! id { ($($tt:tt)*) => { $($tt)* } } fn foo(bar:u32) { let a = id!(ba$0r); } "#, expect![[r#" - *bar* + *bar* - ```rust - bar: u32 - ``` - "#]], + ```rust + bar: u32 // size = 4, align = 4 + ``` + "#]], ); } @@ -1118,12 +1326,12 @@ macro_rules! id { ($($tt:tt)*) => { id_deep!($($tt)*) } } fn foo(bar:u32) { let a = id!(ba$0r); } "#, expect![[r#" - *bar* + *bar* - ```rust - bar: u32 - ``` - "#]], + ```rust + bar: u32 // size = 4, align = 4 + ``` + "#]], ); } @@ -1320,16 +1528,16 @@ fn test_hover_function_pointer_show_identifiers() { check( r#"type foo$0 = fn(a: i32, b: i32) -> i32;"#, expect![[r#" - *foo* + *foo* - ```rust - test - ``` + ```rust + test + ``` - ```rust - type foo = fn(a: i32, b: i32) -> i32 - ``` - "#]], + ```rust + type foo = fn(a: i32, b: i32) -> i32 // size = 8, align = 8, niches = 1 + ``` + "#]], ); } @@ -1338,16 +1546,16 @@ fn test_hover_function_pointer_no_identifier() { check( r#"type foo$0 = fn(i32, _: i32) -> i32;"#, expect![[r#" - *foo* + *foo* - ```rust - test - ``` + ```rust + test + ``` - ```rust - type foo = fn(i32, i32) -> i32 - ``` - "#]], + ```rust + type foo = fn(i32, i32) -> i32 // size = 8, align = 8, niches = 1 + ``` + "#]], ); } @@ -1667,6 +1875,86 @@ pub fn fo$0o() {} ); } +#[test] +fn test_hover_layout_of_variant() { + check( + r#"enum Foo { + Va$0riant1(u8, u16), + Variant2(i32, u8, i64), + }"#, + expect![[r#" + *Variant1* + + ```rust + test::Foo + ``` + + ```rust + Variant1(u8, u16) // size = 4, align = 2 + ``` + "#]], + ); +} + +#[test] +fn test_hover_layout_of_enum() { + check( + r#"enum $0Foo { + Variant1(u8, u16), + Variant2(i32, u8, i64), + }"#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + enum Foo // size = 16 (0x10), align = 8, niches = 254 + ``` + "#]], + ); +} + +#[test] +fn test_hover_no_memory_layout() { + check_hover_no_memory_layout( + r#"struct Foo { fiel$0d_a: u8, field_b: i32, field_c: i16 }"#, + expect![[r#" + *field_a* + + ```rust + test::Foo + ``` + + ```rust + field_a: u8 + ``` + "#]], + ); + + check_hover_no_memory_layout( + r#" +//- minicore: copy +fn main() { + let x = 2; + let y = $0|z| x + z; +} +"#, + expect![[r#" + *|* + ```rust + {closure#0} + impl Fn(i32) -> i32 + ``` + + ## Captures + * `x` by immutable borrow + "#]], + ); +} + #[test] fn test_hover_macro_generated_struct_fn_doc_comment() { cov_mark::check!(hover_macro_generated_struct_fn_doc_comment); @@ -2020,6 +2308,19 @@ fn main() { let s$0t = S{ f1:Arg(0) }; } ); } +#[test] +fn test_hover_generic_excludes_sized_go_to_action() { + check_actions( + r#" +//- minicore: sized +struct S(T); + "#, + expect![[r#" + [] + "#]], + ); +} + #[test] fn test_hover_generic_struct_has_flattened_goto_type_actions() { check_actions( @@ -2079,52 +2380,53 @@ mod M { fn main() { let s$0t = (A(1), B(2), M::C(3) ); } "#, expect![[r#" - [ - GoToType( - [ - HoverGotoTypeData { - mod_path: "test::A", - nav: NavigationTarget { - file_id: FileId( - 0, - ), - full_range: 0..14, - focus_range: 7..8, - name: "A", - kind: Struct, - description: "struct A", - }, + [ + GoToType( + [ + HoverGotoTypeData { + mod_path: "test::A", + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 0..14, + focus_range: 7..8, + name: "A", + kind: Struct, + description: "struct A", }, - HoverGotoTypeData { - mod_path: "test::B", - nav: NavigationTarget { - file_id: FileId( - 0, - ), - full_range: 15..29, - focus_range: 22..23, - name: "B", - kind: Struct, - description: "struct B", - }, + }, + HoverGotoTypeData { + mod_path: "test::B", + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 15..29, + focus_range: 22..23, + name: "B", + kind: Struct, + description: "struct B", }, - HoverGotoTypeData { - mod_path: "test::M::C", - nav: NavigationTarget { - file_id: FileId( - 0, - ), - full_range: 42..60, - focus_range: 53..54, - name: "C", - kind: Struct, - description: "pub struct C", - }, + }, + HoverGotoTypeData { + mod_path: "test::M::C", + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 42..60, + focus_range: 53..54, + name: "C", + kind: Struct, + container_name: "M", + description: "pub struct C", }, - ], - ), - ] - "#]], + }, + ], + ), + ] + "#]], ); } @@ -2453,6 +2755,7 @@ pub mod future { focus_range: 60..66, name: "Future", kind: Trait, + container_name: "future", description: "pub trait Future", }, }, @@ -2908,7 +3211,7 @@ fn main() { *f* ```rust - f: &i32 + f: &i32 // size = 8, align = 8, niches = 1 ``` --- @@ -2958,7 +3261,7 @@ fn main() { *value* ```rust - let value: Const<1> + let value: Const<1> // size = 0, align = 1 ``` "#]], ); @@ -2978,7 +3281,7 @@ fn main() { *value* ```rust - let value: Const<0> + let value: Const<0> // size = 0, align = 1 ``` "#]], ); @@ -2998,7 +3301,7 @@ fn main() { *value* ```rust - let value: Const<-1> + let value: Const<-1> // size = 0, align = 1 ``` "#]], ); @@ -3018,7 +3321,7 @@ fn main() { *value* ```rust - let value: Const + let value: Const // size = 0, align = 1 ``` "#]], ); @@ -3038,7 +3341,7 @@ fn main() { *value* ```rust - let value: Const<'🦀'> + let value: Const<'🦀'> // size = 0, align = 1 ``` "#]], ); @@ -3054,12 +3357,12 @@ impl Foo { } "#, expect![[r#" - *self* + *self* - ```rust - self: &Foo - ``` - "#]], + ```rust + self: &Foo // size = 8, align = 8, niches = 1 + ``` + "#]], ); } @@ -3074,12 +3377,12 @@ impl Foo { } "#, expect![[r#" - *self* + *self* - ```rust - self: Arc - ``` - "#]], + ```rust + self: Arc // size = 0, align = 1 + ``` + "#]], ); } @@ -3115,7 +3418,7 @@ mod Foo$0 { } #[test] -fn hover_doc_outer_inner_attribue() { +fn hover_doc_outer_inner_attribute() { check( r#" #[doc = "Be quick;"] @@ -3146,7 +3449,7 @@ mod Foo$0 { } #[test] -fn hover_doc_block_style_indentend() { +fn hover_doc_block_style_indent_end() { check( r#" /** @@ -3455,16 +3758,16 @@ struct Foo; type Fo$0o2 = Foo<2>; "#, expect![[r#" - *Foo2* + *Foo2* - ```rust - test - ``` + ```rust + test + ``` - ```rust - type Foo2 = Foo<2> - ``` - "#]], + ```rust + type Foo2 = Foo<2> // size = 0, align = 1 + ``` + "#]], ); } @@ -3504,7 +3807,7 @@ enum E { ``` ```rust - A = 8 + A = 8 // size = 1, align = 1 ``` --- @@ -3529,7 +3832,7 @@ enum E { ``` ```rust - A = 12 (0xC) + A = 12 (0xC) // size = 1, align = 1 ``` --- @@ -3555,7 +3858,7 @@ enum E { ``` ```rust - B = 2 + B = 2 // size = 1, align = 1 ``` --- @@ -3581,7 +3884,7 @@ enum E { ``` ```rust - B = 5 + B = 5 // size = 1, align = 1 ``` --- @@ -4034,6 +4337,235 @@ const FOO$0: f64 = 1.0f64; ); } +#[test] +fn hover_const_eval_floating_point() { + check( + r#" +extern "rust-intrinsic" { + pub fn expf64(x: f64) -> f64; +} + +const FOO$0: f64 = expf64(1.2); +"#, + expect![[r#" + *FOO* + + ```rust + test + ``` + + ```rust + const FOO: f64 = 3.3201169227365472 + ``` + "#]], + ); +} + +#[test] +fn hover_const_eval_enum() { + check( + r#" +enum Enum { + V1, + V2, +} + +const VX: Enum = Enum::V1; + +const FOO$0: Enum = VX; +"#, + expect![[r#" + *FOO* + + ```rust + test + ``` + + ```rust + const FOO: Enum = V1 + ``` + "#]], + ); + check( + r#" +//- minicore: option +const FOO$0: Option = Some(2); +"#, + expect![[r#" + *FOO* + + ```rust + test + ``` + + ```rust + const FOO: Option = Some(2) + ``` + "#]], + ); + check( + r#" +//- minicore: option +const FOO$0: Option<&i32> = Some(2).as_ref(); +"#, + expect![[r#" + *FOO* + + ```rust + test + ``` + + ```rust + const FOO: Option<&i32> = Some(&2) + ``` + "#]], + ); +} + +#[test] +fn hover_const_eval_slice() { + check( + r#" +//- minicore: slice, index, coerce_unsized +const FOO$0: &[i32] = &[1, 2, 3 + 4]; +"#, + expect![[r#" + *FOO* + + ```rust + test + ``` + + ```rust + const FOO: &[i32] = &[1, 2, 7] + ``` + "#]], + ); + check( + r#" +//- minicore: slice, index, coerce_unsized +const FOO$0: &[i32; 5] = &[12; 5]; +"#, + expect![[r#" + *FOO* + + ```rust + test + ``` + + ```rust + const FOO: &[i32; 5] = &[12, 12, 12, 12, 12] + ``` + "#]], + ); + check( + r#" +//- minicore: slice, index, coerce_unsized + +const FOO$0: (&i32, &[i32], &i32) = { + let a: &[i32] = &[1, 2, 3]; + (&a[0], a, &a[0]) +} +"#, + expect![[r#" + *FOO* + + ```rust + test + ``` + + ```rust + const FOO: (&i32, &[i32], &i32) = (&1, &[1, 2, 3], &1) + ``` + "#]], + ); + check( + r#" +//- minicore: slice, index, coerce_unsized + +struct Tree(&[Tree]); + +const FOO$0: Tree = { + let x = &[Tree(&[]), Tree(&[Tree(&[])])]; + Tree(&[Tree(x), Tree(x)]) +} +"#, + expect![[r#" + *FOO* + + ```rust + test + ``` + + ```rust + const FOO: Tree = Tree(&[Tree(&[Tree(&[]), Tree(&[Tree(&[])])]), Tree(&[Tree(&[]), Tree(&[Tree(&[])])])]) + ``` + "#]], + ); +} + +#[test] +fn hover_const_eval_str() { + check( + r#" +const FOO$0: &str = "foo"; +"#, + expect![[r#" + *FOO* + + ```rust + test + ``` + + ```rust + const FOO: &str = "foo" + ``` + "#]], + ); + check( + r#" +struct X { + a: &'static str, + b: &'static str, +} +const FOO$0: X = X { + a: "axiom", + b: "buy N large", +}; +"#, + expect![[r#" + *FOO* + + ```rust + test + ``` + + ```rust + const FOO: X = X { a: "axiom", b: "buy N large" } + ``` + "#]], + ); + check( + r#" +const FOO$0: (&str, &str) = { + let x = "foo"; + (x, x) +}; +"#, + expect![[r#" + *FOO* + + ```rust + test + ``` + + ```rust + const FOO: (&str, &str) = ("foo", "foo") + ``` + "#]], + ); +} + #[test] fn hover_const_eval_in_generic_trait() { // Doesn't compile, but we shouldn't crash. @@ -4115,7 +4647,7 @@ fn foo(e: E) { ``` ```rust - A = 3 + A = 3 // size = 0, align = 1 ``` --- @@ -4137,9 +4669,9 @@ fn main() { *tile4* ```rust - let tile4: [u32; 8] + let tile4: [u32; 8] // size = 32 (0x20), align = 4 ``` - "#]], + "#]], ); } @@ -4242,7 +4774,7 @@ fn foo() { /// [threads]: ../book/ch16-01-threads.html#using-move-closures-with-threads mod move_keyword {} "#, - expect![[r##" + expect![[r#" *move* ```rust @@ -4251,11 +4783,11 @@ mod move_keyword {} --- - [closure](https://doc.rust-lang.org/nightly/book/ch13-01-closures.html) - [closures](https://doc.rust-lang.org/nightly/book/ch13-01-closures.html) - [threads](https://doc.rust-lang.org/nightly/book/ch16-01-threads.html#using-move-closures-with-threads) + [closure](https://doc.rust-lang.org/stable/book/ch13-01-closures.html) + [closures](https://doc.rust-lang.org/stable/book/ch13-01-closures.html) + [threads](https://doc.rust-lang.org/stable/book/ch16-01-threads.html#using-move-closures-with-threads) - "##]], + "#]], ); } @@ -4288,7 +4820,7 @@ fn hover_builtin() { check( r#" //- /main.rs crate:main deps:std -cosnt _: &str$0 = ""; } +const _: &str$0 = ""; } //- /libstd.rs crate:std /// Docs for prim_str @@ -5009,7 +5541,7 @@ fn foo() { fn hover_try_expr_res() { check_hover_range( r#" -//- minicore:result +//- minicore: try, from, result struct FooError; fn foo() -> Result<(), FooError> { @@ -5023,7 +5555,7 @@ fn foo() -> Result<(), FooError> { ); check_hover_range( r#" -//- minicore:result +//- minicore: try, from, result struct FooError; struct BarError; @@ -5044,6 +5576,7 @@ fn foo() -> Result<(), FooError> { fn hover_try_expr() { check_hover_range( r#" +//- minicore: try struct NotResult(T, U); struct Short; struct Looooong; @@ -5061,6 +5594,7 @@ fn foo() -> NotResult<(), Looooong> { ); check_hover_range( r#" +//- minicore: try struct NotResult(T, U); struct Short; struct Looooong; @@ -5092,7 +5626,7 @@ fn foo() -> Option<()> { "#, expect![[r#" ```rust - as Try>::Output + i32 ```"#]], ); } @@ -5312,7 +5846,7 @@ enum Enum { ``` ```rust - RecordV { field: u32 } + RecordV { field: u32 } // size = 4, align = 4 ``` "#]], ); diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs index ac477339ec233..292591674191c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -5,7 +5,8 @@ use std::{ use either::Either; use hir::{ - known, HasVisibility, HirDisplay, HirDisplayError, HirWrite, ModuleDef, ModuleDefId, Semantics, + known, ClosureStyle, HasVisibility, HirDisplay, HirDisplayError, HirWrite, ModuleDef, + ModuleDefId, Semantics, }; use ide_db::{base_db::FileRange, famous_defs::FamousDefs, RootDatabase}; use itertools::Itertools; @@ -13,21 +14,23 @@ use smallvec::{smallvec, SmallVec}; use stdx::never; use syntax::{ ast::{self, AstNode}, - match_ast, NodeOrToken, SyntaxNode, TextRange, + match_ast, NodeOrToken, SyntaxNode, TextRange, TextSize, }; +use text_edit::TextEdit; use crate::{navigation_target::TryToNav, FileId}; -mod closing_brace; -mod implicit_static; -mod fn_lifetime_fn; -mod closure_ret; mod adjustment; -mod chaining; -mod param_name; -mod binding_mode; mod bind_pat; +mod binding_mode; +mod chaining; +mod closing_brace; +mod closure_ret; +mod closure_captures; mod discriminant; +mod fn_lifetime_fn; +mod implicit_static; +mod param_name; #[derive(Clone, Debug, PartialEq, Eq)] pub struct InlayHintsConfig { @@ -40,11 +43,13 @@ pub struct InlayHintsConfig { pub adjustment_hints_mode: AdjustmentHintsMode, pub adjustment_hints_hide_outside_unsafe: bool, pub closure_return_type_hints: ClosureReturnTypeHints, + pub closure_capture_hints: bool, pub binding_mode_hints: bool, pub lifetime_elision_hints: LifetimeElisionHints, pub param_names_for_lifetime_elision_hints: bool, pub hide_named_constructor_hints: bool, pub hide_closure_initialization_hints: bool, + pub closure_style: ClosureStyle, pub max_length: Option, pub closing_brace_hints_min_lines: Option, } @@ -87,38 +92,61 @@ pub enum AdjustmentHintsMode { #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum InlayKind { + Adjustment, BindingMode, Chaining, ClosingBrace, - ClosureReturnType, + ClosureCapture, + Discriminant, GenericParamList, - Adjustment, - AdjustmentPostfix, Lifetime, Parameter, Type, - Discriminant, - OpeningParenthesis, - ClosingParenthesis, +} + +#[derive(Debug)] +pub enum InlayHintPosition { + Before, + After, } #[derive(Debug)] pub struct InlayHint { /// The text range this inlay hint applies to. pub range: TextRange, - /// The kind of this inlay hint. This is used to determine side and padding of the hint for - /// rendering purposes. + pub position: InlayHintPosition, + pub pad_left: bool, + pub pad_right: bool, + /// The kind of this inlay hint. pub kind: InlayKind, /// The actual label to show in the inlay hint. pub label: InlayHintLabel, + /// Text edit to apply when "accepting" this inlay hint. + pub text_edit: Option, } impl InlayHint { - fn closing_paren(range: TextRange) -> InlayHint { - InlayHint { range, kind: InlayKind::ClosingParenthesis, label: InlayHintLabel::from(")") } + fn closing_paren_after(kind: InlayKind, range: TextRange) -> InlayHint { + InlayHint { + range, + kind, + label: InlayHintLabel::from(")"), + text_edit: None, + position: InlayHintPosition::After, + pad_left: false, + pad_right: false, + } } - fn opening_paren(range: TextRange) -> InlayHint { - InlayHint { range, kind: InlayKind::OpeningParenthesis, label: InlayHintLabel::from("(") } + fn opening_paren_before(kind: InlayKind, range: TextRange) -> InlayHint { + InlayHint { + range, + kind, + label: InlayHintLabel::from("("), + text_edit: None, + position: InlayHintPosition::Before, + pad_left: false, + pad_right: false, + } } } @@ -283,14 +311,15 @@ impl InlayHintLabelBuilder<'_> { fn label_of_ty( famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>, config: &InlayHintsConfig, - ty: hir::Type, + ty: &hir::Type, ) -> Option { fn rec( sema: &Semantics<'_, RootDatabase>, famous_defs: &FamousDefs<'_, '_>, mut max_length: Option, - ty: hir::Type, + ty: &hir::Type, label_builder: &mut InlayHintLabelBuilder<'_>, + config: &InlayHintsConfig, ) -> Result<(), HirDisplayError> { let iter_item_type = hint_iterator(sema, famous_defs, &ty); match iter_item_type { @@ -321,11 +350,14 @@ fn label_of_ty( label_builder.write_str(LABEL_ITEM)?; label_builder.end_location_link(); label_builder.write_str(LABEL_MIDDLE2)?; - rec(sema, famous_defs, max_length, ty, label_builder)?; + rec(sema, famous_defs, max_length, &ty, label_builder, config)?; label_builder.write_str(LABEL_END)?; Ok(()) } - None => ty.display_truncated(sema.db, max_length).write_to(label_builder), + None => ty + .display_truncated(sema.db, max_length) + .with_closure_style(config.closure_style) + .write_to(label_builder), } } @@ -335,11 +367,28 @@ fn label_of_ty( location: None, result: InlayHintLabel::default(), }; - let _ = rec(sema, famous_defs, config.max_length, ty, &mut label_builder); + let _ = rec(sema, famous_defs, config.max_length, ty, &mut label_builder, config); let r = label_builder.finish(); Some(r) } +fn ty_to_text_edit( + sema: &Semantics<'_, RootDatabase>, + node_for_hint: &SyntaxNode, + ty: &hir::Type, + offset_to_insert: TextSize, + prefix: String, +) -> Option { + let scope = sema.scope(node_for_hint)?; + // FIXME: Limit the length and bail out on excess somehow? + let rendered = ty.display_source_code(scope.db, scope.module().into(), false).ok()?; + + let mut builder = TextEdit::builder(); + builder.insert(offset_to_insert, prefix); + builder.insert(offset_to_insert, rendered); + Some(builder.finish()) +} + // Feature: Inlay Hints // // rust-analyzer shows additional information inline with the source code. @@ -408,10 +457,10 @@ fn hints( ast::Expr::MethodCallExpr(it) => { param_name::hints(hints, sema, config, ast::Expr::from(it)) } - ast::Expr::ClosureExpr(it) => closure_ret::hints(hints, 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::ClosureExpr(it) => { + closure_captures::hints(hints, famous_defs, config, file_id, it.clone()); + closure_ret::hints(hints, famous_defs, config, file_id, it) + }, _ => None, } }, @@ -481,6 +530,7 @@ fn closure_has_block_body(closure: &ast::ClosureExpr) -> bool { #[cfg(test)] mod tests { use expect_test::Expect; + use hir::ClosureStyle; use itertools::Itertools; use test_utils::extract_annotations; @@ -498,12 +548,14 @@ mod tests { chaining_hints: false, lifetime_elision_hints: LifetimeElisionHints::Never, closure_return_type_hints: ClosureReturnTypeHints::Never, + closure_capture_hints: false, adjustment_hints: AdjustmentHints::Never, adjustment_hints_mode: AdjustmentHintsMode::Prefix, adjustment_hints_hide_outside_unsafe: false, binding_mode_hints: false, hide_named_constructor_hints: false, hide_closure_initialization_hints: false, + closure_style: ClosureStyle::ImplFn, param_names_for_lifetime_elision_hints: false, max_length: None, closing_brace_hints_min_lines: None, @@ -530,7 +582,8 @@ mod tests { let inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap(); let actual = inlay_hints .into_iter() - .map(|it| (it.range, it.label.to_string())) + // FIXME: We trim the start because some inlay produces leading whitespace which is not properly supported by our annotation extraction + .map(|it| (it.range, it.label.to_string().trim_start().to_owned())) .sorted_by_key(|(range, _)| range.start()) .collect::>(); expected.sort_by_key(|(range, _)| range.start()); @@ -545,6 +598,37 @@ mod tests { expect.assert_debug_eq(&inlay_hints) } + /// Computes inlay hints for the fixture, applies all the provided text edits and then runs + /// expect test. + #[track_caller] + pub(super) fn check_edit(config: InlayHintsConfig, ra_fixture: &str, expect: Expect) { + let (analysis, file_id) = fixture::file(ra_fixture); + let inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap(); + + let edits = inlay_hints + .into_iter() + .filter_map(|hint| hint.text_edit) + .reduce(|mut acc, next| { + acc.union(next).expect("merging text edits failed"); + acc + }) + .expect("no edit returned"); + + let mut actual = analysis.file_text(file_id).unwrap().to_string(); + edits.apply(&mut actual); + expect.assert_eq(&actual); + } + + #[track_caller] + pub(super) fn check_no_edit(config: InlayHintsConfig, ra_fixture: &str) { + let (analysis, file_id) = fixture::file(ra_fixture); + let inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap(); + + let edits: Vec<_> = inlay_hints.into_iter().filter_map(|hint| hint.text_edit).collect(); + + assert!(edits.is_empty(), "unexpected edits: {edits:?}"); + } + #[test] fn hints_disabled() { check_with_config( diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs index 46505b3044109..10bee2a6accfe 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs @@ -3,7 +3,11 @@ //! let _: u32 = /* */ loop {}; //! let _: &u32 = /* &* */ &mut 0; //! ``` -use hir::{Adjust, Adjustment, AutoBorrow, HirDisplay, Mutability, PointerCast, Safety, Semantics}; +use either::Either; +use hir::{ + Adjust, Adjustment, AutoBorrow, HirDisplay, Mutability, OverloadedDeref, PointerCast, Safety, + Semantics, +}; use ide_db::RootDatabase; use stdx::never; @@ -13,8 +17,8 @@ use syntax::{ }; use crate::{ - AdjustmentHints, AdjustmentHintsMode, InlayHint, InlayHintLabel, InlayHintsConfig, InlayKind, - InlayTooltip, + AdjustmentHints, AdjustmentHintsMode, InlayHint, InlayHintLabel, InlayHintPosition, + InlayHintsConfig, InlayKind, InlayTooltip, }; pub(super) fn hints( @@ -60,22 +64,26 @@ pub(super) fn hints( mode_and_needs_parens_for_adjustment_hints(expr, config.adjustment_hints_mode); if needs_outer_parens { - acc.push(InlayHint::opening_paren(expr.syntax().text_range())); + acc.push(InlayHint::opening_paren_before( + InlayKind::Adjustment, + expr.syntax().text_range(), + )); } if postfix && needs_inner_parens { - acc.push(InlayHint::opening_paren(expr.syntax().text_range())); - acc.push(InlayHint::closing_paren(expr.syntax().text_range())); + acc.push(InlayHint::opening_paren_before( + InlayKind::Adjustment, + expr.syntax().text_range(), + )); + acc.push(InlayHint::closing_paren_after(InlayKind::Adjustment, expr.syntax().text_range())); } - let (mut tmp0, mut tmp1); - let iter: &mut dyn Iterator = if postfix { - tmp0 = adjustments.into_iter(); - &mut tmp0 + let mut iter = if postfix { + Either::Left(adjustments.into_iter()) } else { - tmp1 = adjustments.into_iter().rev(); - &mut tmp1 + Either::Right(adjustments.into_iter().rev()) }; + let iter: &mut dyn Iterator = iter.as_mut().either(|it| it as _, |it| it as _); for Adjustment { source, target, kind } in iter { if source == target { @@ -88,7 +96,13 @@ pub(super) fn hints( Adjust::NeverToAny if config.adjustment_hints == AdjustmentHints::Always => { ("", "never to any") } - Adjust::Deref(_) => ("*", "dereference"), + Adjust::Deref(None) => ("*", "dereference"), + Adjust::Deref(Some(OverloadedDeref(Mutability::Shared))) => { + ("*", "`Deref` dereference") + } + Adjust::Deref(Some(OverloadedDeref(Mutability::Mut))) => { + ("*", "`DerefMut` dereference") + } Adjust::Borrow(AutoBorrow::Ref(Mutability::Shared)) => ("&", "borrow"), Adjust::Borrow(AutoBorrow::Ref(Mutability::Mut)) => ("&mut ", "unique borrow"), Adjust::Borrow(AutoBorrow::RawPtr(Mutability::Shared)) => { @@ -125,7 +139,10 @@ pub(super) fn hints( }; acc.push(InlayHint { range: expr.syntax().text_range(), - kind: if postfix { InlayKind::AdjustmentPostfix } else { InlayKind::Adjustment }, + pad_left: false, + pad_right: false, + position: if postfix { InlayHintPosition::After } else { InlayHintPosition::Before }, + kind: InlayKind::Adjustment, label: InlayHintLabel::simple( if postfix { format!(".{}", text.trim_end()) } else { text.to_owned() }, Some(InlayTooltip::Markdown(format!( @@ -135,19 +152,23 @@ pub(super) fn hints( ))), None, ), + text_edit: None, }); } if !postfix && needs_inner_parens { - acc.push(InlayHint::opening_paren(expr.syntax().text_range())); - acc.push(InlayHint::closing_paren(expr.syntax().text_range())); + acc.push(InlayHint::opening_paren_before( + InlayKind::Adjustment, + expr.syntax().text_range(), + )); + acc.push(InlayHint::closing_paren_after(InlayKind::Adjustment, expr.syntax().text_range())); } if needs_outer_parens { - acc.push(InlayHint::closing_paren(expr.syntax().text_range())); + acc.push(InlayHint::closing_paren_after(InlayKind::Adjustment, expr.syntax().text_range())); } Some(()) } -/// Returns whatever the hint should be postfix and if we need to add paretheses on the inside and/or outside of `expr`, +/// Returns whatever the hint should be postfix and if we need to add parentheses on the inside and/or outside of `expr`, /// if we are going to add (`postfix`) adjustments hints to it. fn mode_and_needs_parens_for_adjustment_hints( expr: &ast::Expr, @@ -182,7 +203,7 @@ fn mode_and_needs_parens_for_adjustment_hints( } } -/// Returns whatever we need to add paretheses on the inside and/or outside of `expr`, +/// Returns whatever we need to add parentheses on the inside and/or outside of `expr`, /// if we are going to add (`postfix`) adjustments hints to it. fn needs_parens_for_adjustment_hints(expr: &ast::Expr, postfix: bool) -> (bool, bool) { // This is a very miserable pile of hacks... @@ -193,10 +214,10 @@ fn needs_parens_for_adjustment_hints(expr: &ast::Expr, postfix: bool) -> (bool, // But we want to check what would happen if we add `*`/`.*` to the inner expression. // To check for inner we need `` expr.needs_parens_in(`*expr`) ``, // to check for outer we need `` `*expr`.needs_parens_in(parent) ``, - // where "expr" is the `expr` parameter, `*expr` is the editted `expr`, + // where "expr" is the `expr` parameter, `*expr` is the edited `expr`, // and "parent" is the parent of the original expression... // - // For this we utilize mutable mutable trees, which is a HACK, but it works. + // For this we utilize mutable trees, which is a HACK, but it works. // // FIXME: comeup with a better API for `needs_parens_in`, so that we don't have to do *this* @@ -242,7 +263,7 @@ fn needs_parens_for_adjustment_hints(expr: &ast::Expr, postfix: bool) -> (bool, }; // At this point - // - `parent` is the parrent of the original expression + // - `parent` is the parent of the original expression // - `dummy_expr` is the original expression wrapped in the operator we want (`*`/`.*`) // - `expr` is the clone of the original expression (with `dummy_expr` as the parent) @@ -264,7 +285,7 @@ mod tests { check_with_config( InlayHintsConfig { adjustment_hints: AdjustmentHints::Always, ..DISABLED_CONFIG }, r#" -//- minicore: coerce_unsized, fn, eq +//- minicore: coerce_unsized, fn, eq, index fn main() { let _: u32 = loop {}; //^^^^^^^ @@ -315,6 +336,8 @@ fn main() { (&Struct).consume(); //^^^^^^^* (&Struct).by_ref(); + //^^^^^^^& + //^^^^^^^* (&mut Struct).consume(); //^^^^^^^^^^^* @@ -322,6 +345,8 @@ fn main() { //^^^^^^^^^^^& //^^^^^^^^^^^* (&mut Struct).by_ref_mut(); + //^^^^^^^^^^^&mut $ + //^^^^^^^^^^^* // Check that block-like expressions don't duplicate hints let _: &mut [u32] = (&mut []); @@ -360,6 +385,19 @@ fn main() { (()) == {()}; // ^^& // ^^^^& + let closure: dyn Fn = || (); + closure(); + //^^^^^^^( + //^^^^^^^& + //^^^^^^^) + Struct[0]; + //^^^^^^( + //^^^^^^& + //^^^^^^) + &mut Struct[0]; + //^^^^^^( + //^^^^^^&mut $ + //^^^^^^) } #[derive(Copy, Clone)] @@ -369,8 +407,13 @@ impl Struct { fn by_ref(&self) {} fn by_ref_mut(&mut self) {} } +struct StructMut; +impl core::ops::Index for Struct { + type Output = (); +} +impl core::ops::IndexMut for Struct {} "#, - ) + ); } #[test] @@ -382,7 +425,7 @@ impl Struct { ..DISABLED_CONFIG }, r#" -//- minicore: coerce_unsized, fn, eq +//- minicore: coerce_unsized, fn, eq, index fn main() { Struct.consume(); @@ -396,6 +439,10 @@ fn main() { //^^^^^^^) //^^^^^^^.* (&Struct).by_ref(); + //^^^^^^^( + //^^^^^^^) + //^^^^^^^.* + //^^^^^^^.& (&mut Struct).consume(); //^^^^^^^^^^^( @@ -407,6 +454,10 @@ fn main() { //^^^^^^^^^^^.* //^^^^^^^^^^^.& (&mut Struct).by_ref_mut(); + //^^^^^^^^^^^( + //^^^^^^^^^^^) + //^^^^^^^^^^^.* + //^^^^^^^^^^^.&mut // Check that block-like expressions don't duplicate hints let _: &mut [u32] = (&mut []); @@ -457,6 +508,13 @@ fn main() { (()) == {()}; // ^^.& // ^^^^.& + let closure: dyn Fn = || (); + closure(); + //^^^^^^^.& + Struct[0]; + //^^^^^^.& + &mut Struct[0]; + //^^^^^^.&mut } #[derive(Copy, Clone)] @@ -466,6 +524,11 @@ impl Struct { fn by_ref(&self) {} fn by_ref_mut(&mut self) {} } +struct StructMut; +impl core::ops::Index for Struct { + type Output = (); +} +impl core::ops::IndexMut for Struct {} "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs index 6a50927333d4f..07b9f9cc1fff6 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs @@ -3,7 +3,7 @@ //! fn f(a: i32, b: i32) -> i32 { a + b } //! let _x /* i32 */= f(4, 4); //! ``` -use hir::{Semantics, TypeInfo}; +use hir::Semantics; use ide_db::{base_db::FileId, famous_defs::FamousDefs, RootDatabase}; use itertools::Itertools; @@ -12,9 +12,10 @@ use syntax::{ match_ast, }; -use crate::{inlay_hints::closure_has_block_body, InlayHint, InlayHintsConfig, InlayKind}; - -use super::label_of_ty; +use crate::{ + inlay_hints::{closure_has_block_body, label_of_ty, ty_to_text_edit}, + InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind, +}; pub(super) fn hints( acc: &mut Vec, @@ -27,15 +28,45 @@ pub(super) fn hints( return None; } + let parent = pat.syntax().parent()?; + let type_ascriptable = match_ast! { + match parent { + ast::Param(it) => { + if it.ty().is_some() { + return None; + } + Some(it.colon_token()) + }, + ast::LetStmt(it) => { + if config.hide_closure_initialization_hints { + if let Some(ast::Expr::ClosureExpr(closure)) = it.initializer() { + if closure_has_block_body(&closure) { + return None; + } + } + } + if it.ty().is_some() { + return None; + } + Some(it.colon_token()) + }, + _ => None + } + }; + let descended = sema.descend_node_into_attributes(pat.clone()).pop(); let desc_pat = descended.as_ref().unwrap_or(pat); - let ty = sema.type_of_pat(&desc_pat.clone().into())?.original; + let ty = sema.type_of_binding_in_pat(desc_pat)?; - if should_not_display_type_hint(sema, config, pat, &ty) { + if ty.is_unknown() { return None; } - let label = label_of_ty(famous_defs, config, ty)?; + if sema.resolve_bind_pat_to_const(pat).is_some() { + return None; + } + + let mut label = label_of_ty(famous_defs, config, &ty)?; if config.hide_named_constructor_hints && is_named_constructor(sema, pat, &label.to_string()).is_some() @@ -43,69 +74,46 @@ pub(super) fn hints( return None; } + let text_edit = if let Some(colon_token) = &type_ascriptable { + ty_to_text_edit( + sema, + desc_pat.syntax(), + &ty, + colon_token + .as_ref() + .map_or_else(|| pat.syntax().text_range(), |t| t.text_range()) + .end(), + if colon_token.is_some() { String::new() } else { String::from(": ") }, + ) + } else { + None + }; + + let render_colons = config.render_colons && !matches!(type_ascriptable, Some(Some(_))); + if render_colons { + label.prepend_str(": "); + } + + let text_range = match pat.name() { + Some(name) => name.syntax().text_range(), + None => pat.syntax().text_range(), + }; acc.push(InlayHint { - range: match pat.name() { - Some(name) => name.syntax().text_range(), - None => pat.syntax().text_range(), + range: match type_ascriptable { + Some(Some(t)) => text_range.cover(t.text_range()), + _ => text_range, }, kind: InlayKind::Type, label, + text_edit, + position: InlayHintPosition::After, + pad_left: !render_colons, + pad_right: false, }); Some(()) } -fn should_not_display_type_hint( - sema: &Semantics<'_, RootDatabase>, - config: &InlayHintsConfig, - bind_pat: &ast::IdentPat, - pat_ty: &hir::Type, -) -> bool { - let db = sema.db; - - if pat_ty.is_unknown() { - return true; - } - - if sema.resolve_bind_pat_to_const(bind_pat).is_some() { - return true; - } - - for node in bind_pat.syntax().ancestors() { - match_ast! { - match node { - ast::LetStmt(it) => { - if config.hide_closure_initialization_hints { - if let Some(ast::Expr::ClosureExpr(closure)) = it.initializer() { - if closure_has_block_body(&closure) { - return true; - } - } - } - return it.ty().is_some() - }, - // FIXME: We might wanna show type hints in parameters for non-top level patterns as well - ast::Param(it) => return it.ty().is_some(), - ast::MatchArm(_) => return pat_is_enum_variant(db, bind_pat, pat_ty), - ast::LetExpr(_) => return pat_is_enum_variant(db, bind_pat, pat_ty), - ast::IfExpr(_) => return false, - ast::WhileExpr(_) => return false, - ast::ForExpr(it) => { - // We *should* display hint only if user provided "in {expr}" and we know the type of expr (and it's not unit). - // Type of expr should be iterable. - return it.in_token().is_none() || - it.iterable() - .and_then(|iterable_expr| sema.type_of_expr(&iterable_expr)) - .map(TypeInfo::original) - .map_or(true, |iterable_ty| iterable_ty.is_unknown() || iterable_ty.is_unit()) - }, - _ => (), - } - } - } - false -} - fn is_named_constructor( sema: &Semantics<'_, RootDatabase>, pat: &ast::IdentPat, @@ -159,30 +167,20 @@ fn is_named_constructor( (ctor_name == ty_name).then_some(()) } -fn pat_is_enum_variant(db: &RootDatabase, bind_pat: &ast::IdentPat, pat_ty: &hir::Type) -> bool { - if let Some(hir::Adt::Enum(enum_data)) = pat_ty.as_adt() { - let pat_text = bind_pat.to_string(); - enum_data - .variants(db) - .into_iter() - .map(|variant| variant.name(db).to_smol_str()) - .any(|enum_name| enum_name == pat_text) - } else { - false - } -} - #[cfg(test)] mod tests { // This module also contains tests for super::closure_ret + use expect_test::expect; + use hir::ClosureStyle; use syntax::{TextRange, TextSize}; use test_utils::extract_annotations; - use crate::{fixture, inlay_hints::InlayHintsConfig}; + use crate::{fixture, inlay_hints::InlayHintsConfig, ClosureReturnTypeHints}; - use crate::inlay_hints::tests::{check, check_with_config, DISABLED_CONFIG, TEST_CONFIG}; - use crate::ClosureReturnTypeHints; + use crate::inlay_hints::tests::{ + check, check_edit, check_no_edit, check_with_config, DISABLED_CONFIG, TEST_CONFIG, + }; #[track_caller] fn check_types(ra_fixture: &str) { @@ -235,7 +233,7 @@ fn main() { let zz_ref = &zz; //^^^^^^ &Test let test = || zz; - //^^^^ || -> Test + //^^^^ impl FnOnce() -> Test }"#, ); } @@ -528,24 +526,7 @@ fn main() { struct Test { a: Option, b: u8 } fn main() { - let test = Some(Test { a: Some(3), b: 1 }); - //^^^^ Option - if let None = &test {}; - if let test = &test {}; - //^^^^ &Option - if let Some(test) = &test {}; - //^^^^ &Test - if let Some(Test { a, b }) = &test {}; - //^ &Option ^ &u8 - if let Some(Test { a: x, b: y }) = &test {}; - //^ &Option ^ &u8 - if let Some(Test { a: Some(x), b: y }) = &test {}; - //^ &u32 ^ &u8 - if let Some(Test { a: None, b: y }) = &test {}; - //^ &u8 - if let Some(Test { b: y, .. }) = &test {}; - //^ &u8 - if test == None {} + }"#, ); } @@ -560,8 +541,8 @@ struct Test { a: Option, b: u8 } fn main() { let test = Some(Test { a: Some(3), b: 1 }); //^^^^ Option - while let Some(Test { a: Some(x), b: y }) = &test {}; - //^ &u32 ^ &u8 + while let Some(Test { a: Some(x), b: y }) = &test {}; + //^ &u32 ^ &u8 }"#, ); } @@ -753,7 +734,7 @@ fn main() { let func = times2; // ^^^^ fn times2(i32) -> i32 let closure = |x: i32| x * 2; - // ^^^^^^^ |i32| -> i32 + // ^^^^^^^ impl Fn(i32) -> i32 } fn fallible() -> ControlFlow<()> { @@ -811,49 +792,90 @@ fn fallible() -> ControlFlow<()> { } #[test] - fn closures() { - check( + fn closure_style() { + check_with_config( + InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG }, r#" +//- minicore: fn fn main() { - let mut start = 0; - //^^^^^ i32 - (0..2).for_each(|increment | { start += increment; }); - //^^^^^^^^^ i32 - - let multiply = - //^^^^^^^^ |i32, i32| -> i32 - | a, b| a * b - //^ i32 ^ i32 - - ; - - let _: i32 = multiply(1, 2); - //^ a ^ b - let multiply_ref = &multiply; - //^^^^^^^^^^^^ &|i32, i32| -> i32 - - let return_42 = || 42; - //^^^^^^^^^ || -> i32 - || { 42 }; - //^^ i32 -}"#, + let x = || 2; + //^ impl Fn() -> i32 + let y = |t: i32| x() + t; + //^ impl Fn(i32) -> i32 + let mut t = 5; + //^ i32 + let z = |k: i32| { t += k; }; + //^ impl FnMut(i32) + let p = (y, z); + //^ (impl Fn(i32) -> i32, impl FnMut(i32)) +} + "#, ); - } - - #[test] - fn return_type_hints_for_closure_without_block() { check_with_config( InlayHintsConfig { - closure_return_type_hints: ClosureReturnTypeHints::Always, + type_hints: true, + closure_style: ClosureStyle::RANotation, ..DISABLED_CONFIG }, r#" +//- minicore: fn fn main() { - let a = || { 0 }; - //^^ i32 - let b = || 0; - //^^ i32 -}"#, + let x = || 2; + //^ || -> i32 + let y = |t: i32| x() + t; + //^ |i32| -> i32 + let mut t = 5; + //^ i32 + let z = |k: i32| { t += k; }; + //^ |i32| -> () + let p = (y, z); + //^ (|i32| -> i32, |i32| -> ()) +} + "#, + ); + check_with_config( + InlayHintsConfig { + type_hints: true, + closure_style: ClosureStyle::ClosureWithId, + ..DISABLED_CONFIG + }, + r#" +//- minicore: fn +fn main() { + let x = || 2; + //^ {closure#0} + let y = |t: i32| x() + t; + //^ {closure#1} + let mut t = 5; + //^ i32 + let z = |k: i32| { t += k; }; + //^ {closure#2} + let p = (y, z); + //^ ({closure#1}, {closure#2}) +} + "#, + ); + check_with_config( + InlayHintsConfig { + type_hints: true, + closure_style: ClosureStyle::Hide, + ..DISABLED_CONFIG + }, + r#" +//- minicore: fn +fn main() { + let x = || 2; + //^ … + let y = |t: i32| x() + t; + //^ … + let mut t = 5; + //^ i32 + let z = |k: i32| { t += k; }; + //^ … + let p = (y, z); + //^ (…, …) +} + "#, ); } @@ -871,13 +893,13 @@ fn main() { let multiple_2 = |x: i32| { x * 2 }; let multiple_2 = |x: i32| x * 2; - // ^^^^^^^^^^ |i32| -> i32 + // ^^^^^^^^^^ impl Fn(i32) -> i32 let (not) = (|x: bool| { !x }); - // ^^^ |bool| -> bool + // ^^^ impl Fn(bool) -> bool let (is_zero, _b) = (|x: usize| { x == 0 }, false); - // ^^^^^^^ |usize| -> bool + // ^^^^^^^ impl Fn(usize) -> bool // ^^ bool let plus_one = |x| { x + 1 }; @@ -923,4 +945,160 @@ fn main() { }"#, ); } + + #[test] + fn edit_for_let_stmt() { + check_edit( + TEST_CONFIG, + r#" +struct S(T); +fn test(v: S<(S, S<()>)>, f: F) { + let a = v; + let S((b, c)) = v; + let a @ S((b, c)) = v; + let a = f; +} +"#, + expect![[r#" + struct S(T); + fn test(v: S<(S, S<()>)>, f: F) { + let a: S<(S, S<()>)> = v; + let S((b, c)) = v; + let a @ S((b, c)): S<(S, S<()>)> = v; + let a: F = f; + } + "#]], + ); + } + + #[test] + fn edit_for_closure_param() { + check_edit( + TEST_CONFIG, + r#" +fn test(t: T) { + let f = |a, b, c| {}; + let result = f(42, "", t); +} +"#, + expect![[r#" + fn test(t: T) { + let f = |a: i32, b: &str, c: T| {}; + let result: () = f(42, "", t); + } + "#]], + ); + } + + #[test] + fn edit_for_closure_ret() { + check_edit( + TEST_CONFIG, + r#" +struct S(T); +fn test() { + let f = || { 3 }; + let f = |a: S| { S(a) }; +} +"#, + expect![[r#" + struct S(T); + fn test() { + let f = || -> i32 { 3 }; + let f = |a: S| -> S> { S(a) }; + } + "#]], + ); + } + + #[test] + fn edit_prefixes_paths() { + check_edit( + TEST_CONFIG, + r#" +pub struct S(T); +mod middle { + pub struct S(T, U); + pub fn make() -> S, super::S> { loop {} } + + mod inner { + pub struct S(T); + } + + fn test() { + let a = make(); + } +} +"#, + expect![[r#" + pub struct S(T); + mod middle { + pub struct S(T, U); + pub fn make() -> S, super::S> { loop {} } + + mod inner { + pub struct S(T); + } + + fn test() { + let a: S, crate::S> = make(); + } + } + "#]], + ); + } + + #[test] + fn no_edit_for_top_pat_where_type_annotation_is_invalid() { + check_no_edit( + TEST_CONFIG, + r#" +fn test() { + if let a = 42 {} + while let a = 42 {} + match 42 { + a => (), + } +} +"#, + ) + } + + #[test] + fn no_edit_for_opaque_type() { + check_no_edit( + TEST_CONFIG, + r#" +trait Trait {} +struct S(T); +fn foo() -> impl Trait {} +fn bar() -> S {} +fn test() { + let a = foo(); + let a = bar(); + let f = || { foo() }; + let f = || { bar() }; +} +"#, + ); + } + + #[test] + fn no_edit_for_closure_return_without_body_block() { + // We can lift this limitation; see FIXME in closure_ret module. + let config = InlayHintsConfig { + closure_return_type_hints: ClosureReturnTypeHints::Always, + ..TEST_CONFIG + }; + check_no_edit( + config, + r#" +struct S(T); +fn test() { + let f = || 3; + let f = |a: S| S(a); +} +"#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs index 5d9729263c270..343cf17e50e0c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs @@ -7,7 +7,7 @@ use ide_db::RootDatabase; use syntax::ast::{self, AstNode}; -use crate::{InlayHint, InlayHintsConfig, InlayKind}; +use crate::{InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind}; pub(super) fn hints( acc: &mut Vec, @@ -49,7 +49,15 @@ pub(super) fn hints( (true, false) => "&", _ => return, }; - acc.push(InlayHint { range, kind: InlayKind::BindingMode, label: r.to_string().into() }); + acc.push(InlayHint { + range, + kind: InlayKind::BindingMode, + label: r.to_string().into(), + text_edit: None, + position: InlayHintPosition::Before, + pad_left: false, + pad_right: mut_reference, + }); }); match pat { ast::Pat::IdentPat(pat) if pat.ref_token().is_none() && pat.mut_token().is_none() => { @@ -63,11 +71,21 @@ pub(super) fn hints( range: pat.syntax().text_range(), kind: InlayKind::BindingMode, label: bm.to_string().into(), + text_edit: None, + position: InlayHintPosition::Before, + pad_left: false, + pad_right: true, }); } ast::Pat::OrPat(pat) if !pattern_adjustments.is_empty() && outer_paren_pat.is_none() => { - acc.push(InlayHint::opening_paren(pat.syntax().text_range())); - acc.push(InlayHint::closing_paren(pat.syntax().text_range())); + acc.push(InlayHint::opening_paren_before( + InlayKind::BindingMode, + pat.syntax().text_range(), + )); + acc.push(InlayHint::closing_paren_after( + InlayKind::BindingMode, + pat.syntax().text_range(), + )); } _ => (), } @@ -142,7 +160,6 @@ struct Struct { field: &'static str, } fn foo(s @ Struct { field, .. }: &Struct) {} - //^^^^^^^^^^^^^^^^^^^^^^^^ref //^^^^^^^^^^^^^^^^^^^^& //^^^^^ref "#, diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs index 1e1771259b1ba..ce1e03a069b24 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs @@ -5,7 +5,7 @@ use syntax::{ Direction, NodeOrToken, SyntaxKind, T, }; -use crate::{FileId, InlayHint, InlayHintsConfig, InlayKind}; +use crate::{FileId, InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind}; use super::label_of_ty; @@ -60,7 +60,11 @@ pub(super) fn hints( acc.push(InlayHint { range: expr.syntax().text_range(), kind: InlayKind::Chaining, - label: label_of_ty(famous_defs, config, ty)?, + label: label_of_ty(famous_defs, config, &ty)?, + text_edit: None, + position: InlayHintPosition::After, + pad_left: true, + pad_right: false, }); } } @@ -103,6 +107,9 @@ fn main() { [ InlayHint { range: 147..172, + position: After, + pad_left: true, + pad_right: false, kind: Chaining, label: [ "", @@ -120,9 +127,13 @@ fn main() { }, "", ], + text_edit: None, }, InlayHint { range: 147..154, + position: After, + pad_left: true, + pad_right: false, kind: Chaining, label: [ "", @@ -140,6 +151,7 @@ fn main() { }, "", ], + text_edit: None, }, ] "#]], @@ -188,6 +200,9 @@ fn main() { [ InlayHint { range: 143..190, + position: After, + pad_left: true, + pad_right: false, kind: Chaining, label: [ "", @@ -205,9 +220,13 @@ fn main() { }, "", ], + text_edit: None, }, InlayHint { range: 143..179, + position: After, + pad_left: true, + pad_right: false, kind: Chaining, label: [ "", @@ -225,6 +244,7 @@ fn main() { }, "", ], + text_edit: None, }, ] "#]], @@ -257,6 +277,9 @@ fn main() { [ InlayHint { range: 143..190, + position: After, + pad_left: true, + pad_right: false, kind: Chaining, label: [ "", @@ -274,9 +297,13 @@ fn main() { }, "", ], + text_edit: None, }, InlayHint { range: 143..179, + position: After, + pad_left: true, + pad_right: false, kind: Chaining, label: [ "", @@ -294,6 +321,7 @@ fn main() { }, "", ], + text_edit: None, }, ] "#]], @@ -327,6 +355,9 @@ fn main() { [ InlayHint { range: 246..283, + position: After, + pad_left: true, + pad_right: false, kind: Chaining, label: [ "", @@ -357,9 +388,13 @@ fn main() { }, ">", ], + text_edit: None, }, InlayHint { range: 246..265, + position: After, + pad_left: true, + pad_right: false, kind: Chaining, label: [ "", @@ -390,6 +425,7 @@ fn main() { }, ">", ], + text_edit: None, }, ] "#]], @@ -425,6 +461,9 @@ fn main() { [ InlayHint { range: 174..241, + position: After, + pad_left: true, + pad_right: false, kind: Chaining, label: [ "impl ", @@ -435,7 +474,7 @@ fn main() { file_id: FileId( 1, ), - range: 3415..3423, + range: 9332..9340, }, ), tooltip: "", @@ -448,16 +487,20 @@ fn main() { file_id: FileId( 1, ), - range: 3447..3451, + range: 9364..9368, }, ), tooltip: "", }, " = ()>", ], + text_edit: None, }, InlayHint { range: 174..224, + position: After, + pad_left: true, + pad_right: false, kind: Chaining, label: [ "impl ", @@ -468,7 +511,7 @@ fn main() { file_id: FileId( 1, ), - range: 3415..3423, + range: 9332..9340, }, ), tooltip: "", @@ -481,16 +524,20 @@ fn main() { file_id: FileId( 1, ), - range: 3447..3451, + range: 9364..9368, }, ), tooltip: "", }, " = ()>", ], + text_edit: None, }, InlayHint { range: 174..206, + position: After, + pad_left: true, + pad_right: false, kind: Chaining, label: [ "impl ", @@ -501,7 +548,7 @@ fn main() { file_id: FileId( 1, ), - range: 3415..3423, + range: 9332..9340, }, ), tooltip: "", @@ -514,16 +561,20 @@ fn main() { file_id: FileId( 1, ), - range: 3447..3451, + range: 9364..9368, }, ), tooltip: "", }, " = ()>", ], + text_edit: None, }, InlayHint { range: 174..189, + position: After, + pad_left: true, + pad_right: false, kind: Chaining, label: [ "&mut ", @@ -541,6 +592,7 @@ fn main() { }, "", ], + text_edit: None, }, ] "#]], @@ -573,6 +625,9 @@ fn main() { [ InlayHint { range: 124..130, + position: After, + pad_left: true, + pad_right: false, kind: Type, label: [ "", @@ -590,9 +645,22 @@ fn main() { }, "", ], + text_edit: Some( + TextEdit { + indels: [ + Indel { + insert: ": Struct", + delete: 130..130, + }, + ], + }, + ), }, InlayHint { range: 145..185, + position: After, + pad_left: true, + pad_right: false, kind: Chaining, label: [ "", @@ -610,9 +678,13 @@ fn main() { }, "", ], + text_edit: None, }, InlayHint { range: 145..168, + position: After, + pad_left: true, + pad_right: false, kind: Chaining, label: [ "", @@ -630,9 +702,13 @@ fn main() { }, "", ], + text_edit: None, }, InlayHint { range: 222..228, + position: Before, + pad_left: false, + pad_right: true, kind: Parameter, label: [ InlayHintLabelPart { @@ -648,6 +724,7 @@ fn main() { tooltip: "", }, ], + text_edit: None, }, ] "#]], diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs index 14c11be54ef54..2cefd5acdc2e4 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs @@ -10,7 +10,7 @@ use syntax::{ match_ast, SyntaxKind, SyntaxNode, T, }; -use crate::{FileId, InlayHint, InlayHintLabel, InlayHintsConfig, InlayKind}; +use crate::{FileId, InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind}; pub(super) fn hints( acc: &mut Vec, @@ -35,7 +35,7 @@ pub(super) fn hints( let ty = imp.self_ty(sema.db); let trait_ = imp.trait_(sema.db); let hint_text = match trait_ { - Some(tr) => format!("impl {} for {}", tr.name(sema.db), ty.display_truncated(sema.db, config.max_length)), + Some(tr) => format!("impl {} for {}", tr.name(sema.db).display(sema.db), ty.display_truncated(sema.db, config.max_length)), None => format!("impl {}", ty.display_truncated(sema.db, config.max_length)), }; (hint_text, None) @@ -112,6 +112,10 @@ pub(super) fn hints( range: closing_token.text_range(), kind: InlayKind::ClosingBrace, label: InlayHintLabel::simple(label, None, linked_location), + text_edit: None, + position: InlayHintPosition::After, + pad_left: true, + pad_right: false, }); None diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs new file mode 100644 index 0000000000000..9d5defcbb71a9 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs @@ -0,0 +1,207 @@ +//! Implementation of "closure return type" inlay hints. +//! +//! Tests live in [`bind_pat`][super::bind_pat] module. +use ide_db::{base_db::FileId, famous_defs::FamousDefs}; +use syntax::ast::{self, AstNode}; +use text_edit::{TextRange, TextSize}; + +use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind}; + +pub(super) fn hints( + acc: &mut Vec, + FamousDefs(sema, _): &FamousDefs<'_, '_>, + config: &InlayHintsConfig, + _file_id: FileId, + closure: ast::ClosureExpr, +) -> Option<()> { + if !config.closure_capture_hints { + return None; + } + let ty = &sema.type_of_expr(&closure.clone().into())?.original; + let c = ty.as_closure()?; + let captures = c.captured_items(sema.db); + + if captures.is_empty() { + return None; + } + + let move_kw_range = match closure.move_token() { + Some(t) => t.text_range(), + None => { + let range = closure.syntax().first_token()?.prev_token()?.text_range(); + let range = TextRange::new(range.end() - TextSize::from(1), range.end()); + acc.push(InlayHint { + range, + kind: InlayKind::ClosureCapture, + label: InlayHintLabel::simple("move", None, None), + text_edit: None, + position: InlayHintPosition::After, + pad_left: false, + pad_right: false, + }); + range + } + }; + acc.push(InlayHint { + range: move_kw_range, + kind: InlayKind::ClosureCapture, + label: InlayHintLabel::from("("), + text_edit: None, + position: InlayHintPosition::After, + pad_left: false, + pad_right: false, + }); + let last = captures.len() - 1; + for (idx, capture) in captures.into_iter().enumerate() { + let local = capture.local(); + let source = local.primary_source(sema.db); + + // force cache the source file, otherwise sema lookup will potentially panic + _ = sema.parse_or_expand(source.file()); + + acc.push(InlayHint { + range: move_kw_range, + kind: InlayKind::ClosureCapture, + label: InlayHintLabel::simple( + format!( + "{}{}", + match capture.kind() { + hir::CaptureKind::SharedRef => "&", + hir::CaptureKind::UniqueSharedRef => "&unique ", + hir::CaptureKind::MutableRef => "&mut ", + hir::CaptureKind::Move => "", + }, + capture.display_place(sema.db) + ), + None, + source.name().and_then(|name| name.syntax().original_file_range_opt(sema.db)), + ), + text_edit: None, + position: InlayHintPosition::After, + pad_left: false, + pad_right: false, + }); + + if idx != last { + acc.push(InlayHint { + range: move_kw_range, + kind: InlayKind::ClosureCapture, + label: InlayHintLabel::simple(", ", None, None), + text_edit: None, + position: InlayHintPosition::After, + pad_left: false, + pad_right: false, + }); + } + } + acc.push(InlayHint { + range: move_kw_range, + kind: InlayKind::ClosureCapture, + label: InlayHintLabel::from(")"), + text_edit: None, + position: InlayHintPosition::After, + pad_left: false, + pad_right: true, + }); + + Some(()) +} + +#[cfg(test)] +mod tests { + use crate::{ + inlay_hints::tests::{check_with_config, DISABLED_CONFIG}, + InlayHintsConfig, + }; + + #[test] + fn all_capture_kinds() { + check_with_config( + InlayHintsConfig { closure_capture_hints: true, ..DISABLED_CONFIG }, + r#" +//- minicore: copy, derive + + +#[derive(Copy, Clone)] +struct Copy; + +struct NonCopy; + +fn main() { + let foo = Copy; + let bar = NonCopy; + let mut baz = NonCopy; + let qux = &mut NonCopy; + || { +// ^ move +// ^ ( +// ^ &foo +// ^ , $ +// ^ bar +// ^ , $ +// ^ baz +// ^ , $ +// ^ qux +// ^ ) + foo; + bar; + baz; + qux; + }; + || { +// ^ move +// ^ ( +// ^ &foo +// ^ , $ +// ^ &bar +// ^ , $ +// ^ &baz +// ^ , $ +// ^ &qux +// ^ ) + &foo; + &bar; + &baz; + &qux; + }; + || { +// ^ move +// ^ ( +// ^ &mut baz +// ^ ) + &mut baz; + }; + || { +// ^ move +// ^ ( +// ^ &mut baz +// ^ , $ +// ^ &mut *qux +// ^ ) + baz = NonCopy; + *qux = NonCopy; + }; +} +"#, + ); + } + + #[test] + fn move_token() { + check_with_config( + InlayHintsConfig { closure_capture_hints: true, ..DISABLED_CONFIG }, + r#" +//- minicore: copy, derive +fn main() { + let foo = u32; + move || { +// ^^^^ ( +// ^^^^ foo +// ^^^^ ) + foo; + }; +} +"#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs index f03a18b8e960f..3b41db0f13d0a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs @@ -1,14 +1,14 @@ //! Implementation of "closure return type" inlay hints. +//! +//! Tests live in [`bind_pat`][super::bind_pat] module. use ide_db::{base_db::FileId, famous_defs::FamousDefs}; use syntax::ast::{self, AstNode}; use crate::{ - inlay_hints::closure_has_block_body, ClosureReturnTypeHints, InlayHint, InlayHintsConfig, - InlayKind, + inlay_hints::{closure_has_block_body, label_of_ty, ty_to_text_edit}, + ClosureReturnTypeHints, InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind, }; -use super::label_of_ty; - pub(super) fn hints( acc: &mut Vec, famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>, @@ -20,29 +20,81 @@ pub(super) fn hints( return None; } - if closure.ret_type().is_some() { - return None; - } + let ret_type = closure.ret_type().map(|rt| (rt.thin_arrow_token(), rt.ty().is_some())); + let arrow = match ret_type { + Some((_, true)) => return None, + Some((arrow, _)) => arrow, + None => None, + }; - if !closure_has_block_body(&closure) - && config.closure_return_type_hints == ClosureReturnTypeHints::WithBlock - { + let has_block_body = closure_has_block_body(&closure); + if !has_block_body && config.closure_return_type_hints == ClosureReturnTypeHints::WithBlock { return None; } let param_list = closure.param_list()?; let closure = sema.descend_node_into_attributes(closure).pop()?; - let ty = sema.type_of_expr(&ast::Expr::ClosureExpr(closure))?.adjusted(); + let ty = sema.type_of_expr(&ast::Expr::ClosureExpr(closure.clone()))?.adjusted(); let callable = ty.as_callable(sema.db)?; let ty = callable.return_type(); - if ty.is_unit() { + if arrow.is_none() && ty.is_unit() { return None; } + + let mut label = label_of_ty(famous_defs, config, &ty)?; + + if arrow.is_none() { + label.prepend_str(" -> "); + } + // FIXME?: We could provide text edit to insert braces for closures with non-block body. + let text_edit = if has_block_body { + ty_to_text_edit( + sema, + closure.syntax(), + &ty, + arrow + .as_ref() + .map_or_else(|| param_list.syntax().text_range(), |t| t.text_range()) + .end(), + if arrow.is_none() { String::from(" -> ") } else { String::new() }, + ) + } else { + None + }; + acc.push(InlayHint { range: param_list.syntax().text_range(), - kind: InlayKind::ClosureReturnType, - label: label_of_ty(famous_defs, config, ty)?, + kind: InlayKind::Type, + label, + text_edit, + position: InlayHintPosition::After, + pad_left: false, + pad_right: false, }); Some(()) } + +#[cfg(test)] +mod tests { + use crate::inlay_hints::tests::{check_with_config, DISABLED_CONFIG}; + + use super::*; + + #[test] + fn return_type_hints_for_closure_without_block() { + check_with_config( + InlayHintsConfig { + closure_return_type_hints: ClosureReturnTypeHints::Always, + ..DISABLED_CONFIG + }, + r#" +fn main() { + let a = || { 0 }; + //^^ -> i32 + let b = || 0; + //^^ -> i32 +}"#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs index 67eaa553ada4e..c4d2ac75cfa97 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs @@ -9,7 +9,8 @@ use ide_db::{base_db::FileId, famous_defs::FamousDefs, RootDatabase}; use syntax::ast::{self, AstNode, HasName}; use crate::{ - DiscriminantHints, InlayHint, InlayHintLabel, InlayHintsConfig, InlayKind, InlayTooltip, + DiscriminantHints, InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind, + InlayTooltip, }; pub(super) fn enum_hints( @@ -19,21 +20,23 @@ pub(super) fn enum_hints( _: FileId, enum_: ast::Enum, ) -> Option<()> { - let enabled = match config.discriminant_hints { - DiscriminantHints::Always => true, - DiscriminantHints::Fieldless => { - !sema.to_def(&enum_)?.is_data_carrying(sema.db) - || enum_.variant_list()?.variants().any(|v| v.expr().is_some()) - } - DiscriminantHints::Never => false, - }; - if !enabled { + if let DiscriminantHints::Never = config.discriminant_hints { + return None; + } + + let def = sema.to_def(&enum_)?; + let data_carrying = def.is_data_carrying(sema.db); + if matches!(config.discriminant_hints, DiscriminantHints::Fieldless) && data_carrying { + return None; + } + // data carrying enums without a primitive repr have no stable discriminants + if data_carrying && def.repr(sema.db).map_or(true, |r| r.int.is_none()) { return None; } for variant in enum_.variant_list()?.variants() { variant_hints(acc, sema, &variant); } - None + Some(()) } fn variant_hints( @@ -41,10 +44,11 @@ fn variant_hints( sema: &Semantics<'_, RootDatabase>, variant: &ast::Variant, ) -> Option<()> { - if variant.eq_token().is_some() { + if variant.expr().is_some() { return None; } + let eq_token = variant.eq_token(); let name = variant.name()?; let descended = sema.descend_node_into_attributes(variant.clone()).pop(); @@ -52,34 +56,43 @@ fn variant_hints( let v = sema.to_def(desc_pat)?; let d = v.eval(sema.db); + let range = match variant.field_list() { + Some(field_list) => name.syntax().text_range().cover(field_list.syntax().text_range()), + None => name.syntax().text_range(), + }; + let eq_ = if eq_token.is_none() { " =" } else { "" }; + let label = InlayHintLabel::simple( + match d { + Ok(x) => { + if x >= 10 { + format!("{eq_} {x} ({x:#X})") + } else { + format!("{eq_} {x}") + } + } + Err(_) => format!("{eq_} ?"), + }, + Some(InlayTooltip::String(match &d { + Ok(_) => "enum variant discriminant".into(), + Err(e) => format!("{e:?}").into(), + })), + None, + ); acc.push(InlayHint { - range: match variant.field_list() { - Some(field_list) => name.syntax().text_range().cover(field_list.syntax().text_range()), - None => name.syntax().text_range(), + range: match eq_token { + Some(t) => range.cover(t.text_range()), + _ => range, }, kind: InlayKind::Discriminant, - label: InlayHintLabel::simple( - match d { - Ok(x) => { - if x >= 10 { - format!("{x} ({x:#X})") - } else { - format!("{x}") - } - } - Err(_) => "?".into(), - }, - Some(InlayTooltip::String(match &d { - Ok(_) => "enum variant discriminant".into(), - Err(e) => format!("{e:?}").into(), - })), - None, - ), + label, + text_edit: None, + position: InlayHintPosition::After, + pad_left: false, + pad_right: false, }); Some(()) } - #[cfg(test)] mod tests { use crate::inlay_hints::{ @@ -111,30 +124,30 @@ mod tests { check_discriminants( r#" enum Enum { - Variant, -//^^^^^^^0 - Variant1, -//^^^^^^^^1 - Variant2, -//^^^^^^^^2 - Variant5 = 5, - Variant6, -//^^^^^^^^6 + Variant, +// ^^^^^^^ = 0$ + Variant1, +// ^^^^^^^^ = 1$ + Variant2, +// ^^^^^^^^ = 2$ + Variant5 = 5, + Variant6, +// ^^^^^^^^ = 6$ } "#, ); check_discriminants_fieldless( r#" enum Enum { - Variant, -//^^^^^^^0 - Variant1, -//^^^^^^^^1 - Variant2, -//^^^^^^^^2 - Variant5 = 5, - Variant6, -//^^^^^^^^6 + Variant, +// ^^^^^^^ = 0 + Variant1, +// ^^^^^^^^ = 1 + Variant2, +// ^^^^^^^^ = 2 + Variant5 = 5, + Variant6, +// ^^^^^^^^ = 6 } "#, ); @@ -144,26 +157,23 @@ enum Enum { fn datacarrying_mixed() { check_discriminants( r#" +#[repr(u8)] enum Enum { Variant(), - //^^^^^^^^^0 +// ^^^^^^^^^ = 0 Variant1, - //^^^^^^^^1 +// ^^^^^^^^ = 1 Variant2 {}, - //^^^^^^^^^^^2 +// ^^^^^^^^^^^ = 2 Variant3, - //^^^^^^^^3 +// ^^^^^^^^ = 3 Variant5 = 5, Variant6, - //^^^^^^^^6 +// ^^^^^^^^ = 6 } "#, ); - } - - #[test] - fn datacarrying_mixed_fieldless_set() { - check_discriminants_fieldless( + check_discriminants( r#" enum Enum { Variant(), @@ -175,20 +185,20 @@ enum Enum { } "#, ); + } + + #[test] + fn datacarrying_mixed_fieldless_set() { check_discriminants_fieldless( r#" +#[repr(u8)] enum Enum { Variant(), - //^^^^^^^^^0 Variant1, - //^^^^^^^^1 Variant2 {}, - //^^^^^^^^^^^2 Variant3, - //^^^^^^^^3 - Variant5 = 5, + Variant5, Variant6, - //^^^^^^^^6 } "#, ); diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs index b7182085b312e..5fce11b785a7a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs @@ -10,7 +10,7 @@ use syntax::{ SyntaxToken, }; -use crate::{InlayHint, InlayHintsConfig, InlayKind, LifetimeElisionHints}; +use crate::{InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind, LifetimeElisionHints}; pub(super) fn hints( acc: &mut Vec, @@ -25,6 +25,10 @@ pub(super) fn hints( range: t.text_range(), kind: InlayKind::Lifetime, label: label.into(), + text_edit: None, + position: InlayHintPosition::After, + pad_left: false, + pad_right: true, }; let param_list = func.param_list()?; @@ -189,12 +193,20 @@ pub(super) fn hints( if is_empty { "" } else { ", " } ) .into(), + text_edit: None, + position: InlayHintPosition::After, + pad_left: false, + pad_right: true, }); } (None, allocated_lifetimes) => acc.push(InlayHint { range: func.name()?.syntax().text_range(), kind: InlayKind::GenericParamList, label: format!("<{}>", allocated_lifetimes.iter().format(", "),).into(), + text_edit: None, + position: InlayHintPosition::After, + pad_left: false, + pad_right: false, }), } Some(()) diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs index 1122ee2e3925d..fc297a8d824f2 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs @@ -8,7 +8,7 @@ use syntax::{ SyntaxKind, }; -use crate::{InlayHint, InlayHintsConfig, InlayKind, LifetimeElisionHints}; +use crate::{InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind, LifetimeElisionHints}; pub(super) fn hints( acc: &mut Vec, @@ -34,6 +34,10 @@ pub(super) fn hints( range: t.text_range(), kind: InlayKind::Lifetime, label: "'static".to_owned().into(), + text_edit: None, + position: InlayHintPosition::After, + pad_left: false, + pad_right: true, }); } } diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs index 9cdae63241044..c4f43f411753e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs @@ -10,7 +10,7 @@ use ide_db::{base_db::FileRange, RootDatabase}; use stdx::to_lower_snake_case; use syntax::ast::{self, AstNode, HasArgList, HasName, UnaryOp}; -use crate::{InlayHint, InlayHintLabel, InlayHintsConfig, InlayKind}; +use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind}; pub(super) fn hints( acc: &mut Vec, @@ -31,16 +31,16 @@ pub(super) fn hints( // Only annotate hints for expressions that exist in the original file let range = sema.original_range_opt(arg.syntax())?; let (param_name, name_syntax) = match param.as_ref()? { - Either::Left(pat) => ("self".to_string(), pat.name()), + Either::Left(pat) => (pat.name()?, pat.name()), Either::Right(pat) => match pat { - ast::Pat::IdentPat(it) => (it.name()?.to_string(), it.name()), + ast::Pat::IdentPat(it) => (it.name()?, it.name()), _ => return None, }, }; Some((name_syntax, param_name, arg, range)) }) .filter(|(_, param_name, arg, _)| { - !should_hide_param_name_hint(sema, &callable, param_name, arg) + !should_hide_param_name_hint(sema, &callable, ¶m_name.text(), arg) }) .map(|(param, param_name, _, FileRange { range, .. })| { let mut linked_location = None; @@ -53,10 +53,17 @@ pub(super) fn hints( } } + let colon = if config.render_colons { ":" } else { "" }; + let label = + InlayHintLabel::simple(format!("{param_name}{colon}"), None, linked_location); InlayHint { range, kind: InlayKind::Parameter, - label: InlayHintLabel::simple(param_name, None, linked_location), + label, + text_edit: None, + position: InlayHintPosition::Before, + pad_left: false, + pad_right: true, } }); diff --git a/src/tools/rust-analyzer/crates/ide/src/interpret_function.rs b/src/tools/rust-analyzer/crates/ide/src/interpret_function.rs new file mode 100644 index 0000000000000..cbcbb4b09db48 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide/src/interpret_function.rs @@ -0,0 +1,46 @@ +use hir::Semantics; +use ide_db::base_db::SourceDatabaseExt; +use ide_db::RootDatabase; +use ide_db::{base_db::FilePosition, LineIndexDatabase}; +use std::{fmt::Write, time::Instant}; +use syntax::TextRange; +use syntax::{algo::find_node_at_offset, ast, AstNode}; + +// Feature: Interpret Function +// +// |=== +// | Editor | Action Name +// +// | VS Code | **rust-analyzer: Interpret Function** +// |=== +pub(crate) fn interpret_function(db: &RootDatabase, position: FilePosition) -> String { + let start_time = Instant::now(); + let mut result = find_and_interpret(db, position) + .unwrap_or_else(|| "Not inside a function body".to_string()); + let duration = Instant::now() - start_time; + writeln!(result, "").unwrap(); + writeln!(result, "----------------------").unwrap(); + writeln!(result, " Finished in {}s", duration.as_secs_f32()).unwrap(); + result +} + +fn find_and_interpret(db: &RootDatabase, position: FilePosition) -> Option { + let sema = Semantics::new(db); + let source_file = sema.parse(position.file_id); + + let item = find_node_at_offset::(source_file.syntax(), position.offset)?; + let def = match item { + ast::Item::Fn(it) => sema.to_def(&it)?, + _ => return None, + }; + let span_formatter = |file_id, text_range: TextRange| { + let line_col = db.line_index(file_id).line_col(text_range.start()); + let path = &db + .source_root(db.file_source_root(file_id)) + .path_for_file(&file_id) + .map(|x| x.to_string()); + let path = path.as_deref().unwrap_or(""); + format!("file://{path}#{}:{}", line_col.line + 1, line_col.col) + }; + Some(def.eval(db, span_formatter)) +} diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index 078b66dd3955f..87e769e423073 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -56,20 +56,24 @@ mod typing; mod view_crate_graph; mod view_hir; mod view_mir; +mod interpret_function; mod view_item_tree; mod shuffle_crate_graph; +mod fetch_crates; -use std::sync::Arc; +use std::ffi::OsStr; use cfg::CfgOptions; +use fetch_crates::CrateInfo; use ide_db::{ base_db::{ salsa::{self, ParallelDatabase}, CrateOrigin, Env, FileLoader, FileSet, SourceDatabase, VfsPath, }, - symbol_index, LineIndexDatabase, + symbol_index, FxHashMap, FxIndexSet, LineIndexDatabase, }; use syntax::SourceFile; +use triomphe::Arc; use crate::navigation_target::{ToNav, TryToNav}; @@ -80,11 +84,14 @@ pub use crate::{ file_structure::{StructureNode, StructureNodeKind}, folding_ranges::{Fold, FoldKind}, highlight_related::{HighlightRelatedConfig, HighlightedRange}, - hover::{HoverAction, HoverConfig, HoverDocFormat, HoverGotoTypeData, HoverResult}, + hover::{ + HoverAction, HoverConfig, HoverDocFormat, HoverGotoTypeData, HoverResult, + MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, + }, inlay_hints::{ AdjustmentHints, AdjustmentHintsMode, ClosureReturnTypeHints, DiscriminantHints, InlayHint, - InlayHintLabel, InlayHintLabelPart, InlayHintsConfig, InlayKind, InlayTooltip, - LifetimeElisionHints, + InlayHintLabel, InlayHintLabelPart, InlayHintPosition, InlayHintsConfig, InlayKind, + InlayTooltip, LifetimeElisionHints, }, join_lines::JoinLinesConfig, markup::Markup, @@ -154,7 +161,11 @@ impl AnalysisHost { } pub fn update_lru_capacity(&mut self, lru_capacity: Option) { - self.db.update_lru_capacity(lru_capacity); + self.db.update_parse_query_lru_capacity(lru_capacity); + } + + pub fn update_lru_capacities(&mut self, lru_capacities: &FxHashMap, usize>) { + self.db.update_lru_capacities(lru_capacities); } /// Returns a snapshot of the current state, which you can query for @@ -233,14 +244,14 @@ impl Analysis { None, None, cfg_options.clone(), - cfg_options, + None, Env::default(), - Ok(Vec::new()), false, - CrateOrigin::CratesIo { repo: None, name: None }, + CrateOrigin::Local { repo: None, name: None }, Err("Analysis::from_single_file has no target layout".into()), + None, ); - change.change_file(file_id, Some(Arc::new(text))); + change.change_file(file_id, Some(Arc::from(text))); change.set_crate_graph(crate_graph); host.apply_change(change); (host.analysis(), file_id) @@ -259,7 +270,7 @@ impl Analysis { } /// Gets the text of the source file. - pub fn file_text(&self, file_id: FileId) -> Cancellable> { + pub fn file_text(&self, file_id: FileId) -> Cancellable> { self.with_db(|db| db.file_text(file_id)) } @@ -313,6 +324,10 @@ impl Analysis { self.with_db(|db| view_mir::view_mir(db, position)) } + pub fn interpret_function(&self, position: FilePosition) -> Cancellable { + self.with_db(|db| interpret_function::interpret_function(db, position)) + } + pub fn view_item_tree(&self, file_id: FileId) -> Cancellable { self.with_db(|db| view_item_tree::view_item_tree(db, file_id)) } @@ -322,6 +337,10 @@ impl Analysis { self.with_db(|db| view_crate_graph::view_crate_graph(db, full)) } + pub fn fetch_crates(&self) -> Cancellable> { + self.with_db(|db| fetch_crates::fetch_crates(db)) + } + pub fn expand_macro(&self, position: FilePosition) -> Cancellable> { self.with_db(|db| expand_macro::expand_macro(db, position)) } @@ -452,12 +471,19 @@ impl Analysis { self.with_db(|db| moniker::moniker(db, position)) } - /// Return URL(s) for the documentation of the symbol under the cursor. + /// Returns URL(s) for the documentation of the symbol under the cursor. + /// # Arguments + /// * `position` - Position in the file. + /// * `target_dir` - Directory where the build output is storeda. pub fn external_docs( &self, position: FilePosition, - ) -> Cancellable> { - self.with_db(|db| doc_links::external_docs(db, &position)) + target_dir: Option<&OsStr>, + sysroot: Option<&OsStr>, + ) -> Cancellable { + self.with_db(|db| { + doc_links::external_docs(db, &position, target_dir, sysroot).unwrap_or_default() + }) } /// Computes parameter information at the given position. @@ -508,6 +534,11 @@ impl Analysis { self.with_db(|db| db.crate_graph()[crate_id].edition) } + /// Returns true if this crate has `no_std` or `no_core` specified. + pub fn is_crate_no_std(&self, crate_id: CrateId) -> Cancellable { + self.with_db(|db| hir::db::DefDatabase::crate_def_map(db, crate_id).is_no_std()) + } + /// Returns the root file of the given crate. pub fn crate_root(&self, crate_id: CrateId) -> Cancellable { self.with_db(|db| db.crate_graph()[crate_id].root_file_id) diff --git a/src/tools/rust-analyzer/crates/ide/src/moniker.rs b/src/tools/rust-analyzer/crates/ide/src/moniker.rs index 349e79ecfdda0..0d57e63d29c91 100644 --- a/src/tools/rust-analyzer/crates/ide/src/moniker.rs +++ b/src/tools/rust-analyzer/crates/ide/src/moniker.rs @@ -1,7 +1,7 @@ //! This module generates [moniker](https://microsoft.github.io/language-server-protocol/specifications/lsif/0.6.0/specification/#exportsImports) //! for LSIF and LSP. -use hir::{AsAssocItem, AssocItemContainer, Crate, Name, Semantics}; +use hir::{AsAssocItem, AssocItemContainer, Crate, Semantics}; use ide_db::{ base_db::{CrateOrigin, FilePosition, LangCrateOrigin}, defs::{Definition, IdentClass}, @@ -27,7 +27,7 @@ pub enum MonikerDescriptorKind { #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct MonikerDescriptor { - pub name: Name, + pub name: String, pub desc: MonikerDescriptorKind, } @@ -41,11 +41,7 @@ impl ToString for MonikerIdentifier { fn to_string(&self) -> String { match self { MonikerIdentifier { description, crate_name } => { - format!( - "{}::{}", - crate_name, - description.iter().map(|x| x.name.to_string()).join("::") - ) + format!("{}::{}", crate_name, description.iter().map(|x| &x.name).join("::")) } } } @@ -136,7 +132,10 @@ pub(crate) fn def_to_moniker( let krate = module.krate(); let mut description = vec![]; description.extend(module.path_to_root(db).into_iter().filter_map(|x| { - Some(MonikerDescriptor { name: x.name(db)?, desc: MonikerDescriptorKind::Namespace }) + Some(MonikerDescriptor { + name: x.name(db)?.display(db).to_string(), + desc: MonikerDescriptorKind::Namespace, + }) })); // Handle associated items within a trait @@ -147,7 +146,7 @@ pub(crate) fn def_to_moniker( // Because different traits can have functions with the same name, // we have to include the trait name as part of the moniker for uniqueness. description.push(MonikerDescriptor { - name: trait_.name(db), + name: trait_.name(db).display(db).to_string(), desc: MonikerDescriptorKind::Type, }); } @@ -156,14 +155,14 @@ pub(crate) fn def_to_moniker( // we add both the struct name and the trait name to the path if let Some(adt) = impl_.self_ty(db).as_adt() { description.push(MonikerDescriptor { - name: adt.name(db), + name: adt.name(db).display(db).to_string(), desc: MonikerDescriptorKind::Type, }); } if let Some(trait_) = impl_.trait_(db) { description.push(MonikerDescriptor { - name: trait_.name(db), + name: trait_.name(db).display(db).to_string(), desc: MonikerDescriptorKind::Type, }); } @@ -173,7 +172,7 @@ pub(crate) fn def_to_moniker( if let Definition::Field(it) = def { description.push(MonikerDescriptor { - name: it.parent_def(db).name(db), + name: it.parent_def(db).name(db).display(db).to_string(), desc: MonikerDescriptorKind::Type, }); } @@ -191,48 +190,63 @@ pub(crate) fn def_to_moniker( return None; } - MonikerDescriptor { name: local.name(db), desc: MonikerDescriptorKind::Parameter } - } - Definition::Macro(m) => { - MonikerDescriptor { name: m.name(db), desc: MonikerDescriptorKind::Macro } - } - Definition::Function(f) => { - MonikerDescriptor { name: f.name(db), desc: MonikerDescriptorKind::Method } - } - Definition::Variant(v) => { - MonikerDescriptor { name: v.name(db), desc: MonikerDescriptorKind::Type } - } - Definition::Const(c) => { - MonikerDescriptor { name: c.name(db)?, desc: MonikerDescriptorKind::Term } - } - Definition::Trait(trait_) => { - MonikerDescriptor { name: trait_.name(db), desc: MonikerDescriptorKind::Type } - } - Definition::TraitAlias(ta) => { - MonikerDescriptor { name: ta.name(db), desc: MonikerDescriptorKind::Type } - } - Definition::TypeAlias(ta) => { - MonikerDescriptor { name: ta.name(db), desc: MonikerDescriptorKind::TypeParameter } - } - Definition::Module(m) => { - MonikerDescriptor { name: m.name(db)?, desc: MonikerDescriptorKind::Namespace } - } - Definition::BuiltinType(b) => { - MonikerDescriptor { name: b.name(), desc: MonikerDescriptorKind::Type } + MonikerDescriptor { + name: local.name(db).display(db).to_string(), + desc: MonikerDescriptorKind::Parameter, + } } + Definition::Macro(m) => MonikerDescriptor { + name: m.name(db).display(db).to_string(), + desc: MonikerDescriptorKind::Macro, + }, + Definition::Function(f) => MonikerDescriptor { + name: f.name(db).display(db).to_string(), + desc: MonikerDescriptorKind::Method, + }, + Definition::Variant(v) => MonikerDescriptor { + name: v.name(db).display(db).to_string(), + desc: MonikerDescriptorKind::Type, + }, + Definition::Const(c) => MonikerDescriptor { + name: c.name(db)?.display(db).to_string(), + desc: MonikerDescriptorKind::Term, + }, + Definition::Trait(trait_) => MonikerDescriptor { + name: trait_.name(db).display(db).to_string(), + desc: MonikerDescriptorKind::Type, + }, + Definition::TraitAlias(ta) => MonikerDescriptor { + name: ta.name(db).display(db).to_string(), + desc: MonikerDescriptorKind::Type, + }, + Definition::TypeAlias(ta) => MonikerDescriptor { + name: ta.name(db).display(db).to_string(), + desc: MonikerDescriptorKind::TypeParameter, + }, + Definition::Module(m) => MonikerDescriptor { + name: m.name(db)?.display(db).to_string(), + desc: MonikerDescriptorKind::Namespace, + }, + Definition::BuiltinType(b) => MonikerDescriptor { + name: b.name().display(db).to_string(), + desc: MonikerDescriptorKind::Type, + }, Definition::SelfType(imp) => MonikerDescriptor { - name: imp.self_ty(db).as_adt()?.name(db), + name: imp.self_ty(db).as_adt()?.name(db).display(db).to_string(), desc: MonikerDescriptorKind::Type, }, - Definition::Field(it) => { - MonikerDescriptor { name: it.name(db), desc: MonikerDescriptorKind::Term } - } - Definition::Adt(adt) => { - MonikerDescriptor { name: adt.name(db), desc: MonikerDescriptorKind::Type } - } - Definition::Static(s) => { - MonikerDescriptor { name: s.name(db), desc: MonikerDescriptorKind::Meta } - } + Definition::Field(it) => MonikerDescriptor { + name: it.name(db).display(db).to_string(), + desc: MonikerDescriptorKind::Term, + }, + Definition::Adt(adt) => MonikerDescriptor { + name: adt.name(db).display(db).to_string(), + desc: MonikerDescriptorKind::Type, + }, + Definition::Static(s) => MonikerDescriptor { + name: s.name(db).display(db).to_string(), + desc: MonikerDescriptorKind::Meta, + }, }; description.push(name_desc); @@ -245,11 +259,17 @@ pub(crate) fn def_to_moniker( kind: if krate == from_crate { MonikerKind::Export } else { MonikerKind::Import }, package_information: { let (name, repo, version) = match krate.origin(db) { - CrateOrigin::CratesIo { repo, name } => ( + CrateOrigin::Library { repo, name } => (name, repo, krate.version(db)), + CrateOrigin::Local { repo, name } => ( name.unwrap_or(krate.display_name(db)?.canonical_name().to_string()), repo, krate.version(db), ), + CrateOrigin::Rustc { name } => ( + name.clone(), + Some("https://github.com/rust-lang/rust/".to_string()), + Some(format!("https://github.com/rust-lang/rust/compiler/{name}",)), + ), CrateOrigin::Lang(lang) => ( krate.display_name(db)?.canonical_name().to_string(), Some("https://github.com/rust-lang/rust/".to_string()), diff --git a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs index 6aae82f981600..385c1b0c00816 100644 --- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs +++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs @@ -4,8 +4,8 @@ use std::fmt; use either::Either; use hir::{ - symbols::FileSymbol, AssocItem, Documentation, FieldSource, HasAttrs, HasSource, HirDisplay, - InFile, LocalSource, ModuleSource, Semantics, + symbols::FileSymbol, AssocItem, Documentation, FieldSource, HasAttrs, HasContainer, HasSource, + HirDisplay, HirFileId, InFile, LocalSource, ModuleSource, }; use ide_db::{ base_db::{FileId, FileRange}, @@ -15,7 +15,7 @@ use ide_db::{defs::Definition, RootDatabase}; use stdx::never; use syntax::{ ast::{self, HasName}, - match_ast, AstNode, SmolStr, SyntaxNode, TextRange, + AstNode, SmolStr, SyntaxNode, TextRange, }; /// `NavigationTarget` represents an element in the editor's UI which you can @@ -45,6 +45,9 @@ pub struct NavigationTarget { pub container_name: Option, pub description: Option, pub docs: Option, + /// In addition to a `name` field, a `NavigationTarget` may also be aliased + /// In such cases we want a `NavigationTarget` to be accessible by its alias + pub alias: Option, } impl fmt::Debug for NavigationTarget { @@ -89,10 +92,9 @@ impl NavigationTarget { pub(crate) fn from_module_to_decl(db: &RootDatabase, module: hir::Module) -> NavigationTarget { let name = module.name(db).map(|it| it.to_smol_str()).unwrap_or_default(); - if let Some(src @ InFile { value, .. }) = &module.declaration_source(db) { - let FileRange { file_id, range: full_range } = src.syntax().original_file_range(db); - let focus_range = - value.name().and_then(|it| orig_focus_range(db, src.file_id, it.syntax())); + if let Some(InFile { value, file_id }) = &module.declaration_source(db) { + let (file_id, full_range, focus_range) = + orig_range_with_focus(db, *file_id, value.syntax(), value.name()); let mut res = NavigationTarget::from_syntax( file_id, name, @@ -128,14 +130,15 @@ impl NavigationTarget { /// Allows `NavigationTarget` to be created from a `NameOwner` pub(crate) fn from_named( db: &RootDatabase, - node @ InFile { file_id, value }: InFile<&dyn ast::HasName>, + InFile { file_id, value }: InFile<&dyn ast::HasName>, kind: SymbolKind, ) -> NavigationTarget { let name = value.name().map(|it| it.text().into()).unwrap_or_else(|| "_".into()); - let focus_range = value.name().and_then(|it| orig_focus_range(db, file_id, it.syntax())); - let FileRange { file_id, range } = node.map(|it| it.syntax()).original_file_range(db); - NavigationTarget::from_syntax(file_id, name, focus_range, range, kind) + let (file_id, full_range, focus_range) = + orig_range_with_focus(db, file_id, value.syntax(), value.name()); + + NavigationTarget::from_syntax(file_id, name, focus_range, full_range, kind) } fn from_syntax( @@ -154,23 +157,43 @@ impl NavigationTarget { container_name: None, description: None, docs: None, + alias: None, } } } impl TryToNav for FileSymbol { fn try_to_nav(&self, db: &RootDatabase) -> Option { - let full_range = self.loc.original_range(db)?; - let name_range = self.loc.original_name_range(db)?; + let full_range = self.loc.original_range(db); + let focus_range = self.loc.original_name_range(db).and_then(|it| { + if it.file_id == full_range.file_id { + Some(it.range) + } else { + None + } + }); Some(NavigationTarget { file_id: full_range.file_id, - name: self.name.clone(), - kind: Some(self.kind.into()), + name: if self.is_alias { self.def.name(db)?.to_smol_str() } else { self.name.clone() }, + alias: if self.is_alias { Some(self.name.clone()) } else { None }, + kind: Some(hir::ModuleDefId::from(self.def).into()), full_range: full_range.range, - focus_range: Some(name_range.range), + focus_range, container_name: self.container_name.clone(), - description: description_from_symbol(db, self), + description: match self.def { + hir::ModuleDef::Module(it) => Some(it.display(db).to_string()), + hir::ModuleDef::Function(it) => Some(it.display(db).to_string()), + hir::ModuleDef::Adt(it) => Some(it.display(db).to_string()), + hir::ModuleDef::Variant(it) => Some(it.display(db).to_string()), + hir::ModuleDef::Const(it) => Some(it.display(db).to_string()), + hir::ModuleDef::Static(it) => Some(it.display(db).to_string()), + hir::ModuleDef::Trait(it) => Some(it.display(db).to_string()), + hir::ModuleDef::TraitAlias(it) => Some(it.display(db).to_string()), + hir::ModuleDef::TypeAlias(it) => Some(it.display(db).to_string()), + hir::ModuleDef::Macro(it) => Some(it.display(db).to_string()), + hir::ModuleDef::BuiltinType(_) => None, + }, docs: None, }) } @@ -221,38 +244,80 @@ impl TryToNav for hir::ModuleDef { } } -pub(crate) trait ToNavFromAst { +pub(crate) trait ToNavFromAst: Sized { const KIND: SymbolKind; + fn container_name(self, db: &RootDatabase) -> Option { + _ = db; + None + } } + +fn container_name(db: &RootDatabase, t: impl HasContainer) -> Option { + match t.container(db) { + hir::ItemContainer::Trait(it) => Some(it.name(db).to_smol_str()), + // FIXME: Handle owners of blocks correctly here + hir::ItemContainer::Module(it) => it.name(db).map(|name| name.to_smol_str()), + _ => None, + } +} + impl ToNavFromAst for hir::Function { const KIND: SymbolKind = SymbolKind::Function; + fn container_name(self, db: &RootDatabase) -> Option { + container_name(db, self) + } } + impl ToNavFromAst for hir::Const { const KIND: SymbolKind = SymbolKind::Const; + fn container_name(self, db: &RootDatabase) -> Option { + container_name(db, self) + } } impl ToNavFromAst for hir::Static { const KIND: SymbolKind = SymbolKind::Static; + fn container_name(self, db: &RootDatabase) -> Option { + container_name(db, self) + } } impl ToNavFromAst for hir::Struct { const KIND: SymbolKind = SymbolKind::Struct; + fn container_name(self, db: &RootDatabase) -> Option { + container_name(db, self) + } } impl ToNavFromAst for hir::Enum { const KIND: SymbolKind = SymbolKind::Enum; + fn container_name(self, db: &RootDatabase) -> Option { + container_name(db, self) + } } impl ToNavFromAst for hir::Variant { const KIND: SymbolKind = SymbolKind::Variant; } impl ToNavFromAst for hir::Union { const KIND: SymbolKind = SymbolKind::Union; + fn container_name(self, db: &RootDatabase) -> Option { + container_name(db, self) + } } impl ToNavFromAst for hir::TypeAlias { const KIND: SymbolKind = SymbolKind::TypeAlias; + fn container_name(self, db: &RootDatabase) -> Option { + container_name(db, self) + } } impl ToNavFromAst for hir::Trait { const KIND: SymbolKind = SymbolKind::Trait; + fn container_name(self, db: &RootDatabase) -> Option { + container_name(db, self) + } } impl ToNavFromAst for hir::TraitAlias { const KIND: SymbolKind = SymbolKind::TraitAlias; + fn container_name(self, db: &RootDatabase) -> Option { + container_name(db, self) + } } impl TryToNav for D @@ -269,6 +334,7 @@ where ); res.docs = self.docs(db); res.description = Some(self.display(db).to_string()); + res.container_name = self.container_name(db); Some(res) } } @@ -280,15 +346,11 @@ impl ToNav for hir::Module { let name = self.name(db).map(|it| it.to_smol_str()).unwrap_or_default(); let (syntax, focus) = match &value { ModuleSource::SourceFile(node) => (node.syntax(), None), - ModuleSource::Module(node) => ( - node.syntax(), - node.name().and_then(|it| orig_focus_range(db, file_id, it.syntax())), - ), + ModuleSource::Module(node) => (node.syntax(), node.name()), ModuleSource::BlockExpr(node) => (node.syntax(), None), }; - let FileRange { file_id, range: full_range } = - InFile::new(file_id, syntax).original_file_range(db); - NavigationTarget::from_syntax(file_id, name, focus, full_range, SymbolKind::Module) + let (file_id, full_range, focus_range) = orig_range_with_focus(db, file_id, syntax, focus); + NavigationTarget::from_syntax(file_id, name, focus_range, full_range, SymbolKind::Module) } } @@ -297,17 +359,14 @@ impl TryToNav for hir::Impl { let InFile { file_id, value } = self.source(db)?; let derive_attr = self.is_builtin_derive(db); - let focus_range = if derive_attr.is_some() { - None - } else { - value.self_ty().and_then(|ty| orig_focus_range(db, file_id, ty.syntax())) - }; + let focus = if derive_attr.is_some() { None } else { value.self_ty() }; - let FileRange { file_id, range: full_range } = match &derive_attr { - Some(attr) => attr.syntax().original_file_range(db), - None => InFile::new(file_id, value.syntax()).original_file_range(db), + let syntax = match &derive_attr { + Some(attr) => attr.value.syntax(), + None => value.syntax(), }; + let (file_id, full_range, focus_range) = orig_range_with_focus(db, file_id, syntax, focus); Some(NavigationTarget::from_syntax( file_id, "impl".into(), @@ -396,9 +455,8 @@ impl ToNav for LocalSource { Either::Left(bind_pat) => (bind_pat.syntax(), bind_pat.name()), Either::Right(it) => (it.syntax(), it.name()), }; - let focus_range = name.and_then(|it| orig_focus_range(db, file_id, it.syntax())); - let FileRange { file_id, range: full_range } = - InFile::new(file_id, node).original_file_range(db); + + let (file_id, full_range, focus_range) = orig_range_with_focus(db, file_id, node, name); let name = local.name(db).to_smol_str(); let kind = if local.is_self(db) { @@ -411,6 +469,7 @@ impl ToNav for LocalSource { NavigationTarget { file_id, name, + alias: None, kind: Some(kind), full_range, focus_range, @@ -432,13 +491,13 @@ impl ToNav for hir::Label { let InFile { file_id, value } = self.source(db); let name = self.name(db).to_smol_str(); - let range = |syntax: &_| InFile::new(file_id, syntax).original_file_range(db); - let FileRange { file_id, range: full_range } = range(value.syntax()); - let focus_range = value.lifetime().map(|lt| range(lt.syntax()).range); + let (file_id, full_range, focus_range) = + orig_range_with_focus(db, file_id, value.syntax(), value.lifetime()); NavigationTarget { file_id, name, + alias: None, kind: Some(SymbolKind::Label), full_range, focus_range, @@ -463,22 +522,18 @@ impl TryToNav for hir::TypeParam { Either::Right(x) => Either::Right(x), }; - let range = |syntax: &_| InFile::new(file_id, syntax).original_file_range(db); - let focus_range = |syntax: &_| InFile::new(file_id, syntax).original_file_range_opt(db); - let FileRange { file_id, range: full_range } = match &value { - Either::Left(type_param) => range(type_param.syntax()), - Either::Right(trait_) => trait_ - .name() - .and_then(|name| focus_range(name.syntax())) - .unwrap_or_else(|| range(trait_.syntax())), + let syntax = match &value { + Either::Left(type_param) => type_param.syntax(), + Either::Right(trait_) => trait_.syntax(), }; - let focus_range = value - .either(|it| it.name(), |it| it.name()) - .and_then(|it| focus_range(it.syntax())) - .map(|it| it.range); + let focus = value.as_ref().either(|it| it.name(), |it| it.name()); + + let (file_id, full_range, focus_range) = orig_range_with_focus(db, file_id, syntax, focus); + Some(NavigationTarget { file_id, name, + alias: None, kind: Some(SymbolKind::TypeParam), full_range, focus_range, @@ -500,14 +555,15 @@ impl TryToNav for hir::LifetimeParam { let InFile { file_id, value } = self.source(db)?; let name = self.name(db).to_smol_str(); - let FileRange { file_id, range: full_range } = + let FileRange { file_id, range } = InFile::new(file_id, value.syntax()).original_file_range(db); Some(NavigationTarget { file_id, name, + alias: None, kind: Some(SymbolKind::LifetimeParam), - full_range, - focus_range: Some(full_range), + full_range: range, + focus_range: Some(range), container_name: None, description: None, docs: None, @@ -528,12 +584,12 @@ impl TryToNav for hir::ConstParam { } }; - let focus_range = value.name().and_then(|it| orig_focus_range(db, file_id, it.syntax())); - let FileRange { file_id, range: full_range } = - InFile::new(file_id, value.syntax()).original_file_range(db); + let (file_id, full_range, focus_range) = + orig_range_with_focus(db, file_id, value.syntax(), value.name()); Some(NavigationTarget { file_id, name, + alias: None, kind: Some(SymbolKind::ConstParam), full_range, focus_range, @@ -544,38 +600,19 @@ impl TryToNav for hir::ConstParam { } } -/// Get a description of a symbol. -/// -/// e.g. `struct Name`, `enum Name`, `fn Name` -pub(crate) fn description_from_symbol(db: &RootDatabase, symbol: &FileSymbol) -> Option { - let sema = Semantics::new(db); - let node = symbol.loc.syntax(&sema)?; - - match_ast! { - match node { - ast::Fn(it) => sema.to_def(&it).map(|it| it.display(db).to_string()), - ast::Struct(it) => sema.to_def(&it).map(|it| it.display(db).to_string()), - ast::Enum(it) => sema.to_def(&it).map(|it| it.display(db).to_string()), - ast::Trait(it) => sema.to_def(&it).map(|it| it.display(db).to_string()), - ast::TraitAlias(it) => sema.to_def(&it).map(|it| it.display(db).to_string()), - ast::Module(it) => sema.to_def(&it).map(|it| it.display(db).to_string()), - ast::TypeAlias(it) => sema.to_def(&it).map(|it| it.display(db).to_string()), - ast::Const(it) => sema.to_def(&it).map(|it| it.display(db).to_string()), - ast::Static(it) => sema.to_def(&it).map(|it| it.display(db).to_string()), - ast::RecordField(it) => sema.to_def(&it).map(|it| it.display(db).to_string()), - ast::Variant(it) => sema.to_def(&it).map(|it| it.display(db).to_string()), - ast::Union(it) => sema.to_def(&it).map(|it| it.display(db).to_string()), - _ => None, - } - } -} - -fn orig_focus_range( +fn orig_range_with_focus( db: &RootDatabase, - file_id: hir::HirFileId, - syntax: &SyntaxNode, -) -> Option { - InFile::new(file_id, syntax).original_file_range_opt(db).map(|it| it.range) + hir_file: HirFileId, + value: &SyntaxNode, + name: Option, +) -> (FileId, TextRange, Option) { + let FileRange { file_id, range: full_range } = + InFile::new(hir_file, value).original_file_range(db); + let focus_range = name + .and_then(|it| InFile::new(hir_file, it.syntax()).original_file_range_opt(db)) + .and_then(|range| if range.file_id == file_id { Some(range.range) } else { None }); + + (file_id, full_range, focus_range) } #[cfg(test)] diff --git a/src/tools/rust-analyzer/crates/ide/src/prime_caches.rs b/src/tools/rust-analyzer/crates/ide/src/prime_caches.rs index 87b3ef380c594..d704d12a05b23 100644 --- a/src/tools/rust-analyzer/crates/ide/src/prime_caches.rs +++ b/src/tools/rust-analyzer/crates/ide/src/prime_caches.rs @@ -12,9 +12,8 @@ use ide_db::{ salsa::{Database, ParallelDatabase, Snapshot}, Cancelled, CrateGraph, CrateId, SourceDatabase, SourceDatabaseExt, }, - FxIndexMap, + FxHashSet, FxIndexMap, }; -use stdx::hash::NoHashHashSet; use crate::RootDatabase; @@ -81,7 +80,11 @@ pub(crate) fn parallel_prime_caches( for _ in 0..num_worker_threads { let worker = prime_caches_worker.clone(); let db = db.snapshot(); - std::thread::spawn(move || Cancelled::catch(|| worker(db))); + + stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker) + .allow_leak(true) + .spawn(move || Cancelled::catch(|| worker(db))) + .expect("failed to spawn thread"); } (work_sender, progress_receiver) @@ -142,7 +145,7 @@ pub(crate) fn parallel_prime_caches( } } -fn compute_crates_to_prime(db: &RootDatabase, graph: &CrateGraph) -> NoHashHashSet { +fn compute_crates_to_prime(db: &RootDatabase, graph: &CrateGraph) -> FxHashSet { // We're only interested in the workspace crates and the `ImportMap`s of their direct // dependencies, though in practice the latter also compute the `DefMap`s. // We don't prime transitive dependencies because they're generally not visible in diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs index 3684c1033f32f..fdc5261ac38b2 100644 --- a/src/tools/rust-analyzer/crates/ide/src/references.rs +++ b/src/tools/rust-analyzer/crates/ide/src/references.rs @@ -17,7 +17,7 @@ use ide_db::{ RootDatabase, }; use itertools::Itertools; -use stdx::hash::NoHashHashMap; +use nohash_hasher::IntMap; use syntax::{ algo::find_node_at_offset, ast::{self, HasName}, @@ -31,7 +31,7 @@ use crate::{FilePosition, NavigationTarget, TryToNav}; #[derive(Debug, Clone)] pub struct ReferenceSearchResult { pub declaration: Option, - pub references: NoHashHashMap)>>, + pub references: IntMap)>>, } #[derive(Debug, Clone)] @@ -715,7 +715,7 @@ fn f() { } "#, expect![[r#" - Foo Struct FileId(1) 17..51 28..31 + Foo Struct FileId(1) 17..51 28..31 foo FileId(0) 53..56 FileId(2) 79..82 @@ -803,7 +803,7 @@ pub(super) struct Foo$0 { } "#, expect![[r#" - Foo Struct FileId(2) 0..41 18..21 + Foo Struct FileId(2) 0..41 18..21 some FileId(1) 20..23 Import FileId(1) 47..50 @@ -1289,7 +1289,7 @@ trait Foo where Self$0 { impl Foo for () {} "#, expect![[r#" - Self TypeParam FileId(0) 6..9 6..9 + Self TypeParam FileId(0) 0..44 6..9 FileId(0) 16..20 FileId(0) 37..41 @@ -1380,7 +1380,7 @@ fn foo(_: impl Bar, _: &dyn Bar) {} trait Foo = where Self$0: ; "#, expect![[r#" - Self TypeParam FileId(0) 6..9 6..9 + Self TypeParam FileId(0) 0..25 6..9 FileId(0) 18..22 "#]], @@ -1542,7 +1542,7 @@ fn f() { FileId(0) 161..165 - func Function FileId(0) 137..146 140..144 + func Function FileId(0) 137..146 140..144 module FileId(0) 181..185 "#]], @@ -1581,7 +1581,7 @@ trait Trait { } "#, expect![[r#" - func Function FileId(0) 48..87 51..55 + func Function FileId(0) 48..87 51..55 Trait FileId(0) 74..78 "#]], @@ -1692,7 +1692,7 @@ fn f() { } "#, expect![[r#" - CONST Const FileId(0) 18..37 24..29 + CONST Const FileId(0) 18..37 24..29 Trait FileId(0) 71..76 FileId(0) 125..130 @@ -1721,7 +1721,7 @@ fn f() { } "#, expect![[r#" - TypeAlias TypeAlias FileId(0) 18..33 23..32 + TypeAlias TypeAlias FileId(0) 18..33 23..32 Trait FileId(0) 66..75 FileId(0) 117..126 @@ -1750,7 +1750,7 @@ fn f() { } "#, expect![[r#" - function Function FileId(0) 18..34 21..29 + function Function FileId(0) 18..34 21..29 Trait FileId(0) 65..73 FileId(0) 112..120 @@ -1894,7 +1894,7 @@ fn f() { } "#, expect![[r#" - TypeAlias TypeAlias FileId(0) 18..33 23..32 + TypeAlias TypeAlias FileId(0) 18..33 23..32 Trait FileId(0) 66..75 FileId(0) 117..126 @@ -1950,7 +1950,7 @@ impl Foo for Bar { fn method() {} "#, expect![[r#" - method Function FileId(0) 16..39 19..25 + method Function FileId(0) 16..39 19..25 Foo FileId(0) 101..107 "#]], diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs index 8a8a9151c4257..27ad63d820d6c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs +++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs @@ -349,8 +349,13 @@ pub(crate) fn runnable_mod( if !has_test_function_or_multiple_test_submodules(sema, &def) { return None; } - let path = - def.path_to_root(sema.db).into_iter().rev().filter_map(|it| it.name(sema.db)).join("::"); + let path = def + .path_to_root(sema.db) + .into_iter() + .rev() + .filter_map(|it| it.name(sema.db)) + .map(|it| it.display(sema.db).to_string()) + .join("::"); let attrs = def.attrs(sema.db); let cfg = attrs.cfg(); @@ -376,7 +381,7 @@ pub(crate) fn runnable_impl( } else { String::new() }; - let mut test_id = format!("{adt_name}{params}"); + let mut test_id = format!("{}{params}", adt_name.display(sema.db)); test_id.retain(|c| c != ' '); let test_id = TestId::Path(test_id); @@ -391,8 +396,13 @@ fn runnable_mod_outline_definition( if !has_test_function_or_multiple_test_submodules(sema, &def) { return None; } - let path = - def.path_to_root(sema.db).into_iter().rev().filter_map(|it| it.name(sema.db)).join("::"); + let path = def + .path_to_root(sema.db) + .into_iter() + .rev() + .filter_map(|it| it.name(sema.db)) + .map(|it| it.display(sema.db).to_string()) + .join("::"); let attrs = def.attrs(sema.db); let cfg = attrs.cfg(); @@ -430,7 +440,7 @@ fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option { let mut path = String::new(); def.canonical_module_path(db)? .flat_map(|it| it.name(db)) - .for_each(|name| format_to!(path, "{}::", name)); + .for_each(|name| format_to!(path, "{}::", name.display(db))); // This probably belongs to canonical_path? if let Some(assoc_item) = def.as_assoc_item(db) { if let hir::AssocItemContainer::Impl(imp) = assoc_item.container(db) { @@ -438,17 +448,17 @@ fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option { if let Some(adt) = ty.as_adt() { let name = adt.name(db); let mut ty_args = ty.generic_parameters(db).peekable(); - format_to!(path, "{}", name); + format_to!(path, "{}", name.display(db)); if ty_args.peek().is_some() { format_to!(path, "<{}>", ty_args.format_with(",", |ty, cb| cb(&ty))); } - format_to!(path, "::{}", def_name); + format_to!(path, "::{}", def_name.display(db)); path.retain(|c| c != ' '); return Some(path); } } } - format_to!(path, "{}", def_name); + format_to!(path, "{}", def_name.display(db)); Some(path) })(); @@ -2232,14 +2242,14 @@ mod tests { file_id: FileId( 0, ), - full_range: 52..115, - focus_range: 67..75, - name: "foo_test", + full_range: 121..185, + focus_range: 136..145, + name: "foo2_test", kind: Function, }, kind: Test { test_id: Path( - "tests::foo_test", + "tests::foo2_test", ), attr: TestAttr { ignore: false, @@ -2253,14 +2263,14 @@ mod tests { file_id: FileId( 0, ), - full_range: 121..185, - focus_range: 136..145, - name: "foo2_test", + full_range: 52..115, + focus_range: 67..75, + name: "foo_test", kind: Function, }, kind: Test { test_id: Path( - "tests::foo2_test", + "tests::foo_test", ), attr: TestAttr { ignore: false, @@ -2579,6 +2589,7 @@ mod r#mod { ), full_range: 47..84, name: "r#for", + container_name: "r#mod", }, kind: DocTest { test_id: Path( @@ -2595,6 +2606,7 @@ mod r#mod { ), full_range: 90..146, name: "r#struct", + container_name: "r#mod", }, kind: DocTest { test_id: Path( diff --git a/src/tools/rust-analyzer/crates/ide/src/shuffle_crate_graph.rs b/src/tools/rust-analyzer/crates/ide/src/shuffle_crate_graph.rs index e606072a82375..f85700daf1f78 100644 --- a/src/tools/rust-analyzer/crates/ide/src/shuffle_crate_graph.rs +++ b/src/tools/rust-analyzer/crates/ide/src/shuffle_crate_graph.rs @@ -1,9 +1,8 @@ -use std::sync::Arc; - use ide_db::{ - base_db::{salsa::Durability, CrateGraph, SourceDatabase}, + base_db::{salsa::Durability, CrateGraph, ProcMacros, SourceDatabase}, FxHashMap, RootDatabase, }; +use triomphe::Arc; // Feature: Shuffle Crate Graph // @@ -16,6 +15,7 @@ use ide_db::{ // |=== pub(crate) fn shuffle_crate_graph(db: &mut RootDatabase) { let crate_graph = db.crate_graph(); + let proc_macros = db.proc_macros(); let mut shuffled_ids = crate_graph.iter().collect::>(); @@ -23,6 +23,7 @@ pub(crate) fn shuffle_crate_graph(db: &mut RootDatabase) { stdx::rand::shuffle(&mut shuffled_ids, |i| rng.rand_range(0..i as u32) as usize); let mut new_graph = CrateGraph::default(); + let mut new_proc_macros = ProcMacros::default(); let mut map = FxHashMap::default(); for old_id in shuffled_ids.iter().copied() { @@ -35,11 +36,12 @@ pub(crate) fn shuffle_crate_graph(db: &mut RootDatabase) { data.cfg_options.clone(), data.potential_cfg_options.clone(), data.env.clone(), - data.proc_macro.clone(), data.is_proc_macro, data.origin.clone(), data.target_layout.clone(), + data.channel, ); + new_proc_macros.insert(new_id, proc_macros[&old_id].clone()); map.insert(old_id, new_id); } @@ -53,4 +55,5 @@ pub(crate) fn shuffle_crate_graph(db: &mut RootDatabase) { } db.set_crate_graph_with_durability(Arc::new(new_graph), Durability::HIGH); + db.set_proc_macros_with_durability(Arc::new(new_proc_macros), Durability::HIGH); } diff --git a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs index 4b2c139f6f455..7795be54e264c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs +++ b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs @@ -15,8 +15,9 @@ use ide_db::{ use stdx::format_to; use syntax::{ algo, - ast::{self, HasArgList}, - match_ast, AstNode, Direction, SyntaxElementChildren, SyntaxToken, TextRange, TextSize, + ast::{self, AstChildren, HasArgList}, + match_ast, AstNode, Direction, NodeOrToken, SyntaxElementChildren, SyntaxNode, SyntaxToken, + TextRange, TextSize, T, }; use crate::RootDatabase; @@ -116,6 +117,20 @@ pub(crate) fn signature_help(db: &RootDatabase, position: FilePosition) -> Optio } return signature_help_for_tuple_struct_pat(&sema, tuple_pat, token); }, + ast::TuplePat(tuple_pat) => { + let cursor_outside = tuple_pat.r_paren_token().as_ref() == Some(&token); + if cursor_outside { + continue; + } + return signature_help_for_tuple_pat(&sema, tuple_pat, token); + }, + ast::TupleExpr(tuple_expr) => { + let cursor_outside = tuple_expr.r_paren_token().as_ref() == Some(&token); + if cursor_outside { + continue; + } + return signature_help_for_tuple_expr(&sema, tuple_expr, token); + }, _ => (), } } @@ -162,7 +177,7 @@ fn signature_help_for_call( match callable.kind() { hir::CallableKind::Function(func) => { res.doc = func.docs(db).map(|it| it.into()); - format_to!(res.signature, "fn {}", func.name(db)); + format_to!(res.signature, "fn {}", func.name(db).display(db)); fn_params = Some(match callable.receiver_param(db) { Some(_self) => func.params_without_self(db), None => func.assoc_fn_params(db), @@ -170,15 +185,15 @@ fn signature_help_for_call( } hir::CallableKind::TupleStruct(strukt) => { res.doc = strukt.docs(db).map(|it| it.into()); - format_to!(res.signature, "struct {}", strukt.name(db)); + format_to!(res.signature, "struct {}", strukt.name(db).display(db)); } hir::CallableKind::TupleEnumVariant(variant) => { res.doc = variant.docs(db).map(|it| it.into()); format_to!( res.signature, "enum {}::{}", - variant.parent_enum(db).name(db), - variant.name(db) + variant.parent_enum(db).name(db).display(db), + variant.name(db).display(db) ); } hir::CallableKind::Closure | hir::CallableKind::FnPtr | hir::CallableKind::Other => (), @@ -248,31 +263,31 @@ fn signature_help_for_generics( match generics_def { hir::GenericDef::Function(it) => { res.doc = it.docs(db).map(|it| it.into()); - format_to!(res.signature, "fn {}", it.name(db)); + format_to!(res.signature, "fn {}", it.name(db).display(db)); } hir::GenericDef::Adt(hir::Adt::Enum(it)) => { res.doc = it.docs(db).map(|it| it.into()); - format_to!(res.signature, "enum {}", it.name(db)); + format_to!(res.signature, "enum {}", it.name(db).display(db)); } hir::GenericDef::Adt(hir::Adt::Struct(it)) => { res.doc = it.docs(db).map(|it| it.into()); - format_to!(res.signature, "struct {}", it.name(db)); + format_to!(res.signature, "struct {}", it.name(db).display(db)); } hir::GenericDef::Adt(hir::Adt::Union(it)) => { res.doc = it.docs(db).map(|it| it.into()); - format_to!(res.signature, "union {}", it.name(db)); + format_to!(res.signature, "union {}", it.name(db).display(db)); } hir::GenericDef::Trait(it) => { res.doc = it.docs(db).map(|it| it.into()); - format_to!(res.signature, "trait {}", it.name(db)); + format_to!(res.signature, "trait {}", it.name(db).display(db)); } hir::GenericDef::TraitAlias(it) => { res.doc = it.docs(db).map(|it| it.into()); - format_to!(res.signature, "trait {}", it.name(db)); + format_to!(res.signature, "trait {}", it.name(db).display(db)); } hir::GenericDef::TypeAlias(it) => { res.doc = it.docs(db).map(|it| it.into()); - format_to!(res.signature, "type {}", it.name(db)); + format_to!(res.signature, "type {}", it.name(db).display(db)); } hir::GenericDef::Variant(it) => { // In paths, generics of an enum can be specified *after* one of its variants. @@ -280,7 +295,7 @@ fn signature_help_for_generics( // We'll use the signature of the enum, but include the docs of the variant. res.doc = it.docs(db).map(|it| it.into()); let enum_ = it.parent_enum(db); - format_to!(res.signature, "enum {}", enum_.name(db)); + format_to!(res.signature, "enum {}", enum_.name(db).display(db)); generics_def = enum_.into(); } // These don't have generic args that can be specified @@ -395,24 +410,26 @@ fn signature_help_for_tuple_struct_pat( pat: ast::TupleStructPat, token: SyntaxToken, ) -> Option { - let rest_pat = pat.fields().find(|it| matches!(it, ast::Pat::RestPat(_))); - let is_left_of_rest_pat = - rest_pat.map_or(true, |it| token.text_range().start() < it.syntax().text_range().end()); - + let path = pat.path()?; + let path_res = sema.resolve_path(&path)?; let mut res = SignatureHelp { doc: None, signature: String::new(), parameters: vec![], active_parameter: None, }; - let db = sema.db; - let path_res = sema.resolve_path(&pat.path()?)?; + let fields: Vec<_> = if let PathResolution::Def(ModuleDef::Variant(variant)) = path_res { let en = variant.parent_enum(db); res.doc = en.docs(db).map(|it| it.into()); - format_to!(res.signature, "enum {}::{} (", en.name(db), variant.name(db)); + format_to!( + res.signature, + "enum {}::{} (", + en.name(db).display(db), + variant.name(db).display(db) + ); variant.fields(db) } else { let adt = match path_res { @@ -424,36 +441,78 @@ fn signature_help_for_tuple_struct_pat( match adt { hir::Adt::Struct(it) => { res.doc = it.docs(db).map(|it| it.into()); - format_to!(res.signature, "struct {} (", it.name(db)); + format_to!(res.signature, "struct {} (", it.name(db).display(db)); it.fields(db) } _ => return None, } }; - let commas = pat - .syntax() - .children_with_tokens() - .filter_map(syntax::NodeOrToken::into_token) - .filter(|t| t.kind() == syntax::T![,]); - res.active_parameter = Some(if is_left_of_rest_pat { - commas.take_while(|t| t.text_range().start() <= token.text_range().start()).count() - } else { - let n_commas = commas - .collect::>() - .into_iter() - .rev() - .take_while(|t| t.text_range().start() > token.text_range().start()) - .count(); - fields.len().saturating_sub(1).saturating_sub(n_commas) - }); + Some(signature_help_for_tuple_pat_ish( + db, + res, + pat.syntax(), + token, + pat.fields(), + fields.into_iter().map(|it| it.ty(db)), + )) +} + +fn signature_help_for_tuple_pat( + sema: &Semantics<'_, RootDatabase>, + pat: ast::TuplePat, + token: SyntaxToken, +) -> Option { + let db = sema.db; + let field_pats = pat.fields(); + let pat = pat.into(); + let ty = sema.type_of_pat(&pat)?; + let fields = ty.original.tuple_fields(db); + + Some(signature_help_for_tuple_pat_ish( + db, + SignatureHelp { + doc: None, + signature: String::from('('), + parameters: vec![], + active_parameter: None, + }, + pat.syntax(), + token, + field_pats, + fields.into_iter(), + )) +} +fn signature_help_for_tuple_expr( + sema: &Semantics<'_, RootDatabase>, + expr: ast::TupleExpr, + token: SyntaxToken, +) -> Option { + let active_parameter = Some( + expr.syntax() + .children_with_tokens() + .filter_map(NodeOrToken::into_token) + .filter(|t| t.kind() == T![,]) + .take_while(|t| t.text_range().start() <= token.text_range().start()) + .count(), + ); + + let db = sema.db; + let mut res = SignatureHelp { + doc: None, + signature: String::from('('), + parameters: vec![], + active_parameter, + }; + let expr = sema.type_of_expr(&expr.into())?; + let fields = expr.original.tuple_fields(db); let mut buf = String::new(); - for ty in fields.into_iter().map(|it| it.ty(db)) { + for ty in fields { format_to!(buf, "{}", ty.display_truncated(db, Some(20))); res.push_call_param(&buf); buf.clear(); } - res.signature.push_str(")"); + res.signature.push(')'); Some(res) } @@ -465,8 +524,8 @@ fn signature_help_for_record_( token: SyntaxToken, ) -> Option { let active_parameter = field_list_children - .filter_map(syntax::NodeOrToken::into_token) - .filter(|t| t.kind() == syntax::T![,]) + .filter_map(NodeOrToken::into_token) + .filter(|t| t.kind() == T![,]) .take_while(|t| t.text_range().start() <= token.text_range().start()) .count(); @@ -486,7 +545,12 @@ fn signature_help_for_record_( let en = variant.parent_enum(db); res.doc = en.docs(db).map(|it| it.into()); - format_to!(res.signature, "enum {}::{} {{ ", en.name(db), variant.name(db)); + format_to!( + res.signature, + "enum {}::{} {{ ", + en.name(db).display(db), + variant.name(db).display(db) + ); } else { let adt = match path_res { PathResolution::SelfType(imp) => imp.self_ty(db).as_adt()?, @@ -498,12 +562,12 @@ fn signature_help_for_record_( hir::Adt::Struct(it) => { fields = it.fields(db); res.doc = it.docs(db).map(|it| it.into()); - format_to!(res.signature, "struct {} {{ ", it.name(db)); + format_to!(res.signature, "struct {} {{ ", it.name(db).display(db)); } hir::Adt::Union(it) => { fields = it.fields(db); res.doc = it.docs(db).map(|it| it.into()); - format_to!(res.signature, "union {} {{ ", it.name(db)); + format_to!(res.signature, "union {} {{ ", it.name(db).display(db)); } _ => return None, } @@ -514,7 +578,7 @@ fn signature_help_for_record_( let mut buf = String::new(); for (field, ty) in fields2 { let name = field.name(db); - format_to!(buf, "{name}: {}", ty.display_truncated(db, Some(20))); + format_to!(buf, "{}: {}", name.display(db), ty.display_truncated(db, Some(20))); res.push_record_field(&buf); buf.clear(); @@ -524,7 +588,7 @@ fn signature_help_for_record_( } for (name, field) in fields { let Some(field) = field else { continue }; - format_to!(buf, "{name}: {}", field.ty(db).display_truncated(db, Some(20))); + format_to!(buf, "{}: {}", name.display(db), field.ty(db).display_truncated(db, Some(20))); res.push_record_field(&buf); buf.clear(); } @@ -532,6 +596,46 @@ fn signature_help_for_record_( Some(res) } +fn signature_help_for_tuple_pat_ish( + db: &RootDatabase, + mut res: SignatureHelp, + pat: &SyntaxNode, + token: SyntaxToken, + mut field_pats: AstChildren, + fields: impl ExactSizeIterator, +) -> SignatureHelp { + let rest_pat = field_pats.find(|it| matches!(it, ast::Pat::RestPat(_))); + let is_left_of_rest_pat = + rest_pat.map_or(true, |it| token.text_range().start() < it.syntax().text_range().end()); + + let commas = pat + .children_with_tokens() + .filter_map(NodeOrToken::into_token) + .filter(|t| t.kind() == T![,]); + + res.active_parameter = { + Some(if is_left_of_rest_pat { + commas.take_while(|t| t.text_range().start() <= token.text_range().start()).count() + } else { + let n_commas = commas + .collect::>() + .into_iter() + .rev() + .take_while(|t| t.text_range().start() > token.text_range().start()) + .count(); + fields.len().saturating_sub(1).saturating_sub(n_commas) + }) + }; + + let mut buf = String::new(); + for ty in fields { + format_to!(buf, "{}", ty.display_truncated(db, Some(20))); + res.push_call_param(&buf); + buf.clear(); + } + res.signature.push_str(")"); + res +} #[cfg(test)] mod tests { use std::iter; @@ -1227,6 +1331,24 @@ fn main() { ) } + #[test] + fn call_info_for_fn_def_over_reference() { + check( + r#" +struct S; +fn foo(s: S) -> i32 { 92 } +fn main() { + let bar = &&&&&foo; + bar($0); +} + "#, + expect![[r#" + fn foo(s: S) -> i32 + ^^^^ + "#]], + ) + } + #[test] fn call_info_for_fn_ptr() { check( @@ -1823,4 +1945,290 @@ fn main() { "#]], ); } + + #[test] + fn test_tuple_expr_free() { + check( + r#" +fn main() { + (0$0, 1, 3); +} +"#, + expect![[r#" + (i32, i32, i32) + ^^^ --- --- + "#]], + ); + check( + r#" +fn main() { + ($0 1, 3); +} +"#, + expect![[r#" + (i32, i32) + ^^^ --- + "#]], + ); + check( + r#" +fn main() { + (1, 3 $0); +} +"#, + expect![[r#" + (i32, i32) + --- ^^^ + "#]], + ); + check( + r#" +fn main() { + (1, 3 $0,); +} +"#, + expect![[r#" + (i32, i32) + --- ^^^ + "#]], + ); + } + + #[test] + fn test_tuple_expr_expected() { + check( + r#" +fn main() { + let _: (&str, u32, u32)= ($0, 1, 3); +} +"#, + expect![[r#" + (&str, u32, u32) + ^^^^ --- --- + "#]], + ); + // FIXME: Should typeck report a 4-ary tuple for the expression here? + check( + r#" +fn main() { + let _: (&str, u32, u32, u32) = ($0, 1, 3); +} +"#, + expect![[r#" + (&str, u32, u32) + ^^^^ --- --- + "#]], + ); + check( + r#" +fn main() { + let _: (&str, u32, u32)= ($0, 1, 3, 5); +} +"#, + expect![[r#" + (&str, u32, u32, i32) + ^^^^ --- --- --- + "#]], + ); + } + + #[test] + fn test_tuple_pat_free() { + check( + r#" +fn main() { + let ($0, 1, 3); +} +"#, + expect![[r#" + ({unknown}, i32, i32) + ^^^^^^^^^ --- --- + "#]], + ); + check( + r#" +fn main() { + let (0$0, 1, 3); +} +"#, + expect![[r#" + (i32, i32, i32) + ^^^ --- --- + "#]], + ); + check( + r#" +fn main() { + let ($0 1, 3); +} +"#, + expect![[r#" + (i32, i32) + ^^^ --- + "#]], + ); + check( + r#" +fn main() { + let (1, 3 $0); +} +"#, + expect![[r#" + (i32, i32) + --- ^^^ + "#]], + ); + check( + r#" +fn main() { + let (1, 3 $0,); +} +"#, + expect![[r#" + (i32, i32) + --- ^^^ + "#]], + ); + check( + r#" +fn main() { + let (1, 3 $0, ..); +} +"#, + expect![[r#" + (i32, i32) + --- ^^^ + "#]], + ); + check( + r#" +fn main() { + let (1, 3, .., $0); +} +"#, + // FIXME: This is wrong, this should not mark the last as active + expect![[r#" + (i32, i32) + --- ^^^ + "#]], + ); + } + + #[test] + fn test_tuple_pat_expected() { + check( + r#" +fn main() { + let (0$0, 1, 3): (i32, i32, i32); +} +"#, + expect![[r#" + (i32, i32, i32) + ^^^ --- --- + "#]], + ); + check( + r#" +fn main() { + let ($0, 1, 3): (i32, i32, i32); +} +"#, + expect![[r#" + (i32, i32, i32) + ^^^ --- --- + "#]], + ); + check( + r#" +fn main() { + let (1, 3 $0): (i32,); +} +"#, + expect![[r#" + (i32, i32) + --- ^^^ + "#]], + ); + check( + r#" +fn main() { + let (1, 3 $0, ..): (i32, i32, i32, i32); +} +"#, + expect![[r#" + (i32, i32, i32, i32) + --- ^^^ --- --- + "#]], + ); + check( + r#" +fn main() { + let (1, 3, .., $0): (i32, i32, i32); +} +"#, + expect![[r#" + (i32, i32, i32) + --- --- ^^^ + "#]], + ); + } + #[test] + fn test_tuple_pat_expected_inferred() { + check( + r#" +fn main() { + let (0$0, 1, 3) = (1, 2 ,3); +} +"#, + expect![[r#" + (i32, i32, i32) + ^^^ --- --- + "#]], + ); + check( + r#" +fn main() { + let ($0 1, 3) = (1, 2, 3); +} +"#, + // FIXME: Should typeck report a 3-ary tuple for the pattern here? + expect![[r#" + (i32, i32) + ^^^ --- + "#]], + ); + check( + r#" +fn main() { + let (1, 3 $0) = (1,); +} +"#, + expect![[r#" + (i32, i32) + --- ^^^ + "#]], + ); + check( + r#" +fn main() { + let (1, 3 $0, ..) = (1, 2, 3, 4); +} +"#, + expect![[r#" + (i32, i32, i32, i32) + --- ^^^ --- --- + "#]], + ); + check( + r#" +fn main() { + let (1, 3, .., $0) = (1, 2, 3); +} +"#, + expect![[r#" + (i32, i32, i32) + --- --- ^^^ + "#]], + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide/src/ssr.rs b/src/tools/rust-analyzer/crates/ide/src/ssr.rs index 497eb1cc130d5..deaf3c9c416b4 100644 --- a/src/tools/rust-analyzer/crates/ide/src/ssr.rs +++ b/src/tools/rust-analyzer/crates/ide/src/ssr.rs @@ -56,8 +56,6 @@ pub(crate) fn ssr_assists( #[cfg(test)] mod tests { - use std::sync::Arc; - use expect_test::expect; use ide_assists::{Assist, AssistResolveStrategy}; use ide_db::{ @@ -65,6 +63,7 @@ mod tests { symbol_index::SymbolsDatabase, FxHashSet, RootDatabase, }; + use triomphe::Arc; use super::ssr_assists; diff --git a/src/tools/rust-analyzer/crates/ide/src/static_index.rs b/src/tools/rust-analyzer/crates/ide/src/static_index.rs index c97691b14a57f..3e3d9f8f85c45 100644 --- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs +++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs @@ -118,9 +118,11 @@ impl StaticIndex<'_> { adjustment_hints_hide_outside_unsafe: false, hide_named_constructor_hints: false, hide_closure_initialization_hints: false, + closure_style: hir::ClosureStyle::ImplFn, param_names_for_lifetime_elision_hints: false, binding_mode_hints: false, max_length: Some(25), + closure_capture_hints: false, closing_brace_hints_min_lines: Some(25), }, file_id, @@ -136,10 +138,10 @@ impl StaticIndex<'_> { }); let hover_config = HoverConfig { links_in_hover: true, + memory_layout: None, documentation: true, keywords: true, format: crate::HoverDocFormat::Markdown, - interpret_tests: false, }; let tokens = tokens.filter(|token| { matches!( diff --git a/src/tools/rust-analyzer/crates/ide/src/status.rs b/src/tools/rust-analyzer/crates/ide/src/status.rs index 7ce782f93be14..d2c77e2dc7975 100644 --- a/src/tools/rust-analyzer/crates/ide/src/status.rs +++ b/src/tools/rust-analyzer/crates/ide/src/status.rs @@ -1,9 +1,18 @@ -use std::{fmt, sync::Arc}; +use std::{fmt, marker::PhantomData}; -use hir::{ExpandResult, MacroFile}; -use ide_db::base_db::{ - salsa::debug::{DebugQueryTable, TableEntry}, - CrateId, FileId, FileTextQuery, SourceDatabase, SourceRootId, +use hir::{ + db::{AstIdMapQuery, AttrsQuery, BlockDefMapQuery, ParseMacroExpansionQuery}, + Attr, Attrs, ExpandResult, MacroFile, Module, +}; +use ide_db::{ + base_db::{ + salsa::{ + debug::{DebugQueryTable, TableEntry}, + Query, QueryTable, + }, + CrateId, FileId, FileTextQuery, ParseQuery, SourceDatabase, SourceRootId, + }, + symbol_index::ModuleSymbolsQuery, }; use ide_db::{ symbol_index::{LibrarySymbolsQuery, SymbolIndex}, @@ -14,13 +23,7 @@ use profile::{memory_usage, Bytes}; use std::env; use stdx::format_to; use syntax::{ast, Parse, SyntaxNode}; - -fn syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats { - ide_db::base_db::ParseQuery.in_db(db).entries::() -} -fn macro_syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats { - hir::db::ParseMacroExpansionQuery.in_db(db).entries::() -} +use triomphe::Arc; // Feature: Status // @@ -34,15 +37,22 @@ fn macro_syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats { // image::https://user-images.githubusercontent.com/48062697/113065584-05f34500-91b1-11eb-98cc-5c196f76be7f.gif[] pub(crate) fn status(db: &RootDatabase, file_id: Option) -> String { let mut buf = String::new(); - format_to!(buf, "{}\n", FileTextQuery.in_db(db).entries::()); - format_to!(buf, "{}\n", LibrarySymbolsQuery.in_db(db).entries::()); - format_to!(buf, "{}\n", syntax_tree_stats(db)); - format_to!(buf, "{} (Macros)\n", macro_syntax_tree_stats(db)); + + format_to!(buf, "{}\n", collect_query(FileTextQuery.in_db(db))); + format_to!(buf, "{}\n", collect_query(ParseQuery.in_db(db))); + format_to!(buf, "{}\n", collect_query(ParseMacroExpansionQuery.in_db(db))); + format_to!(buf, "{}\n", collect_query(LibrarySymbolsQuery.in_db(db))); + format_to!(buf, "{}\n", collect_query(ModuleSymbolsQuery.in_db(db))); format_to!(buf, "{} in total\n", memory_usage()); if env::var("RA_COUNT").is_ok() { format_to!(buf, "\nCounts:\n{}", profile::countme::get_all()); } + format_to!(buf, "\nDebug info:\n"); + format_to!(buf, "{}\n", collect_query(AttrsQuery.in_db(db))); + format_to!(buf, "{} ast id maps\n", collect_query_count(AstIdMapQuery.in_db(db))); + format_to!(buf, "{} block def maps\n", collect_query_count(BlockDefMapQuery.in_db(db))); + if let Some(file_id) = file_id { format_to!(buf, "\nFile info:\n"); let crates = crate::parent_module::crates_for(db, file_id); @@ -52,8 +62,8 @@ pub(crate) fn status(db: &RootDatabase, file_id: Option) -> String { let crate_graph = db.crate_graph(); for krate in crates { let display_crate = |krate: CrateId| match &crate_graph[krate].display_name { - Some(it) => format!("{it}({krate:?})"), - None => format!("{krate:?}"), + Some(it) => format!("{it}({})", krate.into_raw()), + None => format!("{}", krate.into_raw()), }; format_to!(buf, "Crate: {}\n", display_crate(krate)); let deps = crate_graph[krate] @@ -68,6 +78,82 @@ pub(crate) fn status(db: &RootDatabase, file_id: Option) -> String { buf.trim().to_string() } +fn collect_query<'q, Q>(table: QueryTable<'q, Q>) -> ::Collector +where + QueryTable<'q, Q>: DebugQueryTable, + Q: QueryCollect, + ::Storage: 'q, + ::Collector: StatCollect< + as DebugQueryTable>::Key, + as DebugQueryTable>::Value, + >, +{ + struct StatCollectorWrapper(C); + impl, K, V> FromIterator> for StatCollectorWrapper { + fn from_iter(iter: T) -> StatCollectorWrapper + where + T: IntoIterator>, + { + let mut res = C::default(); + for entry in iter { + res.collect_entry(entry.key, entry.value); + } + StatCollectorWrapper(res) + } + } + table.entries::::Collector>>().0 +} + +fn collect_query_count<'q, Q>(table: QueryTable<'q, Q>) -> usize +where + QueryTable<'q, Q>: DebugQueryTable, + Q: Query, + ::Storage: 'q, +{ + struct EntryCounter(usize); + impl FromIterator> for EntryCounter { + fn from_iter(iter: T) -> EntryCounter + where + T: IntoIterator>, + { + EntryCounter(iter.into_iter().count()) + } + } + table.entries::().0 +} + +trait QueryCollect: Query { + type Collector; +} + +impl QueryCollect for LibrarySymbolsQuery { + type Collector = SymbolsStats; +} + +impl QueryCollect for ParseQuery { + type Collector = SyntaxTreeStats; +} + +impl QueryCollect for ParseMacroExpansionQuery { + type Collector = SyntaxTreeStats; +} + +impl QueryCollect for FileTextQuery { + type Collector = FilesStats; +} + +impl QueryCollect for ModuleSymbolsQuery { + type Collector = SymbolsStats; +} + +impl QueryCollect for AttrsQuery { + type Collector = AttrsStats; +} + +trait StatCollect: Default { + fn collect_entry(&mut self, key: K, value: Option); +} + #[derive(Default)] struct FilesStats { total: usize, @@ -80,85 +166,98 @@ impl fmt::Display for FilesStats { } } -impl FromIterator>> for FilesStats { - fn from_iter(iter: T) -> FilesStats - where - T: IntoIterator>>, - { - let mut res = FilesStats::default(); - for entry in iter { - res.total += 1; - res.size += entry.value.unwrap().len(); - } - res +impl StatCollect> for FilesStats { + fn collect_entry(&mut self, _: FileId, value: Option>) { + self.total += 1; + self.size += value.unwrap().len(); } } #[derive(Default)] -pub(crate) struct SyntaxTreeStats { +pub(crate) struct SyntaxTreeStats { total: usize, pub(crate) retained: usize, } -impl fmt::Display for SyntaxTreeStats { +impl fmt::Display for SyntaxTreeStats { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(fmt, "{} trees, {} preserved", self.total, self.retained) + write!( + fmt, + "{} trees, {} preserved{}", + self.total, + self.retained, + if MACROS { " (macros)" } else { "" } + ) } } -impl FromIterator>> for SyntaxTreeStats { - fn from_iter(iter: T) -> SyntaxTreeStats - where - T: IntoIterator>>, - { - let mut res = SyntaxTreeStats::default(); - for entry in iter { - res.total += 1; - res.retained += entry.value.is_some() as usize; - } - res +impl StatCollect> for SyntaxTreeStats { + fn collect_entry(&mut self, _: FileId, value: Option>) { + self.total += 1; + self.retained += value.is_some() as usize; } } -impl FromIterator, M)>>>> - for SyntaxTreeStats -{ - fn from_iter(iter: T) -> SyntaxTreeStats - where - T: IntoIterator, M)>>>>, - { - let mut res = SyntaxTreeStats::default(); - for entry in iter { - res.total += 1; - res.retained += entry.value.is_some() as usize; +impl StatCollect, M)>> for SyntaxTreeStats { + fn collect_entry(&mut self, _: MacroFile, value: Option, M)>>) { + self.total += 1; + self.retained += value.is_some() as usize; + } +} + +struct SymbolsStats { + total: usize, + size: Bytes, + phantom: PhantomData, +} + +impl Default for SymbolsStats { + fn default() -> Self { + Self { total: Default::default(), size: Default::default(), phantom: PhantomData } + } +} + +impl fmt::Display for SymbolsStats { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(fmt, "{} of module index symbols ({})", self.size, self.total) + } +} +impl fmt::Display for SymbolsStats { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(fmt, "{} of library index symbols ({})", self.size, self.total) + } +} +impl StatCollect> for SymbolsStats { + fn collect_entry(&mut self, _: Key, value: Option>) { + if let Some(symbols) = value { + self.total += symbols.len(); + self.size += symbols.memory_size(); } - res } } #[derive(Default)] -struct LibrarySymbolsStats { +struct AttrsStats { + entries: usize, total: usize, - size: Bytes, } -impl fmt::Display for LibrarySymbolsStats { +impl fmt::Display for AttrsStats { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(fmt, "{} of index symbols ({})", self.size, self.total) + let size = + self.entries * std::mem::size_of::() + self.total * std::mem::size_of::(); + let size = Bytes::new(size as _); + write!( + fmt, + "{} attribute query entries, {} total attributes ({} for storing entries)", + self.entries, self.total, size + ) } } -impl FromIterator>> for LibrarySymbolsStats { - fn from_iter(iter: T) -> LibrarySymbolsStats - where - T: IntoIterator>>, - { - let mut res = LibrarySymbolsStats::default(); - for entry in iter { - let symbols = entry.value.unwrap(); - res.total += symbols.len(); - res.size += symbols.memory_size(); - } - res +impl StatCollect for AttrsStats { + fn collect_entry(&mut self, _: Key, value: Option) { + self.entries += 1; + self.total += value.map_or(0, |it| it.len()); } } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs index 454a250f3ded4..8c02fe81648a5 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs @@ -16,13 +16,19 @@ mod tests; use hir::{Name, Semantics}; use ide_db::{FxHashMap, RootDatabase, SymbolKind}; use syntax::{ - ast, AstNode, AstToken, NodeOrToken, SyntaxKind::*, SyntaxNode, TextRange, WalkEvent, T, + ast::{self, IsString}, + AstNode, AstToken, NodeOrToken, + SyntaxKind::*, + SyntaxNode, TextRange, WalkEvent, T, }; use crate::{ syntax_highlighting::{ - escape::highlight_escape_string, format::highlight_format_string, highlights::Highlights, - macro_::MacroHighlighter, tags::Highlight, + escape::{highlight_escape_char, highlight_escape_string}, + format::highlight_format_string, + highlights::Highlights, + macro_::MacroHighlighter, + tags::Highlight, }, FileId, HlMod, HlOperator, HlPunct, HlTag, }; @@ -163,6 +169,7 @@ pub struct HighlightConfig { // injected:: Emitted for doc-string injected highlighting like rust source blocks in documentation. // intraDocLink:: Emitted for intra doc links in doc-strings. // library:: Emitted for items that are defined outside of the current crate. +// macro:: Emitted for tokens inside macro calls. // mutable:: Emitted for mutable locals and statics as well as functions taking `&mut self`. // public:: Emitted for items that are from the current crate and are `pub`. // reference:: Emitted for locals behind a reference and functions taking `self` by reference. @@ -237,6 +244,7 @@ fn traverse( let mut current_macro: Option = None; let mut macro_highlighter = MacroHighlighter::default(); let mut inside_attribute = false; + let mut inside_macro_call = false; // Walk all nodes, keeping track of whether we are inside a macro or not. // If in macro, expand it first and highlight the expanded code. @@ -267,46 +275,50 @@ fn traverse( inside_attribute = false } - Enter(NodeOrToken::Node(node)) if ast::Item::can_cast(node.kind()) => { - match ast::Item::cast(node.clone()) { - Some(ast::Item::MacroRules(mac)) => { - macro_highlighter.init(); - current_macro = Some(mac.into()); - continue; - } - Some(ast::Item::MacroDef(mac)) => { - macro_highlighter.init(); - current_macro = Some(mac.into()); - continue; - } - Some(item) => { - if matches!(node.kind(), FN | CONST | STATIC) { - bindings_shadow_count.clear(); + Enter(NodeOrToken::Node(node)) => match ast::Item::cast(node.clone()) { + Some(item) => { + match item { + ast::Item::MacroRules(mac) => { + macro_highlighter.init(); + current_macro = Some(mac.into()); + continue; + } + ast::Item::MacroDef(mac) => { + macro_highlighter.init(); + current_macro = Some(mac.into()); + continue; + } + ast::Item::Fn(_) | ast::Item::Const(_) | ast::Item::Static(_) => { + bindings_shadow_count.clear() + } + ast::Item::MacroCall(_) => { + inside_macro_call = true; } + _ => (), + } - if attr_or_derive_item.is_none() { - if sema.is_attr_macro_call(&item) { - attr_or_derive_item = Some(AttrOrDerive::Attr(item)); - } else { - let adt = match item { - ast::Item::Enum(it) => Some(ast::Adt::Enum(it)), - ast::Item::Struct(it) => Some(ast::Adt::Struct(it)), - ast::Item::Union(it) => Some(ast::Adt::Union(it)), - _ => None, - }; - match adt { - Some(adt) if sema.is_derive_annotated(&adt) => { - attr_or_derive_item = - Some(AttrOrDerive::Derive(ast::Item::from(adt))); - } - _ => (), + if attr_or_derive_item.is_none() { + if sema.is_attr_macro_call(&item) { + attr_or_derive_item = Some(AttrOrDerive::Attr(item)); + } else { + let adt = match item { + ast::Item::Enum(it) => Some(ast::Adt::Enum(it)), + ast::Item::Struct(it) => Some(ast::Adt::Struct(it)), + ast::Item::Union(it) => Some(ast::Adt::Union(it)), + _ => None, + }; + match adt { + Some(adt) if sema.is_derive_annotated(&adt) => { + attr_or_derive_item = + Some(AttrOrDerive::Derive(ast::Item::from(adt))); } + _ => (), } } } - _ => (), } - } + _ => (), + }, Leave(NodeOrToken::Node(node)) if ast::Item::can_cast(node.kind()) => { match ast::Item::cast(node.clone()) { Some(ast::Item::MacroRules(mac)) => { @@ -324,6 +336,9 @@ fn traverse( { attr_or_derive_item = None; } + Some(ast::Item::MacroCall(_)) => { + inside_macro_call = false; + } _ => (), } } @@ -419,14 +434,35 @@ fn traverse( continue; } highlight_format_string(hl, &string, &expanded_string, range); - highlight_escape_string(hl, &string, range.start()); + + if !string.is_raw() { + highlight_escape_string(hl, &string, range.start()); + } } } else if ast::ByteString::can_cast(token.kind()) && ast::ByteString::can_cast(descended_token.kind()) { if let Some(byte_string) = ast::ByteString::cast(token) { - highlight_escape_string(hl, &byte_string, range.start()); + if !byte_string.is_raw() { + highlight_escape_string(hl, &byte_string, range.start()); + } } + } else if ast::CString::can_cast(token.kind()) + && ast::CString::can_cast(descended_token.kind()) + { + if let Some(c_string) = ast::CString::cast(token) { + if !c_string.is_raw() { + highlight_escape_string(hl, &c_string, range.start()); + } + } + } else if ast::Char::can_cast(token.kind()) + && ast::Char::can_cast(descended_token.kind()) + { + let Some(char) = ast::Char::cast(token) else { + continue; + }; + + highlight_escape_char(hl, &char, range.start()) } } @@ -455,32 +491,42 @@ fn traverse( } // apply config filtering - match &mut highlight.tag { - HlTag::StringLiteral if !config.strings => continue, - // If punctuation is disabled, make the macro bang part of the macro call again. - tag @ HlTag::Punctuation(HlPunct::MacroBang) => { - if !config.macro_bang { - *tag = HlTag::Symbol(SymbolKind::Macro); - } else if !config.specialize_punctuation { - *tag = HlTag::Punctuation(HlPunct::Other); - } - } - HlTag::Punctuation(_) if !config.punctuation => continue, - tag @ HlTag::Punctuation(_) if !config.specialize_punctuation => { - *tag = HlTag::Punctuation(HlPunct::Other); - } - HlTag::Operator(_) if !config.operator && highlight.mods.is_empty() => continue, - tag @ HlTag::Operator(_) if !config.specialize_operator => { - *tag = HlTag::Operator(HlOperator::Other); - } - _ => (), + if !filter_by_config(&mut highlight, config) { + continue; } if inside_attribute { highlight |= HlMod::Attribute } + if inside_macro_call && tt_level > 0 { + highlight |= HlMod::Macro + } hl.add(HlRange { range, highlight, binding_hash }); } } } + +fn filter_by_config(highlight: &mut Highlight, config: HighlightConfig) -> bool { + match &mut highlight.tag { + HlTag::StringLiteral if !config.strings => return false, + // If punctuation is disabled, make the macro bang part of the macro call again. + tag @ HlTag::Punctuation(HlPunct::MacroBang) => { + if !config.macro_bang { + *tag = HlTag::Symbol(SymbolKind::Macro); + } else if !config.specialize_punctuation { + *tag = HlTag::Punctuation(HlPunct::Other); + } + } + HlTag::Punctuation(_) if !config.punctuation => return false, + tag @ HlTag::Punctuation(_) if !config.specialize_punctuation => { + *tag = HlTag::Punctuation(HlPunct::Other); + } + HlTag::Operator(_) if !config.operator && highlight.mods.is_empty() => return false, + tag @ HlTag::Operator(_) if !config.specialize_operator => { + *tag = HlTag::Operator(HlOperator::Other); + } + _ => (), + } + true +} diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/escape.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/escape.rs index 6a1236c793b83..211e3588095cf 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/escape.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/escape.rs @@ -1,8 +1,8 @@ //! Syntax highlighting for escape sequences use crate::syntax_highlighting::highlights::Highlights; use crate::{HlRange, HlTag}; -use syntax::ast::IsString; -use syntax::TextSize; +use syntax::ast::{Char, IsString}; +use syntax::{AstToken, TextRange, TextSize}; pub(super) fn highlight_escape_string( stack: &mut Highlights, @@ -23,3 +23,23 @@ pub(super) fn highlight_escape_string( } }); } + +pub(super) fn highlight_escape_char(stack: &mut Highlights, char: &Char, start: TextSize) { + if char.value().is_none() { + return; + } + + let text = char.text(); + if !text.starts_with('\'') || !text.ends_with('\'') { + return; + } + + let text = &text[1..text.len() - 1]; + if !text.starts_with('\\') { + return; + } + + let range = + TextRange::new(start + TextSize::from(1), start + TextSize::from(text.len() as u32 + 1)); + stack.add(HlRange { range, highlight: HlTag::EscapeSequence.into(), binding_hash: None }) +} diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs index 2111baad74d74..3c40246a69d37 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs @@ -26,7 +26,7 @@ pub(super) fn token(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) -> O } let highlight: Highlight = match token.kind() { - STRING | BYTE_STRING => HlTag::StringLiteral.into(), + STRING | BYTE_STRING | C_STRING => HlTag::StringLiteral.into(), INT_NUMBER if token.parent_ancestors().nth(1).map(|it| it.kind()) == Some(FIELD_EXPR) => { SymbolKind::Field.into() } @@ -340,7 +340,7 @@ fn highlight_def( Definition::Field(_) => Highlight::new(HlTag::Symbol(SymbolKind::Field)), Definition::Module(module) => { let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Module)); - if module.is_crate_root(db) { + if module.is_crate_root() { h |= HlMod::CrateRoot; } h @@ -675,14 +675,12 @@ fn is_consumed_lvalue(node: &SyntaxNode, local: &hir::Local, db: &RootDatabase) /// Returns true if the parent nodes of `node` all match the `SyntaxKind`s in `kinds` exactly. fn parents_match(mut node: NodeOrToken, mut kinds: &[SyntaxKind]) -> bool { - while let (Some(parent), [kind, rest @ ..]) = (&node.parent(), kinds) { + while let (Some(parent), [kind, rest @ ..]) = (node.parent(), kinds) { if parent.kind() != *kind { return false; } - // FIXME: Would be nice to get parent out of the match, but binding by-move and by-value - // in the same pattern is unstable: rust-lang/rust#68354. - node = node.parent().unwrap().into(); + node = parent.into(); kinds = rest; } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs index 3c4cfc78152dd..901df147d32c0 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs @@ -52,7 +52,11 @@ pub(super) fn ra_fixture( if let Some(next) = text.strip_prefix(marker) { if let Some(range) = literal.map_range_up(TextRange::at(offset, TextSize::of(marker))) { - hl.add(HlRange { range, highlight: HlTag::Keyword.into(), binding_hash: None }); + hl.add(HlRange { + range, + highlight: HlTag::Keyword | HlMod::Injected, + binding_hash: None, + }); } text = next; @@ -66,7 +70,16 @@ pub(super) fn ra_fixture( for mut hl_range in analysis .highlight( - HighlightConfig { syntactic_name_ref_highlighting: false, ..config }, + HighlightConfig { + syntactic_name_ref_highlighting: false, + punctuation: true, + operator: true, + strings: true, + specialize_punctuation: config.specialize_punctuation, + specialize_operator: config.operator, + inject_doc_comment: config.inject_doc_comment, + macro_bang: config.macro_bang, + }, tmp_file_id, ) .unwrap() @@ -74,6 +87,7 @@ pub(super) fn ra_fixture( for range in inj.map_range_up(hl_range.range) { if let Some(range) = literal.map_range_up(range) { hl_range.range = range; + hl_range.highlight |= HlMod::Injected; hl.add(hl_range); } } @@ -217,7 +231,16 @@ pub(super) fn doc_comment( if let Ok(ranges) = analysis.with_db(|db| { super::highlight( db, - HighlightConfig { syntactic_name_ref_highlighting: true, ..config }, + HighlightConfig { + syntactic_name_ref_highlighting: true, + punctuation: true, + operator: true, + strings: true, + specialize_punctuation: config.specialize_punctuation, + specialize_operator: config.operator, + inject_doc_comment: config.inject_doc_comment, + macro_bang: config.macro_bang, + }, tmp_file_id, None, ) diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs index a81c4ee0cbd41..f983109115f6c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs @@ -49,7 +49,7 @@ pub enum HlMod { Associated = 0, /// Used with keywords like `async` and `await`. Async, - /// Used to differentiate individual elements within attributes. + /// Used to differentiate individual elements within attribute calls. Attribute, /// Callable item or value. Callable, @@ -72,6 +72,8 @@ pub enum HlMod { IntraDocLink, /// Used for items from other crates. Library, + /// Used to differentiate individual elements within macro calls. + Macro, /// Mutable binding. Mutable, /// Used for public items. @@ -200,7 +202,7 @@ impl fmt::Display for HlTag { } impl HlMod { - const ALL: &'static [HlMod; 19] = &[ + const ALL: &'static [HlMod; HlMod::Unsafe as usize + 1] = &[ HlMod::Associated, HlMod::Async, HlMod::Attribute, @@ -214,6 +216,7 @@ impl HlMod { HlMod::Injected, HlMod::IntraDocLink, HlMod::Library, + HlMod::Macro, HlMod::Mutable, HlMod::Public, HlMod::Reference, @@ -237,6 +240,7 @@ impl HlMod { HlMod::Injected => "injected", HlMod::IntraDocLink => "intra_doc_link", HlMod::Library => "library", + HlMod::Macro => "macro", HlMod::Mutable => "mutable", HlMod::Public => "public", HlMod::Reference => "reference", diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html index 18045f1f55afd..35f240d428471 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html @@ -93,7 +93,7 @@ /// let foo = Foo::new(); /// /// // calls bar on foo - /// assert!(foo.bar()); + /// assert!(foo.bar()); /// /// let bar = foo.bar || Foo::bar; /// @@ -145,7 +145,7 @@ /// ``` /// macro_rules! noop { ($expr:expr) => { $expr }} -/// noop!(1); +/// noop!(1); /// ``` macro_rules! noop { ($expr:expr) => { @@ -165,7 +165,7 @@ /// #[cfg_attr(feature = "alloc", doc = "```rust")] #[cfg_attr(not(feature = "alloc"), doc = "```ignore")] -/// let _ = example(&alloc::vec![1, 2, 3]); +/// let _ = example(&alloc::vec![1, 2, 3]); /// ``` pub fn mix_and_match() {} diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html index af41796e2169b..87b9da46e2cc8 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html @@ -43,5 +43,5 @@ .unresolved_reference { color: #FC5555; text-decoration: wavy underline; }

extern crate std;
-extern crate alloc as abc;
+extern crate alloc as abc;
 
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html index 9f2b1926b511d..6b049f379ac1d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html @@ -178,7 +178,7 @@ impl<T> Option<T> { fn and<U>(self, other: Option<U>) -> Option<(T, U)> { match other { - None => unimplemented!(), + None => unimplemented!(), Nope => Nope, } } @@ -192,7 +192,7 @@ async fn async_main() { let f1 = learn_and_sing(); let f2 = dance(); - futures::join!(f1, f2); + futures::join!(f1, f2); } fn use_foo_items() { @@ -204,7 +204,7 @@ let control_flow = foo::identity(foo::ControlFlow::Continue); if control_flow.should_die() { - foo::die!(); + foo::die!(); } } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html index abcd80c280bf3..d9c3db6fbb510 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html @@ -45,18 +45,18 @@
fn fixture(ra_fixture: &str) {}
 
 fn main() {
-    fixture(r#"
-trait Foo {
-    fn foo() {
-        println!("2 + 2 = {}", 4);
-    }
-}"#
+    fixture(r#"
+trait Foo {
+    fn foo() {
+        println!("2 + 2 = {}", 4);
+    }
+}"#
     );
-    fixture(r"
-fn foo() {
-    foo($0{
-        92
-    }$0)
-}"
+    fixture(r"
+fn foo() {
+    foo($0{
+        92
+    }$0)
+}"
     );
 }
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html index 66f9ede96295f..3900959bedf8d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html @@ -53,6 +53,6 @@ macro_rules! void { ($($tt:tt)*) => {} } -void!(Self); +void!(Self); struct __ where Self:; fn __(_: Self) {}
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html index 54d4279525dd5..2cbbf69641525 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html @@ -42,21 +42,21 @@ .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } -
proc_macros::mirror! {
-    {
-        ,i32 :x pub
-        ,i32 :y pub
-    } Foo struct
-}
+
proc_macros::mirror! {
+    {
+        ,i32 :x pub
+        ,i32 :y pub
+    } Foo struct
+}
 macro_rules! def_fn {
     ($($tt:tt)*) => {$($tt)*}
 }
 
-def_fn! {
-    fn bar() -> u32 {
-        100
-    }
-}
+def_fn! {
+    fn bar() -> u32 {
+        100
+    }
+}
 
 macro_rules! dont_color_me_braces {
     () => {0}
@@ -90,7 +90,7 @@
 }
 
 fn main() {
-    println!("Hello, {}!", 92);
-    dont_color_me_braces!();
-    noop!(noop!(1));
+    println!("Hello, {}!", 92);
+    dont_color_me_braces!();
+    noop!(noop!(1));
 }
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html index a626cda3fe8b4..327e1502d19e0 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html @@ -93,72 +93,83 @@ } fn main() { - println!("Hello {{Hello}}"); + let a = '\n'; + let a = '\t'; + let a = '\e'; // invalid escape + let a = 'e'; + let a = ' '; + let a = '\u{48}'; + let a = '\u{4823}'; + let a = '\x65'; + let a = '\x00'; + + println!("Hello {{Hello}}"); // from https://doc.rust-lang.org/std/fmt/index.html - println!("Hello"); // => "Hello" - println!("Hello, {}!", "world"); // => "Hello, world!" - println!("The number is {}", 1); // => "The number is 1" - println!("{:?}", (3, 4)); // => "(3, 4)" - println!("{value}", value=4); // => "4" - println!("{} {}", 1, 2); // => "1 2" - println!("{:04}", 42); // => "0042" with leading zerosV - println!("{1} {} {0} {}", 1, 2); // => "2 1 1 2" - println!("{argument}", argument = "test"); // => "test" - println!("{name} {}", 1, name = 2); // => "2 1" - println!("{a} {c} {b}", a="a", b='b', c=3); // => "a 3 b" - println!("{{{}}}", 2); // => "{2}" - println!("Hello {:5}!", "x"); - println!("Hello {:1$}!", "x", 5); - println!("Hello {1:0$}!", 5, "x"); - println!("Hello {:width$}!", "x", width = 5); - println!("Hello {:<5}!", "x"); - println!("Hello {:-<5}!", "x"); - println!("Hello {:^5}!", "x"); - println!("Hello {:>5}!", "x"); - println!("Hello {:+}!", 5); - println!("{:#x}!", 27); - println!("Hello {:05}!", 5); - println!("Hello {:05}!", -5); - println!("{:#010x}!", 27); - println!("Hello {0} is {1:.5}", "x", 0.01); - println!("Hello {1} is {2:.0$}", 5, "x", 0.01); - println!("Hello {0} is {2:.1$}", "x", 5, 0.01); - println!("Hello {} is {:.*}", "x", 5, 0.01); - println!("Hello {} is {2:.*}", "x", 5, 0.01); - println!("Hello {} is {number:.prec$}", "x", prec = 5, number = 0.01); - println!("{}, `{name:.*}` has 3 fractional digits", "Hello", 3, name=1234.56); - println!("{}, `{name:.*}` has 3 characters", "Hello", 3, name="1234.56"); - println!("{}, `{name:>8.*}` has 3 right-aligned characters", "Hello", 3, name="1234.56"); + println!("Hello"); // => "Hello" + println!("Hello, {}!", "world"); // => "Hello, world!" + println!("The number is {}", 1); // => "The number is 1" + println!("{:?}", (3, 4)); // => "(3, 4)" + println!("{value}", value=4); // => "4" + println!("{} {}", 1, 2); // => "1 2" + println!("{:04}", 42); // => "0042" with leading zerosV + println!("{1} {} {0} {}", 1, 2); // => "2 1 1 2" + println!("{argument}", argument = "test"); // => "test" + println!("{name} {}", 1, name = 2); // => "2 1" + println!("{a} {c} {b}", a="a", b='b', c=3); // => "a 3 b" + println!("{{{}}}", 2); // => "{2}" + println!("Hello {:5}!", "x"); + println!("Hello {:1$}!", "x", 5); + println!("Hello {1:0$}!", 5, "x"); + println!("Hello {:width$}!", "x", width = 5); + println!("Hello {:<5}!", "x"); + println!("Hello {:-<5}!", "x"); + println!("Hello {:^5}!", "x"); + println!("Hello {:>5}!", "x"); + println!("Hello {:+}!", 5); + println!("{:#x}!", 27); + println!("Hello {:05}!", 5); + println!("Hello {:05}!", -5); + println!("{:#010x}!", 27); + println!("Hello {0} is {1:.5}", "x", 0.01); + println!("Hello {1} is {2:.0$}", 5, "x", 0.01); + println!("Hello {0} is {2:.1$}", "x", 5, 0.01); + println!("Hello {} is {:.*}", "x", 5, 0.01); + println!("Hello {} is {2:.*}", "x", 5, 0.01); + println!("Hello {} is {number:.prec$}", "x", prec = 5, number = 0.01); + println!("{}, `{name:.*}` has 3 fractional digits", "Hello", 3, name=1234.56); + println!("{}, `{name:.*}` has 3 characters", "Hello", 3, name="1234.56"); + println!("{}, `{name:>8.*}` has 3 right-aligned characters", "Hello", 3, name="1234.56"); let _ = "{}" let _ = "{{}}"; - println!("Hello {{}}"); - println!("{{ Hello"); - println!("Hello }}"); - println!("{{Hello}}"); - println!("{{ Hello }}"); - println!("{{Hello }}"); - println!("{{ Hello}}"); + println!("Hello {{}}"); + println!("{{ Hello"); + println!("Hello }}"); + println!("{{Hello}}"); + println!("{{ Hello }}"); + println!("{{Hello }}"); + println!("{{ Hello}}"); - println!(r"Hello, {}!", "world"); + println!(r"Hello, {}!", "world"); // escape sequences - println!("Hello\nWorld"); - println!("\u{48}\x65\x6C\x6C\x6F World"); + println!("Hello\nWorld"); + println!("\u{48}\x65\x6C\x6C\x6F World"); let _ = "\x28\x28\x00\x63\n"; let _ = b"\x28\x28\x00\x63\n"; + let _ = r"\\"; - println!("{\x41}", A = 92); - println!("{ничоси}", ничоси = 92); + println!("{\x41}", A = 92); + println!("{ничоси}", ничоси = 92); - println!("{:x?} {} ", thingy, n2); - panic!("{}", 0); - panic!("more {}", 1); - assert!(true, "{}", 1); - assert!(true, "{} asdasd", 1); - toho!("{}fmt", 0); - asm!("mov eax, {0}"); - format_args!(concat!("{}"), "{}"); + println!("{:x?} {} ", thingy, n2); + panic!("{}", 0); + panic!("more {}", 1); + assert!(true, "{}", 1); + assert!(true, "{} asdasd", 1); + toho!("{}fmt", 0); + asm!("mov eax, {0}"); + format_args!(concat!("{}"), "{}"); }
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html index 1992bdc6ae35f..654d51b8a43d9 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html @@ -89,13 +89,13 @@ let x = &5 as *const _ as *const usize; let u = Union { b: 0 }; - id! { - unsafe { unsafe_deref!() } - }; + id! { + unsafe { unsafe_deref!() } + }; unsafe { - unsafe_deref!(); - id! { unsafe_deref!() }; + unsafe_deref!(); + id! { unsafe_deref!() }; // unsafe fn and method calls unsafe_fn(); diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs index ac9bd8e39dc02..887d18b989a14 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs @@ -439,6 +439,16 @@ macro_rules! toho { } fn main() { + let a = '\n'; + let a = '\t'; + let a = '\e'; // invalid escape + let a = 'e'; + let a = ' '; + let a = '\u{48}'; + let a = '\u{4823}'; + let a = '\x65'; + let a = '\x00'; + println!("Hello {{Hello}}"); // from https://doc.rust-lang.org/std/fmt/index.html println!("Hello"); // => "Hello" @@ -495,6 +505,7 @@ fn main() { let _ = "\x28\x28\x00\x63\n"; let _ = b"\x28\x28\x00\x63\n"; + let _ = r"\\"; println!("{\x41}", A = 92); println!("{ничоси}", ничоси = 92); @@ -1126,5 +1137,5 @@ fn benchmark_syntax_highlighting_parser() { .filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Function)) .count() }; - assert_eq!(hash, 1608); + assert_eq!(hash, 1169); } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_tree.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_tree.rs index bb6827e8a44e4..df1971242657d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_tree.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_tree.rs @@ -1,5 +1,7 @@ -use ide_db::base_db::{FileId, SourceDatabase}; -use ide_db::RootDatabase; +use ide_db::{ + base_db::{FileId, SourceDatabase}, + RootDatabase, +}; use syntax::{ AstNode, NodeOrToken, SourceFile, SyntaxKind::STRING, SyntaxToken, TextRange, TextSize, }; diff --git a/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs b/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs index 17a1e385b7724..8c84461f659f7 100644 --- a/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs +++ b/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs @@ -1,11 +1,9 @@ -use std::sync::Arc; - use dot::{Id, LabelText}; use ide_db::{ base_db::{CrateGraph, CrateId, Dependency, SourceDatabase, SourceDatabaseExt}, - RootDatabase, + FxHashSet, RootDatabase, }; -use stdx::hash::NoHashHashSet; +use triomphe::Arc; // Feature: View Crate Graph // @@ -42,7 +40,7 @@ pub(crate) fn view_crate_graph(db: &RootDatabase, full: bool) -> Result, - crates_to_render: NoHashHashSet, + crates_to_render: FxHashSet, } type Edge<'a> = (CrateId, &'a Dependency); @@ -80,7 +78,7 @@ impl<'a> dot::Labeller<'a, CrateId, Edge<'a>> for DotCrateGraph { } fn node_id(&'a self, n: &CrateId) -> Id<'a> { - Id::new(format!("_{}", n.0)).unwrap() + Id::new(format!("_{}", u32::from(n.into_raw()))).unwrap() } fn node_shape(&'a self, _node: &CrateId) -> Option> { diff --git a/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs b/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs index 9c1f93356ee2d..e072df430fc0d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs +++ b/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs @@ -12,5 +12,5 @@ use ide_db::RootDatabase; // | 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() + db.file_item_tree(file_id.into()).pretty_print(db) } diff --git a/src/tools/rust-analyzer/crates/intern/Cargo.toml b/src/tools/rust-analyzer/crates/intern/Cargo.toml index c73c368a14e0c..dcd0d78812554 100644 --- a/src/tools/rust-analyzer/crates/intern/Cargo.toml +++ b/src/tools/rust-analyzer/crates/intern/Cargo.toml @@ -18,3 +18,4 @@ dashmap = { version = "=5.4.0", features = ["raw-api"] } hashbrown = { version = "0.12.1", default-features = false } once_cell = "1.17.0" rustc-hash = "1.1.0" +triomphe.workspace = true diff --git a/src/tools/rust-analyzer/crates/intern/src/lib.rs b/src/tools/rust-analyzer/crates/intern/src/lib.rs index fb2903696b373..dabbf3a38b502 100644 --- a/src/tools/rust-analyzer/crates/intern/src/lib.rs +++ b/src/tools/rust-analyzer/crates/intern/src/lib.rs @@ -6,13 +6,13 @@ use std::{ fmt::{self, Debug, Display}, hash::{BuildHasherDefault, Hash, Hasher}, ops::Deref, - sync::Arc, }; use dashmap::{DashMap, SharedValue}; -use hashbrown::HashMap; +use hashbrown::{hash_map::RawEntryMut, HashMap}; use once_cell::sync::OnceCell; use rustc_hash::FxHasher; +use triomphe::Arc; type InternMap = DashMap, (), BuildHasherDefault>; type Guard = dashmap::RwLockWriteGuard< @@ -26,56 +26,58 @@ pub struct Interned { impl Interned { pub fn new(obj: T) -> Self { - match Interned::lookup(&obj) { - Ok(this) => this, - Err(shard) => { - let arc = Arc::new(obj); - Self::alloc(arc, shard) - } + let (mut shard, hash) = Self::select(&obj); + // Atomically, + // - check if `obj` is already in the map + // - if so, clone its `Arc` and return it + // - if not, box it up, insert it, and return a clone + // This needs to be atomic (locking the shard) to avoid races with other thread, which could + // insert the same object between us looking it up and inserting it. + match shard.raw_entry_mut().from_key_hashed_nocheck(hash as u64, &obj) { + RawEntryMut::Occupied(occ) => Self { arc: occ.key().clone() }, + RawEntryMut::Vacant(vac) => Self { + arc: vac + .insert_hashed_nocheck(hash as u64, Arc::new(obj), SharedValue::new(())) + .0 + .clone(), + }, } } } -impl Interned { - fn lookup(obj: &T) -> Result> { - let storage = T::storage().get(); - let shard_idx = storage.determine_map(obj); - let shard = &storage.shards()[shard_idx]; - let shard = shard.write(); - +impl Interned { + pub fn new_str(s: &str) -> Self { + let (mut shard, hash) = Self::select(s); // Atomically, // - check if `obj` is already in the map // - if so, clone its `Arc` and return it // - if not, box it up, insert it, and return a clone // This needs to be atomic (locking the shard) to avoid races with other thread, which could // insert the same object between us looking it up and inserting it. - - // FIXME: avoid double lookup/hashing by using raw entry API (once stable, or when - // hashbrown can be plugged into dashmap) - match shard.get_key_value(obj) { - Some((arc, _)) => Ok(Self { arc: arc.clone() }), - None => Err(shard), + match shard.raw_entry_mut().from_key_hashed_nocheck(hash as u64, s) { + RawEntryMut::Occupied(occ) => Self { arc: occ.key().clone() }, + RawEntryMut::Vacant(vac) => Self { + arc: vac + .insert_hashed_nocheck(hash as u64, Arc::from(s), SharedValue::new(())) + .0 + .clone(), + }, } } - - fn alloc(arc: Arc, mut shard: Guard) -> Self { - let arc2 = arc.clone(); - - shard.insert(arc2, SharedValue::new(())); - - Self { arc } - } } -impl Interned { - pub fn new_str(s: &str) -> Self { - match Interned::lookup(s) { - Ok(this) => this, - Err(shard) => { - let arc = Arc::::from(s); - Self::alloc(arc, shard) - } - } +impl Interned { + #[inline] + fn select(obj: &T) -> (Guard, u64) { + let storage = T::storage().get(); + let hash = { + let mut hasher = std::hash::BuildHasher::build_hasher(storage.hasher()); + obj.hash(&mut hasher); + hasher.finish() + }; + let shard_idx = storage.determine_shard(hash as usize); + let shard = &storage.shards()[shard_idx]; + (shard.write(), hash) } } @@ -83,7 +85,7 @@ impl Drop for Interned { #[inline] fn drop(&mut self) { // When the last `Ref` is dropped, remove the object from the global map. - if Arc::strong_count(&self.arc) == 2 { + if Arc::count(&self.arc) == 2 { // Only `self` and the global map point to the object. self.drop_slow(); @@ -94,20 +96,17 @@ impl Drop for Interned { impl Interned { #[cold] fn drop_slow(&mut self) { - let storage = T::storage().get(); - let shard_idx = storage.determine_map(&self.arc); - let shard = &storage.shards()[shard_idx]; - let mut shard = shard.write(); - - // FIXME: avoid double lookup - let (arc, _) = shard.get_key_value(&self.arc).expect("interned value removed prematurely"); + let (mut shard, hash) = Self::select(&self.arc); - if Arc::strong_count(arc) != 2 { + if Arc::count(&self.arc) != 2 { // Another thread has interned another copy return; } - shard.remove(&self.arc); + match shard.raw_entry_mut().from_key_hashed_nocheck(hash, &self.arc) { + RawEntryMut::Occupied(occ) => occ.remove(), + RawEntryMut::Vacant(_) => unreachable!(), + }; // Shrink the backing storage if the shard is less than 50% occupied. if shard.len() * 2 < shard.capacity() { diff --git a/src/tools/rust-analyzer/crates/limit/src/lib.rs b/src/tools/rust-analyzer/crates/limit/src/lib.rs index 6b2534aa4619d..7fb4b513a7157 100644 --- a/src/tools/rust-analyzer/crates/limit/src/lib.rs +++ b/src/tools/rust-analyzer/crates/limit/src/lib.rs @@ -6,6 +6,7 @@ use std::sync::atomic::AtomicUsize; /// Represents a struct used to enforce a numerical limit. +#[derive(Debug)] pub struct Limit { upper_bound: usize, #[cfg(feature = "tracking")] diff --git a/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs b/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs index 894355fcbc9b3..d28dd17def378 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs @@ -20,7 +20,10 @@ fn benchmark_parse_macro_rules() { let rules = macro_rules_fixtures_tt(); let hash: usize = { let _pt = bench("mbe parse macro rules"); - rules.values().map(|it| DeclarativeMacro::parse_macro_rules(it).unwrap().rules.len()).sum() + rules + .values() + .map(|it| DeclarativeMacro::parse_macro_rules(it, true).unwrap().rules.len()) + .sum() }; assert_eq!(hash, 1144); } @@ -50,7 +53,7 @@ fn benchmark_expand_macro_rules() { fn macro_rules_fixtures() -> FxHashMap { macro_rules_fixtures_tt() .into_iter() - .map(|(id, tt)| (id, DeclarativeMacro::parse_macro_rules(&tt).unwrap())) + .map(|(id, tt)| (id, DeclarativeMacro::parse_macro_rules(&tt, true).unwrap())) .collect() } @@ -76,7 +79,7 @@ fn invocation_fixtures(rules: &FxHashMap) -> Vec<(Stri let mut res = Vec::new(); for (name, it) in rules { - for rule in &it.rules { + for rule in it.rules.iter() { // Generate twice for _ in 0..2 { // The input are generated by filling the `Op` randomly. @@ -108,7 +111,7 @@ fn invocation_fixtures(rules: &FxHashMap) -> Vec<(Stri } try_cnt += 1; if try_cnt > 100 { - panic!("invocaton fixture {name} cannot be generated.\n"); + panic!("invocation fixture {name} cannot be generated.\n"); } } } @@ -192,10 +195,10 @@ fn invocation_fixtures(rules: &FxHashMap) -> Vec<(Stri }); parent.token_trees.push(subtree.into()); } - Op::Ignore { .. } | Op::Index { .. } => {} + Op::Ignore { .. } | Op::Index { .. } | Op::Count { .. } => {} }; - // Simple linear congruential generator for determistic result + // Simple linear congruential generator for deterministic result fn rand(seed: &mut usize) -> usize { let a = 1664525; let c = 1013904223; diff --git a/src/tools/rust-analyzer/crates/mbe/src/expander.rs b/src/tools/rust-analyzer/crates/mbe/src/expander.rs index 7537dc3226149..8e2181e977e1d 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/expander.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/expander.rs @@ -13,10 +13,11 @@ use crate::{parser::MetaVarKind, tt, ExpandError, ExpandResult}; pub(crate) fn expand_rules( rules: &[crate::Rule], input: &tt::Subtree, + is_2021: bool, ) -> ExpandResult { let mut match_: Option<(matcher::Match, &crate::Rule)> = None; for rule in rules { - let new_match = matcher::match_(&rule.lhs, input); + let new_match = matcher::match_(&rule.lhs, input, is_2021); if new_match.err.is_none() { // If we find a rule that applies without errors, we're done. @@ -45,7 +46,7 @@ pub(crate) fn expand_rules( transcriber::transcribe(&rule.rhs, &match_.bindings); ExpandResult { value, err: match_.err.or(transcribe_err) } } else { - ExpandResult::with_err( + ExpandResult::new( tt::Subtree { delimiter: tt::Delimiter::unspecified(), token_trees: vec![] }, ExpandError::NoMatchingRule, ) diff --git a/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs b/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs index f4ea9e5c81658..474826079d733 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs @@ -111,8 +111,8 @@ impl Match { } /// Matching errors are added to the `Match`. -pub(super) fn match_(pattern: &MetaTemplate, input: &tt::Subtree) -> Match { - let mut res = match_loop(pattern, input); +pub(super) fn match_(pattern: &MetaTemplate, input: &tt::Subtree, is_2021: bool) -> Match { + let mut res = match_loop(pattern, input, is_2021); res.bound_count = count(res.bindings.bindings()); return res; @@ -332,7 +332,7 @@ struct MatchState<'t> { /// Cached result of meta variable parsing meta_result: Option<(TtIter<'t>, ExpandResult>)>, - /// Is error occuried in this state, will `poised` to "parent" + /// Is error occurred in this state, will `poised` to "parent" is_error: bool, } @@ -354,6 +354,7 @@ struct MatchState<'t> { /// - `eof_items`: the set of items that would be valid if this was the EOF. /// - `bb_items`: the set of items that are waiting for the black-box parser. /// - `error_items`: the set of items in errors, used for error-resilient parsing +#[inline] fn match_loop_inner<'t>( src: TtIter<'t>, stack: &[TtIter<'t>], @@ -364,6 +365,7 @@ fn match_loop_inner<'t>( next_items: &mut Vec>, eof_items: &mut SmallVec<[MatchState<'t>; 1]>, error_items: &mut SmallVec<[MatchState<'t>; 1]>, + is_2021: bool, ) { macro_rules! try_push { ($items: expr, $it:expr) => { @@ -474,7 +476,7 @@ fn match_loop_inner<'t>( OpDelimited::Op(Op::Var { kind, name, .. }) => { if let &Some(kind) = kind { let mut fork = src.clone(); - let match_res = match_meta_var(kind, &mut fork); + let match_res = match_meta_var(kind, &mut fork, is_2021); match match_res.err { None => { // Some meta variables are optional (e.g. vis) @@ -565,7 +567,9 @@ fn match_loop_inner<'t>( item.is_error = true; error_items.push(item); } - OpDelimited::Op(Op::Ignore { .. } | Op::Index { .. }) => {} + OpDelimited::Op(Op::Ignore { .. } | Op::Index { .. } | Op::Count { .. }) => { + stdx::never!("metavariable expression in lhs found"); + } OpDelimited::Open => { if matches!(src.peek_n(0), Some(tt::TokenTree::Subtree(..))) { item.dot.next(); @@ -583,7 +587,7 @@ fn match_loop_inner<'t>( } } -fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree) -> Match { +fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree, is_2021: bool) -> Match { let mut src = TtIter::new(src); let mut stack: SmallVec<[TtIter<'_>; 1]> = SmallVec::new(); let mut res = Match::default(); @@ -622,6 +626,7 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree) -> Match { &mut next_items, &mut eof_items, &mut error_items, + is_2021, ); stdx::always!(cur_items.is_empty()); @@ -731,14 +736,17 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree) -> Match { } } -fn match_meta_var(kind: MetaVarKind, input: &mut TtIter<'_>) -> ExpandResult> { +fn match_meta_var( + kind: MetaVarKind, + input: &mut TtIter<'_>, + is_2021: bool, +) -> ExpandResult> { let fragment = match kind { MetaVarKind::Path => parser::PrefixEntryPoint::Path, MetaVarKind::Ty => parser::PrefixEntryPoint::Ty, - // FIXME: These two should actually behave differently depending on the edition. - // - // https://doc.rust-lang.org/edition-guide/rust-2021/or-patterns-macro-rules.html - MetaVarKind::Pat | MetaVarKind::PatParam => parser::PrefixEntryPoint::Pat, + MetaVarKind::Pat if is_2021 => parser::PrefixEntryPoint::PatTop, + MetaVarKind::Pat => parser::PrefixEntryPoint::Pat, + MetaVarKind::PatParam => parser::PrefixEntryPoint::Pat, MetaVarKind::Stmt => parser::PrefixEntryPoint::Stmt, MetaVarKind::Block => parser::PrefixEntryPoint::Block, MetaVarKind::Meta => parser::PrefixEntryPoint::MetaItem, @@ -805,7 +813,9 @@ fn collect_vars(collector_fun: &mut impl FnMut(SmolStr), pattern: &MetaTemplate) Op::Var { name, .. } => collector_fun(name.clone()), Op::Subtree { tokens, .. } => collect_vars(collector_fun, tokens), Op::Repeat { tokens, .. } => collect_vars(collector_fun, tokens), - Op::Ignore { .. } | Op::Index { .. } | Op::Literal(_) | Op::Ident(_) | Op::Punct(_) => { + Op::Literal(_) | Op::Ident(_) | Op::Punct(_) => {} + Op::Ignore { .. } | Op::Index { .. } | Op::Count { .. } => { + stdx::never!("metavariable expression in lhs found"); } } } diff --git a/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs b/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs index dffb40d4bc886..6161af185871d 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs @@ -7,7 +7,7 @@ use crate::{ expander::{Binding, Bindings, Fragment}, parser::{MetaVarKind, Op, RepeatKind, Separator}, tt::{self, Delimiter}, - ExpandError, ExpandResult, MetaTemplate, + CountError, ExpandError, ExpandResult, MetaTemplate, }; impl Bindings { @@ -15,13 +15,23 @@ impl Bindings { self.inner.contains_key(name) } - fn get(&self, name: &str, nesting: &mut [NestingState]) -> Result { + fn get(&self, name: &str) -> Result<&Binding, ExpandError> { + match self.inner.get(name) { + Some(binding) => Ok(binding), + None => Err(ExpandError::binding_error(format!("could not find binding `{name}`"))), + } + } + + fn get_fragment( + &self, + name: &str, + nesting: &mut [NestingState], + ) -> Result { macro_rules! binding_err { ($($arg:tt)*) => { ExpandError::binding_error(format!($($arg)*)) }; } - let mut b: &Binding = - self.inner.get(name).ok_or_else(|| binding_err!("could not find binding `{name}`"))?; + let mut b = self.get(name)?; for nesting_state in nesting.iter_mut() { nesting_state.hit = true; b = match b { @@ -133,7 +143,7 @@ fn expand_subtree( // remember how many elements are in the arena now - when returning, we want to drain exactly how many elements we added. This way, the recursive uses of the arena get their own "view" of the arena, but will reuse the allocation let start_elements = arena.len(); let mut err = None; - for op in template.iter() { + 'ops: for op in template.iter() { match op { Op::Literal(it) => arena.push(tt::Leaf::from(it.clone()).into()), Op::Ident(it) => arena.push(tt::Leaf::from(it.clone()).into()), @@ -161,13 +171,12 @@ fn expand_subtree( } Op::Ignore { name, id } => { // Expand the variable, but ignore the result. This registers the repetition count. + // FIXME: Any emitted errors are dropped. expand_var(ctx, name, *id); } Op::Index { depth } => { - let index = ctx - .nesting - .get(ctx.nesting.len() - 1 - (*depth as usize)) - .map_or(0, |nest| nest.idx); + let index = + ctx.nesting.get(ctx.nesting.len() - 1 - depth).map_or(0, |nest| nest.idx); arena.push( tt::Leaf::Literal(tt::Literal { text: index.to_string().into(), @@ -176,6 +185,65 @@ fn expand_subtree( .into(), ); } + Op::Count { name, depth } => { + let mut binding = match ctx.bindings.get(name.as_str()) { + Ok(b) => b, + Err(e) => { + if err.is_none() { + err = Some(e); + } + continue; + } + }; + for state in ctx.nesting.iter_mut() { + state.hit = true; + match binding { + Binding::Fragment(_) | Binding::Missing(_) => { + // `count()` will report an error. + break; + } + Binding::Nested(bs) => { + if let Some(b) = bs.get(state.idx) { + binding = b; + } else { + state.at_end = true; + continue 'ops; + } + } + Binding::Empty => { + state.at_end = true; + // FIXME: Breaking here and proceeding to `count()` isn't the most + // correct thing to do here. This could be a binding of some named + // fragment which we don't know the depth of, so `count()` will just + // return 0 for this no matter what `depth` is. See test + // `count_interaction_with_empty_binding` for example. + break; + } + } + } + + let c = match count(ctx, binding, 0, *depth) { + Ok(c) => c, + Err(e) => { + // XXX: It *might* make sense to emit a dummy integer value like `0` here. + // That would type inference a bit more robust in cases like + // `v[${count(t)}]` where index doesn't matter, but also coult also lead to + // wrong infefrence for cases like `tup.${count(t)}` where index itself + // does matter. + if err.is_none() { + err = Some(e.into()); + } + continue; + } + }; + arena.push( + tt::Leaf::Literal(tt::Literal { + text: c.to_string().into(), + span: tt::TokenId::unspecified(), + }) + .into(), + ); + } } } // drain the elements added in this instance of expand_subtree @@ -218,12 +286,9 @@ fn expand_var(ctx: &mut ExpandCtx<'_>, v: &SmolStr, id: tt::TokenId) -> ExpandRe .into(); ExpandResult::ok(Fragment::Tokens(tt)) } else { - ctx.bindings.get(v, &mut ctx.nesting).map_or_else( + ctx.bindings.get_fragment(v, &mut ctx.nesting).map_or_else( |e| ExpandResult { - value: Fragment::Tokens(tt::TokenTree::Subtree(tt::Subtree { - delimiter: tt::Delimiter::unspecified(), - token_trees: vec![], - })), + value: Fragment::Tokens(tt::TokenTree::Subtree(tt::Subtree::empty())), err: Some(e), }, ExpandResult::ok, @@ -245,6 +310,7 @@ fn expand_repeat( let limit = 65536; let mut has_seps = 0; let mut counter = 0; + let mut err = None; loop { let ExpandResult { value: mut t, err: e } = expand_subtree(ctx, template, None, arena); @@ -272,6 +338,7 @@ fn expand_repeat( } if e.is_some() { + err = err.or(e); continue; } @@ -317,7 +384,7 @@ fn expand_repeat( err: Some(ExpandError::UnexpectedToken), }; } - ExpandResult::ok(Fragment::Tokens(tt)) + ExpandResult { value: Fragment::Tokens(tt), err } } fn push_fragment(buf: &mut Vec, fragment: Fragment) { @@ -343,3 +410,34 @@ fn push_subtree(buf: &mut Vec, tt: tt::Subtree) { _ => buf.push(tt.into()), } } + +/// Handles `${count(t, depth)}`. `our_depth` is the recursion depth and `count_depth` is the depth +/// defined by the metavar expression. +fn count( + ctx: &ExpandCtx<'_>, + binding: &Binding, + our_depth: usize, + count_depth: Option, +) -> Result { + match binding { + Binding::Nested(bs) => match count_depth { + None => bs.iter().map(|b| count(ctx, b, our_depth + 1, None)).sum(), + Some(0) => Ok(bs.len()), + Some(d) => bs.iter().map(|b| count(ctx, b, our_depth + 1, Some(d - 1))).sum(), + }, + Binding::Empty => Ok(0), + Binding::Fragment(_) | Binding::Missing(_) => { + if our_depth == 0 { + // `${count(t)}` is placed inside the innermost repetition. This includes cases + // where `t` is not a repeated fragment. + Err(CountError::Misplaced) + } else if count_depth.is_none() { + Ok(1) + } else { + // We've reached at the innermost repeated fragment, but the user wants us to go + // further! + Err(CountError::OutOfBounds) + } + } + } +} diff --git a/src/tools/rust-analyzer/crates/mbe/src/lib.rs b/src/tools/rust-analyzer/crates/mbe/src/lib.rs index ac107a0d6d6d1..5ef20ff8a9b05 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/lib.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/lib.rs @@ -19,6 +19,7 @@ mod benchmark; mod token_map; use ::tt::token_id as tt; +use stdx::impl_from; use std::fmt; @@ -69,7 +70,7 @@ impl fmt::Display for ParseError { } } -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone, Hash)] pub enum ExpandError { BindingError(Box>), LeftoverTokens, @@ -77,8 +78,11 @@ pub enum ExpandError { LimitExceeded, NoMatchingRule, UnexpectedToken, + CountError(CountError), } +impl_from!(CountError for ExpandError); + impl ExpandError { fn binding_error(e: impl Into>) -> ExpandError { ExpandError::BindingError(Box::new(e.into())) @@ -94,6 +98,23 @@ impl fmt::Display for ExpandError { ExpandError::ConversionError => f.write_str("could not convert tokens"), ExpandError::LimitExceeded => f.write_str("Expand exceed limit"), ExpandError::LeftoverTokens => f.write_str("leftover tokens"), + ExpandError::CountError(e) => e.fmt(f), + } + } +} + +// FIXME: Showing these errors could be nicer. +#[derive(Debug, PartialEq, Eq, Clone, Hash)] +pub enum CountError { + OutOfBounds, + Misplaced, +} + +impl fmt::Display for CountError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + CountError::OutOfBounds => f.write_str("${count} out of bounds"), + CountError::Misplaced => f.write_str("${count} misplaced"), } } } @@ -104,9 +125,12 @@ impl fmt::Display for ExpandError { /// and `$()*` have special meaning (see `Var` and `Repeat` data structures) #[derive(Clone, Debug, PartialEq, Eq)] pub struct DeclarativeMacro { - rules: Vec, + rules: Box<[Rule]>, /// Highest id of the token we have in TokenMap shift: Shift, + // This is used for correctly determining the behavior of the pat fragment + // FIXME: This should be tracked by hygiene of the fragment identifier! + is_2021: bool, } #[derive(Clone, Debug, PartialEq, Eq)] @@ -190,7 +214,10 @@ pub enum Origin { impl DeclarativeMacro { /// The old, `macro_rules! m {}` flavor. - pub fn parse_macro_rules(tt: &tt::Subtree) -> Result { + pub fn parse_macro_rules( + tt: &tt::Subtree, + is_2021: bool, + ) -> Result { // Note: this parsing can be implemented using mbe machinery itself, by // matching against `$($lhs:tt => $rhs:tt);*` pattern, but implementing // manually seems easier. @@ -211,11 +238,11 @@ impl DeclarativeMacro { validate(lhs)?; } - Ok(DeclarativeMacro { rules, shift: Shift::new(tt) }) + Ok(DeclarativeMacro { rules: rules.into_boxed_slice(), shift: Shift::new(tt), is_2021 }) } /// The new, unstable `macro m {}` flavor. - pub fn parse_macro2(tt: &tt::Subtree) -> Result { + pub fn parse_macro2(tt: &tt::Subtree, is_2021: bool) -> Result { let mut src = TtIter::new(tt); let mut rules = Vec::new(); @@ -244,14 +271,14 @@ impl DeclarativeMacro { validate(lhs)?; } - Ok(DeclarativeMacro { rules, shift: Shift::new(tt) }) + Ok(DeclarativeMacro { rules: rules.into_boxed_slice(), shift: Shift::new(tt), is_2021 }) } pub fn expand(&self, tt: &tt::Subtree) -> ExpandResult { // apply shift let mut tt = tt.clone(); self.shift.shift_all(&mut tt); - expander::expand_rules(&self.rules, &tt) + expander::expand_rules(&self.rules, &tt, self.is_2021) } pub fn map_id_down(&self, id: tt::TokenId) -> tt::TokenId { @@ -324,12 +351,12 @@ pub struct ValueResult { } impl ValueResult { - pub fn ok(value: T) -> Self { - Self { value, err: None } + pub fn new(value: T, err: E) -> Self { + Self { value, err: Some(err) } } - pub fn with_err(value: T, err: E) -> Self { - Self { value, err: Some(err) } + pub fn ok(value: T) -> Self { + Self { value, err: None } } pub fn only_err(err: E) -> Self diff --git a/src/tools/rust-analyzer/crates/mbe/src/parser.rs b/src/tools/rust-analyzer/crates/mbe/src/parser.rs index fd3d64719ac9a..7a143e7466a93 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/parser.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/parser.rs @@ -20,7 +20,7 @@ use crate::{tt, tt_iter::TtIter, ParseError}; /// Stuff to the right is a [`MetaTemplate`] template which is used to produce /// output. #[derive(Clone, Debug, PartialEq, Eq)] -pub(crate) struct MetaTemplate(pub(crate) Vec); +pub(crate) struct MetaTemplate(pub(crate) Box<[Op]>); impl MetaTemplate { pub(crate) fn parse_pattern(pattern: &tt::Subtree) -> Result { @@ -44,7 +44,7 @@ impl MetaTemplate { res.push(op); } - Ok(MetaTemplate(res)) + Ok(MetaTemplate(res.into_boxed_slice())) } } @@ -52,7 +52,8 @@ impl MetaTemplate { pub(crate) enum Op { Var { name: SmolStr, kind: Option, id: tt::TokenId }, Ignore { name: SmolStr, id: tt::TokenId }, - Index { depth: u32 }, + Index { depth: usize }, + Count { name: SmolStr, depth: Option }, Repeat { tokens: MetaTemplate, kind: RepeatKind, separator: Option }, Subtree { tokens: MetaTemplate, delimiter: tt::Delimiter }, Literal(tt::Literal), @@ -295,9 +296,13 @@ fn parse_metavar_expr(src: &mut TtIter<'_>) -> Result { let ident = args.expect_ident()?; Op::Ignore { name: ident.text.clone(), id: ident.span } } - "index" => { - let depth = if args.len() == 0 { 0 } else { args.expect_u32_literal()? }; - Op::Index { depth } + "index" => Op::Index { depth: parse_depth(&mut args)? }, + "count" => { + let ident = args.expect_ident()?; + // `${count(t)}` and `${count(t,)}` have different meanings. Not sure if this is a bug + // but that's how it's implemented in rustc as of this writing. See rust-lang/rust#111904. + let depth = if try_eat_comma(&mut args) { Some(parse_depth(&mut args)?) } else { None }; + Op::Count { name: ident.text.clone(), depth } } _ => return Err(()), }; @@ -308,3 +313,22 @@ fn parse_metavar_expr(src: &mut TtIter<'_>) -> Result { Ok(op) } + +fn parse_depth(src: &mut TtIter<'_>) -> Result { + if src.len() == 0 { + Ok(0) + } else if let tt::Leaf::Literal(lit) = src.expect_literal()? { + // Suffixes are not allowed. + lit.text.parse().map_err(|_| ()) + } else { + Err(()) + } +} + +fn try_eat_comma(src: &mut TtIter<'_>) -> bool { + if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: ',', .. }))) = src.peek_n(0) { + let _ = src.next(); + return true; + } + false +} diff --git a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs index fb5313401088d..8cbf0f8fc0bd4 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs @@ -190,20 +190,13 @@ fn convert_tokens(conv: &mut C) -> tt::Subtree { let kind = token.kind(conv); if kind == COMMENT { - if let Some(tokens) = conv.convert_doc_comment(&token) { - // FIXME: There has to be a better way to do this - // Add the comments token id to the converted doc string + // Since `convert_doc_comment` can fail, we need to peek the next id, so that we can + // figure out which token id to use for the doc comment, if it is converted successfully. + let next_id = conv.id_alloc().peek_next_id(); + if let Some(tokens) = conv.convert_doc_comment(&token, next_id) { let id = conv.id_alloc().alloc(range, synth_id); - result.extend(tokens.into_iter().map(|mut tt| { - if let tt::TokenTree::Subtree(sub) = &mut tt { - if let Some(tt::TokenTree::Leaf(tt::Leaf::Literal(lit))) = - sub.token_trees.get_mut(2) - { - lit.span = id - } - } - tt - })); + debug_assert_eq!(id, next_id); + result.extend(tokens); } continue; } @@ -382,49 +375,46 @@ fn doc_comment_text(comment: &ast::Comment) -> SmolStr { text.into() } -fn convert_doc_comment(token: &syntax::SyntaxToken) -> Option> { +fn convert_doc_comment( + token: &syntax::SyntaxToken, + span: tt::TokenId, +) -> Option> { cov_mark::hit!(test_meta_doc_comments); let comment = ast::Comment::cast(token.clone())?; let doc = comment.kind().doc?; // Make `doc="\" Comments\"" - let meta_tkns = vec![mk_ident("doc"), mk_punct('='), mk_doc_literal(&comment)]; + let meta_tkns = + vec![mk_ident("doc", span), mk_punct('=', span), mk_doc_literal(&comment, span)]; // Make `#![]` let mut token_trees = Vec::with_capacity(3); - token_trees.push(mk_punct('#')); + token_trees.push(mk_punct('#', span)); if let ast::CommentPlacement::Inner = doc { - token_trees.push(mk_punct('!')); + token_trees.push(mk_punct('!', span)); } token_trees.push(tt::TokenTree::from(tt::Subtree { - delimiter: tt::Delimiter { - open: tt::TokenId::UNSPECIFIED, - close: tt::TokenId::UNSPECIFIED, - kind: tt::DelimiterKind::Bracket, - }, + delimiter: tt::Delimiter { open: span, close: span, kind: tt::DelimiterKind::Bracket }, token_trees: meta_tkns, })); return Some(token_trees); // Helper functions - fn mk_ident(s: &str) -> tt::TokenTree { - tt::TokenTree::from(tt::Leaf::from(tt::Ident { - text: s.into(), - span: tt::TokenId::unspecified(), - })) + fn mk_ident(s: &str, span: tt::TokenId) -> tt::TokenTree { + tt::TokenTree::from(tt::Leaf::from(tt::Ident { text: s.into(), span })) } - fn mk_punct(c: char) -> tt::TokenTree { + fn mk_punct(c: char, span: tt::TokenId) -> tt::TokenTree { tt::TokenTree::from(tt::Leaf::from(tt::Punct { char: c, spacing: tt::Spacing::Alone, - span: tt::TokenId::unspecified(), + span, })) } - fn mk_doc_literal(comment: &ast::Comment) -> tt::TokenTree { - let lit = tt::Literal { text: doc_comment_text(comment), span: tt::TokenId::unspecified() }; + fn mk_doc_literal(comment: &ast::Comment, span: tt::TokenId) -> tt::TokenTree { + let lit = tt::Literal { text: doc_comment_text(comment), span }; tt::TokenTree::from(tt::Leaf::from(lit)) } @@ -480,6 +470,10 @@ impl TokenIdAlloc { } } } + + fn peek_next_id(&self) -> tt::TokenId { + tt::TokenId(self.next_id) + } } /// A raw token (straight from lexer) converter @@ -502,7 +496,11 @@ trait SrcToken: std::fmt::Debug { trait TokenConverter: Sized { type Token: SrcToken; - fn convert_doc_comment(&self, token: &Self::Token) -> Option>; + fn convert_doc_comment( + &self, + token: &Self::Token, + span: tt::TokenId, + ) -> Option>; fn bump(&mut self) -> Option<(Self::Token, TextRange)>; @@ -532,9 +530,9 @@ impl<'a> SrcToken> for usize { impl<'a> TokenConverter for RawConverter<'a> { type Token = usize; - fn convert_doc_comment(&self, &token: &usize) -> Option> { + fn convert_doc_comment(&self, &token: &usize, span: tt::TokenId) -> Option> { let text = self.lexed.text(token); - convert_doc_comment(&doc_comment(text)) + convert_doc_comment(&doc_comment(text), span) } fn bump(&mut self) -> Option<(Self::Token, TextRange)> { @@ -681,8 +679,12 @@ impl SrcToken for SynToken { impl TokenConverter for Converter { type Token = SynToken; - fn convert_doc_comment(&self, token: &Self::Token) -> Option> { - convert_doc_comment(token.token()?) + fn convert_doc_comment( + &self, + token: &Self::Token, + span: tt::TokenId, + ) -> Option> { + convert_doc_comment(token.token()?, span) } fn bump(&mut self) -> Option<(Self::Token, TextRange)> { diff --git a/src/tools/rust-analyzer/crates/mbe/src/tt_iter.rs b/src/tools/rust-analyzer/crates/mbe/src/tt_iter.rs index f744481f3aecb..59dbf156800d4 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/tt_iter.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/tt_iter.rs @@ -73,13 +73,6 @@ impl<'a> TtIter<'a> { } } - pub(crate) fn expect_u32_literal(&mut self) -> Result { - match self.expect_literal()? { - tt::Leaf::Literal(lit) => lit.text.parse().map_err(drop), - _ => Err(()), - } - } - pub(crate) fn expect_single_punct(&mut self) -> Result<&'a tt::Punct, ()> { match self.expect_leaf()? { tt::Leaf::Punct(it) => Ok(it), diff --git a/src/tools/rust-analyzer/crates/parser/Cargo.toml b/src/tools/rust-analyzer/crates/parser/Cargo.toml index 6e962abd75477..09e62c35278e9 100644 --- a/src/tools/rust-analyzer/crates/parser/Cargo.toml +++ b/src/tools/rust-analyzer/crates/parser/Cargo.toml @@ -13,7 +13,7 @@ doctest = false [dependencies] drop_bomb = "0.1.5" -rustc_lexer = { version = "727.0.0", package = "rustc-ap-rustc_lexer" } +rustc_lexer.workspace = true limit.workspace = true diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar.rs b/src/tools/rust-analyzer/crates/parser/src/grammar.rs index 15435a26cea98..1814e0e54c592 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar.rs @@ -66,6 +66,10 @@ pub(crate) mod entry { patterns::pattern_single(p); } + pub(crate) fn pat_top(p: &mut Parser<'_>) { + patterns::pattern_top(p); + } + pub(crate) fn ty(p: &mut Parser<'_>) { types::type_(p); } @@ -218,17 +222,22 @@ fn opt_visibility(p: &mut Parser<'_>, in_tuple_field: bool) -> bool { // pub(self) struct S; // pub(super) struct S; + // test_err crate_visibility_empty_recover + // pub() struct S; + // test pub_parens_typepath // struct B(pub (super::A)); // struct B(pub (crate::A,)); - T![crate] | T![self] | T![super] | T![ident] if p.nth(2) != T![:] => { + T![crate] | T![self] | T![super] | T![ident] | T![')'] if p.nth(2) != T![:] => { // If we are in a tuple struct, then the parens following `pub` // might be an tuple field, not part of the visibility. So in that // case we don't want to consume an identifier. // test pub_tuple_field // struct MyStruct(pub (u32, u32)); - if !(in_tuple_field && matches!(p.nth(1), T![ident])) { + // struct MyStruct(pub (u32)); + // struct MyStruct(pub ()); + if !(in_tuple_field && matches!(p.nth(1), T![ident] | T![')'])) { p.bump(T!['(']); paths::use_path(p); p.expect(T![')']); @@ -243,7 +252,7 @@ fn opt_visibility(p: &mut Parser<'_>, in_tuple_field: bool) -> bool { paths::use_path(p); p.expect(T![')']); } - _ => (), + _ => {} } } m.complete(p, VISIBILITY); diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs index a884d8b6ec801..1cbd1663230bd 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs @@ -4,8 +4,8 @@ use crate::grammar::attributes::ATTRIBUTE_FIRST; use super::*; -pub(crate) use self::atom::{block_expr, match_arm_list}; -pub(super) use self::atom::{literal, LITERAL_FIRST}; +pub(crate) use atom::{block_expr, match_arm_list}; +pub(super) use atom::{literal, LITERAL_FIRST}; #[derive(PartialEq, Eq)] pub(super) enum Semicolon { @@ -188,47 +188,56 @@ struct Restrictions { prefer_stmt: bool, } +enum Associativity { + Left, + Right, +} + /// Binding powers of operators for a Pratt parser. /// /// See +/// +/// Note that Rust doesn't define associativity for some infix operators (e.g. `==` and `..`) and +/// requires parentheses to disambiguate. We just treat them as left associative. #[rustfmt::skip] -fn current_op(p: &Parser<'_>) -> (u8, SyntaxKind) { - const NOT_AN_OP: (u8, SyntaxKind) = (0, T![@]); +fn current_op(p: &Parser<'_>) -> (u8, SyntaxKind, Associativity) { + use Associativity::*; + const NOT_AN_OP: (u8, SyntaxKind, Associativity) = (0, T![@], Left); match p.current() { - T![|] if p.at(T![||]) => (3, T![||]), - T![|] if p.at(T![|=]) => (1, T![|=]), - T![|] => (6, T![|]), - T![>] if p.at(T![>>=]) => (1, T![>>=]), - T![>] if p.at(T![>>]) => (9, T![>>]), - T![>] if p.at(T![>=]) => (5, T![>=]), - T![>] => (5, T![>]), + T![|] if p.at(T![||]) => (3, T![||], Left), + T![|] if p.at(T![|=]) => (1, T![|=], Right), + T![|] => (6, T![|], Left), + T![>] if p.at(T![>>=]) => (1, T![>>=], Right), + T![>] if p.at(T![>>]) => (9, T![>>], Left), + T![>] if p.at(T![>=]) => (5, T![>=], Left), + T![>] => (5, T![>], Left), T![=] if p.at(T![=>]) => NOT_AN_OP, - T![=] if p.at(T![==]) => (5, T![==]), - T![=] => (1, T![=]), - T![<] if p.at(T![<=]) => (5, T![<=]), - T![<] if p.at(T![<<=]) => (1, T![<<=]), - T![<] if p.at(T![<<]) => (9, T![<<]), - T![<] => (5, T![<]), - T![+] if p.at(T![+=]) => (1, T![+=]), - T![+] => (10, T![+]), - T![^] if p.at(T![^=]) => (1, T![^=]), - T![^] => (7, T![^]), - T![%] if p.at(T![%=]) => (1, T![%=]), - T![%] => (11, T![%]), - T![&] if p.at(T![&=]) => (1, T![&=]), + T![=] if p.at(T![==]) => (5, T![==], Left), + T![=] => (1, T![=], Right), + T![<] if p.at(T![<=]) => (5, T![<=], Left), + T![<] if p.at(T![<<=]) => (1, T![<<=], Right), + T![<] if p.at(T![<<]) => (9, T![<<], Left), + T![<] => (5, T![<], Left), + T![+] if p.at(T![+=]) => (1, T![+=], Right), + T![+] => (10, T![+], Left), + T![^] if p.at(T![^=]) => (1, T![^=], Right), + T![^] => (7, T![^], Left), + T![%] if p.at(T![%=]) => (1, T![%=], Right), + T![%] => (11, T![%], Left), + T![&] if p.at(T![&=]) => (1, T![&=], Right), // If you update this, remember to update `expr_let()` too. - T![&] if p.at(T![&&]) => (4, T![&&]), - T![&] => (8, T![&]), - T![/] if p.at(T![/=]) => (1, T![/=]), - T![/] => (11, T![/]), - T![*] if p.at(T![*=]) => (1, T![*=]), - T![*] => (11, T![*]), - T![.] if p.at(T![..=]) => (2, T![..=]), - T![.] if p.at(T![..]) => (2, T![..]), - T![!] if p.at(T![!=]) => (5, T![!=]), - T![-] if p.at(T![-=]) => (1, T![-=]), - T![-] => (10, T![-]), - T![as] => (12, T![as]), + T![&] if p.at(T![&&]) => (4, T![&&], Left), + T![&] => (8, T![&], Left), + T![/] if p.at(T![/=]) => (1, T![/=], Right), + T![/] => (11, T![/], Left), + T![*] if p.at(T![*=]) => (1, T![*=], Right), + T![*] => (11, T![*], Left), + T![.] if p.at(T![..=]) => (2, T![..=], Left), + T![.] if p.at(T![..]) => (2, T![..], Left), + T![!] if p.at(T![!=]) => (5, T![!=], Left), + T![-] if p.at(T![-=]) => (1, T![-=], Right), + T![-] => (10, T![-], Left), + T![as] => (12, T![as], Left), _ => NOT_AN_OP } @@ -273,7 +282,7 @@ fn expr_bp( loop { let is_range = p.at(T![..]) || p.at(T![..=]); - let (op_bp, op) = current_op(p); + let (op_bp, op, associativity) = current_op(p); if op_bp < bp { break; } @@ -306,7 +315,11 @@ fn expr_bp( } } - expr_bp(p, None, Restrictions { prefer_stmt: false, ..r }, op_bp + 1); + let op_bp = match associativity { + Associativity::Left => op_bp + 1, + Associativity::Right => op_bp, + }; + expr_bp(p, None, Restrictions { prefer_stmt: false, ..r }, op_bp); lhs = m.complete(p, if is_range { RANGE_EXPR } else { BIN_EXPR }); } Some((lhs, BlockLike::NotBlock)) @@ -417,7 +430,7 @@ fn postfix_expr( allow_calls = true; block_like = BlockLike::NotBlock; } - return (lhs, block_like); + (lhs, block_like) } fn postfix_dot_expr( diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs index d051dd2682f94..d8553d3f95377 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs @@ -12,6 +12,8 @@ use super::*; // let _ = r"d"; // let _ = b"e"; // let _ = br"f"; +// let _ = c"g"; +// let _ = cr"h"; // } pub(crate) const LITERAL_FIRST: TokenSet = TokenSet::new(&[ T![true], @@ -22,6 +24,7 @@ pub(crate) const LITERAL_FIRST: TokenSet = TokenSet::new(&[ CHAR, STRING, BYTE_STRING, + C_STRING, ]); pub(crate) fn literal(p: &mut Parser<'_>) -> Option { @@ -181,6 +184,16 @@ fn tuple_expr(p: &mut Parser<'_>) -> CompletedMarker { let mut saw_comma = false; let mut saw_expr = false; + + // test_err tuple_expr_leading_comma + // fn foo() { + // (,); + // } + if p.eat(T![,]) { + p.error("expected expression"); + saw_comma = true; + } + while !p.at(EOF) && !p.at(T![')']) { saw_expr = true; diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/generic_args.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/generic_args.rs index 919d9b91ebab5..e589b69934d23 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/generic_args.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/generic_args.rs @@ -28,6 +28,7 @@ const GENERIC_ARG_FIRST: TokenSet = TokenSet::new(&[ BYTE, STRING, BYTE_STRING, + C_STRING, ]) .union(types::TYPE_FIRST); @@ -35,7 +36,7 @@ const GENERIC_ARG_FIRST: TokenSet = TokenSet::new(&[ // type T = S; fn generic_arg(p: &mut Parser<'_>) -> bool { match p.current() { - LIFETIME_IDENT => lifetime_arg(p), + LIFETIME_IDENT if !p.nth_at(1, T![+]) => lifetime_arg(p), T!['{'] | T![true] | T![false] | T![-] => const_arg(p), k if k.is_literal() => const_arg(p), // test associated_type_bounds @@ -76,6 +77,29 @@ fn generic_arg(p: &mut Parser<'_>) -> bool { } } } + IDENT if p.nth_at(1, T!['(']) => { + let m = p.start(); + name_ref(p); + params::param_list_fn_trait(p); + if p.at(T![:]) && !p.at(T![::]) { + // test associated_return_type_bounds + // fn foo>() {} + generic_params::bounds(p); + m.complete(p, ASSOC_TYPE_ARG); + } else { + // test bare_dyn_types_with_paren_as_generic_args + // type A = S; + // type A = S; + // type B = S i32>; + // type C = S i32 + Send>; + opt_ret_type(p); + let m = m.complete(p, PATH_SEGMENT).precede(p).complete(p, PATH); + let m = paths::type_path_for_qualifier(p, m); + let m = m.precede(p).complete(p, PATH_TYPE); + let m = types::opt_type_bounds_as_dyn_trait_type(p, m); + m.precede(p).complete(p, TYPE_ARG); + } + } _ if p.at_ts(types::TYPE_FIRST) => type_arg(p), _ => return false, } diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs index 5e0951bf8b50a..1c056819f4b7f 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs @@ -19,7 +19,7 @@ use super::*; // struct S; pub(super) fn mod_contents(p: &mut Parser<'_>, stop_on_r_curly: bool) { attributes::inner_attrs(p); - while !p.at(EOF) && !(p.at(T!['}']) && stop_on_r_curly) { + while !(p.at(EOF) || (p.at(T!['}']) && stop_on_r_curly)) { item_or_macro(p, stop_on_r_curly); } } diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/paths.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/paths.rs index 26490aa97e0a0..01b8f9e918714 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/paths.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/paths.rs @@ -136,6 +136,7 @@ fn opt_path_type_args(p: &mut Parser<'_>, mode: Mode) { Mode::Type => { // test typepathfn_with_coloncolon // type F = Start::(Middle) -> (Middle)::End; + // type GenericArg = S; if p.at(T![::]) && p.nth_at(2, T!['(']) { p.bump(T![::]); } diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/patterns.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/patterns.rs index 5f4977886f6eb..39ded41bb2413 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/patterns.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/patterns.rs @@ -5,6 +5,7 @@ pub(super) const PATTERN_FIRST: TokenSet = T![box], T![ref], T![mut], + T![const], T!['('], T!['['], T![&], @@ -15,6 +16,10 @@ pub(super) const PATTERN_FIRST: TokenSet = const PAT_TOP_FIRST: TokenSet = PATTERN_FIRST.union(TokenSet::new(&[T![|]])); +/// Set of possible tokens at the start of a range pattern's end bound. +const RANGE_PAT_END_FIRST: TokenSet = + expressions::LITERAL_FIRST.union(paths::PATH_FIRST).union(TokenSet::new(&[T![-], T![const]])); + pub(crate) fn pattern(p: &mut Parser<'_>) { pattern_r(p, PAT_RECOVERY_SET); } @@ -105,6 +110,52 @@ fn pattern_single_r(p: &mut Parser<'_>, recovery_set: TokenSet) { return; } + // test exclusive_range_pat + // fn main() { + // match 42 { + // ..0 => {} + // 1..2 => {} + // } + // } + + // test dot_dot_pat + // fn main() { + // let .. = (); + // // + // // Tuples + // // + // let (a, ..) = (); + // let (a, ..,) = (); + // let Tuple(a, ..) = (); + // let Tuple(a, ..,) = (); + // let (.., ..) = (); + // let Tuple(.., ..) = (); + // let (.., a, ..) = (); + // let Tuple(.., a, ..) = (); + // // + // // Slices + // // + // let [..] = (); + // let [head, ..] = (); + // let [head, tail @ ..] = (); + // let [head, .., cons] = (); + // let [head, mid @ .., cons] = (); + // let [head, .., .., cons] = (); + // let [head, .., mid, tail @ ..] = (); + // let [head, .., mid, .., cons] = (); + // } + if p.at(T![..]) { + let m = p.start(); + p.bump(T![..]); + if p.at_ts(RANGE_PAT_END_FIRST) { + atom_pat(p, recovery_set); + m.complete(p, RANGE_PAT); + } else { + m.complete(p, REST_PAT); + } + return; + } + if let Some(lhs) = atom_pat(p, recovery_set) { for range_op in [T![...], T![..=], T![..]] { if p.at(range_op) { @@ -173,7 +224,6 @@ fn atom_pat(p: &mut Parser<'_>, recovery_set: TokenSet) -> Option path_or_macro_pat(p), _ if is_literal_pat_start(p) => literal_pat(p), - T![.] if p.at(T![..]) => rest_pat(p), T![_] => wildcard_pat(p), T![&] => ref_pat(p), T!['('] => tuple_pat(p), @@ -334,39 +384,6 @@ fn wildcard_pat(p: &mut Parser<'_>) -> CompletedMarker { m.complete(p, WILDCARD_PAT) } -// test dot_dot_pat -// fn main() { -// let .. = (); -// // -// // Tuples -// // -// let (a, ..) = (); -// let (a, ..,) = (); -// let Tuple(a, ..) = (); -// let Tuple(a, ..,) = (); -// let (.., ..) = (); -// let Tuple(.., ..) = (); -// let (.., a, ..) = (); -// let Tuple(.., a, ..) = (); -// // -// // Slices -// // -// let [..] = (); -// let [head, ..] = (); -// let [head, tail @ ..] = (); -// let [head, .., cons] = (); -// let [head, mid @ .., cons] = (); -// let [head, .., .., cons] = (); -// let [head, .., mid, tail @ ..] = (); -// let [head, .., mid, .., cons] = (); -// } -fn rest_pat(p: &mut Parser<'_>) -> CompletedMarker { - assert!(p.at(T![..])); - let m = p.start(); - p.bump(T![..]); - m.complete(p, REST_PAT) -} - // test ref_pat // fn main() { // let &a = (); @@ -396,6 +413,16 @@ fn tuple_pat(p: &mut Parser<'_>) -> CompletedMarker { let mut has_comma = false; let mut has_pat = false; let mut has_rest = false; + + // test_err tuple_pat_leading_comma + // fn foo() { + // let (,); + // } + if p.eat(T![,]) { + p.error("expected pattern"); + has_comma = true; + } + while !p.at(EOF) && !p.at(T![')']) { has_pat = true; if !p.at_ts(PAT_TOP_FIRST) { @@ -483,6 +510,14 @@ fn box_pat(p: &mut Parser<'_>) -> CompletedMarker { // fn main() { // let const { 15 } = (); // let const { foo(); bar() } = (); +// +// match 42 { +// const { 0 } .. const { 1 } => (), +// .. const { 0 } => (), +// const { 2 } .. => (), +// } +// +// let (const { () },) = (); // } fn const_block_pat(p: &mut Parser<'_>) -> CompletedMarker { assert!(p.at(T![const])); diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/types.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/types.rs index 7d0b156c5a06a..93ef48350270b 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/types.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/types.rs @@ -15,6 +15,7 @@ pub(super) const TYPE_FIRST: TokenSet = paths::PATH_FIRST.union(TokenSet::new(&[ T![impl], T![dyn], T![Self], + LIFETIME_IDENT, ])); pub(super) const TYPE_RECOVERY_SET: TokenSet = TokenSet::new(&[ @@ -49,6 +50,7 @@ fn type_with_bounds_cond(p: &mut Parser<'_>, allow_bounds: bool) { // Some path types are not allowed to have bounds (no plus) T![<] => path_type_(p, allow_bounds), _ if paths::is_path_start(p) => path_or_macro_type_(p, allow_bounds), + LIFETIME_IDENT if p.nth_at(1, T![+]) => bare_dyn_trait_type(p), _ => { p.err_recover("expected type", TYPE_RECOVERY_SET); } @@ -59,7 +61,7 @@ pub(super) fn ascription(p: &mut Parser<'_>) { assert!(p.at(T![:])); p.bump(T![:]); if p.at(T![=]) { - // recover from `let x: = expr;`, `const X: = expr;` and similars + // recover from `let x: = expr;`, `const X: = expr;` and similar // hopefully no type starts with `=` p.error("missing type"); return; @@ -275,6 +277,15 @@ fn dyn_trait_type(p: &mut Parser<'_>) { m.complete(p, DYN_TRAIT_TYPE); } +// test bare_dyn_types_with_leading_lifetime +// type A = 'static + Trait; +// type B = S<'static + Trait>; +fn bare_dyn_trait_type(p: &mut Parser<'_>) { + let m = p.start(); + generic_params::bounds_without_colon(p); + m.complete(p, DYN_TRAIT_TYPE); +} + // test path_type // type A = Foo; // type B = ::Foo; @@ -326,13 +337,16 @@ pub(super) fn path_type_(p: &mut Parser<'_>, allow_bounds: bool) { /// This turns a parsed PATH_TYPE or FOR_TYPE optionally into a DYN_TRAIT_TYPE /// with a TYPE_BOUND_LIST -fn opt_type_bounds_as_dyn_trait_type(p: &mut Parser<'_>, type_marker: CompletedMarker) { +pub(super) fn opt_type_bounds_as_dyn_trait_type( + p: &mut Parser<'_>, + type_marker: CompletedMarker, +) -> CompletedMarker { assert!(matches!( type_marker.kind(), SyntaxKind::PATH_TYPE | SyntaxKind::FOR_TYPE | SyntaxKind::MACRO_TYPE )); if !p.at(T![+]) { - return; + return type_marker; } // First create a TYPE_BOUND from the completed PATH_TYPE @@ -349,5 +363,5 @@ fn opt_type_bounds_as_dyn_trait_type(p: &mut Parser<'_>, type_marker: CompletedM let m = generic_params::bounds_without_colon_m(p, m); // Finally precede everything with DYN_TRAIT_TYPE - m.precede(p).complete(p, DYN_TRAIT_TYPE); + m.precede(p).complete(p, DYN_TRAIT_TYPE) } diff --git a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs index 100deff462d25..e4dce21f32abe 100644 --- a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs +++ b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs @@ -36,7 +36,7 @@ impl<'a> LexedStr<'a> { }; for token in rustc_lexer::tokenize(&text[conv.offset..]) { - let token_text = &text[conv.offset..][..token.len]; + let token_text = &text[conv.offset..][..token.len as usize]; conv.extend_token(&token.kind, token_text); } @@ -49,8 +49,8 @@ impl<'a> LexedStr<'a> { return None; } - let token = rustc_lexer::first_token(text); - if token.len != text.len() { + let token = rustc_lexer::tokenize(text).next()?; + if token.len as usize != text.len() { return None; } @@ -175,6 +175,10 @@ impl<'a> Converter<'a> { rustc_lexer::TokenKind::Ident => { SyntaxKind::from_keyword(token_text).unwrap_or(IDENT) } + rustc_lexer::TokenKind::InvalidIdent => { + err = "Ident contains invalid characters"; + IDENT + } rustc_lexer::TokenKind::RawIdent => IDENT, rustc_lexer::TokenKind::Literal { kind, .. } => { @@ -221,6 +225,7 @@ impl<'a> Converter<'a> { err = "unknown literal prefix"; IDENT } + rustc_lexer::TokenKind::Eof => EOF, } }; @@ -268,35 +273,30 @@ impl<'a> Converter<'a> { } BYTE_STRING } - rustc_lexer::LiteralKind::RawStr { err: raw_str_err, .. } => { - if let Some(raw_str_err) = raw_str_err { - err = match raw_str_err { - rustc_lexer::RawStrError::InvalidStarter { .. } => "Missing `\"` symbol after `#` symbols to begin the raw string literal", - rustc_lexer::RawStrError::NoTerminator { expected, found, .. } => if expected == found { - "Missing trailing `\"` to terminate the raw string literal" - } else { - "Missing trailing `\"` with `#` symbols to terminate the raw string literal" - }, - rustc_lexer::RawStrError::TooManyDelimiters { .. } => "Too many `#` symbols: raw strings may be delimited by up to 65535 `#` symbols", - }; - }; + rustc_lexer::LiteralKind::CStr { terminated } => { + if !terminated { + err = "Missing trailing `\"` symbol to terminate the string literal"; + } + C_STRING + } + rustc_lexer::LiteralKind::RawStr { n_hashes } => { + if n_hashes.is_none() { + err = "Invalid raw string literal"; + } STRING } - rustc_lexer::LiteralKind::RawByteStr { err: raw_str_err, .. } => { - if let Some(raw_str_err) = raw_str_err { - err = match raw_str_err { - rustc_lexer::RawStrError::InvalidStarter { .. } => "Missing `\"` symbol after `#` symbols to begin the raw byte string literal", - rustc_lexer::RawStrError::NoTerminator { expected, found, .. } => if expected == found { - "Missing trailing `\"` to terminate the raw byte string literal" - } else { - "Missing trailing `\"` with `#` symbols to terminate the raw byte string literal" - }, - rustc_lexer::RawStrError::TooManyDelimiters { .. } => "Too many `#` symbols: raw byte strings may be delimited by up to 65535 `#` symbols", - }; - }; - + rustc_lexer::LiteralKind::RawByteStr { n_hashes } => { + if n_hashes.is_none() { + err = "Invalid raw string literal"; + } BYTE_STRING } + rustc_lexer::LiteralKind::RawCStr { n_hashes } => { + if n_hashes.is_none() { + err = "Invalid raw string literal"; + } + C_STRING + } }; let err = if err.is_empty() { None } else { Some(err) }; diff --git a/src/tools/rust-analyzer/crates/parser/src/lib.rs b/src/tools/rust-analyzer/crates/parser/src/lib.rs index 8c5aed0232ba3..1aba1f7674ffd 100644 --- a/src/tools/rust-analyzer/crates/parser/src/lib.rs +++ b/src/tools/rust-analyzer/crates/parser/src/lib.rs @@ -131,6 +131,7 @@ pub enum PrefixEntryPoint { Block, Stmt, Pat, + PatTop, Ty, Expr, Path, @@ -145,6 +146,7 @@ impl PrefixEntryPoint { PrefixEntryPoint::Block => grammar::entry::prefix::block, PrefixEntryPoint::Stmt => grammar::entry::prefix::stmt, PrefixEntryPoint::Pat => grammar::entry::prefix::pat, + PrefixEntryPoint::PatTop => grammar::entry::prefix::pat_top, PrefixEntryPoint::Ty => grammar::entry::prefix::ty, PrefixEntryPoint::Expr => grammar::entry::prefix::expr, PrefixEntryPoint::Path => grammar::entry::prefix::path, diff --git a/src/tools/rust-analyzer/crates/parser/src/parser.rs b/src/tools/rust-analyzer/crates/parser/src/parser.rs index 280416ae7c994..ef413c63754f6 100644 --- a/src/tools/rust-analyzer/crates/parser/src/parser.rs +++ b/src/tools/rust-analyzer/crates/parser/src/parser.rs @@ -205,7 +205,7 @@ impl<'t> Parser<'t> { marker.bomb.defuse(); marker = new_marker; }; - self.pos += 1 as usize; + self.pos += 1; self.push_event(Event::FloatSplitHack { ends_in_dot }); (ends_in_dot, marker) } diff --git a/src/tools/rust-analyzer/crates/parser/src/shortcuts.rs b/src/tools/rust-analyzer/crates/parser/src/shortcuts.rs index 47e4adcbbe695..5cdb39700dda3 100644 --- a/src/tools/rust-analyzer/crates/parser/src/shortcuts.rs +++ b/src/tools/rust-analyzer/crates/parser/src/shortcuts.rs @@ -46,10 +46,8 @@ impl<'a> LexedStr<'a> { // Tag the token as joint if it is float with a fractional part // we use this jointness to inform the parser about what token split // event to emit when we encounter a float literal in a field access - if kind == SyntaxKind::FLOAT_NUMBER { - if !self.text(i).ends_with('.') { - res.was_joint(); - } + if kind == SyntaxKind::FLOAT_NUMBER && !self.text(i).ends_with('.') { + res.was_joint(); } } diff --git a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs index cd87b304a2fb3..a8fbcfacf7ece 100644 --- a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs +++ b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs @@ -117,6 +117,7 @@ pub enum SyntaxKind { BYTE, STRING, BYTE_STRING, + C_STRING, ERROR, IDENT, WHITESPACE, @@ -245,6 +246,7 @@ pub enum SyntaxKind { GENERIC_PARAM, LIFETIME_PARAM, TYPE_PARAM, + RETURN_TYPE_ARG, CONST_PARAM, GENERIC_ARG_LIST, LIFETIME, @@ -378,7 +380,7 @@ impl SyntaxKind { ) } pub fn is_literal(self) -> bool { - matches!(self, INT_NUMBER | FLOAT_NUMBER | CHAR | BYTE | STRING | BYTE_STRING) + matches!(self, INT_NUMBER | FLOAT_NUMBER | CHAR | BYTE | STRING | BYTE_STRING | C_STRING) } pub fn from_keyword(ident: &str) -> Option { let kw = match ident { diff --git a/src/tools/rust-analyzer/crates/parser/src/tests/prefix_entries.rs b/src/tools/rust-analyzer/crates/parser/src/tests/prefix_entries.rs index 40f92e58804f9..11f9c34abdf1e 100644 --- a/src/tools/rust-analyzer/crates/parser/src/tests/prefix_entries.rs +++ b/src/tools/rust-analyzer/crates/parser/src/tests/prefix_entries.rs @@ -33,8 +33,7 @@ fn stmt() { fn pat() { check(PrefixEntryPoint::Pat, "x y", "x"); check(PrefixEntryPoint::Pat, "fn f() {}", "fn"); - // FIXME: This one is wrong, we should consume only one pattern. - check(PrefixEntryPoint::Pat, ".. ..", ".. .."); + check(PrefixEntryPoint::Pat, ".. ..", ".."); } #[test] diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_at_eof.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_at_eof.rast index 6ec1780c30b85..cab02d38af2ed 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_at_eof.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_at_eof.rast @@ -1 +1 @@ -BYTE_STRING "br##\"" error: Missing trailing `"` with `#` symbols to terminate the raw byte string literal +BYTE_STRING "br##\"" error: Invalid raw string literal diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_ascii_escape.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_ascii_escape.rast index d65f1bb2ff049..0486a1e8e1d23 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_ascii_escape.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_ascii_escape.rast @@ -1 +1 @@ -BYTE_STRING "br##\"\\x7f" error: Missing trailing `"` with `#` symbols to terminate the raw byte string literal +BYTE_STRING "br##\"\\x7f" error: Invalid raw string literal diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_ferris.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_ferris.rast index 0f9e0a1657ad4..41e3455c1f3f8 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_ferris.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_ferris.rast @@ -1 +1 @@ -BYTE_STRING "br##\"🦀" error: Missing trailing `"` with `#` symbols to terminate the raw byte string literal +BYTE_STRING "br##\"🦀" error: Invalid raw string literal diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_slash.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_slash.rast index 202dcd2d43e38..a11208a81fe8d 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_slash.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_slash.rast @@ -1 +1 @@ -BYTE_STRING "br##\"\\" error: Missing trailing `"` with `#` symbols to terminate the raw byte string literal +BYTE_STRING "br##\"\\" error: Invalid raw string literal diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_slash_n.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_slash_n.rast index d45485b529ed6..10a47ab844757 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_slash_n.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_slash_n.rast @@ -1 +1 @@ -BYTE_STRING "br##\"\\n" error: Missing trailing `"` with `#` symbols to terminate the raw byte string literal +BYTE_STRING "br##\"\\n" error: Invalid raw string literal diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_space.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_space.rast index 1bfabbc3ab622..b41ea3a170155 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_space.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_space.rast @@ -1 +1 @@ -BYTE_STRING "br##\" " error: Missing trailing `"` with `#` symbols to terminate the raw byte string literal +BYTE_STRING "br##\" " error: Invalid raw string literal diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_unicode_escape.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_unicode_escape.rast index 104ab8aaeefa4..63b8a5af80907 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_unicode_escape.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_unicode_escape.rast @@ -1 +1 @@ -BYTE_STRING "br##\"\\u{20AA}" error: Missing trailing `"` with `#` symbols to terminate the raw byte string literal +BYTE_STRING "br##\"\\u{20AA}" error: Invalid raw string literal diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_at_eof.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_at_eof.rast index 71b20fd19db7c..096bb94031527 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_at_eof.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_at_eof.rast @@ -1 +1 @@ -STRING "r##\"" error: Missing trailing `"` with `#` symbols to terminate the raw string literal +STRING "r##\"" error: Invalid raw string literal diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_ascii_escape.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_ascii_escape.rast index dc106dd24a111..f0ad200fea5a5 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_ascii_escape.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_ascii_escape.rast @@ -1 +1 @@ -STRING "r##\"\\x7f" error: Missing trailing `"` with `#` symbols to terminate the raw string literal +STRING "r##\"\\x7f" error: Invalid raw string literal diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_ferris.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_ferris.rast index 30ee029f65679..bc5996d1e67ba 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_ferris.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_ferris.rast @@ -1 +1 @@ -STRING "r##\"🦀" error: Missing trailing `"` with `#` symbols to terminate the raw string literal +STRING "r##\"🦀" error: Invalid raw string literal diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_slash.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_slash.rast index 8a6f6cc436662..b48ec5ddabbe5 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_slash.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_slash.rast @@ -1 +1 @@ -STRING "r##\"\\" error: Missing trailing `"` with `#` symbols to terminate the raw string literal +STRING "r##\"\\" error: Invalid raw string literal diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_slash_n.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_slash_n.rast index f46eff2516ac4..9f32f677766c4 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_slash_n.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_slash_n.rast @@ -1 +1 @@ -STRING "r##\"\\n" error: Missing trailing `"` with `#` symbols to terminate the raw string literal +STRING "r##\"\\n" error: Invalid raw string literal diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_space.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_space.rast index 49b6afea45a5c..2804a43cf1fee 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_space.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_space.rast @@ -1 +1 @@ -STRING "r##\" " error: Missing trailing `"` with `#` symbols to terminate the raw string literal +STRING "r##\" " error: Invalid raw string literal diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_unicode_escape.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_unicode_escape.rast index d10d6d8e8c2fd..eb0a2d2da10af 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_unicode_escape.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_unicode_escape.rast @@ -1 +1 @@ -STRING "r##\"\\u{20AA}" error: Missing trailing `"` with `#` symbols to terminate the raw string literal +STRING "r##\"\\u{20AA}" error: Invalid raw string literal diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_byte_string_at_eof.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_byte_string_at_eof.rast index cf942c92f3b19..52a7f03b6f559 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_byte_string_at_eof.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_byte_string_at_eof.rast @@ -1 +1 @@ -BYTE_STRING "br##" error: Missing `"` symbol after `#` symbols to begin the raw byte string literal +BYTE_STRING "br##" error: Invalid raw string literal diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_byte_string_with_ascii.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_byte_string_with_ascii.rast index 042769c275642..da5550d4cc186 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_byte_string_with_ascii.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_byte_string_with_ascii.rast @@ -1,4 +1,4 @@ -BYTE_STRING "br## " error: Missing `"` symbol after `#` symbols to begin the raw byte string literal +BYTE_STRING "br## " error: Invalid raw string literal IDENT "I" WHITESPACE " " IDENT "lack" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_string_at_eof.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_string_at_eof.rast index 2f7c7529a95c0..50b962e77a480 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_string_at_eof.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_string_at_eof.rast @@ -1 +1 @@ -STRING "r##" error: Missing `"` symbol after `#` symbols to begin the raw string literal +STRING "r##" error: Invalid raw string literal diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_string_with_ascii.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_string_with_ascii.rast index 4a06b0abe748e..1f484299af156 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_string_with_ascii.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_string_with_ascii.rast @@ -1,4 +1,4 @@ -STRING "r## " error: Missing `"` symbol after `#` symbols to begin the raw string literal +STRING "r## " error: Invalid raw string literal IDENT "I" WHITESPACE " " IDENT "lack" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0027_incomplere_where_for.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0027_incomplete_where_for.rast similarity index 100% rename from src/tools/rust-analyzer/crates/parser/test_data/parser/err/0027_incomplere_where_for.rast rename to src/tools/rust-analyzer/crates/parser/test_data/parser/err/0027_incomplete_where_for.rast diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0027_incomplere_where_for.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0027_incomplete_where_for.rs similarity index 100% rename from src/tools/rust-analyzer/crates/parser/test_data/parser/err/0027_incomplere_where_for.rs rename to src/tools/rust-analyzer/crates/parser/test_data/parser/err/0027_incomplete_where_for.rs diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0047_repated_extern_modifier.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0047_repeated_extern_modifier.rast similarity index 100% rename from src/tools/rust-analyzer/crates/parser/test_data/parser/err/0047_repated_extern_modifier.rast rename to src/tools/rust-analyzer/crates/parser/test_data/parser/err/0047_repeated_extern_modifier.rast diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0047_repated_extern_modifier.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0047_repeated_extern_modifier.rs similarity index 100% rename from src/tools/rust-analyzer/crates/parser/test_data/parser/err/0047_repated_extern_modifier.rs rename to src/tools/rust-analyzer/crates/parser/test_data/parser/err/0047_repeated_extern_modifier.rs diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0018_crate_visibility_empty_recover.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0018_crate_visibility_empty_recover.rast new file mode 100644 index 0000000000000..0fe4ca42d79dd --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0018_crate_visibility_empty_recover.rast @@ -0,0 +1,18 @@ +SOURCE_FILE + STRUCT + VISIBILITY + PUB_KW "pub" + L_PAREN "(" + PATH + PATH_SEGMENT + ERROR + R_PAREN ")" + WHITESPACE " " + STRUCT_KW "struct" + WHITESPACE " " + NAME + IDENT "S" + SEMICOLON ";" + WHITESPACE "\n" +error 4: expected identifier +error 5: expected R_PAREN diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0018_crate_visibility_empty_recover.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0018_crate_visibility_empty_recover.rs new file mode 100644 index 0000000000000..e8cf9e6696d1a --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0018_crate_visibility_empty_recover.rs @@ -0,0 +1 @@ +pub() struct S; diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0019_tuple_expr_leading_comma.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0019_tuple_expr_leading_comma.rast new file mode 100644 index 0000000000000..3fbc0da4002e4 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0019_tuple_expr_leading_comma.rast @@ -0,0 +1,24 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "foo" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + EXPR_STMT + TUPLE_EXPR + L_PAREN "(" + COMMA "," + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n" + R_CURLY "}" + WHITESPACE "\n" +error 17: expected expression diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0019_tuple_expr_leading_comma.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0019_tuple_expr_leading_comma.rs new file mode 100644 index 0000000000000..12fab59a77656 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0019_tuple_expr_leading_comma.rs @@ -0,0 +1,3 @@ +fn foo() { + (,); +} diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0020_tuple_pat_leading_comma.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0020_tuple_pat_leading_comma.rast new file mode 100644 index 0000000000000..9c8837292d289 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0020_tuple_pat_leading_comma.rast @@ -0,0 +1,26 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "foo" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + LET_STMT + LET_KW "let" + WHITESPACE " " + TUPLE_PAT + L_PAREN "(" + COMMA "," + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n" + R_CURLY "}" + WHITESPACE "\n" +error 21: expected pattern diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0020_tuple_pat_leading_comma.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0020_tuple_pat_leading_comma.rs new file mode 100644 index 0000000000000..de168521e1d3f --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0020_tuple_pat_leading_comma.rs @@ -0,0 +1,3 @@ +fn foo() { + let (,); +} diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0085_expr_literals.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0085_expr_literals.rast index 403c265ea35be..fe73d9dfe4a75 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0085_expr_literals.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0085_expr_literals.rast @@ -131,6 +131,30 @@ SOURCE_FILE LITERAL BYTE_STRING "br\"f\"" SEMICOLON ";" + WHITESPACE "\n " + LET_STMT + LET_KW "let" + WHITESPACE " " + WILDCARD_PAT + UNDERSCORE "_" + WHITESPACE " " + EQ "=" + WHITESPACE " " + LITERAL + C_STRING "c\"g\"" + SEMICOLON ";" + WHITESPACE "\n " + LET_STMT + LET_KW "let" + WHITESPACE " " + WILDCARD_PAT + UNDERSCORE "_" + WHITESPACE " " + EQ "=" + WHITESPACE " " + LITERAL + C_STRING "cr\"h\"" + SEMICOLON ";" WHITESPACE "\n" R_CURLY "}" WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0085_expr_literals.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0085_expr_literals.rs index 2e11a5a6e68c8..e7f235a83b92c 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0085_expr_literals.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0085_expr_literals.rs @@ -9,4 +9,6 @@ fn foo() { let _ = r"d"; let _ = b"e"; let _ = br"f"; + let _ = c"g"; + let _ = cr"h"; } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0156_const_block_pat.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0156_const_block_pat.rast index 59de2b9f1633c..593867a7b12bb 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0156_const_block_pat.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0156_const_block_pat.rast @@ -74,6 +74,126 @@ SOURCE_FILE L_PAREN "(" R_PAREN ")" SEMICOLON ";" + WHITESPACE "\n\n " + EXPR_STMT + MATCH_EXPR + MATCH_KW "match" + WHITESPACE " " + LITERAL + INT_NUMBER "42" + WHITESPACE " " + MATCH_ARM_LIST + L_CURLY "{" + WHITESPACE "\n " + MATCH_ARM + RANGE_PAT + CONST_BLOCK_PAT + CONST_KW "const" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE " " + LITERAL + INT_NUMBER "0" + WHITESPACE " " + R_CURLY "}" + WHITESPACE " " + DOT2 ".." + WHITESPACE " " + CONST_BLOCK_PAT + CONST_KW "const" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE " " + LITERAL + INT_NUMBER "1" + WHITESPACE " " + R_CURLY "}" + WHITESPACE " " + FAT_ARROW "=>" + WHITESPACE " " + TUPLE_EXPR + L_PAREN "(" + R_PAREN ")" + COMMA "," + WHITESPACE "\n " + MATCH_ARM + RANGE_PAT + DOT2 ".." + WHITESPACE " " + CONST_BLOCK_PAT + CONST_KW "const" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE " " + LITERAL + INT_NUMBER "0" + WHITESPACE " " + R_CURLY "}" + WHITESPACE " " + FAT_ARROW "=>" + WHITESPACE " " + TUPLE_EXPR + L_PAREN "(" + R_PAREN ")" + COMMA "," + WHITESPACE "\n " + MATCH_ARM + RANGE_PAT + CONST_BLOCK_PAT + CONST_KW "const" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE " " + LITERAL + INT_NUMBER "2" + WHITESPACE " " + R_CURLY "}" + WHITESPACE " " + DOT2 ".." + WHITESPACE " " + FAT_ARROW "=>" + WHITESPACE " " + TUPLE_EXPR + L_PAREN "(" + R_PAREN ")" + COMMA "," + WHITESPACE "\n " + R_CURLY "}" + WHITESPACE "\n\n " + LET_STMT + LET_KW "let" + WHITESPACE " " + TUPLE_PAT + L_PAREN "(" + CONST_BLOCK_PAT + CONST_KW "const" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE " " + TUPLE_EXPR + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + R_CURLY "}" + COMMA "," + R_PAREN ")" + WHITESPACE " " + EQ "=" + WHITESPACE " " + TUPLE_EXPR + L_PAREN "(" + R_PAREN ")" + SEMICOLON ";" WHITESPACE "\n" R_CURLY "}" WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0156_const_block_pat.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0156_const_block_pat.rs index dce9defac2f9f..6ecdee849b794 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0156_const_block_pat.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0156_const_block_pat.rs @@ -1,4 +1,12 @@ fn main() { let const { 15 } = (); let const { foo(); bar() } = (); + + match 42 { + const { 0 } .. const { 1 } => (), + .. const { 0 } => (), + const { 2 } .. => (), + } + + let (const { () },) = (); } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0196_pub_tuple_field.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0196_pub_tuple_field.rast index a23ddf69f25fc..c78d16f064c4a 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0196_pub_tuple_field.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0196_pub_tuple_field.rast @@ -28,3 +28,42 @@ SOURCE_FILE R_PAREN ")" SEMICOLON ";" WHITESPACE "\n" + STRUCT + STRUCT_KW "struct" + WHITESPACE " " + NAME + IDENT "MyStruct" + TUPLE_FIELD_LIST + L_PAREN "(" + TUPLE_FIELD + VISIBILITY + PUB_KW "pub" + WHITESPACE " " + PAREN_TYPE + L_PAREN "(" + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "u32" + R_PAREN ")" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n" + STRUCT + STRUCT_KW "struct" + WHITESPACE " " + NAME + IDENT "MyStruct" + TUPLE_FIELD_LIST + L_PAREN "(" + TUPLE_FIELD + VISIBILITY + PUB_KW "pub" + WHITESPACE " " + TUPLE_TYPE + L_PAREN "(" + R_PAREN ")" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0196_pub_tuple_field.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0196_pub_tuple_field.rs index 00d8feba96748..6f725fb7b983a 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0196_pub_tuple_field.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0196_pub_tuple_field.rs @@ -1 +1,3 @@ struct MyStruct(pub (u32, u32)); +struct MyStruct(pub (u32)); +struct MyStruct(pub ()); diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0202_typepathfn_with_coloncolon.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0202_typepathfn_with_coloncolon.rast index b47a5a5c14705..67277d0639a8d 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0202_typepathfn_with_coloncolon.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0202_typepathfn_with_coloncolon.rast @@ -41,3 +41,41 @@ SOURCE_FILE IDENT "End" SEMICOLON ";" WHITESPACE "\n" + TYPE_ALIAS + TYPE_KW "type" + WHITESPACE " " + NAME + IDENT "GenericArg" + WHITESPACE " " + EQ "=" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" + GENERIC_ARG_LIST + L_ANGLE "<" + TYPE_ARG + PATH_TYPE + PATH + PATH + PATH_SEGMENT + NAME_REF + IDENT "Start" + PARAM_LIST + L_PAREN "(" + PARAM + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "Middle" + R_PAREN ")" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "End" + R_ANGLE ">" + SEMICOLON ";" + WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0202_typepathfn_with_coloncolon.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0202_typepathfn_with_coloncolon.rs index 8efd93a7ff685..8c54f6704b3fa 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0202_typepathfn_with_coloncolon.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0202_typepathfn_with_coloncolon.rs @@ -1 +1,2 @@ type F = Start::(Middle) -> (Middle)::End; +type GenericArg = S; diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_exclusive_range_pat.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_exclusive_range_pat.rast new file mode 100644 index 0000000000000..fd2c422d0d191 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_exclusive_range_pat.rast @@ -0,0 +1,58 @@ +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 " + MATCH_EXPR + MATCH_KW "match" + WHITESPACE " " + LITERAL + INT_NUMBER "42" + WHITESPACE " " + MATCH_ARM_LIST + L_CURLY "{" + WHITESPACE "\n " + MATCH_ARM + RANGE_PAT + DOT2 ".." + LITERAL_PAT + LITERAL + INT_NUMBER "0" + WHITESPACE " " + FAT_ARROW "=>" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + R_CURLY "}" + WHITESPACE "\n " + MATCH_ARM + RANGE_PAT + LITERAL_PAT + LITERAL + INT_NUMBER "1" + DOT2 ".." + LITERAL_PAT + LITERAL + INT_NUMBER "2" + WHITESPACE " " + FAT_ARROW "=>" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + R_CURLY "}" + WHITESPACE "\n " + R_CURLY "}" + WHITESPACE "\n" + R_CURLY "}" + WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_exclusive_range_pat.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_exclusive_range_pat.rs new file mode 100644 index 0000000000000..e80505d8bd4ba --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_exclusive_range_pat.rs @@ -0,0 +1,6 @@ +fn main() { + match 42 { + ..0 => {} + 1..2 => {} + } +} diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_associated_return_type_bounds.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_associated_return_type_bounds.rast new file mode 100644 index 0000000000000..2fa52068c9b72 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_associated_return_type_bounds.rast @@ -0,0 +1,102 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "foo" + GENERIC_PARAM_LIST + L_ANGLE "<" + TYPE_PARAM + NAME + IDENT "T" + COLON ":" + WHITESPACE " " + TYPE_BOUND_LIST + TYPE_BOUND + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "Foo" + GENERIC_ARG_LIST + L_ANGLE "<" + ASSOC_TYPE_ARG + NAME_REF + IDENT "foo" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + COLON ":" + WHITESPACE " " + TYPE_BOUND_LIST + TYPE_BOUND + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "Send" + COMMA "," + WHITESPACE " " + ASSOC_TYPE_ARG + NAME_REF + IDENT "bar" + PARAM_LIST + L_PAREN "(" + PARAM + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "i32" + R_PAREN ")" + COLON ":" + WHITESPACE " " + TYPE_BOUND_LIST + TYPE_BOUND + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "Send" + COMMA "," + WHITESPACE " " + ASSOC_TYPE_ARG + NAME_REF + IDENT "baz" + PARAM_LIST + L_PAREN "(" + PARAM + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "i32" + COMMA "," + WHITESPACE " " + PARAM + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "i32" + R_PAREN ")" + COLON ":" + WHITESPACE " " + TYPE_BOUND_LIST + TYPE_BOUND + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "Send" + R_ANGLE ">" + R_ANGLE ">" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + R_CURLY "}" + WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_associated_return_type_bounds.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_associated_return_type_bounds.rs new file mode 100644 index 0000000000000..42029ac592702 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_associated_return_type_bounds.rs @@ -0,0 +1 @@ +fn foo>() {} diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_bare_dyn_types_with_leading_lifetime.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_bare_dyn_types_with_leading_lifetime.rast new file mode 100644 index 0000000000000..d7e67fbcd15c9 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_bare_dyn_types_with_leading_lifetime.rast @@ -0,0 +1,58 @@ +SOURCE_FILE + TYPE_ALIAS + TYPE_KW "type" + WHITESPACE " " + NAME + IDENT "A" + WHITESPACE " " + EQ "=" + WHITESPACE " " + DYN_TRAIT_TYPE + TYPE_BOUND_LIST + TYPE_BOUND + LIFETIME + LIFETIME_IDENT "'static" + WHITESPACE " " + PLUS "+" + WHITESPACE " " + TYPE_BOUND + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "Trait" + SEMICOLON ";" + WHITESPACE "\n" + TYPE_ALIAS + TYPE_KW "type" + WHITESPACE " " + NAME + IDENT "B" + WHITESPACE " " + EQ "=" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" + GENERIC_ARG_LIST + L_ANGLE "<" + TYPE_ARG + DYN_TRAIT_TYPE + TYPE_BOUND_LIST + TYPE_BOUND + LIFETIME + LIFETIME_IDENT "'static" + WHITESPACE " " + PLUS "+" + WHITESPACE " " + TYPE_BOUND + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "Trait" + R_ANGLE ">" + SEMICOLON ";" + WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_bare_dyn_types_with_leading_lifetime.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_bare_dyn_types_with_leading_lifetime.rs new file mode 100644 index 0000000000000..3e9a9a29ddc44 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_bare_dyn_types_with_leading_lifetime.rs @@ -0,0 +1,2 @@ +type A = 'static + Trait; +type B = S<'static + Trait>; diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0209_bare_dyn_types_with_paren_as_generic_args.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0209_bare_dyn_types_with_paren_as_generic_args.rast new file mode 100644 index 0000000000000..d5f97bad898ed --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0209_bare_dyn_types_with_paren_as_generic_args.rast @@ -0,0 +1,175 @@ +SOURCE_FILE + TYPE_ALIAS + TYPE_KW "type" + WHITESPACE " " + NAME + IDENT "A" + WHITESPACE " " + EQ "=" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" + GENERIC_ARG_LIST + L_ANGLE "<" + TYPE_ARG + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "Fn" + PARAM_LIST + L_PAREN "(" + PARAM + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "i32" + R_PAREN ")" + R_ANGLE ">" + SEMICOLON ";" + WHITESPACE "\n" + TYPE_ALIAS + TYPE_KW "type" + WHITESPACE " " + NAME + IDENT "A" + WHITESPACE " " + EQ "=" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" + GENERIC_ARG_LIST + L_ANGLE "<" + TYPE_ARG + DYN_TRAIT_TYPE + TYPE_BOUND_LIST + TYPE_BOUND + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "Fn" + PARAM_LIST + L_PAREN "(" + PARAM + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "i32" + R_PAREN ")" + WHITESPACE " " + PLUS "+" + WHITESPACE " " + TYPE_BOUND + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "Send" + R_ANGLE ">" + SEMICOLON ";" + WHITESPACE "\n" + TYPE_ALIAS + TYPE_KW "type" + WHITESPACE " " + NAME + IDENT "B" + WHITESPACE " " + EQ "=" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" + GENERIC_ARG_LIST + L_ANGLE "<" + TYPE_ARG + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "Fn" + PARAM_LIST + L_PAREN "(" + PARAM + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "i32" + R_PAREN ")" + WHITESPACE " " + RET_TYPE + THIN_ARROW "->" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "i32" + R_ANGLE ">" + SEMICOLON ";" + WHITESPACE "\n" + TYPE_ALIAS + TYPE_KW "type" + WHITESPACE " " + NAME + IDENT "C" + WHITESPACE " " + EQ "=" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" + GENERIC_ARG_LIST + L_ANGLE "<" + TYPE_ARG + DYN_TRAIT_TYPE + TYPE_BOUND_LIST + TYPE_BOUND + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "Fn" + PARAM_LIST + L_PAREN "(" + PARAM + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "i32" + R_PAREN ")" + WHITESPACE " " + RET_TYPE + THIN_ARROW "->" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "i32" + WHITESPACE " " + PLUS "+" + WHITESPACE " " + TYPE_BOUND + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "Send" + R_ANGLE ">" + SEMICOLON ";" + WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0209_bare_dyn_types_with_paren_as_generic_args.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0209_bare_dyn_types_with_paren_as_generic_args.rs new file mode 100644 index 0000000000000..800002b1b8238 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0209_bare_dyn_types_with_paren_as_generic_args.rs @@ -0,0 +1,4 @@ +type A = S; +type A = S; +type B = S i32>; +type C = S i32 + Send>; diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0028_operator_binding_power.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0028_operator_binding_power.rast index ae08c0756aa3a..43802572888f0 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0028_operator_binding_power.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0028_operator_binding_power.rast @@ -183,4 +183,273 @@ SOURCE_FILE COMMENT "//---&*1 - --2 * 9;" WHITESPACE "\n" R_CURLY "}" + WHITESPACE "\n\n" + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "right_associative" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + EXPR_STMT + BIN_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "a" + WHITESPACE " " + EQ "=" + WHITESPACE " " + BIN_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "b" + WHITESPACE " " + EQ "=" + WHITESPACE " " + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "c" + SEMICOLON ";" + WHITESPACE "\n " + EXPR_STMT + BIN_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "a" + WHITESPACE " " + EQ "=" + WHITESPACE " " + BIN_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "b" + WHITESPACE " " + PLUSEQ "+=" + WHITESPACE " " + BIN_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "c" + WHITESPACE " " + MINUSEQ "-=" + WHITESPACE " " + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "d" + SEMICOLON ";" + WHITESPACE "\n " + EXPR_STMT + BIN_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "a" + WHITESPACE " " + EQ "=" + WHITESPACE " " + BIN_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "b" + WHITESPACE " " + STAREQ "*=" + WHITESPACE " " + BIN_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "c" + WHITESPACE " " + SLASHEQ "/=" + WHITESPACE " " + BIN_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "d" + WHITESPACE " " + PERCENTEQ "%=" + WHITESPACE " " + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "e" + SEMICOLON ";" + WHITESPACE "\n " + EXPR_STMT + BIN_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "a" + WHITESPACE " " + EQ "=" + WHITESPACE " " + BIN_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "b" + WHITESPACE " " + AMPEQ "&=" + WHITESPACE " " + BIN_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "c" + WHITESPACE " " + PIPEEQ "|=" + WHITESPACE " " + BIN_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "d" + WHITESPACE " " + CARETEQ "^=" + WHITESPACE " " + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "e" + SEMICOLON ";" + WHITESPACE "\n " + EXPR_STMT + BIN_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "a" + WHITESPACE " " + EQ "=" + WHITESPACE " " + BIN_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "b" + WHITESPACE " " + SHLEQ "<<=" + WHITESPACE " " + BIN_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "c" + WHITESPACE " " + SHREQ ">>=" + WHITESPACE " " + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "d" + SEMICOLON ";" + WHITESPACE "\n" + R_CURLY "}" + WHITESPACE "\n\n" + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "mixed_associativity" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + COMMENT "// (a + b) = (c += ((d * e) = f))" + WHITESPACE "\n " + EXPR_STMT + BIN_EXPR + BIN_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "a" + WHITESPACE " " + PLUS "+" + WHITESPACE " " + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "b" + WHITESPACE " " + EQ "=" + WHITESPACE " " + BIN_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "c" + WHITESPACE " " + PLUSEQ "+=" + WHITESPACE " " + BIN_EXPR + BIN_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "d" + WHITESPACE " " + STAR "*" + WHITESPACE " " + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "e" + WHITESPACE " " + EQ "=" + WHITESPACE " " + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "f" + SEMICOLON ";" + WHITESPACE "\n" + R_CURLY "}" WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0028_operator_binding_power.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0028_operator_binding_power.rs index cc9598470d84c..7ee3013a0c89e 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0028_operator_binding_power.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0028_operator_binding_power.rs @@ -12,3 +12,16 @@ fn binding_power() { //1 = 2 .. 3; //---&*1 - --2 * 9; } + +fn right_associative() { + a = b = c; + a = b += c -= d; + a = b *= c /= d %= e; + a = b &= c |= d ^= e; + a = b <<= c >>= d; +} + +fn mixed_associativity() { + // (a + b) = (c += ((d * e) = f)) + a + b = c += d * e = f; +} diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0045_block_attrs.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0045_block_attrs.rast index bef1380711630..fad574a476950 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0045_block_attrs.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0045_block_attrs.rast @@ -60,7 +60,7 @@ SOURCE_FILE IDENT "doc" TOKEN_TREE L_PAREN "(" - STRING "\"Being validated is not affected by duplcates\"" + STRING "\"Being validated is not affected by duplicates\"" R_PAREN ")" R_BRACK "]" WHITESPACE "\n " diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0045_block_attrs.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0045_block_attrs.rs index f16c4566e77c3..0969ea1659dd6 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0045_block_attrs.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0045_block_attrs.rs @@ -3,7 +3,7 @@ fn inner() { //! As are ModuleDoc style comments { #![doc("Inner attributes are allowed in blocks used as statements")] - #![doc("Being validated is not affected by duplcates")] + #![doc("Being validated is not affected by duplicates")] //! As are ModuleDoc style comments }; { diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0072_destructuring_assignment.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0072_destructuring_assignment.rast index e8b836dfbd09e..ce75c55189a0c 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0072_destructuring_assignment.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0072_destructuring_assignment.rast @@ -168,42 +168,46 @@ SOURCE_FILE WHITESPACE "\n " EXPR_STMT BIN_EXPR - BIN_EXPR - CALL_EXPR - PATH_EXPR - PATH - PATH_SEGMENT - NAME_REF - IDENT "Some" - ARG_LIST - L_PAREN "(" - RANGE_EXPR - DOT2 ".." - R_PAREN ")" - WHITESPACE " " - EQ "=" - WHITESPACE " " - METHOD_CALL_EXPR - CALL_EXPR - PATH_EXPR - PATH - PATH_SEGMENT - NAME_REF - IDENT "Some" - ARG_LIST - L_PAREN "(" - LITERAL - INT_NUMBER "0" - R_PAREN ")" - DOT "." - WHITESPACE "\n " - NAME_REF - IDENT "Ok" - ARG_LIST - L_PAREN "(" - UNDERSCORE_EXPR - UNDERSCORE "_" - R_PAREN ")" + CALL_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "Some" + ARG_LIST + L_PAREN "(" + RANGE_EXPR + DOT2 ".." + R_PAREN ")" + WHITESPACE " " + EQ "=" + WHITESPACE " " + CALL_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "Some" + ARG_LIST + L_PAREN "(" + LITERAL + INT_NUMBER "0" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n " + EXPR_STMT + BIN_EXPR + CALL_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "Ok" + ARG_LIST + L_PAREN "(" + UNDERSCORE_EXPR + UNDERSCORE "_" + R_PAREN ")" WHITESPACE " " EQ "=" WHITESPACE " " diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0072_destructuring_assignment.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0072_destructuring_assignment.rs index 9d3e86603f881..d223b11f239e1 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0072_destructuring_assignment.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0072_destructuring_assignment.rs @@ -4,7 +4,7 @@ fn foo() { (_) = ..; struct S { a: i32 } S { .. } = S { ..S::default() }; - Some(..) = Some(0). + Some(..) = Some(0); Ok(_) = 0; let (a, b); [a, .., b] = [1, .., 2]; diff --git a/src/tools/rust-analyzer/crates/paths/Cargo.toml b/src/tools/rust-analyzer/crates/paths/Cargo.toml index e24e6eceffbd9..28b54be5212f4 100644 --- a/src/tools/rust-analyzer/crates/paths/Cargo.toml +++ b/src/tools/rust-analyzer/crates/paths/Cargo.toml @@ -15,4 +15,4 @@ doctest = false # Adding this dep sadly puts a lot of rust-analyzer crates after the # serde-derive crate. Even though we don't activate the derive feature here, # someone else in the crate graph certainly does! -# serde = "1" +# serde.workspace = true diff --git a/src/tools/rust-analyzer/crates/paths/src/lib.rs b/src/tools/rust-analyzer/crates/paths/src/lib.rs index 6ae23ac841ada..e0c20a4143bd6 100644 --- a/src/tools/rust-analyzer/crates/paths/src/lib.rs +++ b/src/tools/rust-analyzer/crates/paths/src/lib.rs @@ -140,6 +140,11 @@ impl AbsPath { self.0.parent().map(AbsPath::assert) } + /// Equivalent of [`Path::join`] for `AbsPath` with an additional normalize step afterwards. + pub fn absolutize(&self, path: impl AsRef) -> AbsPathBuf { + self.join(path).normalize() + } + /// Equivalent of [`Path::join`] for `AbsPath`. pub fn join(&self, path: impl AsRef) -> AbsPathBuf { self.as_ref().join(path).try_into().unwrap() @@ -166,6 +171,10 @@ impl AbsPath { AbsPathBuf::try_from(self.0.to_path_buf()).unwrap() } + pub fn canonicalize(&self) -> ! { + panic!("We explicitly do not provide canonicalization API, as that is almost always a wrong solution, see #14430") + } + /// Equivalent of [`Path::strip_prefix`] for `AbsPath`. /// /// Returns a relative path. @@ -179,6 +188,13 @@ impl AbsPath { self.0.ends_with(&suffix.0) } + pub fn name_and_extension(&self) -> Option<(&str, Option<&str>)> { + Some(( + self.file_stem()?.to_str()?, + self.extension().and_then(|extension| extension.to_str()), + )) + } + // region:delegate-methods // Note that we deliberately don't implement `Deref` here. diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml index 28469b832468b..d3486e75575b2 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml @@ -19,9 +19,10 @@ object = { version = "0.30.2", default-features = false, features = [ "macho", "pe", ] } -serde = { version = "1.0.137", features = ["derive"] } -serde_json = { version = "1.0.81", features = ["unbounded_depth"] } +serde.workspace = true +serde_json = { workspace = true, features = ["unbounded_depth"] } tracing = "0.1.37" +triomphe.workspace = true memmap2 = "0.5.4" snap = "1.1.0" diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs index 90d06967e8fc0..1603458f756ee 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs @@ -12,11 +12,8 @@ mod process; mod version; use paths::AbsPathBuf; -use std::{ - ffi::OsStr, - fmt, io, - sync::{Arc, Mutex}, -}; +use std::{fmt, io, sync::Mutex}; +use triomphe::Arc; use serde::{Deserialize, Serialize}; @@ -54,18 +51,8 @@ pub struct MacroDylib { } impl MacroDylib { - // FIXME: this is buggy due to TOCTOU, we should check the version in the - // macro process instead. - pub fn new(path: AbsPathBuf) -> io::Result { - let _p = profile::span("MacroDylib::new"); - - let info = version::read_dylib_info(&path)?; - if info.version.0 < 1 || info.version.1 < 47 { - let msg = format!("proc-macro {} built by {info:#?} is not supported by rust-analyzer, please update your Rust version.", path.display()); - return Err(io::Error::new(io::ErrorKind::InvalidData, msg)); - } - - Ok(MacroDylib { path }) + pub fn new(path: AbsPathBuf) -> MacroDylib { + MacroDylib { path } } } @@ -113,11 +100,8 @@ pub struct MacroPanic { impl ProcMacroServer { /// Spawns an external process as the proc macro server and returns a client connected to it. - pub fn spawn( - process_path: AbsPathBuf, - args: impl IntoIterator> + Clone, - ) -> io::Result { - let process = ProcMacroProcessSrv::run(process_path, args)?; + pub fn spawn(process_path: AbsPathBuf) -> io::Result { + let process = ProcMacroProcessSrv::run(process_path)?; Ok(ProcMacroServer { process: Arc::new(Mutex::new(process)) }) } @@ -156,15 +140,16 @@ impl ProcMacro { attr: Option<&tt::Subtree>, env: Vec<(String, String)>, ) -> Result, ServerError> { + let version = self.process.lock().unwrap_or_else(|e| e.into_inner()).version(); let current_dir = env .iter() .find(|(name, _)| name == "CARGO_MANIFEST_DIR") .map(|(_, value)| value.clone()); let task = ExpandMacro { - macro_body: FlatTree::new(subtree), + macro_body: FlatTree::new(subtree, version), macro_name: self.name.to_string(), - attributes: attr.map(FlatTree::new), + attributes: attr.map(|subtree| FlatTree::new(subtree, version)), lib: self.dylib_path.to_path_buf().into(), env, current_dir, @@ -173,7 +158,9 @@ impl ProcMacro { let request = msg::Request::ExpandMacro(task); let response = self.process.lock().unwrap_or_else(|e| e.into_inner()).send_task(request)?; match response { - msg::Response::ExpandMacro(it) => Ok(it.map(FlatTree::to_subtree)), + msg::Response::ExpandMacro(it) => { + Ok(it.map(|tree| FlatTree::to_subtree(tree, version))) + } msg::Response::ListMacros(..) | msg::Response::ApiVersionCheck(..) => { Err(ServerError { message: "unexpected response".to_string(), io: None }) } diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs index 4040efe93f093..4b01643c2a298 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs @@ -12,8 +12,12 @@ use crate::ProcMacroKind; pub use crate::msg::flat::FlatTree; +// The versions of the server protocol pub const NO_VERSION_CHECK_VERSION: u32 = 0; -pub const CURRENT_API_VERSION: u32 = 1; +pub const VERSION_CHECK_VERSION: u32 = 1; +pub const ENCODE_CLOSE_SPAN_VERSION: u32 = 2; + +pub const CURRENT_API_VERSION: u32 = ENCODE_CLOSE_SPAN_VERSION; #[derive(Debug, Serialize, Deserialize)] pub enum Request { @@ -146,7 +150,7 @@ mod tests { fn test_proc_macro_rpc_works() { let tt = fixture_token_tree(); let task = ExpandMacro { - macro_body: FlatTree::new(&tt), + macro_body: FlatTree::new(&tt, CURRENT_API_VERSION), macro_name: Default::default(), attributes: None, lib: std::env::current_dir().unwrap(), @@ -158,6 +162,6 @@ mod tests { // println!("{}", json); let back: ExpandMacro = serde_json::from_str(&json).unwrap(); - assert_eq!(tt, back.macro_body.to_subtree()); + assert_eq!(tt, back.macro_body.to_subtree(CURRENT_API_VERSION)); } } diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs index fd3202e0b284c..44245336f0166 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs @@ -39,7 +39,10 @@ use std::collections::{HashMap, VecDeque}; use serde::{Deserialize, Serialize}; -use crate::tt::{self, TokenId}; +use crate::{ + msg::ENCODE_CLOSE_SPAN_VERSION, + tt::{self, TokenId}, +}; #[derive(Serialize, Deserialize, Debug)] pub struct FlatTree { @@ -52,7 +55,8 @@ pub struct FlatTree { } struct SubtreeRepr { - id: tt::TokenId, + open: tt::TokenId, + close: tt::TokenId, kind: tt::DelimiterKind, tt: [u32; 2], } @@ -74,7 +78,7 @@ struct IdentRepr { } impl FlatTree { - pub fn new(subtree: &tt::Subtree) -> FlatTree { + pub fn new(subtree: &tt::Subtree, version: u32) -> FlatTree { let mut w = Writer { string_table: HashMap::new(), work: VecDeque::new(), @@ -89,7 +93,11 @@ impl FlatTree { w.write(subtree); return FlatTree { - subtree: write_vec(w.subtree, SubtreeRepr::write), + subtree: if version >= ENCODE_CLOSE_SPAN_VERSION { + write_vec(w.subtree, SubtreeRepr::write_with_close_span) + } else { + write_vec(w.subtree, SubtreeRepr::write) + }, literal: write_vec(w.literal, LiteralRepr::write), punct: write_vec(w.punct, PunctRepr::write), ident: write_vec(w.ident, IdentRepr::write), @@ -102,9 +110,13 @@ impl FlatTree { } } - pub fn to_subtree(self) -> tt::Subtree { + pub fn to_subtree(self, version: u32) -> tt::Subtree { return Reader { - subtree: read_vec(self.subtree, SubtreeRepr::read), + subtree: if version >= ENCODE_CLOSE_SPAN_VERSION { + read_vec(self.subtree, SubtreeRepr::read_with_close_span) + } else { + read_vec(self.subtree, SubtreeRepr::read) + }, literal: read_vec(self.literal, LiteralRepr::read), punct: read_vec(self.punct, PunctRepr::read), ident: read_vec(self.ident, IdentRepr::read), @@ -130,9 +142,9 @@ impl SubtreeRepr { tt::DelimiterKind::Brace => 2, tt::DelimiterKind::Bracket => 3, }; - [self.id.0, kind, self.tt[0], self.tt[1]] + [self.open.0, kind, self.tt[0], self.tt[1]] } - fn read([id, kind, lo, len]: [u32; 4]) -> SubtreeRepr { + fn read([open, kind, lo, len]: [u32; 4]) -> SubtreeRepr { let kind = match kind { 0 => tt::DelimiterKind::Invisible, 1 => tt::DelimiterKind::Parenthesis, @@ -140,7 +152,26 @@ impl SubtreeRepr { 3 => tt::DelimiterKind::Bracket, other => panic!("bad kind {other}"), }; - SubtreeRepr { id: TokenId(id), kind, tt: [lo, len] } + SubtreeRepr { open: TokenId(open), close: TokenId::UNSPECIFIED, kind, tt: [lo, len] } + } + fn write_with_close_span(self) -> [u32; 5] { + let kind = match self.kind { + tt::DelimiterKind::Invisible => 0, + tt::DelimiterKind::Parenthesis => 1, + tt::DelimiterKind::Brace => 2, + tt::DelimiterKind::Bracket => 3, + }; + [self.open.0, self.close.0, kind, self.tt[0], self.tt[1]] + } + fn read_with_close_span([open, close, kind, lo, len]: [u32; 5]) -> SubtreeRepr { + let kind = match kind { + 0 => tt::DelimiterKind::Invisible, + 1 => tt::DelimiterKind::Parenthesis, + 2 => tt::DelimiterKind::Brace, + 3 => tt::DelimiterKind::Bracket, + other => panic!("bad kind {other}"), + }; + SubtreeRepr { open: TokenId(open), close: TokenId(close), kind, tt: [lo, len] } } } @@ -244,9 +275,10 @@ impl<'a> Writer<'a> { fn enqueue(&mut self, subtree: &'a tt::Subtree) -> u32 { let idx = self.subtree.len(); - let delimiter_id = subtree.delimiter.open; + let open = subtree.delimiter.open; + let close = subtree.delimiter.close; let delimiter_kind = subtree.delimiter.kind; - self.subtree.push(SubtreeRepr { id: delimiter_id, kind: delimiter_kind, tt: [!0, !0] }); + self.subtree.push(SubtreeRepr { open, close, kind: delimiter_kind, tt: [!0, !0] }); self.work.push_back((idx, subtree)); idx as u32 } @@ -277,11 +309,7 @@ impl Reader { let repr = &self.subtree[i]; let token_trees = &self.token_tree[repr.tt[0] as usize..repr.tt[1] as usize]; let s = tt::Subtree { - delimiter: tt::Delimiter { - open: repr.id, - close: TokenId::UNSPECIFIED, - kind: repr.kind, - }, + delimiter: tt::Delimiter { open: repr.open, close: repr.close, kind: repr.kind }, token_trees: token_trees .iter() .copied() diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs index 1ccbd780fdda3..9a20fa63ed700 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs @@ -1,7 +1,6 @@ //! Handle process life-time and message passing for proc-macro client use std::{ - ffi::{OsStr, OsString}, io::{self, BufRead, BufReader, Write}, process::{Child, ChildStdin, ChildStdout, Command, Stdio}, }; @@ -23,12 +22,9 @@ pub(crate) struct ProcMacroProcessSrv { } impl ProcMacroProcessSrv { - pub(crate) fn run( - process_path: AbsPathBuf, - args: impl IntoIterator> + Clone, - ) -> io::Result { + pub(crate) fn run(process_path: AbsPathBuf) -> io::Result { let create_srv = |null_stderr| { - let mut process = Process::run(process_path.clone(), args.clone(), null_stderr)?; + let mut process = Process::run(process_path.clone(), null_stderr)?; let (stdin, stdout) = process.stdio().expect("couldn't access child stdio"); io::Result::Ok(ProcMacroProcessSrv { _process: process, stdin, stdout, version: 0 }) @@ -56,6 +52,10 @@ impl ProcMacroProcessSrv { } } + pub(crate) fn version(&self) -> u32 { + self.version + } + pub(crate) fn version_check(&mut self) -> Result { let request = Request::ApiVersionCheck {}; let response = self.send_task(request)?; @@ -96,13 +96,8 @@ struct Process { } impl Process { - fn run( - path: AbsPathBuf, - args: impl IntoIterator>, - null_stderr: bool, - ) -> io::Result { - let args: Vec = args.into_iter().map(|s| s.as_ref().into()).collect(); - let child = JodChild(mk_child(&path, args, null_stderr)?); + fn run(path: AbsPathBuf, null_stderr: bool) -> io::Result { + let child = JodChild(mk_child(&path, null_stderr)?); Ok(Process { child }) } @@ -115,13 +110,8 @@ impl Process { } } -fn mk_child( - path: &AbsPath, - args: impl IntoIterator>, - null_stderr: bool, -) -> io::Result { +fn mk_child(path: &AbsPath, null_stderr: bool) -> 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()) diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml index c402bc02253c2..8f03c6ec7b572 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml @@ -10,6 +10,7 @@ rust-version.workspace = true [dependencies] proc-macro-srv.workspace = true +proc-macro-api.workspace = true [features] sysroot-abi = ["proc-macro-srv/sysroot-abi"] diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs index ac9fa9f5a4ce5..bece1951872c4 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs @@ -1,6 +1,6 @@ //! A standalone binary for `proc-macro-srv`. - -use proc_macro_srv::cli; +//! Driver for proc macro server +use std::io; fn main() -> std::io::Result<()> { let v = std::env::var("RUST_ANALYZER_INTERNALS_DO_NOT_USE"); @@ -15,5 +15,37 @@ fn main() -> std::io::Result<()> { } } - cli::run() + run() +} + +#[cfg(not(feature = "sysroot-abi"))] +fn run() -> io::Result<()> { + panic!("proc-macro-srv-cli requires the `sysroot-abi` feature to be enabled"); +} + +#[cfg(feature = "sysroot-abi")] +fn run() -> io::Result<()> { + use proc_macro_api::msg::{self, Message}; + + let read_request = |buf: &mut String| msg::Request::read(&mut io::stdin().lock(), buf); + + let write_response = |msg: msg::Response| msg.write(&mut io::stdout().lock()); + + let mut srv = proc_macro_srv::ProcMacroSrv::default(); + let mut buf = String::new(); + + while let Some(req) = read_request(&mut buf)? { + let res = match req { + msg::Request::ListMacros { dylib_path } => { + msg::Response::ListMacros(srv.list_macros(&dylib_path)) + } + msg::Request::ExpandMacro(task) => msg::Response::ExpandMacro(srv.expand(task)), + msg::Request::ApiVersionCheck {} => { + msg::Response::ApiVersionCheck(proc_macro_api::msg::CURRENT_API_VERSION) + } + }; + write_response(res)? + } + + Ok(()) } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml index f7f07cfcb2e27..d5eb157bfef97 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml @@ -22,6 +22,7 @@ object = { version = "0.30.2", default-features = false, features = [ libloading = "0.7.3" memmap2 = "0.5.4" +stdx.workspace = true tt.workspace = true mbe.workspace = true paths.workspace = true diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/mod.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/mod.rs deleted file mode 100644 index 93805c89354a5..0000000000000 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/mod.rs +++ /dev/null @@ -1,106 +0,0 @@ -//! Macro ABI for version 1.63 of rustc - -#[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::tt; -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/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/buffer.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/buffer.rs deleted file mode 100644 index 48030f8d82dca..0000000000000 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/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/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/client.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/client.rs deleted file mode 100644 index b346c2c189698..0000000000000 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/client.rs +++ /dev/null @@ -1,510 +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, - Group, - Literal, - SourceFile, - MultiSpan, - Diagnostic, - - 'interned: - Punct, - 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 Group { - 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 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); - -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<'_> { - pub(crate) fn is_available() -> bool { - BridgeState::with(|state| match state { - BridgeState::Connected(_) | BridgeState::InUse => true, - BridgeState::NotConnected => false, - }) - } - - fn enter(self, f: impl FnOnce() -> R) -> R { - let force_show_panics = self.force_show_panics; - // 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) - } - })); - }); - - BRIDGE_STATE.with(|state| state.set(BridgeState::Connected(self), f)) - } - - 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), - }) - } -} - -/// 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(Bridge<'_>) -> Buffer, - - pub(super) _marker: PhantomData O>, -} - -impl Copy for Client {} -impl Clone for Client { - fn clone(&self) -> Self { - *self - } -} - -/// 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<()>>( - mut bridge: Bridge<'_>, - f: impl FnOnce(A) -> R, -) -> Buffer { - // The initial `cached_buffer` contains the input. - let mut buf = bridge.cached_buffer.take(); - - panic::catch_unwind(panic::AssertUnwindSafe(|| { - bridge.enter(|| { - let reader = &mut &buf[..]; - let input = A::decode(reader, &mut ()); - - // Put the `cached_buffer` back in the `Bridge`, for requests. - Bridge::with(|bridge| bridge.cached_buffer = buf.take()); - - 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/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/closure.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/closure.rs deleted file mode 100644 index d371ae3cea098..0000000000000 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/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/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/handle.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/handle.rs deleted file mode 100644 index c219a9465d39f..0000000000000 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/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/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/mod.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/mod.rs deleted file mode 100644 index 4967da4931a28..0000000000000 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/mod.rs +++ /dev/null @@ -1,451 +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::Group, $S::Punct, $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>; - }, - Group { - fn drop($self: $S::Group); - fn clone($self: &$S::Group) -> $S::Group; - fn new(delimiter: Delimiter, stream: Option<$S::TokenStream>) -> $S::Group; - fn delimiter($self: &$S::Group) -> Delimiter; - fn stream($self: &$S::Group) -> $S::TokenStream; - fn span($self: &$S::Group) -> $S::Span; - fn span_open($self: &$S::Group) -> $S::Span; - fn span_close($self: &$S::Group) -> $S::Span; - fn set_span($self: &mut $S::Group, span: $S::Span); - }, - Punct { - fn new(ch: char, spacing: Spacing) -> $S::Punct; - fn as_char($self: $S::Punct) -> char; - fn spacing($self: $S::Punct) -> Spacing; - fn span($self: $S::Punct) -> $S::Span; - fn with_span($self: $S::Punct, span: $S::Span) -> $S::Punct; - }, - 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 def_site() -> $S::Span; - fn call_site() -> $S::Span; - fn mixed_site() -> $S::Span; - 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}; - -/// An active connection between a server and a client. -/// The server creates the bridge (`Bridge::run_server` in `server.rs`), -/// then passes it to the client through the function pointer in the `run` -/// field of `client::Client`. The client holds its copy of the `Bridge` -/// in TLS during its execution (`Bridge::{enter, with}` in `client.rs`). -#[repr(C)] -pub struct Bridge<'a> { - /// Reusable buffer (only `clear`-ed, never shrunk), primarily - /// used for making requests, but also for passing input to client. - cached_buffer: 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, - 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 { - (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(Clone)] -pub enum TokenTree { - Group(G), - Punct(P), - Ident(I), - Literal(L), -} - -compound_traits!( - enum TokenTree { - Group(tt), - Punct(tt), - Ident(tt), - Literal(tt), - } -); diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/rpc.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/rpc.rs deleted file mode 100644 index e9d7a46c06f6d..0000000000000 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/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/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/scoped_cell.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/scoped_cell.rs deleted file mode 100644 index 2cde1f65adf9c..0000000000000 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/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/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/selfless_reify.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/selfless_reify.rs deleted file mode 100644 index 4ee4bb87c2bbd..0000000000000 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/selfless_reify.rs +++ /dev/null @@ -1,83 +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(Bridge<'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 `Bridge`, that'd help. - fn reify_to_extern_c_fn_hrt_bridge for extern "C" fn(bridge: super::Bridge<'_>) -> R; -} diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/server.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/server.rs deleted file mode 100644 index 0fb3c69858947..0000000000000 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/server.rs +++ /dev/null @@ -1,332 +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 Group: 'static + Clone; - type Punct: 'static + Copy + Eq + Hash; - 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)* {} - impl Server for S {} - } -} -with_api!(Self, self_, declare_server_traits); - -pub(super) struct MarkedTypes(S); - -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(Bridge<'_>) -> 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(Bridge<'_>) -> Buffer, - force_show_panics: bool, - ) -> Buffer { - let mut dispatch = |buf| dispatcher.dispatch(buf); - - run_client(Bridge { - cached_buffer: 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(Bridge<'_>) -> 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(Bridge { - cached_buffer: 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(Bridge<'_>) -> 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(Bridge { - cached_buffer: 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(Bridge<'_>) -> Buffer, - force_show_panics: bool, -) -> Result { - let mut dispatcher = - Dispatcher { handle_store: HandleStore::new(handle_counters), server: MarkedTypes(server) }; - - let mut buf = Buffer::new(); - 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/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/diagnostic.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/diagnostic.rs deleted file mode 100644 index 3fade2dc4f9cc..0000000000000 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/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/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/mod.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/mod.rs deleted file mode 100644 index 89bd10da5e486..0000000000000 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/mod.rs +++ /dev/null @@ -1,1106 +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::Bridge::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 { - unimplemented!() - } -} - -/// 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::Group, - bridge::client::Punct, - 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 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 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::Group, - bridge::client::Punct, - 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 { - unimplemented!() - } -} - -/// A delimited token stream. -/// -/// A `Group` internally contains a `TokenStream` which is surrounded by `Delimiter`s. -#[derive(Clone)] -pub struct Group(bridge::client::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::client::Group::new(delimiter, stream.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(Some(self.0.stream())) - } - - /// 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()) - } - - /// 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.set_span(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 { - unimplemented!() - } -} - -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::client::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 { - Punct(bridge::client::Punct::new(ch, spacing)) - } - - /// Returns the value of this punctuation character as `char`. - pub fn as_char(&self) -> char { - self.0.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 { - self.0.spacing() - } - - /// 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 = self.0.with_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 { - unimplemented!() - } -} - -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 { - unimplemented!() - } -} - -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 { - unimplemented!() - } -} - -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/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/quote.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/quote.rs deleted file mode 100644 index 39309faa41213..0000000000000 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/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/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/ra_server.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/ra_server.rs deleted file mode 100644 index 30baf3a13f57f..0000000000000 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/ra_server.rs +++ /dev/null @@ -1,840 +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::ops::Bound; -use std::{ascii, vec::IntoIter}; - -use crate::tt; - -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.kind != tt::DelimiterKind::Invisible { - TokenStream { token_trees: vec![TokenTree::Subtree(subtree)] } - } else { - TokenStream { token_trees: subtree.token_trees } - } - } - - pub fn into_subtree(self) -> tt::Subtree { - tt::Subtree { delimiter: tt::Delimiter::UNSPECIFIED, 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.kind == tt::DelimiterKind::Invisible => - { - 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::{tt, 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: tt::Delimiter { - open: tt::TokenId::UNSPECIFIED, - close: tt::TokenId::UNSPECIFIED, - ..subtree.delimiter - }, - 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 { span: tt::TokenId::unspecified(), ..lit }) - } - tt::Leaf::Punct(punct) => { - tt::Leaf::Punct(tt::Punct { span: tt::TokenId::unspecified(), ..punct }) - } - tt::Leaf::Ident(ident) => { - tt::Leaf::Ident(tt::Ident { span: 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 Group = Group; - type Punct = Punct; - 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 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 leaf = tt::Leaf::from(p); - 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(punct), - tt::TokenTree::Subtree(subtree) => bridge::TokenTree::Group(subtree), - }) - .collect() - } -} - -fn delim_to_internal(d: bridge::Delimiter) -> tt::Delimiter { - 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 => tt::DelimiterKind::Invisible, - }; - tt::Delimiter { open: tt::TokenId::unspecified(), close: tt::TokenId::unspecified(), kind } -} - -fn delim_to_external(d: tt::Delimiter) -> bridge::Delimiter { - match d.kind { - tt::DelimiterKind::Parenthesis => bridge::Delimiter::Parenthesis, - tt::DelimiterKind::Brace => bridge::Delimiter::Brace, - tt::DelimiterKind::Bracket => bridge::Delimiter::Bracket, - tt::DelimiterKind::Invisible => 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::Group for RustAnalyzer { - fn new( - &mut self, - delimiter: bridge::Delimiter, - stream: Option, - ) -> Self::Group { - Self::Group { - delimiter: delim_to_internal(delimiter), - token_trees: stream.unwrap_or_default().token_trees, - } - } - fn delimiter(&mut self, group: &Self::Group) -> bridge::Delimiter { - delim_to_external(group.delimiter) - } - - // NOTE: Return value of do not include delimiter - fn stream(&mut self, group: &Self::Group) -> Self::TokenStream { - TokenStream { token_trees: group.token_trees.clone() } - } - - fn span(&mut self, group: &Self::Group) -> Self::Span { - group.delimiter.open - } - - fn set_span(&mut self, group: &mut Self::Group, span: Self::Span) { - group.delimiter.open = span; - } - - fn span_open(&mut self, group: &Self::Group) -> Self::Span { - group.delimiter.open - } - - fn span_close(&mut self, group: &Self::Group) -> Self::Span { - group.delimiter.close - } -} - -impl server::Punct for RustAnalyzer { - fn new(&mut self, ch: char, spacing: bridge::Spacing) -> Self::Punct { - tt::Punct { - char: ch, - spacing: spacing_to_internal(spacing), - span: tt::TokenId::unspecified(), - } - } - fn as_char(&mut self, punct: Self::Punct) -> char { - punct.char - } - fn spacing(&mut self, punct: Self::Punct) -> bridge::Spacing { - spacing_to_external(punct.spacing) - } - fn span(&mut self, punct: Self::Punct) -> Self::Span { - punct.span - } - fn with_span(&mut self, punct: Self::Punct, span: Self::Span) -> Self::Punct { - tt::Punct { span: span, ..punct } - } -} - -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: if is_raw { ::tt::SmolStr::from_iter(["r#", string]) } else { string.into() }, - span, - }))) - } - - fn span(&mut self, ident: Self::Ident) -> Self::Span { - self.ident_interner.get(ident.0).0.span - } - 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 { span: 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(), span: 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(), span: 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(), span: 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(), span: tt::TokenId::unspecified() } - } - - fn f32(&mut self, n: &str) -> Self::Literal { - let n: f32 = n.parse().unwrap(); - let text = format!("{n}f32"); - Literal { text: text.into(), span: tt::TokenId::unspecified() } - } - - fn f64(&mut self, n: &str) -> Self::Literal { - let n: f64 = n.parse().unwrap(); - let text = format!("{n}f64"); - Literal { text: text.into(), span: 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(), span: tt::TokenId::unspecified() } - } - - fn character(&mut self, ch: char) -> Self::Literal { - Literal { text: format!("'{ch}'").into(), span: 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(), span: tt::TokenId::unspecified() } - } - - fn span(&mut self, literal: &Self::Literal) -> Self::Span { - literal.span - } - - fn set_span(&mut self, literal: &mut Self::Literal, span: Self::Span) { - literal.span = 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 def_site(&mut self) -> Self::Span { - // MySpan(self.span_interner.intern(&MySpanData(Span::def_site()))) - // FIXME handle span - tt::TokenId::unspecified() - } - fn call_site(&mut self) -> Self::Span { - // MySpan(self.span_interner.intern(&MySpanData(Span::call_site()))) - // FIXME handle span - tt::TokenId::unspecified() - } - 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 mixed_site(&mut self) -> 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) - } -} - -#[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(), - span: tt::TokenId::unspecified(), - })), - tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { - text: "T".into(), - span: tt::TokenId::unspecified(), - })), - tt::TokenTree::Subtree(tt::Subtree { - delimiter: tt::Delimiter { - open: tt::TokenId::unspecified(), - close: 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: tt::Delimiter { - open: tt::TokenId::unspecified(), - close: tt::TokenId::unspecified(), - kind: tt::DelimiterKind::Parenthesis, - }, - token_trees: vec![tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { - text: "a".into(), - span: 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(), - span: tt::TokenId::unspecified(), - })) - ); - } -} diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/mod.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/mod.rs deleted file mode 100644 index 04be39cffa4ad..0000000000000 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/mod.rs +++ /dev/null @@ -1,146 +0,0 @@ -//! Procedural macros are implemented by compiling the macro providing crate -//! to a dynamic library with a particular ABI which the compiler uses to expand -//! macros. Unfortunately this ABI is not specified and can change from version -//! to version of the compiler. To support this we copy the ABI from the rust -//! 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 -//! provider. -//! -//! # Adding a new ABI -//! -//! To add a new ABI you'll need to copy the source of the target proc_macro -//! crate from the source tree of the Rust compiler into this directory tree. -//! Then you'll need to modify it -//! - Remove any feature! or other things which won't compile on stable -//! - change any absolute imports to relative imports within the ABI tree -//! -//! Then you'll need to add a branch to the `Abi` enum and an implementation of -//! `Abi::expand`, `Abi::list_macros` and `Abi::from_lib` for the new ABI. See -//! `proc_macro_srv/src/abis/abi_1_47/mod.rs` for an example. Finally you'll -//! need to update the conditionals in `Abi::from_lib` to return your new ABI -//! for the relevant versions of the rust compiler -//! - -mod abi_1_63; -#[cfg(feature = "sysroot-abi")] -mod abi_sysroot; - -// see `build.rs` -include!(concat!(env!("OUT_DIR"), "/rustc_version.rs")); - -// Used by `test/utils.rs` -#[cfg(all(test, feature = "sysroot-abi"))] -pub(crate) use abi_sysroot::TokenStream as TestTokenStream; - -use super::dylib::LoadProcMacroDylibError; -pub(crate) use abi_1_63::Abi as Abi_1_63; -#[cfg(feature = "sysroot-abi")] -pub(crate) use abi_sysroot::Abi as Abi_Sysroot; -use libloading::Library; -use proc_macro_api::{ProcMacroKind, RustCInfo}; - -use crate::tt; - -pub struct PanicMessage { - message: Option, -} - -impl PanicMessage { - pub fn as_str(&self) -> Option { - self.message.clone() - } -} - -pub(crate) enum Abi { - Abi1_63(Abi_1_63), - #[cfg(feature = "sysroot-abi")] - AbiSysroot(Abi_Sysroot), -} - -impl Abi { - /// Load a new ABI. - /// - /// # Arguments - /// - /// *`lib` - The dynamic library containing the macro implementations - /// *`symbol_name` - The symbol name the macros can be found attributes - /// *`info` - RustCInfo about the compiler that was used to compile the - /// macro crate. This is the information we use to figure out - /// which ABI to return - pub fn from_lib( - lib: &Library, - symbol_name: String, - info: RustCInfo, - ) -> Result { - // the sysroot ABI relies on `extern proc_macro` with unstable features, - // instead of a snapshot of the proc macro bridge's source code. it's only - // enabled if we have an exact version match. - #[cfg(feature = "sysroot-abi")] - { - if info.version_string == RUSTC_VERSION_STRING { - let inner = unsafe { Abi_Sysroot::from_lib(lib, symbol_name) }?; - return Ok(Abi::AbiSysroot(inner)); - } - - // if we reached this point, versions didn't match. in testing, we - // want that to panic - this could mean that the format of `rustc - // --version` no longer matches the format of the version string - // stored in the `.rustc` section, and we want to catch that in-tree - // with `x.py test` - #[cfg(test)] - { - let allow_mismatch = std::env::var("PROC_MACRO_SRV_ALLOW_SYSROOT_MISMATCH"); - if let Ok("1") = allow_mismatch.as_deref() { - // only used by rust-analyzer developers, when working on the - // sysroot ABI from the rust-analyzer repository - which should - // only happen pre-subtree. this can be removed later. - } else { - panic!( - "sysroot ABI mismatch: dylib rustc version (read from .rustc section): {:?} != proc-macro-srv version (read from 'rustc --version'): {:?}", - info.version_string, RUSTC_VERSION_STRING - ); - } - } - } - - // FIXME: this should use exclusive ranges when they're stable - // https://github.com/rust-lang/rust/issues/37854 - match (info.version.0, info.version.1) { - (1, 63) => { - let inner = unsafe { Abi_1_63::from_lib(lib, symbol_name) }?; - Ok(Abi::Abi1_63(inner)) - } - _ => Err(LoadProcMacroDylibError::UnsupportedABI(info.version_string)), - } - } - - pub fn expand( - &self, - macro_name: &str, - macro_body: &tt::Subtree, - attributes: Option<&tt::Subtree>, - ) -> Result { - match self { - Self::Abi1_63(abi) => abi.expand(macro_name, macro_body, attributes), - #[cfg(feature = "sysroot-abi")] - Self::AbiSysroot(abi) => abi.expand(macro_name, macro_body, attributes), - } - } - - pub fn list_macros(&self) -> Vec<(String, ProcMacroKind)> { - match self { - Self::Abi1_63(abi) => abi.list_macros(), - #[cfg(feature = "sysroot-abi")] - Self::AbiSysroot(abi) => abi.list_macros(), - } - } -} - -#[test] -fn test_version_check() { - let path = paths::AbsPathBuf::assert(crate::proc_macro_test_dylib_path()); - let info = proc_macro_api::read_dylib_info(&path).unwrap(); - assert!(info.version.1 >= 50); -} diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/cli.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/cli.rs deleted file mode 100644 index 05168feb629f2..0000000000000 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/cli.rs +++ /dev/null @@ -1,34 +0,0 @@ -//! Driver for proc macro server -use std::io; - -use proc_macro_api::msg::{self, Message}; - -use crate::ProcMacroSrv; - -pub fn run() -> io::Result<()> { - let mut srv = ProcMacroSrv::default(); - let mut buf = String::new(); - - while let Some(req) = read_request(&mut buf)? { - let res = match req { - msg::Request::ListMacros { dylib_path } => { - msg::Response::ListMacros(srv.list_macros(&dylib_path)) - } - msg::Request::ExpandMacro(task) => msg::Response::ExpandMacro(srv.expand(task)), - msg::Request::ApiVersionCheck {} => { - msg::Response::ApiVersionCheck(proc_macro_api::msg::CURRENT_API_VERSION) - } - }; - write_response(res)? - } - - Ok(()) -} - -fn read_request(buf: &mut String) -> io::Result> { - msg::Request::read(&mut io::stdin().lock(), buf) -} - -fn write_response(msg: msg::Response) -> io::Result<()> { - msg.write(&mut io::stdout().lock()) -} diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs index 89ffd1f493e29..dd05e250c2def 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs @@ -13,10 +13,6 @@ use object::Object; use paths::AbsPath; use proc_macro_api::{read_dylib_info, ProcMacroKind}; -use crate::tt; - -use super::abis::Abi; - const NEW_REGISTRAR_SYMBOL: &str = "_rustc_proc_macro_decls_"; fn invalid_data_err(e: impl Into>) -> io::Error { @@ -82,14 +78,17 @@ fn load_library(file: &Path) -> Result { pub enum LoadProcMacroDylibError { Io(io::Error), LibLoading(libloading::Error), - UnsupportedABI(String), + AbiMismatch(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(v) => write!(f, "unsupported ABI `{v}`"), + Self::AbiMismatch(v) => { + use crate::RUSTC_VERSION_STRING; + write!(f, "mismatched ABI expected: `{RUSTC_VERSION_STRING}`, got `{v}`") + } Self::LibLoading(e) => e.fmt(f), } } @@ -110,7 +109,7 @@ impl From for LoadProcMacroDylibError { struct ProcMacroLibraryLibloading { // Hold on to the library so it doesn't unload _lib: Library, - abi: Abi, + proc_macros: crate::proc_macros::ProcMacros, } impl ProcMacroLibraryLibloading { @@ -125,8 +124,9 @@ impl ProcMacroLibraryLibloading { let version_info = read_dylib_info(abs_file)?; let lib = load_library(file).map_err(invalid_data_err)?; - let abi = Abi::from_lib(&lib, symbol_name, version_info)?; - Ok(ProcMacroLibraryLibloading { _lib: lib, abi }) + let proc_macros = + crate::proc_macros::ProcMacros::from_lib(&lib, symbol_name, version_info)?; + Ok(ProcMacroLibraryLibloading { _lib: lib, proc_macros }) } } @@ -150,15 +150,15 @@ impl Expander { pub fn expand( &self, macro_name: &str, - macro_body: &tt::Subtree, - attributes: Option<&tt::Subtree>, - ) -> Result { - let result = self.inner.abi.expand(macro_name, macro_body, attributes); + macro_body: &crate::tt::Subtree, + attributes: Option<&crate::tt::Subtree>, + ) -> Result { + let result = self.inner.proc_macros.expand(macro_name, macro_body, attributes); result.map_err(|e| e.as_str().unwrap_or_else(|| "".to_string())) } pub fn list_macros(&self) -> Vec<(String, ProcMacroKind)> { - self.inner.abi.list_macros() + self.inner.proc_macros.list_macros() } } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs index ee70fe7d4f541..84bd15efb8bee 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs @@ -10,17 +10,16 @@ //! * By **copying** the whole rustc `lib_proc_macro` code, we are able to build this with `stable` //! rustc rather than `unstable`. (Although in general ABI compatibility is still an issue)… +#![cfg(feature = "sysroot-abi")] +#![feature(proc_macro_internals, proc_macro_diagnostic, proc_macro_span)] #![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] -#![cfg_attr( - feature = "sysroot-abi", - feature(proc_macro_internals, proc_macro_diagnostic, proc_macro_span) -)] #![allow(unreachable_pub)] -mod dylib; -mod abis; +extern crate proc_macro; -pub mod cli; +mod dylib; +mod server; +mod proc_macros; use std::{ collections::{hash_map::Entry, HashMap}, @@ -33,24 +32,27 @@ use std::{ }; use proc_macro_api::{ - msg::{ExpandMacro, FlatTree, PanicMessage}, + msg::{self, CURRENT_API_VERSION}, ProcMacroKind, }; use ::tt::token_id as tt; +// see `build.rs` +include!(concat!(env!("OUT_DIR"), "/rustc_version.rs")); + #[derive(Default)] -pub(crate) struct ProcMacroSrv { +pub 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 { + pub fn expand(&mut self, task: msg::ExpandMacro) -> Result { let expander = self.expander(task.lib.as_ref()).map_err(|err| { debug_assert!(false, "should list macros before asking to expand"); - PanicMessage(format!("failed to load macro: {err}")) + msg::PanicMessage(format!("failed to load macro: {err}")) })?; let prev_env = EnvSnapshot::new(); @@ -68,8 +70,8 @@ impl ProcMacroSrv { None => None, }; - let macro_body = task.macro_body.to_subtree(); - let attributes = task.attributes.map(|it| it.to_subtree()); + let macro_body = task.macro_body.to_subtree(CURRENT_API_VERSION); + let attributes = task.attributes.map(|it| it.to_subtree(CURRENT_API_VERSION)); let result = thread::scope(|s| { let thread = thread::Builder::new() .stack_size(EXPANDER_STACK_SIZE) @@ -77,7 +79,7 @@ impl ProcMacroSrv { .spawn_scoped(s, || { expander .expand(&task.macro_name, ¯o_body, attributes.as_ref()) - .map(|it| FlatTree::new(&it)) + .map(|it| msg::FlatTree::new(&it, CURRENT_API_VERSION)) }); let res = match thread { Ok(handle) => handle.join(), @@ -102,10 +104,10 @@ impl ProcMacroSrv { } } - result.map_err(PanicMessage) + result.map_err(msg::PanicMessage) } - pub(crate) fn list_macros( + pub fn list_macros( &mut self, dylib_path: &Path, ) -> Result, String> { @@ -129,6 +131,16 @@ impl ProcMacroSrv { } } +pub struct PanicMessage { + message: Option, +} + +impl PanicMessage { + pub fn as_str(&self) -> Option { + self.message.clone() + } +} + struct EnvSnapshot { vars: HashMap, } @@ -138,10 +150,13 @@ impl EnvSnapshot { EnvSnapshot { vars: env::vars_os().collect() } } - fn rollback(self) { - let mut old_vars = self.vars; + fn rollback(self) {} +} + +impl Drop for EnvSnapshot { + fn drop(&mut self) { for (name, value) in env::vars_os() { - let old_value = old_vars.remove(&name); + let old_value = self.vars.remove(&name); if old_value != Some(value) { match old_value { None => env::remove_var(name), @@ -149,13 +164,13 @@ impl EnvSnapshot { } } } - for (name, old_value) in old_vars { + for (name, old_value) in self.vars.drain() { env::set_var(name, old_value) } } } -#[cfg(all(feature = "sysroot-abi", test))] +#[cfg(test)] mod tests; #[cfg(test)] diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/mod.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/proc_macros.rs similarity index 52% rename from src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/mod.rs rename to src/tools/rust-analyzer/crates/proc-macro-srv/src/proc_macros.rs index 0a3b8866a7fd5..3c6f320331928 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/mod.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/proc_macros.rs @@ -1,45 +1,55 @@ //! Proc macro ABI -extern crate proc_macro; - -#[allow(dead_code)] -#[doc(hidden)] -mod ra_server; - use libloading::Library; -use proc_macro_api::ProcMacroKind; +use proc_macro_api::{ProcMacroKind, RustCInfo}; -use super::{tt, PanicMessage}; +use crate::{dylib::LoadProcMacroDylibError, server::SYMBOL_INTERNER, tt}; -pub use ra_server::TokenStream; - -pub(crate) struct Abi { +pub(crate) struct ProcMacros { exported_macros: Vec, } -impl From for PanicMessage { +impl From for crate::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() }) +impl ProcMacros { + /// Load a new ABI. + /// + /// # Arguments + /// + /// *`lib` - The dynamic library containing the macro implementations + /// *`symbol_name` - The symbol name the macros can be found attributes + /// *`info` - RustCInfo about the compiler that was used to compile the + /// macro crate. This is the information we use to figure out + /// which ABI to return + pub(crate) fn from_lib( + lib: &Library, + symbol_name: String, + info: RustCInfo, + ) -> Result { + if info.version_string == crate::RUSTC_VERSION_STRING { + let macros = unsafe { + lib.get::<&&[proc_macro::bridge::client::ProcMacro]>(symbol_name.as_bytes()) + }?; + + return Ok(Self { exported_macros: macros.to_vec() }); + } + Err(LoadProcMacroDylibError::AbiMismatch(info.version_string)) } - pub fn expand( + pub(crate) fn expand( &self, macro_name: &str, macro_body: &tt::Subtree, attributes: Option<&tt::Subtree>, - ) -> Result { - let parsed_body = ra_server::TokenStream::with_subtree(macro_body.clone()); + ) -> Result { + let parsed_body = crate::server::TokenStream::with_subtree(macro_body.clone()); - let parsed_attributes = attributes.map_or(ra_server::TokenStream::new(), |attr| { - ra_server::TokenStream::with_subtree(attr.clone()) + let parsed_attributes = attributes.map_or(crate::server::TokenStream::new(), |attr| { + crate::server::TokenStream::with_subtree(attr.clone()) }); for proc_macro in &self.exported_macros { @@ -49,34 +59,34 @@ impl Abi { } if *trait_name == macro_name => { let res = client.run( &proc_macro::bridge::server::SameThread, - ra_server::RustAnalyzer::default(), + crate::server::RustAnalyzer { interner: &SYMBOL_INTERNER }, parsed_body, true, ); - return res.map(|it| it.into_subtree()).map_err(PanicMessage::from); + return res.map(|it| it.into_subtree()).map_err(crate::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(), + crate::server::RustAnalyzer { interner: &SYMBOL_INTERNER }, parsed_body, true, ); - return res.map(|it| it.into_subtree()).map_err(PanicMessage::from); + return res.map(|it| it.into_subtree()).map_err(crate::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(), + crate::server::RustAnalyzer { interner: &SYMBOL_INTERNER }, parsed_attributes, parsed_body, true, ); - return res.map(|it| it.into_subtree()).map_err(PanicMessage::from); + return res.map(|it| it.into_subtree()).map_err(crate::PanicMessage::from); } _ => continue, } @@ -85,7 +95,7 @@ impl Abi { Err(proc_macro::bridge::PanicMessage::String("Nothing to expand".to_string()).into()) } - pub fn list_macros(&self) -> Vec<(String, ProcMacroKind)> { + pub(crate) fn list_macros(&self) -> Vec<(String, ProcMacroKind)> { self.exported_macros .iter() .map(|proc_macro| match proc_macro { @@ -102,3 +112,16 @@ impl Abi { .collect() } } + +#[test] +fn test_version_check() { + let path = paths::AbsPathBuf::assert(crate::proc_macro_test_dylib_path()); + let info = proc_macro_api::read_dylib_info(&path).unwrap(); + assert_eq!( + info.version_string, + crate::RUSTC_VERSION_STRING, + "sysroot ABI mismatch: dylib rustc version (read from .rustc section): {:?} != proc-macro-srv version (read from 'rustc --version'): {:?}", + info.version_string, + crate::RUSTC_VERSION_STRING, + ); +} diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server.rs similarity index 91% rename from src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server.rs rename to src/tools/rust-analyzer/crates/proc-macro-srv/src/server.rs index a9cd8e705a4cf..37a45bcbb83b8 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server.rs @@ -8,9 +8,9 @@ //! //! FIXME: No span and source file information is implemented yet -use super::proc_macro::{ - self, +use proc_macro::{ bridge::{self, server}, + LineColumn, }; mod token_stream; @@ -26,8 +26,10 @@ use crate::tt; type Group = tt::Subtree; type TokenTree = tt::TokenTree; +#[allow(unused)] type Punct = tt::Punct; type Spacing = tt::Spacing; +#[allow(unused)] type Literal = tt::Literal; type Span = tt::TokenId; @@ -36,14 +38,11 @@ pub struct SourceFile { // FIXME stub } -type Level = super::proc_macro::Level; -type LineColumn = super::proc_macro::LineColumn; - pub struct FreeFunctions; -#[derive(Default)] pub struct RustAnalyzer { // FIXME: store span information here. + pub(crate) interner: SymbolInternerRef, } impl server::Types for RustAnalyzer { @@ -68,7 +67,7 @@ impl server::FreeFunctions for RustAnalyzer { // FIXME: keep track of LitKind and Suffix Ok(bridge::Literal { kind: bridge::LitKind::Err, - symbol: Symbol::intern(s), + symbol: Symbol::intern(self.interner, s), suffix: None, span: tt::TokenId::unspecified(), }) @@ -109,7 +108,7 @@ impl server::TokenStream for RustAnalyzer { } bridge::TokenTree::Ident(ident) => { - let text = ident.sym.text(); + let text = ident.sym.text(self.interner); let text = if ident.is_raw { ::tt::SmolStr::from_iter(["r#", &text]) } else { text }; let ident: tt::Ident = tt::Ident { text, span: ident.span }; @@ -120,8 +119,9 @@ impl server::TokenStream for RustAnalyzer { bridge::TokenTree::Literal(literal) => { let literal = LiteralFormatter(literal); - let text = literal - .with_stringify_parts(|parts| ::tt::SmolStr::from_iter(parts.iter().copied())); + let text = literal.with_stringify_parts(self.interner, |parts| { + ::tt::SmolStr::from_iter(parts.iter().copied()) + }); let literal = tt::Literal { text, span: literal.0.span }; let leaf = tt::Leaf::from(literal); @@ -185,7 +185,7 @@ impl server::TokenStream for RustAnalyzer { .map(|tree| match tree { tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => { bridge::TokenTree::Ident(bridge::Ident { - sym: Symbol::intern(ident.text.trim_start_matches("r#")), + sym: Symbol::intern(self.interner, ident.text.trim_start_matches("r#")), is_raw: ident.text.starts_with("r#"), span: ident.span, }) @@ -194,7 +194,7 @@ impl server::TokenStream for RustAnalyzer { bridge::TokenTree::Literal(bridge::Literal { // FIXME: handle literal kinds kind: bridge::LitKind::Err, - symbol: Symbol::intern(&lit.text), + symbol: Symbol::intern(self.interner, &lit.text), // FIXME: handle suffixes suffix: None, span: lit.span, @@ -240,6 +240,7 @@ fn delim_to_external(d: tt::Delimiter) -> proc_macro::Delimiter { } } +#[allow(unused)] fn spacing_to_internal(spacing: proc_macro::Spacing) -> Spacing { match spacing { proc_macro::Spacing::Alone => Spacing::Alone, @@ -247,6 +248,7 @@ fn spacing_to_internal(spacing: proc_macro::Spacing) -> Spacing { } } +#[allow(unused)] fn spacing_to_external(spacing: Spacing) -> proc_macro::Spacing { match spacing { Spacing::Alone => proc_macro::Spacing::Alone, @@ -354,11 +356,13 @@ impl server::Server for RustAnalyzer { } fn intern_symbol(ident: &str) -> Self::Symbol { - Symbol::intern(&::tt::SmolStr::from(ident)) + // FIXME: should be self.interner once the proc-macro api allows is + Symbol::intern(&SYMBOL_INTERNER, &::tt::SmolStr::from(ident)) } fn with_symbol_string(symbol: &Self::Symbol, f: impl FnOnce(&str)) { - f(symbol.text().as_str()) + // FIXME: should be self.interner once the proc-macro api allows is + f(symbol.text(&SYMBOL_INTERNER).as_str()) } } @@ -369,7 +373,11 @@ impl LiteralFormatter { /// literal's representation. This is done to allow the `ToString` and /// `Display` implementations to borrow references to symbol values, and /// both be optimized to reduce overhead. - fn with_stringify_parts(&self, f: impl FnOnce(&[&str]) -> R) -> R { + fn with_stringify_parts( + &self, + interner: SymbolInternerRef, + f: impl FnOnce(&[&str]) -> R, + ) -> R { /// Returns a string containing exactly `num` '#' characters. /// Uses a 256-character source string literal which is always safe to /// index with a `u8` index. @@ -384,7 +392,7 @@ impl LiteralFormatter { &HASHES[..num as usize] } - self.with_symbol_and_suffix(|symbol, suffix| match self.0.kind { + self.with_symbol_and_suffix(interner, |symbol, suffix| match self.0.kind { bridge::LitKind::Byte => f(&["b'", symbol, "'", suffix]), bridge::LitKind::Char => f(&["'", symbol, "'", suffix]), bridge::LitKind::Str => f(&["\"", symbol, "\"", suffix]), @@ -401,9 +409,13 @@ impl LiteralFormatter { }) } - fn with_symbol_and_suffix(&self, f: impl FnOnce(&str, &str) -> R) -> R { - let symbol = self.0.symbol.text(); - let suffix = self.0.suffix.map(|s| s.text()).unwrap_or_default(); + fn with_symbol_and_suffix( + &self, + interner: SymbolInternerRef, + f: impl FnOnce(&str, &str) -> R, + ) -> R { + let symbol = self.0.symbol.text(interner); + let suffix = self.0.suffix.map(|s| s.text(interner)).unwrap_or_default(); f(symbol.as_str(), suffix.as_str()) } } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server/symbol.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server/symbol.rs similarity index 58% rename from src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server/symbol.rs rename to src/tools/rust-analyzer/crates/proc-macro-srv/src/server/symbol.rs index 51dfba2ea9fb4..540d06457f2f3 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server/symbol.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server/symbol.rs @@ -1,28 +1,30 @@ //! Symbol interner for proc-macro-srv -use std::{cell::RefCell, collections::HashMap}; +use std::{cell::RefCell, collections::HashMap, thread::LocalKey}; use tt::SmolStr; thread_local! { - static SYMBOL_INTERNER: RefCell = Default::default(); + pub(crate) static SYMBOL_INTERNER: RefCell = Default::default(); } // ID for an interned symbol. #[derive(Hash, Eq, PartialEq, Copy, Clone)] pub struct Symbol(u32); +pub(crate) type SymbolInternerRef = &'static LocalKey>; + impl Symbol { - pub fn intern(data: &str) -> Symbol { - SYMBOL_INTERNER.with(|i| i.borrow_mut().intern(data)) + pub(super) fn intern(interner: SymbolInternerRef, data: &str) -> Symbol { + interner.with(|i| i.borrow_mut().intern(data)) } - pub fn text(&self) -> SmolStr { - SYMBOL_INTERNER.with(|i| i.borrow().get(self).clone()) + pub(super) fn text(&self, interner: SymbolInternerRef) -> SmolStr { + interner.with(|i| i.borrow().get(self).clone()) } } #[derive(Default)] -struct SymbolInterner { +pub(crate) struct SymbolInterner { idents: HashMap, ident_data: Vec, } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server/token_stream.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server/token_stream.rs similarity index 93% rename from src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server/token_stream.rs rename to src/tools/rust-analyzer/crates/proc-macro-srv/src/server/token_stream.rs index d091d431900a0..2589d8b64d489 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server/token_stream.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server/token_stream.rs @@ -4,15 +4,15 @@ use crate::tt::{self, TokenTree}; #[derive(Debug, Default, Clone)] pub struct TokenStream { - pub token_trees: Vec, + pub(super) token_trees: Vec, } impl TokenStream { - pub fn new() -> Self { + pub(crate) fn new() -> Self { TokenStream::default() } - pub fn with_subtree(subtree: tt::Subtree) -> Self { + pub(crate) fn with_subtree(subtree: tt::Subtree) -> Self { if subtree.delimiter.kind != tt::DelimiterKind::Invisible { TokenStream { token_trees: vec![TokenTree::Subtree(subtree)] } } else { @@ -20,11 +20,11 @@ impl TokenStream { } } - pub fn into_subtree(self) -> tt::Subtree { + pub(crate) fn into_subtree(self) -> tt::Subtree { tt::Subtree { delimiter: tt::Delimiter::UNSPECIFIED, token_trees: self.token_trees } } - pub fn is_empty(&self) -> bool { + pub(super) fn is_empty(&self) -> bool { self.token_trees.is_empty() } } @@ -78,12 +78,12 @@ impl Extend for TokenStream { } } -pub struct TokenStreamBuilder { +pub(super) struct TokenStreamBuilder { acc: TokenStream, } -/// Public implementation details for the `TokenStream` type, such as iterators. -pub mod token_stream { +/// pub(super)lic implementation details for the `TokenStream` type, such as iterators. +pub(super) mod token_stream { use std::str::FromStr; use super::{tt, TokenStream, TokenTree}; diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs index efbeb90ca9dd5..49b4d973b63bb 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs @@ -5,14 +5,14 @@ use std::str::FromStr; use crate::{dylib, proc_macro_test_dylib_path, ProcMacroSrv}; -fn parse_string(code: &str) -> Option { +fn parse_string(code: &str) -> Option { // This is a bit strange. We need to parse a string into a token stream into // order to create a tt::SubTree from it in fixtures. `into_subtree` is // implemented by all the ABIs we have so we arbitrarily choose one ABI to // write a `parse_string` function for and use that. The tests don't really // care which ABI we're using as the `into_subtree` function isn't part of // the ABI and shouldn't change between ABI versions. - crate::abis::TestTokenStream::from_str(code).ok() + crate::server::TokenStream::from_str(code).ok() } pub fn assert_expand(macro_name: &str, ra_fixture: &str, expect: Expect) { diff --git a/src/tools/rust-analyzer/crates/profile/Cargo.toml b/src/tools/rust-analyzer/crates/profile/Cargo.toml index 6273ea51db839..602e742751063 100644 --- a/src/tools/rust-analyzer/crates/profile/Cargo.toml +++ b/src/tools/rust-analyzer/crates/profile/Cargo.toml @@ -20,7 +20,7 @@ countme = { version = "3.0.1", features = ["enable"] } jemalloc-ctl = { version = "0.5.0", package = "tikv-jemalloc-ctl", optional = true } [target.'cfg(target_os = "linux")'.dependencies] -perf-event = "0.4.7" +perf-event = "=0.4.7" [target.'cfg(windows)'.dependencies] winapi = { version = "0.3.9", features = ["processthreadsapi", "psapi"] } diff --git a/src/tools/rust-analyzer/crates/profile/src/memory_usage.rs b/src/tools/rust-analyzer/crates/profile/src/memory_usage.rs index 8017f865792b3..f089c78e0c195 100644 --- a/src/tools/rust-analyzer/crates/profile/src/memory_usage.rs +++ b/src/tools/rust-analyzer/crates/profile/src/memory_usage.rs @@ -90,6 +90,12 @@ fn memusage_linux() -> MemoryUsage { #[derive(Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] pub struct Bytes(isize); +impl Bytes { + pub fn new(bytes: isize) -> Bytes { + Bytes(bytes) + } +} + impl Bytes { pub fn megabytes(self) -> isize { self.0 / 1024 / 1024 diff --git a/src/tools/rust-analyzer/crates/project-model/Cargo.toml b/src/tools/rust-analyzer/crates/project-model/Cargo.toml index 22d6a6e78957c..3abff64a83b2f 100644 --- a/src/tools/rust-analyzer/crates/project-model/Cargo.toml +++ b/src/tools/rust-analyzer/crates/project-model/Cargo.toml @@ -16,10 +16,12 @@ tracing = "0.1.35" rustc-hash = "1.1.0" cargo_metadata = "0.15.0" semver = "1.0.14" -serde = { version = "1.0.137", features = ["derive"] } -serde_json = "1.0.86" +serde_json.workspace = true +serde.workspace = true +triomphe.workspace = true anyhow = "1.0.62" la-arena = { version = "0.3.0", path = "../../lib/la-arena" } +itertools = "0.10.5" # local deps base-db.workspace = true diff --git a/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs b/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs index 4e5d640f175e4..6cbf403cb2e14 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs @@ -14,9 +14,10 @@ use std::{ }; use cargo_metadata::{camino::Utf8Path, Message}; +use itertools::Itertools; use la_arena::ArenaMap; use paths::{AbsPath, AbsPathBuf}; -use rustc_hash::FxHashMap; +use rustc_hash::{FxHashMap, FxHashSet}; use semver::Version; use serde::Deserialize; @@ -56,7 +57,10 @@ impl BuildScriptOutput { } impl WorkspaceBuildScripts { - fn build_command(config: &CargoConfig) -> io::Result { + fn build_command( + config: &CargoConfig, + allowed_features: &FxHashSet, + ) -> io::Result { let mut cmd = match config.run_build_script_command.as_deref() { Some([program, args @ ..]) => { let mut cmd = Command::new(program); @@ -88,7 +92,12 @@ impl WorkspaceBuildScripts { } if !features.is_empty() { cmd.arg("--features"); - cmd.arg(features.join(" ")); + cmd.arg( + features + .iter() + .filter(|&feat| allowed_features.contains(feat)) + .join(","), + ); } } } @@ -127,13 +136,20 @@ impl WorkspaceBuildScripts { } .as_ref(); - match Self::run_per_ws(Self::build_command(config)?, workspace, current_dir, progress) { + let allowed_features = workspace.workspace_features(); + + match Self::run_per_ws( + Self::build_command(config, &allowed_features)?, + workspace, + current_dir, + 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)?; + let mut cmd = Self::build_command(config, &allowed_features)?; cmd.args(["-Z", "unstable-options", "--keep-going"]).env("RUSTC_BOOTSTRAP", "1"); let mut res = Self::run_per_ws(cmd, workspace, current_dir, progress)?; res.error = Some(error); @@ -161,7 +177,7 @@ impl WorkspaceBuildScripts { )) } }; - let cmd = Self::build_command(config)?; + let cmd = Self::build_command(config, &Default::default())?; // NB: Cargo.toml could have been modified between `cargo metadata` and // `cargo check`. We shouldn't assume that package ids we see here are // exactly those from `config`. @@ -415,7 +431,6 @@ impl WorkspaceBuildScripts { let dir_entry = entry.ok()?; if dir_entry.file_type().ok()?.is_file() { let path = dir_entry.path(); - tracing::info!("p{:?}", path); let extension = path.extension()?; if extension == std::env::consts::DLL_EXTENSION { let name = path.file_stem()?.to_str()?.split_once('-')?.0.to_owned(); diff --git a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs index 01162b1a8ba0c..92b454150c3e9 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs @@ -1,6 +1,5 @@ //! See [`CargoWorkspace`]. -use std::iter; use std::path::PathBuf; use std::str::from_utf8; use std::{ops, process::Command}; @@ -10,7 +9,7 @@ use base_db::Edition; use cargo_metadata::{CargoOpt, MetadataCommand}; use la_arena::{Arena, Idx}; use paths::{AbsPath, AbsPathBuf}; -use rustc_hash::FxHashMap; +use rustc_hash::{FxHashMap, FxHashSet}; use serde::Deserialize; use serde_json::from_value; @@ -32,6 +31,7 @@ pub struct CargoWorkspace { packages: Arena, targets: Arena, workspace_root: AbsPathBuf, + target_directory: AbsPathBuf, } impl ops::Index for CargoWorkspace { @@ -57,20 +57,6 @@ pub enum RustLibSource { Discover, } -/// Crates to disable `#[cfg(test)]` on. -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum UnsetTestCrates { - None, - Only(Vec), - All, -} - -impl Default for UnsetTestCrates { - fn default() -> Self { - Self::None - } -} - #[derive(Clone, Debug, PartialEq, Eq)] pub enum CargoFeatures { All, @@ -99,8 +85,7 @@ pub struct CargoConfig { pub sysroot_src: Option, /// rustc private crate source pub rustc_source: Option, - /// crates to disable `#[cfg(test)]` on - pub unset_test_crates: UnsetTestCrates, + pub cfg_overrides: CfgOverrides, /// Invoke `cargo check` through the RUSTC_WRAPPER. pub wrap_rustc_in_build_scripts: bool, /// The command to run instead of `cargo check` for building build scripts. @@ -113,27 +98,6 @@ pub struct CargoConfig { pub invocation_location: InvocationLocation, } -impl CargoConfig { - pub fn cfg_overrides(&self) -> CfgOverrides { - match &self.unset_test_crates { - UnsetTestCrates::None => CfgOverrides::Selective(iter::empty().collect()), - UnsetTestCrates::Only(unset_test_crates) => CfgOverrides::Selective( - unset_test_crates - .iter() - .cloned() - .zip(iter::repeat_with(|| { - cfg::CfgDiff::new(Vec::new(), vec![cfg::CfgAtom::Flag("test".into())]) - .unwrap() - })) - .collect(), - ), - UnsetTestCrates::All => CfgOverrides::Wildcard( - cfg::CfgDiff::new(Vec::new(), vec![cfg::CfgAtom::Flag("test".into())]).unwrap(), - ), - } - } -} - pub type Package = Idx; pub type Target = Idx; @@ -293,13 +257,29 @@ impl CargoWorkspace { } meta.current_dir(current_dir.as_os_str()); + let mut other_options = vec![]; + // cargo metadata only supports a subset of flags of what cargo usually accepts, and usually + // the only relevant flags for metadata here are unstable ones, so we pass those along + // but nothing else + let mut extra_args = config.extra_args.iter(); + while let Some(arg) = extra_args.next() { + if arg == "-Z" { + if let Some(arg) = extra_args.next() { + other_options.push("-Z".to_owned()); + other_options.push(arg.to_owned()); + } + } + } + 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); + other_options.append( + &mut targets + .into_iter() + .flat_map(|target| ["--filter-platform".to_owned().to_string(), target]) + .collect(), + ); } + meta.other_options(other_options); // FIXME: Fetching metadata is a slow process, as it might require // calling crates.io. We should be reporting progress here, but it's @@ -411,7 +391,10 @@ impl CargoWorkspace { let workspace_root = AbsPathBuf::assert(PathBuf::from(meta.workspace_root.into_os_string())); - CargoWorkspace { packages, targets, workspace_root } + let target_directory = + AbsPathBuf::assert(PathBuf::from(meta.target_directory.into_os_string())); + + CargoWorkspace { packages, targets, workspace_root, target_directory } } pub fn packages(&self) -> impl Iterator + ExactSizeIterator + '_ { @@ -429,6 +412,10 @@ impl CargoWorkspace { &self.workspace_root } + pub fn target_directory(&self) -> &AbsPath { + &self.target_directory + } + pub fn package_flag(&self, package: &PackageData) -> String { if self.is_unique(&package.name) { package.name.clone() @@ -467,6 +454,21 @@ impl CargoWorkspace { None } + /// Returns the union of the features of all member crates in this workspace. + pub fn workspace_features(&self) -> FxHashSet { + self.packages() + .filter_map(|package| { + let package = &self[package]; + if package.is_member { + Some(package.features.keys().cloned()) + } else { + None + } + }) + .flatten() + .collect() + } + fn is_unique(&self, name: &str) -> bool { self.packages.iter().filter(|(_, v)| v.name == name).count() == 1 } diff --git a/src/tools/rust-analyzer/crates/project-model/src/cfg_flag.rs b/src/tools/rust-analyzer/crates/project-model/src/cfg_flag.rs index c134b78ab3a2e..e366d441c1bd7 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/cfg_flag.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/cfg_flag.rs @@ -49,6 +49,14 @@ impl Extend for CfgOptions { } } +impl FromIterator for CfgOptions { + fn from_iter>(iter: T) -> Self { + let mut this = CfgOptions::default(); + this.extend(iter); + this + } +} + impl fmt::Display for CfgFlag { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { diff --git a/src/tools/rust-analyzer/crates/project-model/src/lib.rs b/src/tools/rust-analyzer/crates/project-model/src/lib.rs index 70cb71ae3bde8..61acc646f8101 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/lib.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/lib.rs @@ -44,7 +44,7 @@ pub use crate::{ build_scripts::WorkspaceBuildScripts, cargo_workspace::{ CargoConfig, CargoFeatures, CargoWorkspace, Package, PackageData, PackageDependency, - RustLibSource, Target, TargetData, TargetKind, UnsetTestCrates, + RustLibSource, Target, TargetData, TargetKind, }, manifest_path::ManifestPath, project_json::{ProjectJson, ProjectJsonData}, diff --git a/src/tools/rust-analyzer/crates/project-model/src/manifest_path.rs b/src/tools/rust-analyzer/crates/project-model/src/manifest_path.rs index 980d92d3df9db..3f60e4dd92f43 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/manifest_path.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/manifest_path.rs @@ -34,6 +34,10 @@ impl ManifestPath { pub fn parent(&self) -> &AbsPath { self.file.parent().unwrap() } + + pub fn canonicalize(&self) -> ! { + (&**self).canonicalize() + } } impl ops::Deref for ManifestPath { diff --git a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs index 4b2448e47f1ff..80897f7478cf9 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs @@ -49,12 +49,12 @@ //! user explores them belongs to that extension (it's totally valid to change //! rust-project.json over time via configuration request!) -use std::path::PathBuf; - use base_db::{CrateDisplayName, CrateId, CrateName, Dependency, Edition}; +use la_arena::RawIdx; use paths::{AbsPath, AbsPathBuf}; use rustc_hash::FxHashMap; use serde::{de, Deserialize}; +use std::path::PathBuf; use crate::cfg_flag::CfgFlag; @@ -98,26 +98,23 @@ impl ProjectJson { /// * `data` - The parsed contents of `rust-project.json`, or project json that's passed via /// configuration. pub fn new(base: &AbsPath, data: ProjectJsonData) -> ProjectJson { + let absolutize_on_base = |p| base.absolutize(p); ProjectJson { - sysroot: data.sysroot.map(|it| base.join(it)), - sysroot_src: data.sysroot_src.map(|it| base.join(it)), + sysroot: data.sysroot.map(absolutize_on_base), + sysroot_src: data.sysroot_src.map(absolutize_on_base), project_root: base.to_path_buf(), crates: data .crates .into_iter() .map(|crate_data| { - let is_workspace_member = crate_data.is_workspace_member.unwrap_or_else(|| { - crate_data.root_module.is_relative() - && !crate_data.root_module.starts_with("..") - || crate_data.root_module.starts_with(base) - }); - let root_module = base.join(crate_data.root_module).normalize(); + let root_module = absolutize_on_base(crate_data.root_module); + let is_workspace_member = crate_data + .is_workspace_member + .unwrap_or_else(|| root_module.starts_with(base)); let (include, exclude) = match crate_data.source { Some(src) => { let absolutize = |dirs: Vec| { - dirs.into_iter() - .map(|it| base.join(it).normalize()) - .collect::>() + dirs.into_iter().map(absolutize_on_base).collect::>() }; (absolutize(src.include_dirs), absolutize(src.exclude_dirs)) } @@ -135,7 +132,10 @@ impl ProjectJson { .deps .into_iter() .map(|dep_data| { - Dependency::new(dep_data.name, CrateId(dep_data.krate as u32)) + Dependency::new( + dep_data.name, + CrateId::from_raw(RawIdx::from(dep_data.krate as u32)), + ) }) .collect::>(), cfg: crate_data.cfg, @@ -143,7 +143,7 @@ impl ProjectJson { env: crate_data.env, proc_macro_dylib_path: crate_data .proc_macro_dylib_path - .map(|it| base.join(it)), + .map(absolutize_on_base), is_workspace_member, include, exclude, @@ -151,7 +151,7 @@ impl ProjectJson { repository: crate_data.repository, } }) - .collect::>(), + .collect(), } } @@ -162,7 +162,10 @@ impl ProjectJson { /// Returns an iterator over the crates in the project. pub fn crates(&self) -> impl Iterator + '_ { - self.crates.iter().enumerate().map(|(idx, krate)| (CrateId(idx as u32), krate)) + self.crates + .iter() + .enumerate() + .map(|(idx, krate)| (CrateId::from_raw(RawIdx::from(idx as u32)), krate)) } /// Returns the path to the project's root folder. @@ -236,7 +239,7 @@ struct CrateSource { exclude_dirs: Vec, } -fn deserialize_crate_name<'de, D>(de: D) -> Result +fn deserialize_crate_name<'de, D>(de: D) -> std::result::Result where D: de::Deserializer<'de>, { diff --git a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs index 74e41eda76384..e3a2de927c985 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs @@ -12,13 +12,15 @@ use la_arena::{Arena, Idx}; use paths::{AbsPath, AbsPathBuf}; use rustc_hash::FxHashMap; -use crate::{utf8_stdout, ManifestPath}; +use crate::{utf8_stdout, CargoConfig, CargoWorkspace, ManifestPath}; #[derive(Debug, Clone, Eq, PartialEq)] pub struct Sysroot { root: AbsPathBuf, src_root: AbsPathBuf, crates: Arena, + /// Stores the result of `cargo metadata` of the `RA_UNSTABLE_SYSROOT_HACK` workspace. + pub hack_cargo_workspace: Option, } pub(crate) type SysrootCrate = Idx; @@ -74,6 +76,23 @@ impl Sysroot { pub fn is_empty(&self) -> bool { self.crates.is_empty() } + + pub fn loading_warning(&self) -> Option { + if self.by_name("core").is_none() { + let var_note = if env::var_os("RUST_SRC_PATH").is_some() { + " (`RUST_SRC_PATH` might be incorrect, try unsetting it)" + } else { + " try running `rustup component add rust-src` to possible fix this" + }; + Some(format!( + "could not find libcore in loaded sysroot at `{}`{}", + self.src_root.as_path().display(), + var_note, + )) + } else { + None + } + } } // FIXME: Expose a builder api as loading the sysroot got way too modular and complicated. @@ -103,14 +122,36 @@ impl Sysroot { pub fn with_sysroot_dir(sysroot_dir: AbsPathBuf) -> Result { let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir).ok_or_else(|| { - format_err!("can't load standard library from sysroot {}", sysroot_dir.display()) + format_err!("can't load standard library from sysroot path {}", sysroot_dir.display()) })?; Ok(Sysroot::load(sysroot_dir, sysroot_src_dir)) } - pub fn load(sysroot_dir: AbsPathBuf, sysroot_src_dir: AbsPathBuf) -> Sysroot { - let mut sysroot = - Sysroot { root: sysroot_dir, src_root: sysroot_src_dir, crates: Arena::default() }; + pub fn load(sysroot_dir: AbsPathBuf, mut sysroot_src_dir: AbsPathBuf) -> Sysroot { + // FIXME: Remove this `hack_cargo_workspace` field completely once we support sysroot dependencies + let hack_cargo_workspace = if let Ok(path) = std::env::var("RA_UNSTABLE_SYSROOT_HACK") { + let cargo_toml = ManifestPath::try_from( + AbsPathBuf::try_from(&*format!("{path}/Cargo.toml")).unwrap(), + ) + .unwrap(); + sysroot_src_dir = AbsPathBuf::try_from(&*path).unwrap().join("library"); + CargoWorkspace::fetch_metadata( + &cargo_toml, + &AbsPathBuf::try_from("/").unwrap(), + &CargoConfig::default(), + &|_| (), + ) + .map(CargoWorkspace::new) + .ok() + } else { + None + }; + let mut sysroot = Sysroot { + root: sysroot_dir, + src_root: sysroot_src_dir, + crates: Arena::default(), + hack_cargo_workspace, + }; for path in SYSROOT_CRATES.trim().lines() { let name = path.split('/').last().unwrap(); @@ -153,19 +194,6 @@ impl Sysroot { } } - if sysroot.by_name("core").is_none() { - let var_note = if env::var_os("RUST_SRC_PATH").is_some() { - " (`RUST_SRC_PATH` might be incorrect, try unsetting it)" - } else { - "" - }; - tracing::error!( - "could not find libcore in sysroot path `{}`{}", - sysroot.src_root.as_path().display(), - var_note, - ); - } - sysroot } diff --git a/src/tools/rust-analyzer/crates/project-model/src/target_data_layout.rs b/src/tools/rust-analyzer/crates/project-model/src/target_data_layout.rs index 42c06ad0ed371..30ca7b348e84f 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/target_data_layout.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/target_data_layout.rs @@ -16,7 +16,7 @@ pub fn get( let mut cmd = Command::new(toolchain::rustc()); cmd.envs(extra_env); cmd.current_dir(cargo_toml.parent()) - .args(["-Z", "unstable-options", "rustc", "--print", "target-spec-json"]) + .args(["-Z", "unstable-options", "--print", "target-spec-json"]) .env("RUSTC_BOOTSTRAP", "1"); if let Some(target) = target { cmd.args(["--target", target]); diff --git a/src/tools/rust-analyzer/crates/project-model/src/tests.rs b/src/tools/rust-analyzer/crates/project-model/src/tests.rs index 3754accbb03d8..7815b9dda77f2 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/tests.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/tests.rs @@ -3,10 +3,11 @@ use std::{ path::{Path, PathBuf}, }; -use base_db::{CrateGraph, FileId}; +use base_db::{CrateGraph, FileId, ProcMacroPaths}; use cfg::{CfgAtom, CfgDiff}; -use expect_test::{expect, Expect}; +use expect_test::{expect_file, ExpectFile}; use paths::{AbsPath, AbsPathBuf}; +use rustc_hash::FxHashMap; use serde::de::DeserializeOwned; use crate::{ @@ -14,11 +15,14 @@ use crate::{ WorkspaceBuildScripts, }; -fn load_cargo(file: &str) -> CrateGraph { +fn load_cargo(file: &str) -> (CrateGraph, ProcMacroPaths) { load_cargo_with_overrides(file, CfgOverrides::default()) } -fn load_cargo_with_overrides(file: &str, cfg_overrides: CfgOverrides) -> CrateGraph { +fn load_cargo_with_overrides( + file: &str, + cfg_overrides: CfgOverrides, +) -> (CrateGraph, ProcMacroPaths) { let meta = get_test_json_file(file); let cargo_workspace = CargoWorkspace::new(meta); let project_workspace = ProjectWorkspace::Cargo { @@ -34,11 +38,39 @@ fn load_cargo_with_overrides(file: &str, cfg_overrides: CfgOverrides) -> CrateGr to_crate_graph(project_workspace) } -fn load_rust_project(file: &str) -> CrateGraph { +fn load_cargo_with_sysroot( + file_map: &mut FxHashMap, + file: &str, +) -> (CrateGraph, ProcMacroPaths) { + let meta = get_test_json_file(file); + let cargo_workspace = CargoWorkspace::new(meta); + let project_workspace = ProjectWorkspace::Cargo { + cargo: cargo_workspace, + build_scripts: WorkspaceBuildScripts::default(), + sysroot: Ok(get_fake_sysroot()), + rustc: Err(None), + rustc_cfg: Vec::new(), + cfg_overrides: Default::default(), + toolchain: None, + target_layout: Err("target_data_layout not loaded".into()), + }; + project_workspace.to_crate_graph( + &mut { + |path| { + let len = file_map.len(); + Some(*file_map.entry(path.to_path_buf()).or_insert(FileId(len as u32))) + } + }, + &Default::default(), + ) +} + +fn load_rust_project(file: &str) -> (CrateGraph, ProcMacroPaths) { let data = get_test_json_file(file); let project = rooted_project_json(data); let sysroot = Ok(get_fake_sysroot()); - let project_workspace = ProjectWorkspace::Json { project, sysroot, rustc_cfg: Vec::new() }; + let project_workspace = + ProjectWorkspace::Json { project, sysroot, rustc_cfg: Vec::new(), toolchain: None }; to_crate_graph(project_workspace) } @@ -70,6 +102,18 @@ fn replace_root(s: &mut String, direction: bool) { } } +fn replace_fake_sys_root(s: &mut String) { + let fake_sysroot_path = get_test_path("fake-sysroot"); + let fake_sysroot_path = if cfg!(windows) { + let normalized_path = + fake_sysroot_path.to_str().expect("expected str").replace(r#"\"#, r#"\\"#); + format!(r#"{}\\"#, normalized_path) + } else { + format!("{}/", fake_sysroot_path.to_str().expect("expected str")) + }; + *s = s.replace(&fake_sysroot_path, "$FAKESYSROOT$") +} + fn get_test_path(file: &str) -> PathBuf { let base = PathBuf::from(env!("CARGO_MANIFEST_DIR")); base.join("test_data").join(file) @@ -92,9 +136,8 @@ fn rooted_project_json(data: ProjectJsonData) -> ProjectJson { ProjectJson::new(base, data) } -fn to_crate_graph(project_workspace: ProjectWorkspace) -> CrateGraph { +fn to_crate_graph(project_workspace: ProjectWorkspace) -> (CrateGraph, ProcMacroPaths) { project_workspace.to_crate_graph( - &mut |_, _| Ok(Vec::new()), &mut { let mut counter = 0; move |_path| { @@ -106,1808 +149,70 @@ fn to_crate_graph(project_workspace: ProjectWorkspace) -> CrateGraph { ) } -fn check_crate_graph(crate_graph: CrateGraph, expect: Expect) { +fn check_crate_graph(crate_graph: CrateGraph, expect: ExpectFile) { let mut crate_graph = format!("{crate_graph:#?}"); replace_root(&mut crate_graph, false); + replace_fake_sys_root(&mut crate_graph); expect.assert_eq(&crate_graph); } #[test] fn cargo_hello_world_project_model_with_wildcard_overrides() { - let cfg_overrides = CfgOverrides::Wildcard( - CfgDiff::new(Vec::new(), vec![CfgAtom::Flag("test".into())]).unwrap(), - ); - let crate_graph = load_cargo_with_overrides("hello-world-metadata.json", cfg_overrides); + let cfg_overrides = CfgOverrides { + global: CfgDiff::new(Vec::new(), vec![CfgAtom::Flag("test".into())]).unwrap(), + selective: Default::default(), + }; + let (crate_graph, _proc_macros) = + load_cargo_with_overrides("hello-world-metadata.json", cfg_overrides); check_crate_graph( crate_graph, - expect![[r#" - CrateGraph { - arena: { - CrateId( - 0, - ): CrateData { - root_file_id: FileId( - 1, - ), - edition: Edition2018, - version: Some( - "0.1.0", - ), - display_name: Some( - CrateDisplayName { - crate_name: CrateName( - "hello_world", - ), - canonical_name: "hello-world", - }, - ), - cfg_options: CfgOptions( - [ - "debug_assertions", - ], - ), - potential_cfg_options: CfgOptions( - [ - "debug_assertions", - ], - ), - target_layout: Err( - "target_data_layout not loaded", - ), - env: Env { - entries: { - "CARGO_PKG_LICENSE": "", - "CARGO_PKG_VERSION_MAJOR": "0", - "CARGO_MANIFEST_DIR": "$ROOT$hello-world", - "CARGO_PKG_VERSION": "0.1.0", - "CARGO_PKG_AUTHORS": "", - "CARGO_CRATE_NAME": "hello_world", - "CARGO_PKG_LICENSE_FILE": "", - "CARGO_PKG_HOMEPAGE": "", - "CARGO_PKG_DESCRIPTION": "", - "CARGO_PKG_NAME": "hello-world", - "CARGO_PKG_VERSION_PATCH": "0", - "CARGO": "cargo", - "CARGO_PKG_REPOSITORY": "", - "CARGO_PKG_VERSION_MINOR": "1", - "CARGO_PKG_VERSION_PRE": "", - }, - }, - dependencies: [ - Dependency { - crate_id: CrateId( - 4, - ), - name: CrateName( - "libc", - ), - prelude: true, - }, - ], - proc_macro: Err( - "crate has not (yet) been built", - ), - origin: CratesIo { - repo: None, - name: Some( - "hello-world", - ), - }, - is_proc_macro: false, - }, - CrateId( - 1, - ): CrateData { - root_file_id: FileId( - 2, - ), - edition: Edition2018, - version: Some( - "0.1.0", - ), - display_name: Some( - CrateDisplayName { - crate_name: CrateName( - "hello_world", - ), - canonical_name: "hello-world", - }, - ), - cfg_options: CfgOptions( - [ - "debug_assertions", - ], - ), - potential_cfg_options: CfgOptions( - [ - "debug_assertions", - ], - ), - target_layout: Err( - "target_data_layout not loaded", - ), - env: Env { - entries: { - "CARGO_PKG_LICENSE": "", - "CARGO_PKG_VERSION_MAJOR": "0", - "CARGO_MANIFEST_DIR": "$ROOT$hello-world", - "CARGO_PKG_VERSION": "0.1.0", - "CARGO_PKG_AUTHORS": "", - "CARGO_CRATE_NAME": "hello_world", - "CARGO_PKG_LICENSE_FILE": "", - "CARGO_PKG_HOMEPAGE": "", - "CARGO_PKG_DESCRIPTION": "", - "CARGO_PKG_NAME": "hello-world", - "CARGO_PKG_VERSION_PATCH": "0", - "CARGO": "cargo", - "CARGO_PKG_REPOSITORY": "", - "CARGO_PKG_VERSION_MINOR": "1", - "CARGO_PKG_VERSION_PRE": "", - }, - }, - dependencies: [ - Dependency { - crate_id: CrateId( - 0, - ), - name: CrateName( - "hello_world", - ), - prelude: true, - }, - Dependency { - crate_id: CrateId( - 4, - ), - name: CrateName( - "libc", - ), - prelude: true, - }, - ], - proc_macro: Err( - "crate has not (yet) been built", - ), - origin: CratesIo { - repo: None, - name: Some( - "hello-world", - ), - }, - is_proc_macro: false, - }, - CrateId( - 2, - ): CrateData { - root_file_id: FileId( - 3, - ), - edition: Edition2018, - version: Some( - "0.1.0", - ), - display_name: Some( - CrateDisplayName { - crate_name: CrateName( - "an_example", - ), - canonical_name: "an-example", - }, - ), - cfg_options: CfgOptions( - [ - "debug_assertions", - ], - ), - potential_cfg_options: CfgOptions( - [ - "debug_assertions", - ], - ), - target_layout: Err( - "target_data_layout not loaded", - ), - env: Env { - entries: { - "CARGO_PKG_LICENSE": "", - "CARGO_PKG_VERSION_MAJOR": "0", - "CARGO_MANIFEST_DIR": "$ROOT$hello-world", - "CARGO_PKG_VERSION": "0.1.0", - "CARGO_PKG_AUTHORS": "", - "CARGO_CRATE_NAME": "hello_world", - "CARGO_PKG_LICENSE_FILE": "", - "CARGO_PKG_HOMEPAGE": "", - "CARGO_PKG_DESCRIPTION": "", - "CARGO_PKG_NAME": "hello-world", - "CARGO_PKG_VERSION_PATCH": "0", - "CARGO": "cargo", - "CARGO_PKG_REPOSITORY": "", - "CARGO_PKG_VERSION_MINOR": "1", - "CARGO_PKG_VERSION_PRE": "", - }, - }, - dependencies: [ - Dependency { - crate_id: CrateId( - 0, - ), - name: CrateName( - "hello_world", - ), - prelude: true, - }, - Dependency { - crate_id: CrateId( - 4, - ), - name: CrateName( - "libc", - ), - prelude: true, - }, - ], - proc_macro: Err( - "crate has not (yet) been built", - ), - origin: CratesIo { - repo: None, - name: Some( - "hello-world", - ), - }, - is_proc_macro: false, - }, - CrateId( - 3, - ): CrateData { - root_file_id: FileId( - 4, - ), - edition: Edition2018, - version: Some( - "0.1.0", - ), - display_name: Some( - CrateDisplayName { - crate_name: CrateName( - "it", - ), - canonical_name: "it", - }, - ), - cfg_options: CfgOptions( - [ - "debug_assertions", - ], - ), - potential_cfg_options: CfgOptions( - [ - "debug_assertions", - ], - ), - target_layout: Err( - "target_data_layout not loaded", - ), - env: Env { - entries: { - "CARGO_PKG_LICENSE": "", - "CARGO_PKG_VERSION_MAJOR": "0", - "CARGO_MANIFEST_DIR": "$ROOT$hello-world", - "CARGO_PKG_VERSION": "0.1.0", - "CARGO_PKG_AUTHORS": "", - "CARGO_CRATE_NAME": "hello_world", - "CARGO_PKG_LICENSE_FILE": "", - "CARGO_PKG_HOMEPAGE": "", - "CARGO_PKG_DESCRIPTION": "", - "CARGO_PKG_NAME": "hello-world", - "CARGO_PKG_VERSION_PATCH": "0", - "CARGO": "cargo", - "CARGO_PKG_REPOSITORY": "", - "CARGO_PKG_VERSION_MINOR": "1", - "CARGO_PKG_VERSION_PRE": "", - }, - }, - dependencies: [ - Dependency { - crate_id: CrateId( - 0, - ), - name: CrateName( - "hello_world", - ), - prelude: true, - }, - Dependency { - crate_id: CrateId( - 4, - ), - name: CrateName( - "libc", - ), - prelude: true, - }, - ], - proc_macro: Err( - "crate has not (yet) been built", - ), - origin: CratesIo { - repo: None, - name: Some( - "hello-world", - ), - }, - is_proc_macro: false, - }, - CrateId( - 4, - ): CrateData { - root_file_id: FileId( - 5, - ), - edition: Edition2015, - version: Some( - "0.2.98", - ), - display_name: Some( - CrateDisplayName { - crate_name: CrateName( - "libc", - ), - canonical_name: "libc", - }, - ), - cfg_options: CfgOptions( - [ - "debug_assertions", - "feature=default", - "feature=std", - ], - ), - potential_cfg_options: CfgOptions( - [ - "debug_assertions", - "feature=align", - "feature=const-extern-fn", - "feature=default", - "feature=extra_traits", - "feature=rustc-dep-of-std", - "feature=std", - "feature=use_std", - ], - ), - target_layout: Err( - "target_data_layout not loaded", - ), - env: Env { - entries: { - "CARGO_PKG_LICENSE": "", - "CARGO_PKG_VERSION_MAJOR": "0", - "CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98", - "CARGO_PKG_VERSION": "0.2.98", - "CARGO_PKG_AUTHORS": "", - "CARGO_CRATE_NAME": "libc", - "CARGO_PKG_LICENSE_FILE": "", - "CARGO_PKG_HOMEPAGE": "", - "CARGO_PKG_DESCRIPTION": "", - "CARGO_PKG_NAME": "libc", - "CARGO_PKG_VERSION_PATCH": "98", - "CARGO": "cargo", - "CARGO_PKG_REPOSITORY": "", - "CARGO_PKG_VERSION_MINOR": "2", - "CARGO_PKG_VERSION_PRE": "", - }, - }, - dependencies: [], - proc_macro: Err( - "crate has not (yet) been built", - ), - origin: CratesIo { - repo: Some( - "https://github.com/rust-lang/libc", - ), - name: Some( - "libc", - ), - }, - is_proc_macro: false, - }, - }, - }"#]], + expect_file![ + "../test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt" + ], ) } #[test] fn cargo_hello_world_project_model_with_selective_overrides() { - let cfg_overrides = { - CfgOverrides::Selective( - std::iter::once(( - "libc".to_owned(), - CfgDiff::new(Vec::new(), vec![CfgAtom::Flag("test".into())]).unwrap(), - )) - .collect(), - ) + let cfg_overrides = CfgOverrides { + global: Default::default(), + selective: std::iter::once(( + "libc".to_owned(), + CfgDiff::new(Vec::new(), vec![CfgAtom::Flag("test".into())]).unwrap(), + )) + .collect(), }; - let crate_graph = load_cargo_with_overrides("hello-world-metadata.json", cfg_overrides); + let (crate_graph, _proc_macros) = + load_cargo_with_overrides("hello-world-metadata.json", cfg_overrides); check_crate_graph( crate_graph, - expect![[r#" - CrateGraph { - arena: { - CrateId( - 0, - ): CrateData { - root_file_id: FileId( - 1, - ), - edition: Edition2018, - version: Some( - "0.1.0", - ), - display_name: Some( - CrateDisplayName { - crate_name: CrateName( - "hello_world", - ), - canonical_name: "hello-world", - }, - ), - cfg_options: CfgOptions( - [ - "debug_assertions", - "test", - ], - ), - potential_cfg_options: CfgOptions( - [ - "debug_assertions", - "test", - ], - ), - target_layout: Err( - "target_data_layout not loaded", - ), - env: Env { - entries: { - "CARGO_PKG_LICENSE": "", - "CARGO_PKG_VERSION_MAJOR": "0", - "CARGO_MANIFEST_DIR": "$ROOT$hello-world", - "CARGO_PKG_VERSION": "0.1.0", - "CARGO_PKG_AUTHORS": "", - "CARGO_CRATE_NAME": "hello_world", - "CARGO_PKG_LICENSE_FILE": "", - "CARGO_PKG_HOMEPAGE": "", - "CARGO_PKG_DESCRIPTION": "", - "CARGO_PKG_NAME": "hello-world", - "CARGO_PKG_VERSION_PATCH": "0", - "CARGO": "cargo", - "CARGO_PKG_REPOSITORY": "", - "CARGO_PKG_VERSION_MINOR": "1", - "CARGO_PKG_VERSION_PRE": "", - }, - }, - dependencies: [ - Dependency { - crate_id: CrateId( - 4, - ), - name: CrateName( - "libc", - ), - prelude: true, - }, - ], - proc_macro: Err( - "crate has not (yet) been built", - ), - origin: CratesIo { - repo: None, - name: Some( - "hello-world", - ), - }, - is_proc_macro: false, - }, - CrateId( - 1, - ): CrateData { - root_file_id: FileId( - 2, - ), - edition: Edition2018, - version: Some( - "0.1.0", - ), - display_name: Some( - CrateDisplayName { - crate_name: CrateName( - "hello_world", - ), - canonical_name: "hello-world", - }, - ), - cfg_options: CfgOptions( - [ - "debug_assertions", - "test", - ], - ), - potential_cfg_options: CfgOptions( - [ - "debug_assertions", - "test", - ], - ), - target_layout: Err( - "target_data_layout not loaded", - ), - env: Env { - entries: { - "CARGO_PKG_LICENSE": "", - "CARGO_PKG_VERSION_MAJOR": "0", - "CARGO_MANIFEST_DIR": "$ROOT$hello-world", - "CARGO_PKG_VERSION": "0.1.0", - "CARGO_PKG_AUTHORS": "", - "CARGO_CRATE_NAME": "hello_world", - "CARGO_PKG_LICENSE_FILE": "", - "CARGO_PKG_HOMEPAGE": "", - "CARGO_PKG_DESCRIPTION": "", - "CARGO_PKG_NAME": "hello-world", - "CARGO_PKG_VERSION_PATCH": "0", - "CARGO": "cargo", - "CARGO_PKG_REPOSITORY": "", - "CARGO_PKG_VERSION_MINOR": "1", - "CARGO_PKG_VERSION_PRE": "", - }, - }, - dependencies: [ - Dependency { - crate_id: CrateId( - 0, - ), - name: CrateName( - "hello_world", - ), - prelude: true, - }, - Dependency { - crate_id: CrateId( - 4, - ), - name: CrateName( - "libc", - ), - prelude: true, - }, - ], - proc_macro: Err( - "crate has not (yet) been built", - ), - origin: CratesIo { - repo: None, - name: Some( - "hello-world", - ), - }, - is_proc_macro: false, - }, - CrateId( - 2, - ): CrateData { - root_file_id: FileId( - 3, - ), - edition: Edition2018, - version: Some( - "0.1.0", - ), - display_name: Some( - CrateDisplayName { - crate_name: CrateName( - "an_example", - ), - canonical_name: "an-example", - }, - ), - cfg_options: CfgOptions( - [ - "debug_assertions", - "test", - ], - ), - potential_cfg_options: CfgOptions( - [ - "debug_assertions", - "test", - ], - ), - target_layout: Err( - "target_data_layout not loaded", - ), - env: Env { - entries: { - "CARGO_PKG_LICENSE": "", - "CARGO_PKG_VERSION_MAJOR": "0", - "CARGO_MANIFEST_DIR": "$ROOT$hello-world", - "CARGO_PKG_VERSION": "0.1.0", - "CARGO_PKG_AUTHORS": "", - "CARGO_CRATE_NAME": "hello_world", - "CARGO_PKG_LICENSE_FILE": "", - "CARGO_PKG_HOMEPAGE": "", - "CARGO_PKG_DESCRIPTION": "", - "CARGO_PKG_NAME": "hello-world", - "CARGO_PKG_VERSION_PATCH": "0", - "CARGO": "cargo", - "CARGO_PKG_REPOSITORY": "", - "CARGO_PKG_VERSION_MINOR": "1", - "CARGO_PKG_VERSION_PRE": "", - }, - }, - dependencies: [ - Dependency { - crate_id: CrateId( - 0, - ), - name: CrateName( - "hello_world", - ), - prelude: true, - }, - Dependency { - crate_id: CrateId( - 4, - ), - name: CrateName( - "libc", - ), - prelude: true, - }, - ], - proc_macro: Err( - "crate has not (yet) been built", - ), - origin: CratesIo { - repo: None, - name: Some( - "hello-world", - ), - }, - is_proc_macro: false, - }, - CrateId( - 3, - ): CrateData { - root_file_id: FileId( - 4, - ), - edition: Edition2018, - version: Some( - "0.1.0", - ), - display_name: Some( - CrateDisplayName { - crate_name: CrateName( - "it", - ), - canonical_name: "it", - }, - ), - cfg_options: CfgOptions( - [ - "debug_assertions", - "test", - ], - ), - potential_cfg_options: CfgOptions( - [ - "debug_assertions", - "test", - ], - ), - target_layout: Err( - "target_data_layout not loaded", - ), - env: Env { - entries: { - "CARGO_PKG_LICENSE": "", - "CARGO_PKG_VERSION_MAJOR": "0", - "CARGO_MANIFEST_DIR": "$ROOT$hello-world", - "CARGO_PKG_VERSION": "0.1.0", - "CARGO_PKG_AUTHORS": "", - "CARGO_CRATE_NAME": "hello_world", - "CARGO_PKG_LICENSE_FILE": "", - "CARGO_PKG_HOMEPAGE": "", - "CARGO_PKG_DESCRIPTION": "", - "CARGO_PKG_NAME": "hello-world", - "CARGO_PKG_VERSION_PATCH": "0", - "CARGO": "cargo", - "CARGO_PKG_REPOSITORY": "", - "CARGO_PKG_VERSION_MINOR": "1", - "CARGO_PKG_VERSION_PRE": "", - }, - }, - dependencies: [ - Dependency { - crate_id: CrateId( - 0, - ), - name: CrateName( - "hello_world", - ), - prelude: true, - }, - Dependency { - crate_id: CrateId( - 4, - ), - name: CrateName( - "libc", - ), - prelude: true, - }, - ], - proc_macro: Err( - "crate has not (yet) been built", - ), - origin: CratesIo { - repo: None, - name: Some( - "hello-world", - ), - }, - is_proc_macro: false, - }, - CrateId( - 4, - ): CrateData { - root_file_id: FileId( - 5, - ), - edition: Edition2015, - version: Some( - "0.2.98", - ), - display_name: Some( - CrateDisplayName { - crate_name: CrateName( - "libc", - ), - canonical_name: "libc", - }, - ), - cfg_options: CfgOptions( - [ - "debug_assertions", - "feature=default", - "feature=std", - ], - ), - potential_cfg_options: CfgOptions( - [ - "debug_assertions", - "feature=align", - "feature=const-extern-fn", - "feature=default", - "feature=extra_traits", - "feature=rustc-dep-of-std", - "feature=std", - "feature=use_std", - ], - ), - target_layout: Err( - "target_data_layout not loaded", - ), - env: Env { - entries: { - "CARGO_PKG_LICENSE": "", - "CARGO_PKG_VERSION_MAJOR": "0", - "CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98", - "CARGO_PKG_VERSION": "0.2.98", - "CARGO_PKG_AUTHORS": "", - "CARGO_CRATE_NAME": "libc", - "CARGO_PKG_LICENSE_FILE": "", - "CARGO_PKG_HOMEPAGE": "", - "CARGO_PKG_DESCRIPTION": "", - "CARGO_PKG_NAME": "libc", - "CARGO_PKG_VERSION_PATCH": "98", - "CARGO": "cargo", - "CARGO_PKG_REPOSITORY": "", - "CARGO_PKG_VERSION_MINOR": "2", - "CARGO_PKG_VERSION_PRE": "", - }, - }, - dependencies: [], - proc_macro: Err( - "crate has not (yet) been built", - ), - origin: CratesIo { - repo: Some( - "https://github.com/rust-lang/libc", - ), - name: Some( - "libc", - ), - }, - is_proc_macro: false, - }, - }, - }"#]], + expect_file![ + "../test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt" + ], ) } #[test] fn cargo_hello_world_project_model() { - let crate_graph = load_cargo("hello-world-metadata.json"); + let (crate_graph, _proc_macros) = load_cargo("hello-world-metadata.json"); check_crate_graph( crate_graph, - expect![[r#" - CrateGraph { - arena: { - CrateId( - 0, - ): CrateData { - root_file_id: FileId( - 1, - ), - edition: Edition2018, - version: Some( - "0.1.0", - ), - display_name: Some( - CrateDisplayName { - crate_name: CrateName( - "hello_world", - ), - canonical_name: "hello-world", - }, - ), - cfg_options: CfgOptions( - [ - "debug_assertions", - "test", - ], - ), - potential_cfg_options: CfgOptions( - [ - "debug_assertions", - "test", - ], - ), - target_layout: Err( - "target_data_layout not loaded", - ), - env: Env { - entries: { - "CARGO_PKG_LICENSE": "", - "CARGO_PKG_VERSION_MAJOR": "0", - "CARGO_MANIFEST_DIR": "$ROOT$hello-world", - "CARGO_PKG_VERSION": "0.1.0", - "CARGO_PKG_AUTHORS": "", - "CARGO_CRATE_NAME": "hello_world", - "CARGO_PKG_LICENSE_FILE": "", - "CARGO_PKG_HOMEPAGE": "", - "CARGO_PKG_DESCRIPTION": "", - "CARGO_PKG_NAME": "hello-world", - "CARGO_PKG_VERSION_PATCH": "0", - "CARGO": "cargo", - "CARGO_PKG_REPOSITORY": "", - "CARGO_PKG_VERSION_MINOR": "1", - "CARGO_PKG_VERSION_PRE": "", - }, - }, - dependencies: [ - Dependency { - crate_id: CrateId( - 4, - ), - name: CrateName( - "libc", - ), - prelude: true, - }, - ], - proc_macro: Err( - "crate has not (yet) been built", - ), - origin: CratesIo { - repo: None, - name: Some( - "hello-world", - ), - }, - is_proc_macro: false, - }, - CrateId( - 1, - ): CrateData { - root_file_id: FileId( - 2, - ), - edition: Edition2018, - version: Some( - "0.1.0", - ), - display_name: Some( - CrateDisplayName { - crate_name: CrateName( - "hello_world", - ), - canonical_name: "hello-world", - }, - ), - cfg_options: CfgOptions( - [ - "debug_assertions", - "test", - ], - ), - potential_cfg_options: CfgOptions( - [ - "debug_assertions", - "test", - ], - ), - target_layout: Err( - "target_data_layout not loaded", - ), - env: Env { - entries: { - "CARGO_PKG_LICENSE": "", - "CARGO_PKG_VERSION_MAJOR": "0", - "CARGO_MANIFEST_DIR": "$ROOT$hello-world", - "CARGO_PKG_VERSION": "0.1.0", - "CARGO_PKG_AUTHORS": "", - "CARGO_CRATE_NAME": "hello_world", - "CARGO_PKG_LICENSE_FILE": "", - "CARGO_PKG_HOMEPAGE": "", - "CARGO_PKG_DESCRIPTION": "", - "CARGO_PKG_NAME": "hello-world", - "CARGO_PKG_VERSION_PATCH": "0", - "CARGO": "cargo", - "CARGO_PKG_REPOSITORY": "", - "CARGO_PKG_VERSION_MINOR": "1", - "CARGO_PKG_VERSION_PRE": "", - }, - }, - dependencies: [ - Dependency { - crate_id: CrateId( - 0, - ), - name: CrateName( - "hello_world", - ), - prelude: true, - }, - Dependency { - crate_id: CrateId( - 4, - ), - name: CrateName( - "libc", - ), - prelude: true, - }, - ], - proc_macro: Err( - "crate has not (yet) been built", - ), - origin: CratesIo { - repo: None, - name: Some( - "hello-world", - ), - }, - is_proc_macro: false, - }, - CrateId( - 2, - ): CrateData { - root_file_id: FileId( - 3, - ), - edition: Edition2018, - version: Some( - "0.1.0", - ), - display_name: Some( - CrateDisplayName { - crate_name: CrateName( - "an_example", - ), - canonical_name: "an-example", - }, - ), - cfg_options: CfgOptions( - [ - "debug_assertions", - "test", - ], - ), - potential_cfg_options: CfgOptions( - [ - "debug_assertions", - "test", - ], - ), - target_layout: Err( - "target_data_layout not loaded", - ), - env: Env { - entries: { - "CARGO_PKG_LICENSE": "", - "CARGO_PKG_VERSION_MAJOR": "0", - "CARGO_MANIFEST_DIR": "$ROOT$hello-world", - "CARGO_PKG_VERSION": "0.1.0", - "CARGO_PKG_AUTHORS": "", - "CARGO_CRATE_NAME": "hello_world", - "CARGO_PKG_LICENSE_FILE": "", - "CARGO_PKG_HOMEPAGE": "", - "CARGO_PKG_DESCRIPTION": "", - "CARGO_PKG_NAME": "hello-world", - "CARGO_PKG_VERSION_PATCH": "0", - "CARGO": "cargo", - "CARGO_PKG_REPOSITORY": "", - "CARGO_PKG_VERSION_MINOR": "1", - "CARGO_PKG_VERSION_PRE": "", - }, - }, - dependencies: [ - Dependency { - crate_id: CrateId( - 0, - ), - name: CrateName( - "hello_world", - ), - prelude: true, - }, - Dependency { - crate_id: CrateId( - 4, - ), - name: CrateName( - "libc", - ), - prelude: true, - }, - ], - proc_macro: Err( - "crate has not (yet) been built", - ), - origin: CratesIo { - repo: None, - name: Some( - "hello-world", - ), - }, - is_proc_macro: false, - }, - CrateId( - 3, - ): CrateData { - root_file_id: FileId( - 4, - ), - edition: Edition2018, - version: Some( - "0.1.0", - ), - display_name: Some( - CrateDisplayName { - crate_name: CrateName( - "it", - ), - canonical_name: "it", - }, - ), - cfg_options: CfgOptions( - [ - "debug_assertions", - "test", - ], - ), - potential_cfg_options: CfgOptions( - [ - "debug_assertions", - "test", - ], - ), - target_layout: Err( - "target_data_layout not loaded", - ), - env: Env { - entries: { - "CARGO_PKG_LICENSE": "", - "CARGO_PKG_VERSION_MAJOR": "0", - "CARGO_MANIFEST_DIR": "$ROOT$hello-world", - "CARGO_PKG_VERSION": "0.1.0", - "CARGO_PKG_AUTHORS": "", - "CARGO_CRATE_NAME": "hello_world", - "CARGO_PKG_LICENSE_FILE": "", - "CARGO_PKG_HOMEPAGE": "", - "CARGO_PKG_DESCRIPTION": "", - "CARGO_PKG_NAME": "hello-world", - "CARGO_PKG_VERSION_PATCH": "0", - "CARGO": "cargo", - "CARGO_PKG_REPOSITORY": "", - "CARGO_PKG_VERSION_MINOR": "1", - "CARGO_PKG_VERSION_PRE": "", - }, - }, - dependencies: [ - Dependency { - crate_id: CrateId( - 0, - ), - name: CrateName( - "hello_world", - ), - prelude: true, - }, - Dependency { - crate_id: CrateId( - 4, - ), - name: CrateName( - "libc", - ), - prelude: true, - }, - ], - proc_macro: Err( - "crate has not (yet) been built", - ), - origin: CratesIo { - repo: None, - name: Some( - "hello-world", - ), - }, - is_proc_macro: false, - }, - CrateId( - 4, - ): CrateData { - root_file_id: FileId( - 5, - ), - edition: Edition2015, - version: Some( - "0.2.98", - ), - display_name: Some( - CrateDisplayName { - crate_name: CrateName( - "libc", - ), - canonical_name: "libc", - }, - ), - cfg_options: CfgOptions( - [ - "debug_assertions", - "feature=default", - "feature=std", - ], - ), - potential_cfg_options: CfgOptions( - [ - "debug_assertions", - "feature=align", - "feature=const-extern-fn", - "feature=default", - "feature=extra_traits", - "feature=rustc-dep-of-std", - "feature=std", - "feature=use_std", - ], - ), - target_layout: Err( - "target_data_layout not loaded", - ), - env: Env { - entries: { - "CARGO_PKG_LICENSE": "", - "CARGO_PKG_VERSION_MAJOR": "0", - "CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98", - "CARGO_PKG_VERSION": "0.2.98", - "CARGO_PKG_AUTHORS": "", - "CARGO_CRATE_NAME": "libc", - "CARGO_PKG_LICENSE_FILE": "", - "CARGO_PKG_HOMEPAGE": "", - "CARGO_PKG_DESCRIPTION": "", - "CARGO_PKG_NAME": "libc", - "CARGO_PKG_VERSION_PATCH": "98", - "CARGO": "cargo", - "CARGO_PKG_REPOSITORY": "", - "CARGO_PKG_VERSION_MINOR": "2", - "CARGO_PKG_VERSION_PRE": "", - }, - }, - dependencies: [], - proc_macro: Err( - "crate has not (yet) been built", - ), - origin: CratesIo { - repo: Some( - "https://github.com/rust-lang/libc", - ), - name: Some( - "libc", - ), - }, - is_proc_macro: false, - }, - }, - }"#]], + expect_file!["../test_data/output/cargo_hello_world_project_model.txt"], ) } #[test] fn rust_project_hello_world_project_model() { - let crate_graph = load_rust_project("hello-world-project.json"); + let (crate_graph, _proc_macros) = load_rust_project("hello-world-project.json"); check_crate_graph( crate_graph, - expect![[r#" - CrateGraph { - arena: { - CrateId( - 0, - ): CrateData { - root_file_id: FileId( - 1, - ), - edition: Edition2021, - version: None, - display_name: Some( - CrateDisplayName { - crate_name: CrateName( - "alloc", - ), - canonical_name: "alloc", - }, - ), - cfg_options: CfgOptions( - [], - ), - potential_cfg_options: CfgOptions( - [], - ), - target_layout: Err( - "rust-project.json projects have no target layout set", - ), - env: Env { - entries: {}, - }, - dependencies: [ - Dependency { - crate_id: CrateId( - 1, - ), - name: CrateName( - "core", - ), - prelude: true, - }, - ], - proc_macro: Err( - "no proc macro loaded for sysroot crate", - ), - origin: Lang( - Alloc, - ), - is_proc_macro: false, - }, - CrateId( - 1, - ): CrateData { - root_file_id: FileId( - 2, - ), - edition: Edition2021, - version: None, - display_name: Some( - CrateDisplayName { - crate_name: CrateName( - "core", - ), - canonical_name: "core", - }, - ), - cfg_options: CfgOptions( - [], - ), - potential_cfg_options: CfgOptions( - [], - ), - target_layout: Err( - "rust-project.json projects have no target layout set", - ), - env: Env { - entries: {}, - }, - dependencies: [], - proc_macro: Err( - "no proc macro loaded for sysroot crate", - ), - origin: Lang( - Core, - ), - is_proc_macro: false, - }, - CrateId( - 2, - ): CrateData { - root_file_id: FileId( - 3, - ), - edition: Edition2021, - version: None, - display_name: Some( - CrateDisplayName { - crate_name: CrateName( - "panic_abort", - ), - canonical_name: "panic_abort", - }, - ), - cfg_options: CfgOptions( - [], - ), - potential_cfg_options: CfgOptions( - [], - ), - target_layout: Err( - "rust-project.json projects have no target layout set", - ), - env: Env { - entries: {}, - }, - dependencies: [], - proc_macro: Err( - "no proc macro loaded for sysroot crate", - ), - origin: Lang( - Other, - ), - is_proc_macro: false, - }, - CrateId( - 3, - ): CrateData { - root_file_id: FileId( - 4, - ), - edition: Edition2021, - version: None, - display_name: Some( - CrateDisplayName { - crate_name: CrateName( - "panic_unwind", - ), - canonical_name: "panic_unwind", - }, - ), - cfg_options: CfgOptions( - [], - ), - potential_cfg_options: CfgOptions( - [], - ), - target_layout: Err( - "rust-project.json projects have no target layout set", - ), - env: Env { - entries: {}, - }, - dependencies: [], - proc_macro: Err( - "no proc macro loaded for sysroot crate", - ), - origin: Lang( - Other, - ), - is_proc_macro: false, - }, - CrateId( - 4, - ): CrateData { - root_file_id: FileId( - 5, - ), - edition: Edition2021, - version: None, - display_name: Some( - CrateDisplayName { - crate_name: CrateName( - "proc_macro", - ), - canonical_name: "proc_macro", - }, - ), - cfg_options: CfgOptions( - [], - ), - potential_cfg_options: CfgOptions( - [], - ), - target_layout: Err( - "rust-project.json projects have no target layout set", - ), - env: Env { - entries: {}, - }, - dependencies: [ - Dependency { - crate_id: CrateId( - 6, - ), - name: CrateName( - "std", - ), - prelude: true, - }, - Dependency { - crate_id: CrateId( - 1, - ), - name: CrateName( - "core", - ), - prelude: true, - }, - ], - proc_macro: Err( - "no proc macro loaded for sysroot crate", - ), - origin: Lang( - Other, - ), - is_proc_macro: false, - }, - CrateId( - 5, - ): CrateData { - root_file_id: FileId( - 6, - ), - edition: Edition2021, - version: None, - display_name: Some( - CrateDisplayName { - crate_name: CrateName( - "profiler_builtins", - ), - canonical_name: "profiler_builtins", - }, - ), - cfg_options: CfgOptions( - [], - ), - potential_cfg_options: CfgOptions( - [], - ), - target_layout: Err( - "rust-project.json projects have no target layout set", - ), - env: Env { - entries: {}, - }, - dependencies: [], - proc_macro: Err( - "no proc macro loaded for sysroot crate", - ), - origin: Lang( - Other, - ), - is_proc_macro: false, - }, - CrateId( - 6, - ): CrateData { - root_file_id: FileId( - 7, - ), - edition: Edition2021, - version: None, - display_name: Some( - CrateDisplayName { - crate_name: CrateName( - "std", - ), - canonical_name: "std", - }, - ), - cfg_options: CfgOptions( - [], - ), - potential_cfg_options: CfgOptions( - [], - ), - target_layout: Err( - "rust-project.json projects have no target layout set", - ), - env: Env { - entries: {}, - }, - dependencies: [ - Dependency { - crate_id: CrateId( - 0, - ), - name: CrateName( - "alloc", - ), - prelude: true, - }, - Dependency { - crate_id: CrateId( - 3, - ), - name: CrateName( - "panic_unwind", - ), - prelude: true, - }, - Dependency { - crate_id: CrateId( - 2, - ), - name: CrateName( - "panic_abort", - ), - prelude: true, - }, - Dependency { - crate_id: CrateId( - 1, - ), - name: CrateName( - "core", - ), - prelude: true, - }, - Dependency { - crate_id: CrateId( - 5, - ), - name: CrateName( - "profiler_builtins", - ), - prelude: true, - }, - Dependency { - crate_id: CrateId( - 9, - ), - name: CrateName( - "unwind", - ), - prelude: true, - }, - Dependency { - crate_id: CrateId( - 7, - ), - name: CrateName( - "std_detect", - ), - prelude: true, - }, - Dependency { - crate_id: CrateId( - 8, - ), - name: CrateName( - "test", - ), - prelude: true, - }, - ], - proc_macro: Err( - "no proc macro loaded for sysroot crate", - ), - origin: Lang( - Std, - ), - is_proc_macro: false, - }, - CrateId( - 7, - ): CrateData { - root_file_id: FileId( - 8, - ), - edition: Edition2021, - version: None, - display_name: Some( - CrateDisplayName { - crate_name: CrateName( - "std_detect", - ), - canonical_name: "std_detect", - }, - ), - cfg_options: CfgOptions( - [], - ), - potential_cfg_options: CfgOptions( - [], - ), - target_layout: Err( - "rust-project.json projects have no target layout set", - ), - env: Env { - entries: {}, - }, - dependencies: [], - proc_macro: Err( - "no proc macro loaded for sysroot crate", - ), - origin: Lang( - Other, - ), - is_proc_macro: false, - }, - CrateId( - 8, - ): CrateData { - root_file_id: FileId( - 9, - ), - edition: Edition2021, - version: None, - display_name: Some( - CrateDisplayName { - crate_name: CrateName( - "test", - ), - canonical_name: "test", - }, - ), - cfg_options: CfgOptions( - [], - ), - potential_cfg_options: CfgOptions( - [], - ), - target_layout: Err( - "rust-project.json projects have no target layout set", - ), - env: Env { - entries: {}, - }, - dependencies: [], - proc_macro: Err( - "no proc macro loaded for sysroot crate", - ), - origin: Lang( - Test, - ), - is_proc_macro: false, - }, - CrateId( - 9, - ): CrateData { - root_file_id: FileId( - 10, - ), - edition: Edition2021, - version: None, - display_name: Some( - CrateDisplayName { - crate_name: CrateName( - "unwind", - ), - canonical_name: "unwind", - }, - ), - cfg_options: CfgOptions( - [], - ), - potential_cfg_options: CfgOptions( - [], - ), - target_layout: Err( - "rust-project.json projects have no target layout set", - ), - env: Env { - entries: {}, - }, - dependencies: [], - proc_macro: Err( - "no proc macro loaded for sysroot crate", - ), - origin: Lang( - Other, - ), - is_proc_macro: false, - }, - CrateId( - 10, - ): CrateData { - root_file_id: FileId( - 11, - ), - edition: Edition2018, - version: None, - display_name: Some( - CrateDisplayName { - crate_name: CrateName( - "hello_world", - ), - canonical_name: "hello_world", - }, - ), - cfg_options: CfgOptions( - [], - ), - potential_cfg_options: CfgOptions( - [], - ), - target_layout: Err( - "rust-project.json projects have no target layout set", - ), - env: Env { - entries: {}, - }, - dependencies: [ - Dependency { - crate_id: CrateId( - 1, - ), - name: CrateName( - "core", - ), - prelude: true, - }, - Dependency { - crate_id: CrateId( - 0, - ), - name: CrateName( - "alloc", - ), - prelude: true, - }, - Dependency { - crate_id: CrateId( - 6, - ), - name: CrateName( - "std", - ), - prelude: true, - }, - Dependency { - crate_id: CrateId( - 8, - ), - name: CrateName( - "test", - ), - prelude: false, - }, - ], - proc_macro: Err( - "no proc macro dylib present", - ), - origin: CratesIo { - repo: None, - name: Some( - "hello_world", - ), - }, - is_proc_macro: false, - }, - }, - }"#]], + expect_file!["../test_data/output/rust_project_hello_world_project_model.txt"], ); } #[test] fn rust_project_is_proc_macro_has_proc_macro_dep() { - let crate_graph = load_rust_project("is-proc-macro-project.json"); + let (crate_graph, _proc_macros) = load_rust_project("is-proc-macro-project.json"); // Since the project only defines one crate (outside the sysroot crates), // it should be the one with the biggest Id. let crate_id = crate_graph.iter().max().unwrap(); @@ -1916,3 +221,31 @@ fn rust_project_is_proc_macro_has_proc_macro_dep() { // on the proc_macro sysroot crate. crate_data.dependencies.iter().find(|&dep| dep.name.deref() == "proc_macro").unwrap(); } + +#[test] +fn crate_graph_dedup_identical() { + let (mut crate_graph, proc_macros) = + load_cargo_with_sysroot(&mut Default::default(), "regex-metadata.json"); + crate_graph.sort_deps(); + + let (d_crate_graph, mut d_proc_macros) = (crate_graph.clone(), proc_macros.clone()); + + crate_graph.extend(d_crate_graph.clone(), &mut d_proc_macros); + assert!(crate_graph.iter().eq(d_crate_graph.iter())); + assert_eq!(proc_macros, d_proc_macros); +} + +#[test] +fn crate_graph_dedup() { + let path_map = &mut Default::default(); + let (mut crate_graph, _proc_macros) = + load_cargo_with_sysroot(path_map, "ripgrep-metadata.json"); + assert_eq!(crate_graph.iter().count(), 81); + crate_graph.sort_deps(); + let (regex_crate_graph, mut regex_proc_macros) = + load_cargo_with_sysroot(path_map, "regex-metadata.json"); + assert_eq!(regex_crate_graph.iter().count(), 60); + + crate_graph.extend(regex_crate_graph, &mut regex_proc_macros); + assert_eq!(crate_graph.iter().count(), 118); +} diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs index d1e53e12eebb5..0aca620a67553 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs @@ -2,53 +2,43 @@ //! metadata` or `rust-project.json`) into representation stored in the salsa //! database -- `CrateGraph`. -use std::{collections::VecDeque, fmt, fs, process::Command, sync::Arc}; +use std::{collections::VecDeque, fmt, fs, process::Command, sync}; use anyhow::{format_err, Context, Result}; use base_db::{ CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, Edition, Env, - FileId, LangCrateOrigin, ProcMacroLoadResult, TargetLayoutLoadResult, + FileId, LangCrateOrigin, ProcMacroPaths, ReleaseChannel, TargetLayoutLoadResult, }; use cfg::{CfgDiff, CfgOptions}; use paths::{AbsPath, AbsPathBuf}; use rustc_hash::{FxHashMap, FxHashSet}; use semver::Version; -use stdx::{always, hash::NoHashHashMap}; +use stdx::always; +use triomphe::Arc; use crate::{ build_scripts::BuildScriptOutput, cargo_workspace::{DepKind, PackageData, RustLibSource}, cfg_flag::CfgFlag, + project_json::Crate, rustc_cfg, sysroot::SysrootCrate, target_data_layout, utf8_stdout, CargoConfig, CargoWorkspace, InvocationStrategy, ManifestPath, - Package, ProjectJson, ProjectManifest, Sysroot, TargetKind, WorkspaceBuildScripts, + Package, ProjectJson, ProjectManifest, Sysroot, TargetData, TargetKind, WorkspaceBuildScripts, }; /// A set of cfg-overrides per crate. -/// -/// `Wildcard(..)` is useful e.g. disabling `#[cfg(test)]` on all crates, -/// without having to first obtain a list of all crates. -#[derive(Debug, Clone, Eq, PartialEq)] -pub enum CfgOverrides { - /// A single global set of overrides matching all crates. - Wildcard(CfgDiff), +#[derive(Default, Debug, Clone, Eq, PartialEq)] +pub struct CfgOverrides { + /// A global set of overrides matching all crates. + pub global: CfgDiff, /// A set of overrides matching specific crates. - Selective(FxHashMap), -} - -impl Default for CfgOverrides { - fn default() -> Self { - Self::Selective(FxHashMap::default()) - } + pub selective: FxHashMap, } impl CfgOverrides { pub fn len(&self) -> usize { - match self { - CfgOverrides::Wildcard(_) => 1, - CfgOverrides::Selective(hash_map) => hash_map.len(), - } + self.global.len() + self.selective.iter().map(|(_, it)| it.len()).sum::() } } @@ -82,7 +72,14 @@ pub enum ProjectWorkspace { target_layout: Result, }, /// Project workspace was manually specified using a `rust-project.json` file. - Json { project: ProjectJson, sysroot: Result>, rustc_cfg: Vec }, + Json { + project: ProjectJson, + sysroot: Result>, + /// Holds cfg flags for the current target. We get those by running + /// `rustc --print cfg`. + rustc_cfg: Vec, + toolchain: Option, + }, // FIXME: The primary limitation of this approach is that the set of detached files needs to be fixed at the beginning. // That's not the end user experience we should strive for. // Ideally, you should be able to just open a random detached file in existing cargo projects, and get the basic features working. @@ -96,6 +93,8 @@ pub enum ProjectWorkspace { DetachedFiles { files: Vec, sysroot: Result>, + /// Holds cfg flags for the current target. We get those by running + /// `rustc --print cfg`. rustc_cfg: Vec, }, } @@ -127,12 +126,13 @@ impl fmt::Debug for ProjectWorkspace { .field("toolchain", &toolchain) .field("data_layout", &data_layout) .finish(), - ProjectWorkspace::Json { project, sysroot, rustc_cfg } => { + ProjectWorkspace::Json { project, sysroot, rustc_cfg, toolchain } => { let mut debug_struct = f.debug_struct("Json"); debug_struct.field("n_crates", &project.n_crates()); if let Ok(sysroot) = sysroot { debug_struct.field("n_sysroot_crates", &sysroot.crates().len()); } + debug_struct.field("toolchain", &toolchain); debug_struct.field("n_rustc_cfg", &rustc_cfg.len()); debug_struct.finish() } @@ -152,6 +152,19 @@ impl ProjectWorkspace { config: &CargoConfig, progress: &dyn Fn(String), ) -> Result { + let version = |current_dir, cmd_path, prefix: &str| { + let cargo_version = utf8_stdout({ + let mut cmd = Command::new(cmd_path); + cmd.envs(&config.extra_env); + cmd.arg("--version").current_dir(current_dir); + cmd + })?; + anyhow::Ok( + cargo_version + .get(prefix.len()..) + .and_then(|it| Version::parse(it.split_whitespace().next()?).ok()), + ) + }; let res = match manifest { ProjectManifest::ProjectJson(project_json) => { let file = fs::read_to_string(&project_json).with_context(|| { @@ -161,24 +174,17 @@ impl ProjectWorkspace { format!("Failed to deserialize json file {}", project_json.display()) })?; let project_location = project_json.parent().to_path_buf(); + let toolchain = version(&*project_location, toolchain::rustc(), "rustc ")?; let project_json = ProjectJson::new(&project_location, data); ProjectWorkspace::load_inline( project_json, config.target.as_deref(), &config.extra_env, + toolchain, ) } ProjectManifest::CargoToml(cargo_toml) => { - let cargo_version = utf8_stdout({ - let mut cmd = Command::new(toolchain::cargo()); - cmd.envs(&config.extra_env); - cmd.arg("--version"); - cmd - })?; - let toolchain = cargo_version - .get("cargo ".len()..) - .and_then(|it| Version::parse(it.split_whitespace().next()?).ok()); - + let toolchain = version(cargo_toml.parent(), toolchain::cargo(), "cargo ")?; let meta = CargoWorkspace::fetch_metadata( &cargo_toml, cargo_toml.parent(), @@ -274,7 +280,7 @@ impl ProjectWorkspace { let rustc_cfg = rustc_cfg::get(Some(&cargo_toml), config.target.as_deref(), &config.extra_env); - let cfg_overrides = config.cfg_overrides(); + let cfg_overrides = config.cfg_overrides.clone(); let data_layout = target_data_layout::get( Some(&cargo_toml), config.target.as_deref(), @@ -303,6 +309,7 @@ impl ProjectWorkspace { project_json: ProjectJson, target: Option<&str>, extra_env: &FxHashMap, + toolchain: Option, ) -> ProjectWorkspace { let sysroot = match (project_json.sysroot.clone(), project_json.sysroot_src.clone()) { (Some(sysroot), Some(sysroot_src)) => Ok(Sysroot::load(sysroot, sysroot_src)), @@ -327,7 +334,7 @@ impl ProjectWorkspace { } let rustc_cfg = rustc_cfg::get(None, target, extra_env); - ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg } + ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg, toolchain } } pub fn load_detached_files( @@ -403,7 +410,7 @@ impl ProjectWorkspace { let outputs = &mut match WorkspaceBuildScripts::run_once(config, &cargo_ws, progress) { Ok(it) => Ok(it.into_iter()), // io::Error is not Clone? - Err(e) => Err(Arc::new(e)), + Err(e) => Err(sync::Arc::new(e)), }; workspaces @@ -440,18 +447,35 @@ impl ProjectWorkspace { } } - pub fn find_sysroot_proc_macro_srv(&self) -> Option { + pub fn find_sysroot_proc_macro_srv(&self) -> Result { match self { ProjectWorkspace::Cargo { sysroot: Ok(sysroot), .. } - | ProjectWorkspace::Json { sysroot: Ok(sysroot), .. } => { + | ProjectWorkspace::Json { sysroot: Ok(sysroot), .. } + | ProjectWorkspace::DetachedFiles { sysroot: Ok(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()) + .ok_or_else(|| { + anyhow::anyhow!( + "cannot find proc-macro server in sysroot `{}`", + sysroot.root().display() + ) + }) + } + ProjectWorkspace::DetachedFiles { .. } => { + Err(anyhow::anyhow!("cannot find proc-macro server, no sysroot was found")) } - _ => None, + ProjectWorkspace::Cargo { cargo, .. } => Err(anyhow::anyhow!( + "cannot find proc-macro-srv, the workspace `{}` is missing a sysroot", + cargo.workspace_root().display() + )), + ProjectWorkspace::Json { project, .. } => Err(anyhow::anyhow!( + "cannot find proc-macro-srv, the workspace `{}` is missing a sysroot", + project.path().display() + )), } } @@ -469,7 +493,7 @@ impl ProjectWorkspace { }) }; match self { - ProjectWorkspace::Json { project, sysroot, rustc_cfg: _ } => project + ProjectWorkspace::Json { project, sysroot, rustc_cfg: _, toolchain: _ } => project .crates() .map(|(_, krate)| PackageRoot { is_local: krate.is_workspace_member, @@ -570,22 +594,23 @@ impl ProjectWorkspace { pub fn to_crate_graph( &self, - load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult, load: &mut dyn FnMut(&AbsPath) -> Option, extra_env: &FxHashMap, - ) -> CrateGraph { + ) -> (CrateGraph, ProcMacroPaths) { let _p = profile::span("ProjectWorkspace::to_crate_graph"); - let mut crate_graph = match self { - ProjectWorkspace::Json { project, sysroot, rustc_cfg } => project_json_to_crate_graph( - rustc_cfg.clone(), - load_proc_macro, - load, - project, - sysroot.as_ref().ok(), - extra_env, - Err("rust-project.json projects have no target layout set".into()), - ), + let (mut crate_graph, proc_macros) = match self { + ProjectWorkspace::Json { project, sysroot, rustc_cfg, toolchain } => { + project_json_to_crate_graph( + rustc_cfg.clone(), + load, + project, + sysroot.as_ref().ok(), + extra_env, + Err("rust-project.json projects have no target layout set".into()), + toolchain.as_ref().and_then(|it| ReleaseChannel::from_str(it.pre.as_str())), + ) + } ProjectWorkspace::Cargo { cargo, sysroot, @@ -593,21 +618,22 @@ impl ProjectWorkspace { rustc_cfg, cfg_overrides, build_scripts, - toolchain: _, + toolchain, target_layout, } => cargo_to_crate_graph( - load_proc_macro, load, rustc.as_ref().ok(), cargo, sysroot.as_ref().ok(), rustc_cfg.clone(), cfg_overrides, + None, build_scripts, match target_layout.as_ref() { Ok(it) => Ok(Arc::from(it.as_str())), Err(it) => Err(Arc::from(it.as_str())), }, + toolchain.as_ref().and_then(|it| ReleaseChannel::from_str(it.pre.as_str())), ), ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => { detached_files_to_crate_graph( @@ -624,7 +650,7 @@ impl ProjectWorkspace { } else { tracing::debug!("Did not patch std to depend on cfg-if") } - crate_graph + (crate_graph, proc_macros) } pub fn eq_ignore_build_data(&self, other: &Self) -> bool { @@ -659,9 +685,19 @@ impl ProjectWorkspace { && sysroot == o_sysroot } ( - Self::Json { project, sysroot, rustc_cfg }, - Self::Json { project: o_project, sysroot: o_sysroot, rustc_cfg: o_rustc_cfg }, - ) => project == o_project && rustc_cfg == o_rustc_cfg && sysroot == o_sysroot, + Self::Json { project, sysroot, rustc_cfg, toolchain }, + Self::Json { + project: o_project, + sysroot: o_sysroot, + rustc_cfg: o_rustc_cfg, + toolchain: o_toolchain, + }, + ) => { + project == o_project + && rustc_cfg == o_rustc_cfg + && sysroot == o_sysroot + && toolchain == o_toolchain + } ( Self::DetachedFiles { files, sysroot, rustc_cfg }, Self::DetachedFiles { files: o_files, sysroot: o_sysroot, rustc_cfg: o_rustc_cfg }, @@ -669,130 +705,146 @@ impl ProjectWorkspace { _ => false, } } + + /// Returns `true` if the project workspace is [`Json`]. + /// + /// [`Json`]: ProjectWorkspace::Json + #[must_use] + pub fn is_json(&self) -> bool { + matches!(self, Self::Json { .. }) + } } fn project_json_to_crate_graph( rustc_cfg: Vec, - load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult, load: &mut dyn FnMut(&AbsPath) -> Option, project: &ProjectJson, sysroot: Option<&Sysroot>, extra_env: &FxHashMap, target_layout: TargetLayoutLoadResult, -) -> CrateGraph { - let mut crate_graph = CrateGraph::default(); + channel: Option, +) -> (CrateGraph, ProcMacroPaths) { + let mut res = (CrateGraph::default(), ProcMacroPaths::default()); + let (crate_graph, proc_macros) = &mut res; let sysroot_deps = sysroot.as_ref().map(|sysroot| { sysroot_to_crate_graph( - &mut crate_graph, + crate_graph, sysroot, rustc_cfg.clone(), target_layout.clone(), load, + channel, ) }); let mut cfg_cache: FxHashMap<&str, Vec> = FxHashMap::default(); - let crates: NoHashHashMap = project + let crates: FxHashMap = project .crates() - .filter_map(|(crate_id, krate)| { - let file_path = &krate.root_module; - let file_id = load(file_path)?; - Some((crate_id, krate, file_id)) - }) - .map(|(crate_id, krate, file_id)| { - let env = krate.env.clone().into_iter().collect(); - let proc_macro = match krate.proc_macro_dylib_path.clone() { - Some(it) => load_proc_macro( - krate.display_name.as_ref().map(|it| it.canonical_name()).unwrap_or(""), - &it, - ), - None => Err("no proc macro dylib present".into()), - }; - - let target_cfgs = match krate.target.as_deref() { - Some(target) => cfg_cache - .entry(target) - .or_insert_with(|| rustc_cfg::get(None, Some(target), extra_env)), - None => &rustc_cfg, - }; - - let mut cfg_options = CfgOptions::default(); - cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned()); - ( + .filter_map(|(crate_id, krate)| Some((crate_id, krate, load(&krate.root_module)?))) + .map( + |( crate_id, - crate_graph.add_crate_root( + Crate { + display_name, + edition, + version, + cfg, + target, + env, + proc_macro_dylib_path, + is_proc_macro, + repository, + .. + }, + file_id, + )| { + let env = env.clone().into_iter().collect(); + + let target_cfgs = match target.as_deref() { + Some(target) => cfg_cache + .entry(target) + .or_insert_with(|| rustc_cfg::get(None, Some(target), extra_env)), + None => &rustc_cfg, + }; + + let crate_graph_crate_id = crate_graph.add_crate_root( file_id, - krate.edition, - krate.display_name.clone(), - krate.version.clone(), - cfg_options.clone(), - cfg_options, + *edition, + display_name.clone(), + version.clone(), + target_cfgs.iter().chain(cfg.iter()).cloned().collect(), + None, env, - proc_macro, - krate.is_proc_macro, - if krate.display_name.is_some() { - CrateOrigin::CratesIo { - repo: krate.repository.clone(), - name: krate - .display_name - .clone() - .map(|n| n.canonical_name().to_string()), + *is_proc_macro, + if let Some(name) = display_name.clone() { + CrateOrigin::Local { + repo: repository.clone(), + name: Some(name.canonical_name().to_string()), } } else { - CrateOrigin::CratesIo { repo: None, name: None } + CrateOrigin::Local { repo: None, name: None } }, target_layout.clone(), - ), - ) - }) + channel, + ); + if *is_proc_macro { + if let Some(path) = proc_macro_dylib_path.clone() { + let node = Ok(( + display_name.as_ref().map(|it| it.canonical_name().to_owned()), + path, + )); + proc_macros.insert(crate_graph_crate_id, node); + } + } + (crate_id, crate_graph_crate_id) + }, + ) .collect(); 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_to_crate_graph(&mut crate_graph, from); - if krate.is_proc_macro { - if let Some(proc_macro) = libproc_macro { - add_dep( - &mut crate_graph, - from, - CrateName::new("proc_macro").unwrap(), - *proc_macro, - ); - } + public_deps.add_to_crate_graph(crate_graph, from); + if let Some(proc_macro) = libproc_macro { + add_proc_macro_dep(crate_graph, from, *proc_macro, krate.is_proc_macro); } } for dep in &krate.deps { if let Some(&to) = crates.get(&dep.crate_id) { - add_dep(&mut crate_graph, from, dep.name.clone(), to) + add_dep(crate_graph, from, dep.name.clone(), to) } } } } - crate_graph + res } fn cargo_to_crate_graph( - load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult, load: &mut dyn FnMut(&AbsPath) -> Option, rustc: Option<&(CargoWorkspace, WorkspaceBuildScripts)>, cargo: &CargoWorkspace, sysroot: Option<&Sysroot>, rustc_cfg: Vec, override_cfg: &CfgOverrides, + // Don't compute cfg and use this if present + forced_cfg: Option, build_scripts: &WorkspaceBuildScripts, target_layout: TargetLayoutLoadResult, -) -> CrateGraph { + channel: Option, +) -> (CrateGraph, ProcMacroPaths) { let _p = profile::span("cargo_to_crate_graph"); - let mut crate_graph = CrateGraph::default(); + let mut res = (CrateGraph::default(), ProcMacroPaths::default()); + let crate_graph = &mut res.0; + let proc_macros = &mut res.1; let (public_deps, libproc_macro) = match sysroot { Some(sysroot) => sysroot_to_crate_graph( - &mut crate_graph, + crate_graph, sysroot, rustc_cfg.clone(), target_layout.clone(), load, + channel, ), None => (SysrootPublicDeps::default(), None), }; @@ -804,37 +856,40 @@ fn cargo_to_crate_graph( cfg_options }; + // Mapping of a package to its library target let mut pkg_to_lib_crate = FxHashMap::default(); - 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; + // Next, create crates for each package, target pair for pkg in cargo.packages() { - let mut cfg_options = cfg_options.clone(); + has_private |= cargo[pkg].metadata.rustc_private; - let overrides = match override_cfg { - CfgOverrides::Wildcard(cfg_diff) => Some(cfg_diff), - CfgOverrides::Selective(cfg_overrides) => cfg_overrides.get(&cargo[pkg].name), - }; + let cfg_options = forced_cfg.clone().unwrap_or_else(|| { + let mut cfg_options = cfg_options.clone(); - // Add test cfg for local crates - if cargo[pkg].is_local { - cfg_options.insert_atom("test".into()); - } + // Add test cfg for local crates + if cargo[pkg].is_local { + cfg_options.insert_atom("test".into()); + } - if let Some(overrides) = overrides { - // FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen - // in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while - // working on rust-lang/rust as that's the only time it appears outside sysroot). - // - // A more ideal solution might be to reanalyze crates based on where the cursor is and - // figure out the set of cfgs that would have to apply to make it active. + if !override_cfg.global.is_empty() { + cfg_options.apply_diff(override_cfg.global.clone()); + }; + if let Some(diff) = override_cfg.selective.get(&cargo[pkg].name) { + // FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen + // in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while + // working on rust-lang/rust as that's the only time it appears outside sysroot). + // + // A more ideal solution might be to reanalyze crates based on where the cursor is and + // figure out the set of cfgs that would have to apply to make it active. - cfg_options.apply_diff(overrides.clone()); - }; + cfg_options.apply_diff(diff.clone()); + }; + cfg_options + }); - has_private |= cargo[pkg].metadata.rustc_private; let mut lib_tgt = None; for &tgt in cargo[pkg].targets.iter() { if cargo[tgt].kind != TargetKind::Lib && !cargo[pkg].is_member { @@ -845,44 +900,57 @@ fn cargo_to_crate_graph( // https://github.com/rust-lang/rust-analyzer/issues/11300 continue; } + let &TargetData { ref name, kind, is_proc_macro, ref root, .. } = &cargo[tgt]; - if let Some(file_id) = load(&cargo[tgt].root) { - let crate_id = add_target_crate_root( - &mut crate_graph, - &cargo[pkg], - build_scripts.get_output(pkg), - cfg_options.clone(), - &mut |path| load_proc_macro(&cargo[tgt].name, path), - file_id, - &cargo[tgt].name, - cargo[tgt].is_proc_macro, - target_layout.clone(), - ); - if cargo[tgt].kind == TargetKind::Lib { - lib_tgt = Some((crate_id, cargo[tgt].name.clone())); + if kind == TargetKind::Lib + && sysroot.map_or(false, |sysroot| root.starts_with(sysroot.src_root())) + { + if let Some(&(_, crate_id, _)) = + public_deps.deps.iter().find(|(dep_name, ..)| dep_name.as_smol_str() == name) + { + pkg_crates.entry(pkg).or_insert_with(Vec::new).push((crate_id, kind)); + + lib_tgt = Some((crate_id, name.clone())); pkg_to_lib_crate.insert(pkg, crate_id); + // sysroot is inside the workspace, prevent the sysroot crates from being duplicated here + continue; } - // Even crates that don't set proc-macro = true are allowed to depend on proc_macro - // (just none of the APIs work when called outside of a proc macro). - if let Some(proc_macro) = libproc_macro { - add_dep_with_prelude( - &mut crate_graph, - crate_id, - CrateName::new("proc_macro").unwrap(), - proc_macro, - cargo[tgt].is_proc_macro, - ); - } + } - pkg_crates.entry(pkg).or_insert_with(Vec::new).push((crate_id, cargo[tgt].kind)); + let Some(file_id) = load(root) else { continue }; + + let crate_id = add_target_crate_root( + crate_graph, + proc_macros, + &cargo[pkg], + build_scripts.get_output(pkg), + cfg_options.clone(), + file_id, + name, + is_proc_macro, + target_layout.clone(), + false, + channel, + ); + if kind == TargetKind::Lib { + lib_tgt = Some((crate_id, name.clone())); + pkg_to_lib_crate.insert(pkg, crate_id); } + // Even crates that don't set proc-macro = true are allowed to depend on proc_macro + // (just none of the APIs work when called outside of a proc macro). + if let Some(proc_macro) = libproc_macro { + add_proc_macro_dep(crate_graph, crate_id, proc_macro, is_proc_macro); + } + + pkg_crates.entry(pkg).or_insert_with(Vec::new).push((crate_id, kind)); } // 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_to_crate_graph(&mut crate_graph, from); + public_deps.add_to_crate_graph(crate_graph, from); + // Add dep edge of all targets to the package's lib target if let Some((to, name)) = lib_tgt.clone() { if to != from && kind != TargetKind::BuildScript { // (build script can not depend on its library target) @@ -891,7 +959,7 @@ fn cargo_to_crate_graph( // cargo metadata does not do any normalization, // so we do it ourselves currently let name = CrateName::normalize_dashes(&name); - add_dep(&mut crate_graph, from, name, to); + add_dep(crate_graph, from, name, to); } } } @@ -900,21 +968,18 @@ fn cargo_to_crate_graph( // Now add a dep edge from all targets of upstream to the lib // target of downstream. for pkg in cargo.packages() { - for dep in cargo[pkg].dependencies.iter() { - let name = CrateName::new(&dep.name).unwrap(); - if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) { - for &(from, kind) in pkg_crates.get(&pkg).into_iter().flatten() { - if dep.kind == DepKind::Build && kind != TargetKind::BuildScript { - // Only build scripts may depend on build dependencies. - continue; - } - if dep.kind != DepKind::Build && kind == TargetKind::BuildScript { - // Build scripts may only depend on build dependencies. - continue; - } + for dep in &cargo[pkg].dependencies { + let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) else { continue }; + let Some(targets) = pkg_crates.get(&pkg) else { continue }; - add_dep(&mut crate_graph, from, name.clone(), to) + let name = CrateName::new(&dep.name).unwrap(); + for &(from, kind) in targets { + // Build scripts may only depend on build dependencies. + if (dep.kind == DepKind::Build) != (kind == TargetKind::BuildScript) { + continue; } + + add_dep(crate_graph, from, name.clone(), to) } } } @@ -924,10 +989,10 @@ fn cargo_to_crate_graph( // and create dependencies on them for the crates which opt-in to that if let Some((rustc_workspace, rustc_build_scripts)) = rustc { handle_rustc_crates( - &mut crate_graph, + crate_graph, + proc_macros, &mut pkg_to_lib_crate, load, - load_proc_macro, rustc_workspace, cargo, &public_deps, @@ -943,10 +1008,11 @@ fn cargo_to_crate_graph( rustc_build_scripts }, target_layout, + channel, ); } } - crate_graph + res } fn detached_files_to_crate_graph( @@ -955,7 +1021,7 @@ fn detached_files_to_crate_graph( detached_files: &[AbsPathBuf], sysroot: Option<&Sysroot>, target_layout: TargetLayoutLoadResult, -) -> CrateGraph { +) -> (CrateGraph, ProcMacroPaths) { let _p = profile::span("detached_files_to_crate_graph"); let mut crate_graph = CrateGraph::default(); let (public_deps, _libproc_macro) = match sysroot { @@ -965,6 +1031,7 @@ fn detached_files_to_crate_graph( rustc_cfg.clone(), target_layout.clone(), load, + None, ), None => (SysrootPublicDeps::default(), None), }; @@ -990,27 +1057,27 @@ fn detached_files_to_crate_graph( display_name.clone(), None, cfg_options.clone(), - cfg_options.clone(), + None, Env::default(), - Ok(Vec::new()), false, - CrateOrigin::CratesIo { + CrateOrigin::Local { repo: None, name: display_name.map(|n| n.canonical_name().to_string()), }, target_layout.clone(), + None, ); public_deps.add_to_crate_graph(&mut crate_graph, detached_file_crate); } - crate_graph + (crate_graph, FxHashMap::default()) } fn handle_rustc_crates( crate_graph: &mut CrateGraph, + proc_macros: &mut ProcMacroPaths, pkg_to_lib_crate: &mut FxHashMap, load: &mut dyn FnMut(&AbsPath) -> Option, - load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult, rustc_workspace: &CargoWorkspace, cargo: &CargoWorkspace, public_deps: &SysrootPublicDeps, @@ -1020,6 +1087,7 @@ fn handle_rustc_crates( override_cfg: &CfgOverrides, build_scripts: &WorkspaceBuildScripts, target_layout: TargetLayoutLoadResult, + channel: Option, ) { let mut rustc_pkg_crates = FxHashMap::default(); // The root package of the rustc-dev component is rustc_driver, so we match that @@ -1044,14 +1112,10 @@ fn handle_rustc_crates( let mut cfg_options = cfg_options.clone(); - let overrides = match override_cfg { - CfgOverrides::Wildcard(cfg_diff) => Some(cfg_diff), - CfgOverrides::Selective(cfg_overrides) => { - cfg_overrides.get(&rustc_workspace[pkg].name) - } + if !override_cfg.global.is_empty() { + cfg_options.apply_diff(override_cfg.global.clone()); }; - - if let Some(overrides) = overrides { + if let Some(diff) = override_cfg.selective.get(&rustc_workspace[pkg].name) { // FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen // in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while // working on rust-lang/rust as that's the only time it appears outside sysroot). @@ -1059,7 +1123,7 @@ fn handle_rustc_crates( // A more ideal solution might be to reanalyze crates based on where the cursor is and // figure out the set of cfgs that would have to apply to make it active. - cfg_options.apply_diff(overrides.clone()); + cfg_options.apply_diff(diff.clone()); }; for &tgt in rustc_workspace[pkg].targets.iter() { @@ -1069,23 +1133,24 @@ fn handle_rustc_crates( if let Some(file_id) = load(&rustc_workspace[tgt].root) { let crate_id = add_target_crate_root( crate_graph, + proc_macros, &rustc_workspace[pkg], build_scripts.get_output(pkg), cfg_options.clone(), - &mut |path| load_proc_macro(&rustc_workspace[tgt].name, path), file_id, &rustc_workspace[tgt].name, rustc_workspace[tgt].is_proc_macro, target_layout.clone(), + true, + channel, ); pkg_to_lib_crate.insert(pkg, crate_id); // Add dependencies on core / std / alloc for this crate public_deps.add_to_crate_graph(crate_graph, crate_id); if let Some(proc_macro) = libproc_macro { - add_dep_with_prelude( + add_proc_macro_dep( crate_graph, crate_id, - CrateName::new("proc_macro").unwrap(), proc_macro, rustc_workspace[tgt].is_proc_macro, ); @@ -1134,22 +1199,29 @@ fn handle_rustc_crates( fn add_target_crate_root( crate_graph: &mut CrateGraph, + proc_macros: &mut ProcMacroPaths, pkg: &PackageData, build_data: Option<&BuildScriptOutput>, cfg_options: CfgOptions, - load_proc_macro: &mut dyn FnMut(&AbsPath) -> ProcMacroLoadResult, file_id: FileId, cargo_name: &str, is_proc_macro: bool, target_layout: TargetLayoutLoadResult, + rustc_crate: bool, + channel: Option, ) -> CrateId { let edition = pkg.edition; - let mut potential_cfg_options = cfg_options.clone(); - potential_cfg_options.extend( - pkg.features - .iter() - .map(|feat| CfgFlag::KeyValue { key: "feature".into(), value: feat.0.into() }), - ); + let potential_cfg_options = if pkg.features.is_empty() { + None + } else { + let mut potential_cfg_options = cfg_options.clone(); + potential_cfg_options.extend( + pkg.features + .iter() + .map(|feat| CfgFlag::KeyValue { key: "feature".into(), value: feat.0.into() }), + ); + Some(potential_cfg_options) + }; let cfg_options = { let mut opts = cfg_options; for feature in pkg.active_features.iter() { @@ -1170,14 +1242,8 @@ fn add_target_crate_root( } } - let proc_macro = match build_data.as_ref().map(|it| it.proc_macro_dylib_path.as_ref()) { - Some(Some(it)) => load_proc_macro(it), - Some(None) => Err("no proc macro dylib present".into()), - None => Err("crate has not (yet) been built".into()), - }; - let display_name = CrateDisplayName::from_canonical_name(cargo_name.to_string()); - crate_graph.add_crate_root( + let crate_id = crate_graph.add_crate_root( file_id, edition, Some(display_name), @@ -1185,11 +1251,28 @@ fn add_target_crate_root( cfg_options, potential_cfg_options, env, - proc_macro, is_proc_macro, - CrateOrigin::CratesIo { repo: pkg.repository.clone(), name: Some(pkg.name.clone()) }, + if rustc_crate { + CrateOrigin::Rustc { name: pkg.name.clone() } + } else if pkg.is_member { + CrateOrigin::Local { repo: pkg.repository.clone(), name: Some(pkg.name.clone()) } + } else { + CrateOrigin::Library { repo: pkg.repository.clone(), name: pkg.name.clone() } + }, target_layout, - ) + channel, + ); + if is_proc_macro { + let proc_macro = match build_data.as_ref().map(|it| it.proc_macro_dylib_path.as_ref()) { + Some(it) => it.cloned().map(|path| Ok((Some(cargo_name.to_owned()), path))), + None => Some(Err("crate has not yet been built".to_owned())), + }; + if let Some(proc_macro) = proc_macro { + proc_macros.insert(crate_id, proc_macro); + } + } + + crate_id } #[derive(Default)] @@ -1212,34 +1295,47 @@ fn sysroot_to_crate_graph( rustc_cfg: Vec, target_layout: TargetLayoutLoadResult, load: &mut dyn FnMut(&AbsPath) -> Option, + channel: Option, ) -> (SysrootPublicDeps, Option) { let _p = profile::span("sysroot_to_crate_graph"); let mut cfg_options = CfgOptions::default(); - cfg_options.extend(rustc_cfg); - let sysroot_crates: FxHashMap = sysroot - .crates() - .filter_map(|krate| { - let file_id = load(&sysroot[krate].root)?; - - let env = Env::default(); - let display_name = CrateDisplayName::from_canonical_name(sysroot[krate].name.clone()); - let crate_id = crate_graph.add_crate_root( - file_id, - Edition::CURRENT, - Some(display_name), - None, - cfg_options.clone(), - cfg_options.clone(), - env, - Err("no proc macro loaded for sysroot crate".into()), - false, - CrateOrigin::Lang(LangCrateOrigin::from(&*sysroot[krate].name)), - target_layout.clone(), - ); - Some((krate, crate_id)) - }) - .collect(); - + cfg_options.extend(rustc_cfg.clone()); + let sysroot_crates: FxHashMap = match &sysroot.hack_cargo_workspace { + Some(cargo) => handle_hack_cargo_workspace( + load, + cargo, + rustc_cfg, + cfg_options, + target_layout, + channel, + crate_graph, + sysroot, + ), + None => sysroot + .crates() + .filter_map(|krate| { + let file_id = load(&sysroot[krate].root)?; + + let env = Env::default(); + let display_name = + CrateDisplayName::from_canonical_name(sysroot[krate].name.clone()); + let crate_id = crate_graph.add_crate_root( + file_id, + Edition::CURRENT, + Some(display_name), + None, + cfg_options.clone(), + None, + env, + false, + CrateOrigin::Lang(LangCrateOrigin::from(&*sysroot[krate].name)), + target_layout.clone(), + channel, + ); + Some((krate, crate_id)) + }) + .collect(), + }; for from in sysroot.crates() { for &to in sysroot[from].deps.iter() { let name = CrateName::new(&sysroot[to].name).unwrap(); @@ -1260,6 +1356,63 @@ fn sysroot_to_crate_graph( (public_deps, libproc_macro) } +fn handle_hack_cargo_workspace( + load: &mut dyn FnMut(&AbsPath) -> Option, + cargo: &CargoWorkspace, + rustc_cfg: Vec, + cfg_options: CfgOptions, + target_layout: Result, Arc>, + channel: Option, + crate_graph: &mut CrateGraph, + sysroot: &Sysroot, +) -> FxHashMap { + let (cg, mut pm) = cargo_to_crate_graph( + load, + None, + cargo, + None, + rustc_cfg, + &CfgOverrides::default(), + Some(cfg_options), + &WorkspaceBuildScripts::default(), + target_layout, + channel, + ); + crate_graph.extend(cg, &mut pm); + for crate_name in ["std", "alloc", "core"] { + let original = crate_graph + .iter() + .find(|x| { + crate_graph[*x] + .display_name + .as_ref() + .map(|x| x.canonical_name() == crate_name) + .unwrap_or(false) + }) + .unwrap(); + let fake_crate_name = format!("rustc-std-workspace-{}", crate_name); + let fake = crate_graph + .iter() + .find(|x| { + crate_graph[*x] + .display_name + .as_ref() + .map(|x| x.canonical_name() == fake_crate_name) + .unwrap_or(false) + }) + .unwrap(); + crate_graph.remove_and_replace(fake, original).unwrap(); + } + sysroot + .crates() + .filter_map(|krate| { + let file_id = load(&sysroot[krate].root)?; + let crate_id = crate_graph.crate_id_for_crate_root(file_id)?; + Some((krate, crate_id)) + }) + .collect() +} + fn add_dep(graph: &mut CrateGraph, from: CrateId, name: CrateName, to: CrateId) { add_dep_inner(graph, from, Dependency::new(name, to)) } @@ -1274,6 +1427,10 @@ fn add_dep_with_prelude( add_dep_inner(graph, from, Dependency::with_prelude(name, to, prelude)) } +fn add_proc_macro_dep(crate_graph: &mut CrateGraph, from: CrateId, to: CrateId, prelude: bool) { + add_dep_with_prelude(crate_graph, from, CrateName::new("proc_macro").unwrap(), to, prelude); +} + fn add_dep_inner(graph: &mut CrateGraph, from: CrateId, dep: Dependency) { if let Err(err) = graph.add_dep(from, dep) { tracing::error!("{}", err) diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt new file mode 100644 index 0000000000000..e595cd827293e --- /dev/null +++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt @@ -0,0 +1,344 @@ +{ + 0: CrateData { + root_file_id: FileId( + 1, + ), + edition: Edition2018, + version: Some( + "0.1.0", + ), + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "hello_world", + ), + canonical_name: "hello-world", + }, + ), + cfg_options: CfgOptions( + [ + "debug_assertions", + "test", + ], + ), + potential_cfg_options: None, + env: Env { + entries: { + "CARGO_PKG_LICENSE": "", + "CARGO_PKG_VERSION_MAJOR": "0", + "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_PKG_VERSION": "0.1.0", + "CARGO_PKG_AUTHORS": "", + "CARGO_CRATE_NAME": "hello_world", + "CARGO_PKG_LICENSE_FILE": "", + "CARGO_PKG_HOMEPAGE": "", + "CARGO_PKG_DESCRIPTION": "", + "CARGO_PKG_NAME": "hello-world", + "CARGO_PKG_VERSION_PATCH": "0", + "CARGO": "cargo", + "CARGO_PKG_REPOSITORY": "", + "CARGO_PKG_VERSION_MINOR": "1", + "CARGO_PKG_VERSION_PRE": "", + }, + }, + dependencies: [ + Dependency { + crate_id: Idx::(4), + name: CrateName( + "libc", + ), + prelude: true, + }, + ], + origin: Local { + repo: None, + name: Some( + "hello-world", + ), + }, + is_proc_macro: false, + target_layout: Err( + "target_data_layout not loaded", + ), + channel: None, + }, + 1: CrateData { + root_file_id: FileId( + 2, + ), + edition: Edition2018, + version: Some( + "0.1.0", + ), + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "hello_world", + ), + canonical_name: "hello-world", + }, + ), + cfg_options: CfgOptions( + [ + "debug_assertions", + "test", + ], + ), + potential_cfg_options: None, + env: Env { + entries: { + "CARGO_PKG_LICENSE": "", + "CARGO_PKG_VERSION_MAJOR": "0", + "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_PKG_VERSION": "0.1.0", + "CARGO_PKG_AUTHORS": "", + "CARGO_CRATE_NAME": "hello_world", + "CARGO_PKG_LICENSE_FILE": "", + "CARGO_PKG_HOMEPAGE": "", + "CARGO_PKG_DESCRIPTION": "", + "CARGO_PKG_NAME": "hello-world", + "CARGO_PKG_VERSION_PATCH": "0", + "CARGO": "cargo", + "CARGO_PKG_REPOSITORY": "", + "CARGO_PKG_VERSION_MINOR": "1", + "CARGO_PKG_VERSION_PRE": "", + }, + }, + dependencies: [ + Dependency { + crate_id: Idx::(0), + name: CrateName( + "hello_world", + ), + prelude: true, + }, + Dependency { + crate_id: Idx::(4), + name: CrateName( + "libc", + ), + prelude: true, + }, + ], + origin: Local { + repo: None, + name: Some( + "hello-world", + ), + }, + is_proc_macro: false, + target_layout: Err( + "target_data_layout not loaded", + ), + channel: None, + }, + 2: CrateData { + root_file_id: FileId( + 3, + ), + edition: Edition2018, + version: Some( + "0.1.0", + ), + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "an_example", + ), + canonical_name: "an-example", + }, + ), + cfg_options: CfgOptions( + [ + "debug_assertions", + "test", + ], + ), + potential_cfg_options: None, + env: Env { + entries: { + "CARGO_PKG_LICENSE": "", + "CARGO_PKG_VERSION_MAJOR": "0", + "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_PKG_VERSION": "0.1.0", + "CARGO_PKG_AUTHORS": "", + "CARGO_CRATE_NAME": "hello_world", + "CARGO_PKG_LICENSE_FILE": "", + "CARGO_PKG_HOMEPAGE": "", + "CARGO_PKG_DESCRIPTION": "", + "CARGO_PKG_NAME": "hello-world", + "CARGO_PKG_VERSION_PATCH": "0", + "CARGO": "cargo", + "CARGO_PKG_REPOSITORY": "", + "CARGO_PKG_VERSION_MINOR": "1", + "CARGO_PKG_VERSION_PRE": "", + }, + }, + dependencies: [ + Dependency { + crate_id: Idx::(0), + name: CrateName( + "hello_world", + ), + prelude: true, + }, + Dependency { + crate_id: Idx::(4), + name: CrateName( + "libc", + ), + prelude: true, + }, + ], + origin: Local { + repo: None, + name: Some( + "hello-world", + ), + }, + is_proc_macro: false, + target_layout: Err( + "target_data_layout not loaded", + ), + channel: None, + }, + 3: CrateData { + root_file_id: FileId( + 4, + ), + edition: Edition2018, + version: Some( + "0.1.0", + ), + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "it", + ), + canonical_name: "it", + }, + ), + cfg_options: CfgOptions( + [ + "debug_assertions", + "test", + ], + ), + potential_cfg_options: None, + env: Env { + entries: { + "CARGO_PKG_LICENSE": "", + "CARGO_PKG_VERSION_MAJOR": "0", + "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_PKG_VERSION": "0.1.0", + "CARGO_PKG_AUTHORS": "", + "CARGO_CRATE_NAME": "hello_world", + "CARGO_PKG_LICENSE_FILE": "", + "CARGO_PKG_HOMEPAGE": "", + "CARGO_PKG_DESCRIPTION": "", + "CARGO_PKG_NAME": "hello-world", + "CARGO_PKG_VERSION_PATCH": "0", + "CARGO": "cargo", + "CARGO_PKG_REPOSITORY": "", + "CARGO_PKG_VERSION_MINOR": "1", + "CARGO_PKG_VERSION_PRE": "", + }, + }, + dependencies: [ + Dependency { + crate_id: Idx::(0), + name: CrateName( + "hello_world", + ), + prelude: true, + }, + Dependency { + crate_id: Idx::(4), + name: CrateName( + "libc", + ), + prelude: true, + }, + ], + origin: Local { + repo: None, + name: Some( + "hello-world", + ), + }, + is_proc_macro: false, + target_layout: Err( + "target_data_layout not loaded", + ), + channel: None, + }, + 4: CrateData { + root_file_id: FileId( + 5, + ), + edition: Edition2015, + version: Some( + "0.2.98", + ), + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "libc", + ), + canonical_name: "libc", + }, + ), + cfg_options: CfgOptions( + [ + "debug_assertions", + "feature=default", + "feature=std", + ], + ), + potential_cfg_options: Some( + CfgOptions( + [ + "debug_assertions", + "feature=align", + "feature=const-extern-fn", + "feature=default", + "feature=extra_traits", + "feature=rustc-dep-of-std", + "feature=std", + "feature=use_std", + ], + ), + ), + env: Env { + entries: { + "CARGO_PKG_LICENSE": "", + "CARGO_PKG_VERSION_MAJOR": "0", + "CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98", + "CARGO_PKG_VERSION": "0.2.98", + "CARGO_PKG_AUTHORS": "", + "CARGO_CRATE_NAME": "libc", + "CARGO_PKG_LICENSE_FILE": "", + "CARGO_PKG_HOMEPAGE": "", + "CARGO_PKG_DESCRIPTION": "", + "CARGO_PKG_NAME": "libc", + "CARGO_PKG_VERSION_PATCH": "98", + "CARGO": "cargo", + "CARGO_PKG_REPOSITORY": "", + "CARGO_PKG_VERSION_MINOR": "2", + "CARGO_PKG_VERSION_PRE": "", + }, + }, + dependencies: [], + origin: Library { + repo: Some( + "https://github.com/rust-lang/libc", + ), + name: "libc", + }, + is_proc_macro: false, + target_layout: Err( + "target_data_layout not loaded", + ), + channel: None, + }, +} \ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt new file mode 100644 index 0000000000000..e595cd827293e --- /dev/null +++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt @@ -0,0 +1,344 @@ +{ + 0: CrateData { + root_file_id: FileId( + 1, + ), + edition: Edition2018, + version: Some( + "0.1.0", + ), + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "hello_world", + ), + canonical_name: "hello-world", + }, + ), + cfg_options: CfgOptions( + [ + "debug_assertions", + "test", + ], + ), + potential_cfg_options: None, + env: Env { + entries: { + "CARGO_PKG_LICENSE": "", + "CARGO_PKG_VERSION_MAJOR": "0", + "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_PKG_VERSION": "0.1.0", + "CARGO_PKG_AUTHORS": "", + "CARGO_CRATE_NAME": "hello_world", + "CARGO_PKG_LICENSE_FILE": "", + "CARGO_PKG_HOMEPAGE": "", + "CARGO_PKG_DESCRIPTION": "", + "CARGO_PKG_NAME": "hello-world", + "CARGO_PKG_VERSION_PATCH": "0", + "CARGO": "cargo", + "CARGO_PKG_REPOSITORY": "", + "CARGO_PKG_VERSION_MINOR": "1", + "CARGO_PKG_VERSION_PRE": "", + }, + }, + dependencies: [ + Dependency { + crate_id: Idx::(4), + name: CrateName( + "libc", + ), + prelude: true, + }, + ], + origin: Local { + repo: None, + name: Some( + "hello-world", + ), + }, + is_proc_macro: false, + target_layout: Err( + "target_data_layout not loaded", + ), + channel: None, + }, + 1: CrateData { + root_file_id: FileId( + 2, + ), + edition: Edition2018, + version: Some( + "0.1.0", + ), + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "hello_world", + ), + canonical_name: "hello-world", + }, + ), + cfg_options: CfgOptions( + [ + "debug_assertions", + "test", + ], + ), + potential_cfg_options: None, + env: Env { + entries: { + "CARGO_PKG_LICENSE": "", + "CARGO_PKG_VERSION_MAJOR": "0", + "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_PKG_VERSION": "0.1.0", + "CARGO_PKG_AUTHORS": "", + "CARGO_CRATE_NAME": "hello_world", + "CARGO_PKG_LICENSE_FILE": "", + "CARGO_PKG_HOMEPAGE": "", + "CARGO_PKG_DESCRIPTION": "", + "CARGO_PKG_NAME": "hello-world", + "CARGO_PKG_VERSION_PATCH": "0", + "CARGO": "cargo", + "CARGO_PKG_REPOSITORY": "", + "CARGO_PKG_VERSION_MINOR": "1", + "CARGO_PKG_VERSION_PRE": "", + }, + }, + dependencies: [ + Dependency { + crate_id: Idx::(0), + name: CrateName( + "hello_world", + ), + prelude: true, + }, + Dependency { + crate_id: Idx::(4), + name: CrateName( + "libc", + ), + prelude: true, + }, + ], + origin: Local { + repo: None, + name: Some( + "hello-world", + ), + }, + is_proc_macro: false, + target_layout: Err( + "target_data_layout not loaded", + ), + channel: None, + }, + 2: CrateData { + root_file_id: FileId( + 3, + ), + edition: Edition2018, + version: Some( + "0.1.0", + ), + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "an_example", + ), + canonical_name: "an-example", + }, + ), + cfg_options: CfgOptions( + [ + "debug_assertions", + "test", + ], + ), + potential_cfg_options: None, + env: Env { + entries: { + "CARGO_PKG_LICENSE": "", + "CARGO_PKG_VERSION_MAJOR": "0", + "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_PKG_VERSION": "0.1.0", + "CARGO_PKG_AUTHORS": "", + "CARGO_CRATE_NAME": "hello_world", + "CARGO_PKG_LICENSE_FILE": "", + "CARGO_PKG_HOMEPAGE": "", + "CARGO_PKG_DESCRIPTION": "", + "CARGO_PKG_NAME": "hello-world", + "CARGO_PKG_VERSION_PATCH": "0", + "CARGO": "cargo", + "CARGO_PKG_REPOSITORY": "", + "CARGO_PKG_VERSION_MINOR": "1", + "CARGO_PKG_VERSION_PRE": "", + }, + }, + dependencies: [ + Dependency { + crate_id: Idx::(0), + name: CrateName( + "hello_world", + ), + prelude: true, + }, + Dependency { + crate_id: Idx::(4), + name: CrateName( + "libc", + ), + prelude: true, + }, + ], + origin: Local { + repo: None, + name: Some( + "hello-world", + ), + }, + is_proc_macro: false, + target_layout: Err( + "target_data_layout not loaded", + ), + channel: None, + }, + 3: CrateData { + root_file_id: FileId( + 4, + ), + edition: Edition2018, + version: Some( + "0.1.0", + ), + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "it", + ), + canonical_name: "it", + }, + ), + cfg_options: CfgOptions( + [ + "debug_assertions", + "test", + ], + ), + potential_cfg_options: None, + env: Env { + entries: { + "CARGO_PKG_LICENSE": "", + "CARGO_PKG_VERSION_MAJOR": "0", + "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_PKG_VERSION": "0.1.0", + "CARGO_PKG_AUTHORS": "", + "CARGO_CRATE_NAME": "hello_world", + "CARGO_PKG_LICENSE_FILE": "", + "CARGO_PKG_HOMEPAGE": "", + "CARGO_PKG_DESCRIPTION": "", + "CARGO_PKG_NAME": "hello-world", + "CARGO_PKG_VERSION_PATCH": "0", + "CARGO": "cargo", + "CARGO_PKG_REPOSITORY": "", + "CARGO_PKG_VERSION_MINOR": "1", + "CARGO_PKG_VERSION_PRE": "", + }, + }, + dependencies: [ + Dependency { + crate_id: Idx::(0), + name: CrateName( + "hello_world", + ), + prelude: true, + }, + Dependency { + crate_id: Idx::(4), + name: CrateName( + "libc", + ), + prelude: true, + }, + ], + origin: Local { + repo: None, + name: Some( + "hello-world", + ), + }, + is_proc_macro: false, + target_layout: Err( + "target_data_layout not loaded", + ), + channel: None, + }, + 4: CrateData { + root_file_id: FileId( + 5, + ), + edition: Edition2015, + version: Some( + "0.2.98", + ), + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "libc", + ), + canonical_name: "libc", + }, + ), + cfg_options: CfgOptions( + [ + "debug_assertions", + "feature=default", + "feature=std", + ], + ), + potential_cfg_options: Some( + CfgOptions( + [ + "debug_assertions", + "feature=align", + "feature=const-extern-fn", + "feature=default", + "feature=extra_traits", + "feature=rustc-dep-of-std", + "feature=std", + "feature=use_std", + ], + ), + ), + env: Env { + entries: { + "CARGO_PKG_LICENSE": "", + "CARGO_PKG_VERSION_MAJOR": "0", + "CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98", + "CARGO_PKG_VERSION": "0.2.98", + "CARGO_PKG_AUTHORS": "", + "CARGO_CRATE_NAME": "libc", + "CARGO_PKG_LICENSE_FILE": "", + "CARGO_PKG_HOMEPAGE": "", + "CARGO_PKG_DESCRIPTION": "", + "CARGO_PKG_NAME": "libc", + "CARGO_PKG_VERSION_PATCH": "98", + "CARGO": "cargo", + "CARGO_PKG_REPOSITORY": "", + "CARGO_PKG_VERSION_MINOR": "2", + "CARGO_PKG_VERSION_PRE": "", + }, + }, + dependencies: [], + origin: Library { + repo: Some( + "https://github.com/rust-lang/libc", + ), + name: "libc", + }, + is_proc_macro: false, + target_layout: Err( + "target_data_layout not loaded", + ), + channel: None, + }, +} \ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt new file mode 100644 index 0000000000000..f10c55d0462a2 --- /dev/null +++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt @@ -0,0 +1,340 @@ +{ + 0: CrateData { + root_file_id: FileId( + 1, + ), + edition: Edition2018, + version: Some( + "0.1.0", + ), + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "hello_world", + ), + canonical_name: "hello-world", + }, + ), + cfg_options: CfgOptions( + [ + "debug_assertions", + ], + ), + potential_cfg_options: None, + env: Env { + entries: { + "CARGO_PKG_LICENSE": "", + "CARGO_PKG_VERSION_MAJOR": "0", + "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_PKG_VERSION": "0.1.0", + "CARGO_PKG_AUTHORS": "", + "CARGO_CRATE_NAME": "hello_world", + "CARGO_PKG_LICENSE_FILE": "", + "CARGO_PKG_HOMEPAGE": "", + "CARGO_PKG_DESCRIPTION": "", + "CARGO_PKG_NAME": "hello-world", + "CARGO_PKG_VERSION_PATCH": "0", + "CARGO": "cargo", + "CARGO_PKG_REPOSITORY": "", + "CARGO_PKG_VERSION_MINOR": "1", + "CARGO_PKG_VERSION_PRE": "", + }, + }, + dependencies: [ + Dependency { + crate_id: Idx::(4), + name: CrateName( + "libc", + ), + prelude: true, + }, + ], + origin: Local { + repo: None, + name: Some( + "hello-world", + ), + }, + is_proc_macro: false, + target_layout: Err( + "target_data_layout not loaded", + ), + channel: None, + }, + 1: CrateData { + root_file_id: FileId( + 2, + ), + edition: Edition2018, + version: Some( + "0.1.0", + ), + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "hello_world", + ), + canonical_name: "hello-world", + }, + ), + cfg_options: CfgOptions( + [ + "debug_assertions", + ], + ), + potential_cfg_options: None, + env: Env { + entries: { + "CARGO_PKG_LICENSE": "", + "CARGO_PKG_VERSION_MAJOR": "0", + "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_PKG_VERSION": "0.1.0", + "CARGO_PKG_AUTHORS": "", + "CARGO_CRATE_NAME": "hello_world", + "CARGO_PKG_LICENSE_FILE": "", + "CARGO_PKG_HOMEPAGE": "", + "CARGO_PKG_DESCRIPTION": "", + "CARGO_PKG_NAME": "hello-world", + "CARGO_PKG_VERSION_PATCH": "0", + "CARGO": "cargo", + "CARGO_PKG_REPOSITORY": "", + "CARGO_PKG_VERSION_MINOR": "1", + "CARGO_PKG_VERSION_PRE": "", + }, + }, + dependencies: [ + Dependency { + crate_id: Idx::(0), + name: CrateName( + "hello_world", + ), + prelude: true, + }, + Dependency { + crate_id: Idx::(4), + name: CrateName( + "libc", + ), + prelude: true, + }, + ], + origin: Local { + repo: None, + name: Some( + "hello-world", + ), + }, + is_proc_macro: false, + target_layout: Err( + "target_data_layout not loaded", + ), + channel: None, + }, + 2: CrateData { + root_file_id: FileId( + 3, + ), + edition: Edition2018, + version: Some( + "0.1.0", + ), + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "an_example", + ), + canonical_name: "an-example", + }, + ), + cfg_options: CfgOptions( + [ + "debug_assertions", + ], + ), + potential_cfg_options: None, + env: Env { + entries: { + "CARGO_PKG_LICENSE": "", + "CARGO_PKG_VERSION_MAJOR": "0", + "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_PKG_VERSION": "0.1.0", + "CARGO_PKG_AUTHORS": "", + "CARGO_CRATE_NAME": "hello_world", + "CARGO_PKG_LICENSE_FILE": "", + "CARGO_PKG_HOMEPAGE": "", + "CARGO_PKG_DESCRIPTION": "", + "CARGO_PKG_NAME": "hello-world", + "CARGO_PKG_VERSION_PATCH": "0", + "CARGO": "cargo", + "CARGO_PKG_REPOSITORY": "", + "CARGO_PKG_VERSION_MINOR": "1", + "CARGO_PKG_VERSION_PRE": "", + }, + }, + dependencies: [ + Dependency { + crate_id: Idx::(0), + name: CrateName( + "hello_world", + ), + prelude: true, + }, + Dependency { + crate_id: Idx::(4), + name: CrateName( + "libc", + ), + prelude: true, + }, + ], + origin: Local { + repo: None, + name: Some( + "hello-world", + ), + }, + is_proc_macro: false, + target_layout: Err( + "target_data_layout not loaded", + ), + channel: None, + }, + 3: CrateData { + root_file_id: FileId( + 4, + ), + edition: Edition2018, + version: Some( + "0.1.0", + ), + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "it", + ), + canonical_name: "it", + }, + ), + cfg_options: CfgOptions( + [ + "debug_assertions", + ], + ), + potential_cfg_options: None, + env: Env { + entries: { + "CARGO_PKG_LICENSE": "", + "CARGO_PKG_VERSION_MAJOR": "0", + "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_PKG_VERSION": "0.1.0", + "CARGO_PKG_AUTHORS": "", + "CARGO_CRATE_NAME": "hello_world", + "CARGO_PKG_LICENSE_FILE": "", + "CARGO_PKG_HOMEPAGE": "", + "CARGO_PKG_DESCRIPTION": "", + "CARGO_PKG_NAME": "hello-world", + "CARGO_PKG_VERSION_PATCH": "0", + "CARGO": "cargo", + "CARGO_PKG_REPOSITORY": "", + "CARGO_PKG_VERSION_MINOR": "1", + "CARGO_PKG_VERSION_PRE": "", + }, + }, + dependencies: [ + Dependency { + crate_id: Idx::(0), + name: CrateName( + "hello_world", + ), + prelude: true, + }, + Dependency { + crate_id: Idx::(4), + name: CrateName( + "libc", + ), + prelude: true, + }, + ], + origin: Local { + repo: None, + name: Some( + "hello-world", + ), + }, + is_proc_macro: false, + target_layout: Err( + "target_data_layout not loaded", + ), + channel: None, + }, + 4: CrateData { + root_file_id: FileId( + 5, + ), + edition: Edition2015, + version: Some( + "0.2.98", + ), + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "libc", + ), + canonical_name: "libc", + }, + ), + cfg_options: CfgOptions( + [ + "debug_assertions", + "feature=default", + "feature=std", + ], + ), + potential_cfg_options: Some( + CfgOptions( + [ + "debug_assertions", + "feature=align", + "feature=const-extern-fn", + "feature=default", + "feature=extra_traits", + "feature=rustc-dep-of-std", + "feature=std", + "feature=use_std", + ], + ), + ), + env: Env { + entries: { + "CARGO_PKG_LICENSE": "", + "CARGO_PKG_VERSION_MAJOR": "0", + "CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98", + "CARGO_PKG_VERSION": "0.2.98", + "CARGO_PKG_AUTHORS": "", + "CARGO_CRATE_NAME": "libc", + "CARGO_PKG_LICENSE_FILE": "", + "CARGO_PKG_HOMEPAGE": "", + "CARGO_PKG_DESCRIPTION": "", + "CARGO_PKG_NAME": "libc", + "CARGO_PKG_VERSION_PATCH": "98", + "CARGO": "cargo", + "CARGO_PKG_REPOSITORY": "", + "CARGO_PKG_VERSION_MINOR": "2", + "CARGO_PKG_VERSION_PRE": "", + }, + }, + dependencies: [], + origin: Library { + repo: Some( + "https://github.com/rust-lang/libc", + ), + name: "libc", + }, + is_proc_macro: false, + target_layout: Err( + "target_data_layout not loaded", + ), + channel: None, + }, +} \ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt new file mode 100644 index 0000000000000..fb3f5933b178c --- /dev/null +++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt @@ -0,0 +1,462 @@ +{ + 0: CrateData { + root_file_id: FileId( + 1, + ), + edition: Edition2021, + version: None, + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "alloc", + ), + canonical_name: "alloc", + }, + ), + cfg_options: CfgOptions( + [], + ), + potential_cfg_options: None, + env: Env { + entries: {}, + }, + dependencies: [ + Dependency { + crate_id: Idx::(1), + name: CrateName( + "core", + ), + prelude: true, + }, + ], + origin: Lang( + Alloc, + ), + is_proc_macro: false, + target_layout: Err( + "rust-project.json projects have no target layout set", + ), + channel: None, + }, + 1: CrateData { + root_file_id: FileId( + 2, + ), + edition: Edition2021, + version: None, + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "core", + ), + canonical_name: "core", + }, + ), + cfg_options: CfgOptions( + [], + ), + potential_cfg_options: None, + env: Env { + entries: {}, + }, + dependencies: [], + origin: Lang( + Core, + ), + is_proc_macro: false, + target_layout: Err( + "rust-project.json projects have no target layout set", + ), + channel: None, + }, + 2: CrateData { + root_file_id: FileId( + 3, + ), + edition: Edition2021, + version: None, + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "panic_abort", + ), + canonical_name: "panic_abort", + }, + ), + cfg_options: CfgOptions( + [], + ), + potential_cfg_options: None, + env: Env { + entries: {}, + }, + dependencies: [], + origin: Lang( + Other, + ), + is_proc_macro: false, + target_layout: Err( + "rust-project.json projects have no target layout set", + ), + channel: None, + }, + 3: CrateData { + root_file_id: FileId( + 4, + ), + edition: Edition2021, + version: None, + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "panic_unwind", + ), + canonical_name: "panic_unwind", + }, + ), + cfg_options: CfgOptions( + [], + ), + potential_cfg_options: None, + env: Env { + entries: {}, + }, + dependencies: [], + origin: Lang( + Other, + ), + is_proc_macro: false, + target_layout: Err( + "rust-project.json projects have no target layout set", + ), + channel: None, + }, + 4: CrateData { + root_file_id: FileId( + 5, + ), + edition: Edition2021, + version: None, + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "proc_macro", + ), + canonical_name: "proc_macro", + }, + ), + cfg_options: CfgOptions( + [], + ), + potential_cfg_options: None, + env: Env { + entries: {}, + }, + dependencies: [ + Dependency { + crate_id: Idx::(6), + name: CrateName( + "std", + ), + prelude: true, + }, + Dependency { + crate_id: Idx::(1), + name: CrateName( + "core", + ), + prelude: true, + }, + ], + origin: Lang( + Other, + ), + is_proc_macro: false, + target_layout: Err( + "rust-project.json projects have no target layout set", + ), + channel: None, + }, + 5: CrateData { + root_file_id: FileId( + 6, + ), + edition: Edition2021, + version: None, + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "profiler_builtins", + ), + canonical_name: "profiler_builtins", + }, + ), + cfg_options: CfgOptions( + [], + ), + potential_cfg_options: None, + env: Env { + entries: {}, + }, + dependencies: [], + origin: Lang( + Other, + ), + is_proc_macro: false, + target_layout: Err( + "rust-project.json projects have no target layout set", + ), + channel: None, + }, + 6: CrateData { + root_file_id: FileId( + 7, + ), + edition: Edition2021, + version: None, + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "std", + ), + canonical_name: "std", + }, + ), + cfg_options: CfgOptions( + [], + ), + potential_cfg_options: None, + env: Env { + entries: {}, + }, + dependencies: [ + Dependency { + crate_id: Idx::(0), + name: CrateName( + "alloc", + ), + prelude: true, + }, + Dependency { + crate_id: Idx::(3), + name: CrateName( + "panic_unwind", + ), + prelude: true, + }, + Dependency { + crate_id: Idx::(2), + name: CrateName( + "panic_abort", + ), + prelude: true, + }, + Dependency { + crate_id: Idx::(1), + name: CrateName( + "core", + ), + prelude: true, + }, + Dependency { + crate_id: Idx::(5), + name: CrateName( + "profiler_builtins", + ), + prelude: true, + }, + Dependency { + crate_id: Idx::(9), + name: CrateName( + "unwind", + ), + prelude: true, + }, + Dependency { + crate_id: Idx::(7), + name: CrateName( + "std_detect", + ), + prelude: true, + }, + Dependency { + crate_id: Idx::(8), + name: CrateName( + "test", + ), + prelude: true, + }, + ], + origin: Lang( + Std, + ), + is_proc_macro: false, + target_layout: Err( + "rust-project.json projects have no target layout set", + ), + channel: None, + }, + 7: CrateData { + root_file_id: FileId( + 8, + ), + edition: Edition2021, + version: None, + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "std_detect", + ), + canonical_name: "std_detect", + }, + ), + cfg_options: CfgOptions( + [], + ), + potential_cfg_options: None, + env: Env { + entries: {}, + }, + dependencies: [], + origin: Lang( + Other, + ), + is_proc_macro: false, + target_layout: Err( + "rust-project.json projects have no target layout set", + ), + channel: None, + }, + 8: CrateData { + root_file_id: FileId( + 9, + ), + edition: Edition2021, + version: None, + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "test", + ), + canonical_name: "test", + }, + ), + cfg_options: CfgOptions( + [], + ), + potential_cfg_options: None, + env: Env { + entries: {}, + }, + dependencies: [], + origin: Lang( + Test, + ), + is_proc_macro: false, + target_layout: Err( + "rust-project.json projects have no target layout set", + ), + channel: None, + }, + 9: CrateData { + root_file_id: FileId( + 10, + ), + edition: Edition2021, + version: None, + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "unwind", + ), + canonical_name: "unwind", + }, + ), + cfg_options: CfgOptions( + [], + ), + potential_cfg_options: None, + env: Env { + entries: {}, + }, + dependencies: [], + origin: Lang( + Other, + ), + is_proc_macro: false, + target_layout: Err( + "rust-project.json projects have no target layout set", + ), + channel: None, + }, + 10: CrateData { + root_file_id: FileId( + 11, + ), + edition: Edition2018, + version: None, + display_name: Some( + CrateDisplayName { + crate_name: CrateName( + "hello_world", + ), + canonical_name: "hello_world", + }, + ), + cfg_options: CfgOptions( + [], + ), + potential_cfg_options: None, + env: Env { + entries: {}, + }, + dependencies: [ + Dependency { + crate_id: Idx::(1), + name: CrateName( + "core", + ), + prelude: true, + }, + Dependency { + crate_id: Idx::(0), + name: CrateName( + "alloc", + ), + prelude: true, + }, + Dependency { + crate_id: Idx::(6), + name: CrateName( + "std", + ), + prelude: true, + }, + Dependency { + crate_id: Idx::(8), + name: CrateName( + "test", + ), + prelude: false, + }, + Dependency { + crate_id: Idx::(4), + name: CrateName( + "proc_macro", + ), + prelude: false, + }, + ], + origin: Local { + repo: None, + name: Some( + "hello_world", + ), + }, + is_proc_macro: false, + target_layout: Err( + "rust-project.json projects have no target layout set", + ), + channel: None, + }, +} \ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/regex-metadata.json b/src/tools/rust-analyzer/crates/project-model/test_data/regex-metadata.json new file mode 100644 index 0000000000000..371464dd20aa1 --- /dev/null +++ b/src/tools/rust-analyzer/crates/project-model/test_data/regex-metadata.json @@ -0,0 +1,6420 @@ +{ + "packages": [ + { + "name": "aho-corasick", + "version": "0.7.20", + "id": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Unlicense OR MIT", + "license_file": null, + "description": "Fast multiple substring searching.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "memchr", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.4.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "aho_corasick", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/aho-corasick-0.7.20/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": { + "default": [ + "std" + ], + "std": [ + "memchr/std" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/aho-corasick-0.7.20/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant " + ], + "categories": [ + "text-processing" + ], + "keywords": [ + "string", + "search", + "text", + "aho", + "multi" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/aho-corasick", + "homepage": "https://github.com/BurntSushi/aho-corasick", + "documentation": null, + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "cc", + "version": "1.0.79", + "id": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A build-time dependency for Cargo build scripts to assist in invoking the native\nC compiler to compile native C code into a static archive to be linked into Rust\ncode.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "jobserver", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.16", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "tempfile", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "cc", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "bin" + ], + "crate_types": [ + "bin" + ], + "name": "gcc-shim", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/src/bin/gcc-shim.rs", + "edition": "2018", + "doc": true, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "cc_env", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/cc_env.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "cflags", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/cflags.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "cxxflags", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/cxxflags.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/test.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": { + "jobserver": [ + "dep:jobserver" + ], + "parallel": [ + "jobserver" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Alex Crichton " + ], + "categories": [ + "development-tools::build-utils" + ], + "keywords": [ + "build-dependencies" + ], + "readme": "README.md", + "repository": "https://github.com/rust-lang/cc-rs", + "homepage": "https://github.com/rust-lang/cc-rs", + "documentation": "https://docs.rs/cc", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "cfg-if", + "version": "0.1.10", + "id": "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "A macro to ergonomically define an item depending on a large number of #[cfg]\nparameters. Structured like an if-else chain, the first matching branch is the\nitem that gets emitted.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "compiler_builtins", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.2", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustc-std-workspace-core", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.0", + "kind": null, + "rename": "core", + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "cfg-if", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-0.1.10/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "xcrate", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-0.1.10/tests/xcrate.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": { + "compiler_builtins": [ + "dep:compiler_builtins" + ], + "core": [ + "dep:core" + ], + "rustc-dep-of-std": [ + "core", + "compiler_builtins" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-0.1.10/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Alex Crichton " + ], + "categories": [], + "keywords": [], + "readme": "README.md", + "repository": "https://github.com/alexcrichton/cfg-if", + "homepage": "https://github.com/alexcrichton/cfg-if", + "documentation": "https://docs.rs/cfg-if", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "cfg-if", + "version": "1.0.0", + "id": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "A macro to ergonomically define an item depending on a large number of #[cfg]\nparameters. Structured like an if-else chain, the first matching branch is the\nitem that gets emitted.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "compiler_builtins", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.2", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustc-std-workspace-core", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.0", + "kind": null, + "rename": "core", + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "cfg-if", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-1.0.0/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "xcrate", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-1.0.0/tests/xcrate.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": { + "compiler_builtins": [ + "dep:compiler_builtins" + ], + "core": [ + "dep:core" + ], + "rustc-dep-of-std": [ + "core", + "compiler_builtins" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-1.0.0/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Alex Crichton " + ], + "categories": [], + "keywords": [], + "readme": "README.md", + "repository": "https://github.com/alexcrichton/cfg-if", + "homepage": "https://github.com/alexcrichton/cfg-if", + "documentation": "https://docs.rs/cfg-if", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "docopt", + "version": "1.1.1", + "id": "docopt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Unlicense/MIT", + "license_file": null, + "description": "Command line argument parsing.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "lazy_static", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.3", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.4.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [ + "std", + "unicode" + ], + "target": null, + "registry": null + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "derive" + ], + "target": null, + "registry": null + }, + { + "name": "strsim", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.10", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "docopt", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "bin" + ], + "crate_types": [ + "bin" + ], + "name": "docopt-wordlist", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/src/wordlist.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "cargo", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/examples/cargo.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "cp", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/examples/cp.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "decode", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/examples/decode.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "hashmap", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/examples/hashmap.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "optional_command", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/examples/optional_command.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "verbose_multiple", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/examples/verbose_multiple.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant " + ], + "categories": [ + "command-line-interface" + ], + "keywords": [ + "docopt", + "argument", + "command", + "argv" + ], + "readme": "README.md", + "repository": "https://github.com/docopt/docopt.rs", + "homepage": "https://github.com/docopt/docopt.rs", + "documentation": "http://burntsushi.net/rustdoc/docopt/", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "getrandom", + "version": "0.2.9", + "id": "getrandom 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A small cross-platform library for retrieving random data from system source", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "cfg-if", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "compiler_builtins", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustc-std-workspace-core", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": "core", + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "js-sys", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": "cfg(all(any(target_arch = \"wasm32\", target_arch = \"wasm64\"), target_os = \"unknown\"))", + "registry": null + }, + { + "name": "wasm-bindgen", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2.62", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": false, + "features": [], + "target": "cfg(all(any(target_arch = \"wasm32\", target_arch = \"wasm64\"), target_os = \"unknown\"))", + "registry": null + }, + { + "name": "wasm-bindgen-test", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.18", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": "cfg(all(any(target_arch = \"wasm32\", target_arch = \"wasm64\"), target_os = \"unknown\"))", + "registry": null + }, + { + "name": "wasi", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.11", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": "cfg(target_os = \"wasi\")", + "registry": null + }, + { + "name": "libc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2.139", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": "cfg(unix)", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "getrandom", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.9/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "custom", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.9/tests/custom.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "normal", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.9/tests/normal.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "rdrand", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.9/tests/rdrand.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "buffer", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.9/benches/buffer.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "compiler_builtins": [ + "dep:compiler_builtins" + ], + "core": [ + "dep:core" + ], + "custom": [], + "js": [ + "wasm-bindgen", + "js-sys" + ], + "js-sys": [ + "dep:js-sys" + ], + "rdrand": [], + "rustc-dep-of-std": [ + "compiler_builtins", + "core", + "libc/rustc-dep-of-std", + "wasi/rustc-dep-of-std" + ], + "std": [], + "test-in-browser": [], + "wasm-bindgen": [ + "dep:wasm-bindgen" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.9/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "features": [ + "std", + "custom" + ], + "rustdoc-args": [ + "--cfg", + "docsrs" + ] + } + } + }, + "publish": null, + "authors": [ + "The Rand Project Developers" + ], + "categories": [ + "os", + "no-std" + ], + "keywords": [], + "readme": "README.md", + "repository": "https://github.com/rust-random/getrandom", + "homepage": null, + "documentation": "https://docs.rs/getrandom", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "lazy_static", + "version": "1.4.0", + "id": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "A macro for declaring lazily evaluated statics in Rust.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "spin", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.5.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "doc-comment", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "lazy_static", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "no_std", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/tests/no_std.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/tests/test.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": { + "spin": [ + "dep:spin" + ], + "spin_no_std": [ + "spin" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Marvin Löbel " + ], + "categories": [ + "no-std", + "rust-patterns", + "memory-management" + ], + "keywords": [ + "macro", + "lazy", + "static" + ], + "readme": "README.md", + "repository": "https://github.com/rust-lang-nursery/lazy-static.rs", + "homepage": null, + "documentation": "https://docs.rs/lazy_static", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "libc", + "version": "0.2.142", + "id": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Raw FFI bindings to platform libraries like libc.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "rustc-std-workspace-core", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "libc", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "const_fn", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/tests/const_fn.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/build.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "align": [], + "const-extern-fn": [], + "default": [ + "std" + ], + "extra_traits": [], + "rustc-dep-of-std": [ + "align", + "rustc-std-workspace-core" + ], + "rustc-std-workspace-core": [ + "dep:rustc-std-workspace-core" + ], + "std": [], + "use_std": [ + "std" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "features": [ + "const-extern-fn", + "extra_traits" + ] + } + } + }, + "publish": null, + "authors": [ + "The Rust Project Developers" + ], + "categories": [ + "external-ffi-bindings", + "no-std", + "os" + ], + "keywords": [ + "libc", + "ffi", + "bindings", + "operating", + "system" + ], + "readme": "README.md", + "repository": "https://github.com/rust-lang/libc", + "homepage": "https://github.com/rust-lang/libc", + "documentation": "https://docs.rs/libc/", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "memchr", + "version": "2.5.0", + "id": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Unlicense/MIT", + "license_file": null, + "description": "Safe interface to memchr.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "compiler_builtins", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.2", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustc-std-workspace-core", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.0", + "kind": null, + "rename": "core", + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "libc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2.18", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "quickcheck", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "memchr", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memchr-2.5.0/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memchr-2.5.0/build.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "compiler_builtins": [ + "dep:compiler_builtins" + ], + "core": [ + "dep:core" + ], + "default": [ + "std" + ], + "libc": [ + "dep:libc" + ], + "rustc-dep-of-std": [ + "core", + "compiler_builtins" + ], + "std": [], + "use_std": [ + "std" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memchr-2.5.0/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant ", + "bluss" + ], + "categories": [], + "keywords": [ + "memchr", + "char", + "scan", + "strchr", + "string" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/memchr", + "homepage": "https://github.com/BurntSushi/memchr", + "documentation": "https://docs.rs/memchr/", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "memmap", + "version": "0.6.2", + "id": "memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "Cross-platform Rust API for memory-mapped file IO", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "tempdir", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "libc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": "cfg(unix)", + "registry": null + }, + { + "name": "winapi", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "basetsd", + "handleapi", + "memoryapi", + "minwindef", + "std", + "sysinfoapi" + ], + "target": "cfg(windows)", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "memmap", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memmap-0.6.2/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "cat", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memmap-0.6.2/examples/cat.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memmap-0.6.2/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Dan Burkert " + ], + "categories": [], + "keywords": [ + "mmap", + "memory-map", + "io", + "file" + ], + "readme": "README.md", + "repository": "https://github.com/danburkert/memmap-rs", + "homepage": null, + "documentation": "https://docs.rs/memmap", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "pkg-config", + "version": "0.3.26", + "id": "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A library to run the pkg-config system tool at build time in order to be used in\nCargo build scripts.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "lazy_static", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "pkg-config", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pkg-config-0.3.26/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pkg-config-0.3.26/tests/test.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pkg-config-0.3.26/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Alex Crichton " + ], + "categories": [], + "keywords": [ + "build-dependencies" + ], + "readme": "README.md", + "repository": "https://github.com/rust-lang/pkg-config-rs", + "homepage": null, + "documentation": "https://docs.rs/pkg-config", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "proc-macro2", + "version": "1.0.56", + "id": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A substitute implementation of the compiler's `proc_macro` API to decouple token-based libraries from the procedural macro use case.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "unicode-ident", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "quote", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustversion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "proc-macro2", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "comments", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/comments.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "features", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/features.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "marker", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/marker.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/test.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_fmt", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/test_fmt.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_size", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/test_size.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/build.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "default": [ + "proc-macro" + ], + "nightly": [], + "proc-macro": [], + "span-locations": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "rustc-args": [ + "--cfg", + "procmacro2_semver_exempt" + ], + "rustdoc-args": [ + "--cfg", + "procmacro2_semver_exempt", + "--cfg", + "doc_cfg" + ], + "targets": [ + "x86_64-unknown-linux-gnu" + ] + } + }, + "playground": { + "features": [ + "span-locations" + ] + } + }, + "publish": null, + "authors": [ + "David Tolnay ", + "Alex Crichton " + ], + "categories": [ + "development-tools::procedural-macro-helpers" + ], + "keywords": [ + "macros", + "syn" + ], + "readme": "README.md", + "repository": "https://github.com/dtolnay/proc-macro2", + "homepage": null, + "documentation": "https://docs.rs/proc-macro2", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": "1.31" + }, + { + "name": "quickcheck", + "version": "1.0.3", + "id": "quickcheck 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Unlicense/MIT", + "license_file": null, + "description": "Automatic property based testing with shrinking.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "env_logger", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8.2", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "log", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rand", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [ + "getrandom", + "small_rng" + ], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "quickcheck", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "btree_set_range", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/examples/btree_set_range.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "out_of_bounds", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/examples/out_of_bounds.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "reverse", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/examples/reverse.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "reverse_single", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/examples/reverse_single.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "sieve", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/examples/sieve.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "sort", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/examples/sort.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "default": [ + "regex", + "use_logging" + ], + "env_logger": [ + "dep:env_logger" + ], + "log": [ + "dep:log" + ], + "regex": [ + "env_logger/regex" + ], + "use_logging": [ + "log", + "env_logger" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant " + ], + "categories": [ + "development-tools::testing" + ], + "keywords": [ + "testing", + "quickcheck", + "property", + "shrinking", + "fuzz" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/quickcheck", + "homepage": "https://github.com/BurntSushi/quickcheck", + "documentation": "https://docs.rs/quickcheck", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "quote", + "version": "1.0.26", + "id": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Quasi-quoting macro quote!(...)", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "proc-macro2", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.52", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustversion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "trybuild", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.66", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "diff" + ], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "quote", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "compiletest", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/tests/compiletest.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/tests/test.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/build.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "default": [ + "proc-macro" + ], + "proc-macro": [ + "proc-macro2/proc-macro" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "targets": [ + "x86_64-unknown-linux-gnu" + ] + } + } + }, + "publish": null, + "authors": [ + "David Tolnay " + ], + "categories": [ + "development-tools::procedural-macro-helpers" + ], + "keywords": [ + "macros", + "syn" + ], + "readme": "README.md", + "repository": "https://github.com/dtolnay/quote", + "homepage": null, + "documentation": "https://docs.rs/quote/", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": "1.31" + }, + { + "name": "rand", + "version": "0.8.5", + "id": "rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Random number generators and other randomness functionality.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "log", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4.4", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "packed_simd_2", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.7", + "kind": null, + "rename": "packed_simd", + "optional": true, + "uses_default_features": true, + "features": [ + "into_bits" + ], + "target": null, + "registry": null + }, + { + "name": "rand_chacha", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rand_core", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.6.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.103", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [ + "derive" + ], + "target": null, + "registry": null + }, + { + "name": "bincode", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.2.1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rand_pcg", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "libc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2.22", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": false, + "features": [], + "target": "cfg(unix)", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "rand", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/rand-0.8.5/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": { + "alloc": [ + "rand_core/alloc" + ], + "default": [ + "std", + "std_rng" + ], + "getrandom": [ + "rand_core/getrandom" + ], + "libc": [ + "dep:libc" + ], + "log": [ + "dep:log" + ], + "min_const_gen": [], + "nightly": [], + "packed_simd": [ + "dep:packed_simd" + ], + "rand_chacha": [ + "dep:rand_chacha" + ], + "serde": [ + "dep:serde" + ], + "serde1": [ + "serde", + "rand_core/serde1" + ], + "simd_support": [ + "packed_simd" + ], + "small_rng": [], + "std": [ + "rand_core/std", + "rand_chacha/std", + "alloc", + "getrandom", + "libc" + ], + "std_rng": [ + "rand_chacha" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/rand-0.8.5/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "all-features": true, + "rustdoc-args": [ + "--cfg", + "doc_cfg" + ] + } + }, + "playground": { + "features": [ + "small_rng", + "serde1" + ] + } + }, + "publish": null, + "authors": [ + "The Rand Project Developers", + "The Rust Project Developers" + ], + "categories": [ + "algorithms", + "no-std" + ], + "keywords": [ + "random", + "rng" + ], + "readme": "README.md", + "repository": "https://github.com/rust-random/rand", + "homepage": "https://rust-random.github.io/book", + "documentation": "https://docs.rs/rand", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "rand_core", + "version": "0.6.4", + "id": "rand_core 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Core random number generator traits and tools for implementation.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "getrandom", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [ + "derive" + ], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "rand_core", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/rand_core-0.6.4/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": { + "alloc": [], + "getrandom": [ + "dep:getrandom" + ], + "serde": [ + "dep:serde" + ], + "serde1": [ + "serde" + ], + "std": [ + "alloc", + "getrandom", + "getrandom/std" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/rand_core-0.6.4/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "all-features": true, + "rustdoc-args": [ + "--cfg", + "doc_cfg" + ] + } + }, + "playground": { + "all-features": true + } + }, + "publish": null, + "authors": [ + "The Rand Project Developers", + "The Rust Project Developers" + ], + "categories": [ + "algorithms", + "no-std" + ], + "keywords": [ + "random", + "rng" + ], + "readme": "README.md", + "repository": "https://github.com/rust-random/rand", + "homepage": "https://rust-random.github.io/book", + "documentation": "https://docs.rs/rand_core", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "regex", + "version": "1.7.1", + "id": "regex 1.7.1 (path+file:///$ROOT$regex)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "An implementation of regular expressions for Rust. This implementation uses\nfinite automata and guarantees linear time matching on all inputs.\n", + "source": null, + "dependencies": [ + { + "name": "aho-corasick", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.7.18", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "memchr", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.4.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex-syntax", + "source": null, + "req": "^0.6.27", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$regex/regex-syntax" + }, + { + "name": "lazy_static", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "quickcheck", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rand", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [ + "getrandom", + "small_rng" + ], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "regex", + "src_path": "$ROOT$regex/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": false, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna-bytes", + "src_path": "$ROOT$regex/examples/shootout-regex-dna-bytes.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna-cheat", + "src_path": "$ROOT$regex/examples/shootout-regex-dna-cheat.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna-replace", + "src_path": "$ROOT$regex/examples/shootout-regex-dna-replace.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna-single-cheat", + "src_path": "$ROOT$regex/examples/shootout-regex-dna-single-cheat.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna-single", + "src_path": "$ROOT$regex/examples/shootout-regex-dna-single.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna", + "src_path": "$ROOT$regex/examples/shootout-regex-dna.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "default", + "src_path": "$ROOT$regex/tests/test_default.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "default-bytes", + "src_path": "$ROOT$regex/tests/test_default_bytes.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "nfa", + "src_path": "$ROOT$regex/tests/test_nfa.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "nfa-utf8bytes", + "src_path": "$ROOT$regex/tests/test_nfa_utf8bytes.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "nfa-bytes", + "src_path": "$ROOT$regex/tests/test_nfa_bytes.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "backtrack", + "src_path": "$ROOT$regex/tests/test_backtrack.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "backtrack-utf8bytes", + "src_path": "$ROOT$regex/tests/test_backtrack_utf8bytes.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "backtrack-bytes", + "src_path": "$ROOT$regex/tests/test_backtrack_bytes.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "crates-regex", + "src_path": "$ROOT$regex/tests/test_crates_regex.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": { + "aho-corasick": [ + "dep:aho-corasick" + ], + "default": [ + "std", + "perf", + "unicode", + "regex-syntax/default" + ], + "memchr": [ + "dep:memchr" + ], + "pattern": [], + "perf": [ + "perf-cache", + "perf-dfa", + "perf-inline", + "perf-literal" + ], + "perf-cache": [], + "perf-dfa": [], + "perf-inline": [], + "perf-literal": [ + "aho-corasick", + "memchr" + ], + "std": [], + "unicode": [ + "unicode-age", + "unicode-bool", + "unicode-case", + "unicode-gencat", + "unicode-perl", + "unicode-script", + "unicode-segment", + "regex-syntax/unicode" + ], + "unicode-age": [ + "regex-syntax/unicode-age" + ], + "unicode-bool": [ + "regex-syntax/unicode-bool" + ], + "unicode-case": [ + "regex-syntax/unicode-case" + ], + "unicode-gencat": [ + "regex-syntax/unicode-gencat" + ], + "unicode-perl": [ + "regex-syntax/unicode-perl" + ], + "unicode-script": [ + "regex-syntax/unicode-script" + ], + "unicode-segment": [ + "regex-syntax/unicode-segment" + ], + "unstable": [ + "pattern" + ], + "use_std": [ + "std" + ] + }, + "manifest_path": "$ROOT$regex/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "The Rust Project Developers" + ], + "categories": [ + "text-processing" + ], + "keywords": [], + "readme": "README.md", + "repository": "https://github.com/rust-lang/regex", + "homepage": "https://github.com/rust-lang/regex", + "documentation": "https://docs.rs/regex", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "regex", + "version": "1.8.1", + "id": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "An implementation of regular expressions for Rust. This implementation uses\nfinite automata and guarantees linear time matching on all inputs.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "aho-corasick", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "memchr", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.5.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex-syntax", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.7.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "lazy_static", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "quickcheck", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rand", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [ + "getrandom", + "small_rng" + ], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "regex", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/src/lib.rs", + "edition": "2021", + "doc": true, + "doctest": false, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna-bytes", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-bytes.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna-cheat", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-cheat.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna-replace", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-replace.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna-single-cheat", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-single-cheat.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna-single", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-single.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "default", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_default.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "default-bytes", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_default_bytes.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "nfa", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_nfa.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "nfa-utf8bytes", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_nfa_utf8bytes.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "nfa-bytes", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_nfa_bytes.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "backtrack", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_backtrack.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "backtrack-utf8bytes", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_backtrack_utf8bytes.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "backtrack-bytes", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_backtrack_bytes.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "crates-regex", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_crates_regex.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": { + "aho-corasick": [ + "dep:aho-corasick" + ], + "default": [ + "std", + "perf", + "unicode", + "regex-syntax/default" + ], + "memchr": [ + "dep:memchr" + ], + "pattern": [], + "perf": [ + "perf-cache", + "perf-dfa", + "perf-inline", + "perf-literal" + ], + "perf-cache": [], + "perf-dfa": [], + "perf-inline": [], + "perf-literal": [ + "aho-corasick", + "memchr" + ], + "std": [], + "unicode": [ + "unicode-age", + "unicode-bool", + "unicode-case", + "unicode-gencat", + "unicode-perl", + "unicode-script", + "unicode-segment", + "regex-syntax/unicode" + ], + "unicode-age": [ + "regex-syntax/unicode-age" + ], + "unicode-bool": [ + "regex-syntax/unicode-bool" + ], + "unicode-case": [ + "regex-syntax/unicode-case" + ], + "unicode-gencat": [ + "regex-syntax/unicode-gencat" + ], + "unicode-perl": [ + "regex-syntax/unicode-perl" + ], + "unicode-script": [ + "regex-syntax/unicode-script" + ], + "unicode-segment": [ + "regex-syntax/unicode-segment" + ], + "unstable": [ + "pattern" + ], + "use_std": [ + "std" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "The Rust Project Developers" + ], + "categories": [ + "text-processing" + ], + "keywords": [], + "readme": "README.md", + "repository": "https://github.com/rust-lang/regex", + "homepage": "https://github.com/rust-lang/regex", + "documentation": "https://docs.rs/regex", + "edition": "2021", + "links": null, + "default_run": null, + "rust_version": "1.60.0" + }, + { + "name": "regex-benchmark", + "version": "0.1.0", + "id": "regex-benchmark 0.1.0 (path+file:///$ROOT$regex/bench)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Regex benchmarks for Rust's and other engines.", + "source": null, + "dependencies": [ + { + "name": "cfg-if", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "docopt", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "lazy_static", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "libc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "libpcre-sys", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "memmap", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.6.2", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "onig", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^3", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex", + "source": null, + "req": "^1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$regex" + }, + { + "name": "regex-syntax", + "source": null, + "req": "^0.6", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$regex/regex-syntax" + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "derive" + ], + "target": null, + "registry": null + }, + { + "name": "cc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "build", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "pkg-config", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.9", + "kind": "build", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "bin" + ], + "crate_types": [ + "bin" + ], + "name": "regex-run-one", + "src_path": "$ROOT$regex/bench/src/main.rs", + "edition": "2018", + "doc": true, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "bench", + "src_path": "$ROOT$regex/bench/src/bench.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$regex/bench/build.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "libpcre-sys": [ + "dep:libpcre-sys" + ], + "onig": [ + "dep:onig" + ], + "re-onig": [ + "onig" + ], + "re-pcre1": [ + "libpcre-sys" + ], + "re-pcre2": [], + "re-re2": [], + "re-rust": [], + "re-rust-bytes": [], + "re-tcl": [] + }, + "manifest_path": "$ROOT$regex/bench/Cargo.toml", + "metadata": null, + "publish": [], + "authors": [ + "The Rust Project Developers" + ], + "categories": [], + "keywords": [], + "readme": null, + "repository": "https://github.com/rust-lang/regex", + "homepage": "https://github.com/rust-lang/regex", + "documentation": "https://docs.rs/regex", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "regex-debug", + "version": "0.1.0", + "id": "regex-debug 0.1.0 (path+file:///$ROOT$regex/regex-debug)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A tool useful for debugging regular expressions.", + "source": null, + "dependencies": [ + { + "name": "docopt", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex", + "source": null, + "req": "^1.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$regex" + }, + { + "name": "regex-syntax", + "source": null, + "req": "^0.6", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$regex/regex-syntax" + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "derive" + ], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "bin" + ], + "crate_types": [ + "bin" + ], + "name": "regex-debug", + "src_path": "$ROOT$regex/regex-debug/src/main.rs", + "edition": "2018", + "doc": true, + "doctest": false, + "test": true + } + ], + "features": {}, + "manifest_path": "$ROOT$regex/regex-debug/Cargo.toml", + "metadata": null, + "publish": [], + "authors": [ + "The Rust Project Developers" + ], + "categories": [], + "keywords": [], + "readme": null, + "repository": "https://github.com/rust-lang/regex", + "homepage": "https://github.com/rust-lang/regex", + "documentation": "https://docs.rs/regex", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "regex-syntax", + "version": "0.6.28", + "id": "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A regular expression parser.", + "source": null, + "dependencies": [], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "regex-syntax", + "src_path": "$ROOT$regex/regex-syntax/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "bench", + "src_path": "$ROOT$regex/regex-syntax/benches/bench.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "default": [ + "unicode" + ], + "unicode": [ + "unicode-age", + "unicode-bool", + "unicode-case", + "unicode-gencat", + "unicode-perl", + "unicode-script", + "unicode-segment" + ], + "unicode-age": [], + "unicode-bool": [], + "unicode-case": [], + "unicode-gencat": [], + "unicode-perl": [], + "unicode-script": [], + "unicode-segment": [] + }, + "manifest_path": "$ROOT$regex/regex-syntax/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "The Rust Project Developers" + ], + "categories": [], + "keywords": [], + "readme": "README.md", + "repository": "https://github.com/rust-lang/regex", + "homepage": "https://github.com/rust-lang/regex", + "documentation": "https://docs.rs/regex-syntax", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "regex-syntax", + "version": "0.7.1", + "id": "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A regular expression parser.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "regex-syntax", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.7.1/src/lib.rs", + "edition": "2021", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "bench", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.7.1/benches/bench.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "default": [ + "std", + "unicode" + ], + "std": [], + "unicode": [ + "unicode-age", + "unicode-bool", + "unicode-case", + "unicode-gencat", + "unicode-perl", + "unicode-script", + "unicode-segment" + ], + "unicode-age": [], + "unicode-bool": [], + "unicode-case": [], + "unicode-gencat": [], + "unicode-perl": [], + "unicode-script": [], + "unicode-segment": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.7.1/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "all-features": true, + "rustdoc-args": [ + "--cfg", + "docsrs" + ] + } + } + }, + "publish": null, + "authors": [ + "The Rust Project Developers" + ], + "categories": [], + "keywords": [], + "readme": "README.md", + "repository": "https://github.com/rust-lang/regex", + "homepage": "https://github.com/rust-lang/regex", + "documentation": "https://docs.rs/regex-syntax", + "edition": "2021", + "links": null, + "default_run": null, + "rust_version": "1.60.0" + }, + { + "name": "rure", + "version": "0.2.2", + "id": "rure 0.2.2 (path+file:///$ROOT$regex/regex-capi)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A C API for Rust's regular expression library.\n", + "source": null, + "dependencies": [ + { + "name": "libc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex", + "source": null, + "req": "^1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$regex" + } + ], + "targets": [ + { + "kind": [ + "staticlib", + "cdylib", + "rlib" + ], + "crate_types": [ + "staticlib", + "cdylib", + "rlib" + ], + "name": "rure", + "src_path": "$ROOT$regex/regex-capi/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": {}, + "manifest_path": "$ROOT$regex/regex-capi/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "The Rust Project Developers" + ], + "categories": [], + "keywords": [], + "readme": "README.md", + "repository": "https://github.com/rust-lang/regex", + "homepage": "https://github.com/rust-lang/regex", + "documentation": "https://github.com/rust-lang/regex/tree/master/regex-capi", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "serde", + "version": "1.0.160", + "id": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A generic serialization/deserialization framework", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "serde_derive", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "=1.0.160", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde_derive", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "serde", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.160/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.160/build.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "alloc": [], + "default": [ + "std" + ], + "derive": [ + "serde_derive" + ], + "rc": [], + "serde_derive": [ + "dep:serde_derive" + ], + "std": [], + "unstable": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.160/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "features": [ + "derive" + ], + "targets": [ + "x86_64-unknown-linux-gnu" + ] + } + }, + "playground": { + "features": [ + "derive", + "rc" + ] + } + }, + "publish": null, + "authors": [ + "Erick Tryzelaar ", + "David Tolnay " + ], + "categories": [ + "encoding", + "no-std" + ], + "keywords": [ + "serde", + "serialization", + "no_std" + ], + "readme": "crates-io.md", + "repository": "https://github.com/serde-rs/serde", + "homepage": "https://serde.rs", + "documentation": "https://docs.rs/serde", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": "1.19" + }, + { + "name": "serde_derive", + "version": "1.0.160", + "id": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Macros 1.1 implementation of #[derive(Serialize, Deserialize)]", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "proc-macro2", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "quote", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "syn", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.0.3", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "proc-macro" + ], + "crate_types": [ + "proc-macro" + ], + "name": "serde_derive", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_derive-1.0.160/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_derive-1.0.160/build.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "default": [], + "deserialize_in_place": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_derive-1.0.160/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "targets": [ + "x86_64-unknown-linux-gnu" + ] + } + } + }, + "publish": null, + "authors": [ + "Erick Tryzelaar ", + "David Tolnay " + ], + "categories": [ + "no-std" + ], + "keywords": [ + "serde", + "serialization", + "no_std", + "derive" + ], + "readme": "crates-io.md", + "repository": "https://github.com/serde-rs/serde", + "homepage": "https://serde.rs", + "documentation": "https://serde.rs/derive.html", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": "1.56" + }, + { + "name": "strsim", + "version": "0.10.0", + "id": "strsim 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT", + "license_file": null, + "description": "Implementations of string similarity metrics. Includes Hamming, Levenshtein,\nOSA, Damerau-Levenshtein, Jaro, Jaro-Winkler, and Sørensen-Dice.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "strsim", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.10.0/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "lib", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.10.0/tests/lib.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "benches", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.10.0/benches/benches.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.10.0/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Danny Guo " + ], + "categories": [], + "keywords": [ + "string", + "similarity", + "Hamming", + "Levenshtein", + "Jaro" + ], + "readme": "README.md", + "repository": "https://github.com/dguo/strsim-rs", + "homepage": "https://github.com/dguo/strsim-rs", + "documentation": "https://docs.rs/strsim/", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "syn", + "version": "2.0.15", + "id": "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Parser for Rust source code", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "proc-macro2", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.55", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "quote", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.25", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "unicode-ident", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "anyhow", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "automod", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "flate2", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "insta", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rayon", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "ref-cast", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "reqwest", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.11", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "blocking" + ], + "target": null, + "registry": null + }, + { + "name": "rustversion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "syn-test-suite", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "tar", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4.16", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "termcolor", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "walkdir", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.3.2", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "syn", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/src/lib.rs", + "edition": "2021", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "regression", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/regression.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_asyncness", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_asyncness.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_attribute", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_attribute.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_derive_input", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_derive_input.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_expr", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_expr.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_generics", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_generics.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_grouping", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_grouping.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_ident", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_ident.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_item", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_item.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_iterators", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_iterators.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_lit", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_lit.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_meta", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_meta.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_parse_buffer", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_parse_buffer.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_parse_stream", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_parse_stream.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_pat", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_pat.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_path", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_path.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_precedence", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_precedence.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_receiver", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_receiver.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_round_trip", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_round_trip.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_shebang", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_shebang.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_should_parse", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_should_parse.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_size", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_size.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_stmt", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_stmt.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_token_trees", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_token_trees.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_ty", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_ty.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_visibility", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_visibility.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "zzz_stable", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/zzz_stable.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "rust", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/benches/rust.rs", + "edition": "2021", + "required-features": [ + "full", + "parsing" + ], + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "file", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/benches/file.rs", + "edition": "2021", + "required-features": [ + "full", + "parsing" + ], + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "clone-impls": [], + "default": [ + "derive", + "parsing", + "printing", + "clone-impls", + "proc-macro" + ], + "derive": [], + "extra-traits": [], + "fold": [], + "full": [], + "parsing": [], + "printing": [ + "quote" + ], + "proc-macro": [ + "proc-macro2/proc-macro", + "quote/proc-macro" + ], + "quote": [ + "dep:quote" + ], + "test": [ + "syn-test-suite/all-features" + ], + "visit": [], + "visit-mut": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "all-features": true, + "rustdoc-args": [ + "--cfg", + "doc_cfg" + ], + "targets": [ + "x86_64-unknown-linux-gnu" + ] + } + }, + "playground": { + "features": [ + "full", + "visit", + "visit-mut", + "fold", + "extra-traits" + ] + } + }, + "publish": null, + "authors": [ + "David Tolnay " + ], + "categories": [ + "development-tools::procedural-macro-helpers", + "parser-implementations" + ], + "keywords": [ + "macros", + "syn" + ], + "readme": "README.md", + "repository": "https://github.com/dtolnay/syn", + "homepage": null, + "documentation": "https://docs.rs/syn", + "edition": "2021", + "links": null, + "default_run": null, + "rust_version": "1.56" + }, + { + "name": "unicode-ident", + "version": "1.0.8", + "id": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "(MIT OR Apache-2.0) AND Unicode-DFS-2016", + "license_file": null, + "description": "Determine whether characters have the XID_Start or XID_Continue properties according to Unicode Standard Annex #31", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "criterion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "fst", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rand", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "small_rng" + ], + "target": null, + "registry": null + }, + { + "name": "roaring", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.10", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "ucd-trie", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "unicode-xid", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2.4", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "unicode-ident", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "compare", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/tests/compare.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "static_size", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/tests/static_size.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "xid", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/benches/xid.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "targets": [ + "x86_64-unknown-linux-gnu" + ] + } + } + }, + "publish": null, + "authors": [ + "David Tolnay " + ], + "categories": [ + "development-tools::procedural-macro-helpers", + "no-std" + ], + "keywords": [ + "unicode", + "xid" + ], + "readme": "README.md", + "repository": "https://github.com/dtolnay/unicode-ident", + "homepage": null, + "documentation": "https://docs.rs/unicode-ident", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": "1.31" + }, + { + "name": "wasi", + "version": "0.11.0+wasi-snapshot-preview1", + "id": "wasi 0.11.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", + "license_file": null, + "description": "Experimental WASI API bindings for Rust", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "compiler_builtins", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustc-std-workspace-core", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": "core", + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustc-std-workspace-alloc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "wasi", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/wasi-0.11.0+wasi-snapshot-preview1/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": { + "compiler_builtins": [ + "dep:compiler_builtins" + ], + "core": [ + "dep:core" + ], + "default": [ + "std" + ], + "rustc-dep-of-std": [ + "compiler_builtins", + "core", + "rustc-std-workspace-alloc" + ], + "rustc-std-workspace-alloc": [ + "dep:rustc-std-workspace-alloc" + ], + "std": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/wasi-0.11.0+wasi-snapshot-preview1/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "The Cranelift Project Developers" + ], + "categories": [ + "no-std", + "wasm" + ], + "keywords": [ + "webassembly", + "wasm" + ], + "readme": "README.md", + "repository": "https://github.com/bytecodealliance/wasi", + "homepage": null, + "documentation": "https://docs.rs/wasi", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "winapi", + "version": "0.3.9", + "id": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "Raw FFI bindings for all of Windows API.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "winapi-i686-pc-windows-gnu", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": "i686-pc-windows-gnu", + "registry": null + }, + { + "name": "winapi-x86_64-pc-windows-gnu", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": "x86_64-pc-windows-gnu", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "winapi", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-0.3.9/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-0.3.9/build.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "accctrl": [], + "aclapi": [], + "activation": [], + "adhoc": [], + "appmgmt": [], + "audioclient": [], + "audiosessiontypes": [], + "avrt": [], + "basetsd": [], + "bcrypt": [], + "bits": [], + "bits10_1": [], + "bits1_5": [], + "bits2_0": [], + "bits2_5": [], + "bits3_0": [], + "bits4_0": [], + "bits5_0": [], + "bitscfg": [], + "bitsmsg": [], + "bluetoothapis": [], + "bluetoothleapis": [], + "bthdef": [], + "bthioctl": [], + "bthledef": [], + "bthsdpdef": [], + "bugcodes": [], + "cderr": [], + "cfg": [], + "cfgmgr32": [], + "cguid": [], + "combaseapi": [], + "coml2api": [], + "commapi": [], + "commctrl": [], + "commdlg": [], + "commoncontrols": [], + "consoleapi": [], + "corecrt": [], + "corsym": [], + "d2d1": [], + "d2d1_1": [], + "d2d1_2": [], + "d2d1_3": [], + "d2d1effectauthor": [], + "d2d1effects": [], + "d2d1effects_1": [], + "d2d1effects_2": [], + "d2d1svg": [], + "d2dbasetypes": [], + "d3d": [], + "d3d10": [], + "d3d10_1": [], + "d3d10_1shader": [], + "d3d10effect": [], + "d3d10misc": [], + "d3d10sdklayers": [], + "d3d10shader": [], + "d3d11": [], + "d3d11_1": [], + "d3d11_2": [], + "d3d11_3": [], + "d3d11_4": [], + "d3d11on12": [], + "d3d11sdklayers": [], + "d3d11shader": [], + "d3d11tokenizedprogramformat": [], + "d3d12": [], + "d3d12sdklayers": [], + "d3d12shader": [], + "d3d9": [], + "d3d9caps": [], + "d3d9types": [], + "d3dcommon": [], + "d3dcompiler": [], + "d3dcsx": [], + "d3dkmdt": [], + "d3dkmthk": [], + "d3dukmdt": [], + "d3dx10core": [], + "d3dx10math": [], + "d3dx10mesh": [], + "datetimeapi": [], + "davclnt": [], + "dbghelp": [], + "dbt": [], + "dcommon": [], + "dcomp": [], + "dcompanimation": [], + "dcomptypes": [], + "dde": [], + "ddraw": [], + "ddrawi": [], + "ddrawint": [], + "debug": [ + "impl-debug" + ], + "debugapi": [], + "devguid": [], + "devicetopology": [], + "devpkey": [], + "devpropdef": [], + "dinput": [], + "dinputd": [], + "dispex": [], + "dmksctl": [], + "dmusicc": [], + "docobj": [], + "documenttarget": [], + "dot1x": [], + "dpa_dsa": [], + "dpapi": [], + "dsgetdc": [], + "dsound": [], + "dsrole": [], + "dvp": [], + "dwmapi": [], + "dwrite": [], + "dwrite_1": [], + "dwrite_2": [], + "dwrite_3": [], + "dxdiag": [], + "dxfile": [], + "dxgi": [], + "dxgi1_2": [], + "dxgi1_3": [], + "dxgi1_4": [], + "dxgi1_5": [], + "dxgi1_6": [], + "dxgidebug": [], + "dxgiformat": [], + "dxgitype": [], + "dxva2api": [], + "dxvahd": [], + "eaptypes": [], + "enclaveapi": [], + "endpointvolume": [], + "errhandlingapi": [], + "everything": [], + "evntcons": [], + "evntprov": [], + "evntrace": [], + "excpt": [], + "exdisp": [], + "fibersapi": [], + "fileapi": [], + "functiondiscoverykeys_devpkey": [], + "gl-gl": [], + "guiddef": [], + "handleapi": [], + "heapapi": [], + "hidclass": [], + "hidpi": [], + "hidsdi": [], + "hidusage": [], + "highlevelmonitorconfigurationapi": [], + "hstring": [], + "http": [], + "ifdef": [], + "ifmib": [], + "imm": [], + "impl-debug": [], + "impl-default": [], + "in6addr": [], + "inaddr": [], + "inspectable": [], + "interlockedapi": [], + "intsafe": [], + "ioapiset": [], + "ipexport": [], + "iphlpapi": [], + "ipifcons": [], + "ipmib": [], + "iprtrmib": [], + "iptypes": [], + "jobapi": [], + "jobapi2": [], + "knownfolders": [], + "ks": [], + "ksmedia": [], + "ktmtypes": [], + "ktmw32": [], + "l2cmn": [], + "libloaderapi": [], + "limits": [], + "lmaccess": [], + "lmalert": [], + "lmapibuf": [], + "lmat": [], + "lmcons": [], + "lmdfs": [], + "lmerrlog": [], + "lmjoin": [], + "lmmsg": [], + "lmremutl": [], + "lmrepl": [], + "lmserver": [], + "lmshare": [], + "lmstats": [], + "lmsvc": [], + "lmuse": [], + "lmwksta": [], + "lowlevelmonitorconfigurationapi": [], + "lsalookup": [], + "memoryapi": [], + "minschannel": [], + "minwinbase": [], + "minwindef": [], + "mmdeviceapi": [], + "mmeapi": [], + "mmreg": [], + "mmsystem": [], + "mprapidef": [], + "msaatext": [], + "mscat": [], + "mschapp": [], + "mssip": [], + "mstcpip": [], + "mswsock": [], + "mswsockdef": [], + "namedpipeapi": [], + "namespaceapi": [], + "nb30": [], + "ncrypt": [], + "netioapi": [], + "nldef": [], + "ntddndis": [], + "ntddscsi": [], + "ntddser": [], + "ntdef": [], + "ntlsa": [], + "ntsecapi": [], + "ntstatus": [], + "oaidl": [], + "objbase": [], + "objidl": [], + "objidlbase": [], + "ocidl": [], + "ole2": [], + "oleauto": [], + "olectl": [], + "oleidl": [], + "opmapi": [], + "pdh": [], + "perflib": [], + "physicalmonitorenumerationapi": [], + "playsoundapi": [], + "portabledevice": [], + "portabledeviceapi": [], + "portabledevicetypes": [], + "powerbase": [], + "powersetting": [], + "powrprof": [], + "processenv": [], + "processsnapshot": [], + "processthreadsapi": [], + "processtopologyapi": [], + "profileapi": [], + "propidl": [], + "propkey": [], + "propkeydef": [], + "propsys": [], + "prsht": [], + "psapi": [], + "qos": [], + "realtimeapiset": [], + "reason": [], + "restartmanager": [], + "restrictederrorinfo": [], + "rmxfguid": [], + "roapi": [], + "robuffer": [], + "roerrorapi": [], + "rpc": [], + "rpcdce": [], + "rpcndr": [], + "rtinfo": [], + "sapi": [], + "sapi51": [], + "sapi53": [], + "sapiddk": [], + "sapiddk51": [], + "schannel": [], + "sddl": [], + "securityappcontainer": [], + "securitybaseapi": [], + "servprov": [], + "setupapi": [], + "shellapi": [], + "shellscalingapi": [], + "shlobj": [], + "shobjidl": [], + "shobjidl_core": [], + "shtypes": [], + "softpub": [], + "spapidef": [], + "spellcheck": [], + "sporder": [], + "sql": [], + "sqlext": [], + "sqltypes": [], + "sqlucode": [], + "sspi": [], + "std": [], + "stralign": [], + "stringapiset": [], + "strmif": [], + "subauth": [], + "synchapi": [], + "sysinfoapi": [], + "systemtopologyapi": [], + "taskschd": [], + "tcpestats": [], + "tcpmib": [], + "textstor": [], + "threadpoolapiset": [], + "threadpoollegacyapiset": [], + "timeapi": [], + "timezoneapi": [], + "tlhelp32": [], + "transportsettingcommon": [], + "tvout": [], + "udpmib": [], + "unknwnbase": [], + "urlhist": [], + "urlmon": [], + "usb": [], + "usbioctl": [], + "usbiodef": [], + "usbscan": [], + "usbspec": [], + "userenv": [], + "usp10": [], + "utilapiset": [], + "uxtheme": [], + "vadefs": [], + "vcruntime": [], + "vsbackup": [], + "vss": [], + "vsserror": [], + "vswriter": [], + "wbemads": [], + "wbemcli": [], + "wbemdisp": [], + "wbemprov": [], + "wbemtran": [], + "wct": [], + "werapi": [], + "winbase": [], + "wincodec": [], + "wincodecsdk": [], + "wincon": [], + "wincontypes": [], + "wincred": [], + "wincrypt": [], + "windef": [], + "windot11": [], + "windowsceip": [], + "windowsx": [], + "winefs": [], + "winerror": [], + "winevt": [], + "wingdi": [], + "winhttp": [], + "wininet": [], + "winineti": [], + "winioctl": [], + "winnetwk": [], + "winnls": [], + "winnt": [], + "winreg": [], + "winsafer": [], + "winscard": [], + "winsmcrd": [], + "winsock2": [], + "winspool": [], + "winstring": [], + "winsvc": [], + "wintrust": [], + "winusb": [], + "winusbio": [], + "winuser": [], + "winver": [], + "wlanapi": [], + "wlanihv": [], + "wlanihvtypes": [], + "wlantypes": [], + "wlclient": [], + "wmistr": [], + "wnnc": [], + "wow64apiset": [], + "wpdmtpextensions": [], + "ws2bth": [], + "ws2def": [], + "ws2ipdef": [], + "ws2spi": [], + "ws2tcpip": [], + "wtsapi32": [], + "wtypes": [], + "wtypesbase": [], + "xinput": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-0.3.9/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "default-target": "x86_64-pc-windows-msvc", + "features": [ + "everything", + "impl-debug", + "impl-default" + ], + "targets": [ + "aarch64-pc-windows-msvc", + "i686-pc-windows-msvc", + "x86_64-pc-windows-msvc" + ] + } + } + }, + "publish": null, + "authors": [ + "Peter Atashian " + ], + "categories": [ + "external-ffi-bindings", + "no-std", + "os::windows-apis" + ], + "keywords": [ + "windows", + "ffi", + "win32", + "com", + "directx" + ], + "readme": "README.md", + "repository": "https://github.com/retep998/winapi-rs", + "homepage": null, + "documentation": "https://docs.rs/winapi/", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "winapi-i686-pc-windows-gnu", + "version": "0.4.0", + "id": "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "Import libraries for the i686-pc-windows-gnu target. Please don't use this crate directly, depend on winapi instead.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "winapi-i686-pc-windows-gnu", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-i686-pc-windows-gnu-0.4.0/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-i686-pc-windows-gnu-0.4.0/build.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-i686-pc-windows-gnu-0.4.0/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Peter Atashian " + ], + "categories": [], + "keywords": [ + "windows" + ], + "readme": null, + "repository": "https://github.com/retep998/winapi-rs", + "homepage": null, + "documentation": null, + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "winapi-x86_64-pc-windows-gnu", + "version": "0.4.0", + "id": "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "Import libraries for the x86_64-pc-windows-gnu target. Please don't use this crate directly, depend on winapi instead.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "winapi-x86_64-pc-windows-gnu", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-x86_64-pc-windows-gnu-0.4.0/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-x86_64-pc-windows-gnu-0.4.0/build.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-x86_64-pc-windows-gnu-0.4.0/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Peter Atashian " + ], + "categories": [], + "keywords": [ + "windows" + ], + "readme": null, + "repository": "https://github.com/retep998/winapi-rs", + "homepage": null, + "documentation": null, + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + } + ], + "workspace_members": [ + "regex-benchmark 0.1.0 (path+file:///$ROOT$regex/bench)", + "regex 1.7.1 (path+file:///$ROOT$regex)", + "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)", + "rure 0.2.2 (path+file:///$ROOT$regex/regex-capi)", + "regex-debug 0.1.0 (path+file:///$ROOT$regex/regex-debug)" + ], + "resolve": { + "nodes": [ + { + "id": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "memchr", + "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "default", + "std" + ] + }, + { + "id": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "docopt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "strsim 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "lazy_static", + "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "regex", + "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "serde", + "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "strsim", + "pkg": "strsim 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "getrandom 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "wasi 0.11.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "cfg_if", + "pkg": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "libc", + "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(unix)" + } + ] + }, + { + "name": "wasi", + "pkg": "wasi 0.11.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(target_os = \"wasi\")" + } + ] + } + ], + "features": [] + }, + { + "id": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [ + "default", + "std" + ] + }, + { + "id": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [ + "default", + "std" + ] + }, + { + "id": "memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "libc", + "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(unix)" + } + ] + }, + { + "name": "winapi", + "pkg": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(windows)" + } + ] + } + ], + "features": [] + }, + { + "id": "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "unicode_ident", + "pkg": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "default", + "proc-macro" + ] + }, + { + "id": "quickcheck 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "rand", + "pkg": "rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "proc_macro2", + "pkg": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "default", + "proc-macro" + ] + }, + { + "id": "rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "rand_core 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "rand_core", + "pkg": "rand_core 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "getrandom", + "small_rng" + ] + }, + { + "id": "rand_core 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "getrandom 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "getrandom", + "pkg": "getrandom 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "getrandom" + ] + }, + { + "id": "regex 1.7.1 (path+file:///$ROOT$regex)", + "dependencies": [ + "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "quickcheck 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)" + ], + "deps": [ + { + "name": "aho_corasick", + "pkg": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "lazy_static", + "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ] + }, + { + "name": "memchr", + "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "quickcheck", + "pkg": "quickcheck 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ] + }, + { + "name": "rand", + "pkg": "rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ] + }, + { + "name": "regex_syntax", + "pkg": "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "aho-corasick", + "default", + "memchr", + "perf", + "perf-cache", + "perf-dfa", + "perf-inline", + "perf-literal", + "std", + "unicode", + "unicode-age", + "unicode-bool", + "unicode-case", + "unicode-gencat", + "unicode-perl", + "unicode-script", + "unicode-segment" + ] + }, + { + "id": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "regex_syntax", + "pkg": "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "std", + "unicode", + "unicode-age", + "unicode-bool", + "unicode-case", + "unicode-gencat", + "unicode-perl", + "unicode-script", + "unicode-segment" + ] + }, + { + "id": "regex-benchmark 0.1.0 (path+file:///$ROOT$regex/bench)", + "dependencies": [ + "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "docopt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.7.1 (path+file:///$ROOT$regex)", + "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)", + "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "cc", + "pkg": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "build", + "target": null + } + ] + }, + { + "name": "cfg_if", + "pkg": "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "docopt", + "pkg": "docopt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "lazy_static", + "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "libc", + "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "memmap", + "pkg": "memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "pkg_config", + "pkg": "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "build", + "target": null + } + ] + }, + { + "name": "regex", + "pkg": "regex 1.7.1 (path+file:///$ROOT$regex)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "regex_syntax", + "pkg": "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "serde", + "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "regex-debug 0.1.0 (path+file:///$ROOT$regex/regex-debug)", + "dependencies": [ + "docopt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.7.1 (path+file:///$ROOT$regex)", + "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)", + "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "docopt", + "pkg": "docopt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "regex", + "pkg": "regex 1.7.1 (path+file:///$ROOT$regex)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "regex_syntax", + "pkg": "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "serde", + "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)", + "dependencies": [], + "deps": [], + "features": [ + "default", + "unicode", + "unicode-age", + "unicode-bool", + "unicode-case", + "unicode-gencat", + "unicode-perl", + "unicode-script", + "unicode-segment" + ] + }, + { + "id": "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [ + "unicode", + "unicode-age", + "unicode-bool", + "unicode-case", + "unicode-gencat", + "unicode-perl", + "unicode-script", + "unicode-segment" + ] + }, + { + "id": "rure 0.2.2 (path+file:///$ROOT$regex/regex-capi)", + "dependencies": [ + "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.7.1 (path+file:///$ROOT$regex)" + ], + "deps": [ + { + "name": "libc", + "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "regex", + "pkg": "regex 1.7.1 (path+file:///$ROOT$regex)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "serde_derive", + "pkg": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "default", + "derive", + "serde_derive", + "std" + ] + }, + { + "id": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "proc_macro2", + "pkg": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "quote", + "pkg": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "syn", + "pkg": "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "default" + ] + }, + { + "id": "strsim 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "proc_macro2", + "pkg": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "quote", + "pkg": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "unicode_ident", + "pkg": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "clone-impls", + "default", + "derive", + "parsing", + "printing", + "proc-macro", + "quote" + ] + }, + { + "id": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "wasi 0.11.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "winapi_i686_pc_windows_gnu", + "pkg": "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "i686-pc-windows-gnu" + } + ] + }, + { + "name": "winapi_x86_64_pc_windows_gnu", + "pkg": "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "x86_64-pc-windows-gnu" + } + ] + } + ], + "features": [ + "basetsd", + "handleapi", + "memoryapi", + "minwindef", + "std", + "sysinfoapi" + ] + }, + { + "id": "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + } + ], + "root": "regex 1.7.1 (path+file:///$ROOT$regex)" + }, + "target_directory": "$ROOT$regex/target", + "version": 1, + "workspace_root": "$ROOT$regex", + "metadata": null +} diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/ripgrep-metadata.json b/src/tools/rust-analyzer/crates/project-model/test_data/ripgrep-metadata.json new file mode 100644 index 0000000000000..131ff5dd71571 --- /dev/null +++ b/src/tools/rust-analyzer/crates/project-model/test_data/ripgrep-metadata.json @@ -0,0 +1,12816 @@ +{ + "packages": [ + { + "name": "aho-corasick", + "version": "0.7.20", + "id": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Unlicense OR MIT", + "license_file": null, + "description": "Fast multiple substring searching.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "memchr", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.4.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "aho_corasick", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/aho-corasick-0.7.20/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": { + "default": [ + "std" + ], + "std": [ + "memchr/std" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/aho-corasick-0.7.20/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant " + ], + "categories": [ + "text-processing" + ], + "keywords": [ + "string", + "search", + "text", + "aho", + "multi" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/aho-corasick", + "homepage": "https://github.com/BurntSushi/aho-corasick", + "documentation": null, + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "aho-corasick", + "version": "1.0.1", + "id": "aho-corasick 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Unlicense OR MIT", + "license_file": null, + "description": "Fast multiple substring searching.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "log", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4.17", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "memchr", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.4.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "doc-comment", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "aho_corasick", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/aho-corasick-1.0.1/src/lib.rs", + "edition": "2021", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": { + "default": [ + "std", + "perf-literal" + ], + "logging": [ + "dep:log" + ], + "perf-literal": [ + "dep:memchr" + ], + "std": [ + "memchr?/std" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/aho-corasick-1.0.1/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "all-features": true, + "rustdoc-args": [ + "--cfg", + "docsrs" + ] + } + } + }, + "publish": null, + "authors": [ + "Andrew Gallant " + ], + "categories": [ + "text-processing" + ], + "keywords": [ + "string", + "search", + "text", + "pattern", + "multi" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/aho-corasick", + "homepage": "https://github.com/BurntSushi/aho-corasick", + "documentation": null, + "edition": "2021", + "links": null, + "default_run": null, + "rust_version": "1.60.0" + }, + { + "name": "atty", + "version": "0.2.14", + "id": "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT", + "license_file": null, + "description": "A simple interface for querying atty", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "hermit-abi", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.6", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": "cfg(target_os = \"hermit\")", + "registry": null + }, + { + "name": "libc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": "cfg(unix)", + "registry": null + }, + { + "name": "winapi", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "consoleapi", + "processenv", + "minwinbase", + "minwindef", + "winbase" + ], + "target": "cfg(windows)", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "atty", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/atty-0.2.14/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "atty", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/atty-0.2.14/examples/atty.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/atty-0.2.14/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "softprops " + ], + "categories": [], + "keywords": [ + "terminal", + "tty", + "isatty" + ], + "readme": "README.md", + "repository": "https://github.com/softprops/atty", + "homepage": "https://github.com/softprops/atty", + "documentation": "http://softprops.github.io/atty", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "base64", + "version": "0.20.0", + "id": "base64 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "encodes and decodes base64 as bytes or utf8", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "criterion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rand", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8.5", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "small_rng" + ], + "target": null, + "registry": null + }, + { + "name": "rstest", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.12.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rstest_reuse", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "structopt", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.26", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "base64", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/base64-0.20.0/src/lib.rs", + "edition": "2021", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "base64", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/base64-0.20.0/examples/base64.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "encode", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/base64-0.20.0/tests/encode.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "tests", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/base64-0.20.0/tests/tests.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "benchmarks", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/base64-0.20.0/benches/benchmarks.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "alloc": [], + "default": [ + "std" + ], + "std": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/base64-0.20.0/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Alice Maz ", + "Marshall Pierce " + ], + "categories": [ + "encoding" + ], + "keywords": [ + "base64", + "utf8", + "encode", + "decode", + "no_std" + ], + "readme": "README.md", + "repository": "https://github.com/marshallpierce/rust-base64", + "homepage": null, + "documentation": "https://docs.rs/base64", + "edition": "2021", + "links": null, + "default_run": null, + "rust_version": "1.57.0" + }, + { + "name": "bitflags", + "version": "1.3.2", + "id": "bitflags 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "A macro to generate structures which behave like bitflags.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "compiler_builtins", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.2", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustc-std-workspace-core", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.0", + "kind": null, + "rename": "core", + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustversion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde_derive", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde_json", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "trybuild", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "walkdir", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "bitflags", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bitflags-1.3.2/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "basic", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bitflags-1.3.2/tests/basic.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "compile", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bitflags-1.3.2/tests/compile.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": { + "compiler_builtins": [ + "dep:compiler_builtins" + ], + "core": [ + "dep:core" + ], + "default": [], + "example_generated": [], + "rustc-dep-of-std": [ + "core", + "compiler_builtins" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bitflags-1.3.2/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "features": [ + "example_generated" + ] + } + } + }, + "publish": null, + "authors": [ + "The Rust Project Developers" + ], + "categories": [ + "no-std" + ], + "keywords": [ + "bit", + "bitmask", + "bitflags", + "flags" + ], + "readme": "README.md", + "repository": "https://github.com/bitflags/bitflags", + "homepage": "https://github.com/bitflags/bitflags", + "documentation": "https://docs.rs/bitflags", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "bstr", + "version": "1.4.0", + "id": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A string type that is not required to be valid UTF-8.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "memchr", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.4.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "once_cell", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.14.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex-automata", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.5", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.85", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "quickcheck", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "ucd-parse", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "unicode-segmentation", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.2.1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "bstr", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/src/lib.rs", + "edition": "2021", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "graphemes", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/graphemes.rs", + "edition": "2021", + "required-features": [ + "std", + "unicode" + ], + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "lines", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/lines.rs", + "edition": "2021", + "required-features": [ + "std" + ], + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "uppercase", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/uppercase.rs", + "edition": "2021", + "required-features": [ + "std", + "unicode" + ], + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "words", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/words.rs", + "edition": "2021", + "required-features": [ + "std", + "unicode" + ], + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "graphemes-std", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/graphemes-std.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "lines-std", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/lines-std.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "uppercase-std", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/uppercase-std.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "words-std", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/words-std.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "alloc": [ + "serde?/alloc" + ], + "default": [ + "std", + "unicode" + ], + "serde": [ + "dep:serde" + ], + "std": [ + "alloc", + "memchr/std", + "serde?/std" + ], + "unicode": [ + "dep:once_cell", + "dep:regex-automata" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "all-features": true, + "rustdoc-args": [ + "--cfg", + "docsrs" + ] + } + } + }, + "publish": null, + "authors": [ + "Andrew Gallant " + ], + "categories": [ + "text-processing", + "encoding" + ], + "keywords": [ + "string", + "str", + "byte", + "bytes", + "text" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/bstr", + "homepage": "https://github.com/BurntSushi/bstr", + "documentation": "https://docs.rs/bstr", + "edition": "2021", + "links": null, + "default_run": null, + "rust_version": "1.60" + }, + { + "name": "bytecount", + "version": "0.6.3", + "id": "bytecount 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Apache-2.0/MIT", + "license_file": null, + "description": "count occurrences of a given byte, or the number of UTF-8 code points, in a byte slice, fast", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "packed_simd_2", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.8", + "kind": null, + "rename": "packed_simd", + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "criterion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "quickcheck", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rand", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "bytecount", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bytecount-0.6.3/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "check", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bytecount-0.6.3/tests/check.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "bench", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bytecount-0.6.3/benches/bench.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "generic-simd": [ + "packed_simd" + ], + "html_report": [], + "packed_simd": [ + "dep:packed_simd" + ], + "runtime-dispatch-simd": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bytecount-0.6.3/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andre Bogus ", + "Joshua Landau " + ], + "categories": [ + "algorithms", + "no-std" + ], + "keywords": [], + "readme": "README.md", + "repository": "https://github.com/llogiq/bytecount", + "homepage": null, + "documentation": null, + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "cc", + "version": "1.0.79", + "id": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A build-time dependency for Cargo build scripts to assist in invoking the native\nC compiler to compile native C code into a static archive to be linked into Rust\ncode.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "jobserver", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.16", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "tempfile", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "cc", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "bin" + ], + "crate_types": [ + "bin" + ], + "name": "gcc-shim", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/src/bin/gcc-shim.rs", + "edition": "2018", + "doc": true, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "cc_env", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/cc_env.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "cflags", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/cflags.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "cxxflags", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/cxxflags.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/test.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": { + "jobserver": [ + "dep:jobserver" + ], + "parallel": [ + "jobserver" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Alex Crichton " + ], + "categories": [ + "development-tools::build-utils" + ], + "keywords": [ + "build-dependencies" + ], + "readme": "README.md", + "repository": "https://github.com/rust-lang/cc-rs", + "homepage": "https://github.com/rust-lang/cc-rs", + "documentation": "https://docs.rs/cc", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "cfg-if", + "version": "1.0.0", + "id": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "A macro to ergonomically define an item depending on a large number of #[cfg]\nparameters. Structured like an if-else chain, the first matching branch is the\nitem that gets emitted.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "compiler_builtins", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.2", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustc-std-workspace-core", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.0", + "kind": null, + "rename": "core", + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "cfg-if", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-1.0.0/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "xcrate", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-1.0.0/tests/xcrate.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": { + "compiler_builtins": [ + "dep:compiler_builtins" + ], + "core": [ + "dep:core" + ], + "rustc-dep-of-std": [ + "core", + "compiler_builtins" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-1.0.0/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Alex Crichton " + ], + "categories": [], + "keywords": [], + "readme": "README.md", + "repository": "https://github.com/alexcrichton/cfg-if", + "homepage": "https://github.com/alexcrichton/cfg-if", + "documentation": "https://docs.rs/cfg-if", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "clap", + "version": "2.34.0", + "id": "clap 2.34.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT", + "license_file": null, + "description": "A simple to use, efficient, and full-featured Command Line Argument Parser\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "atty", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2.2", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "bitflags", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "clippy", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "~0.0.166", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "strsim", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "term_size", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "textwrap", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.11.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "unicode-width", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.4", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "vec_map", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "yaml-rust", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.5", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "lazy_static", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "version-sync", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "ansi_term", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.12", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": "cfg(not(windows))", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "clap", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/clap-2.34.0/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": { + "ansi_term": [ + "dep:ansi_term" + ], + "atty": [ + "dep:atty" + ], + "clippy": [ + "dep:clippy" + ], + "color": [ + "ansi_term", + "atty" + ], + "debug": [], + "default": [ + "suggestions", + "color", + "vec_map" + ], + "doc": [ + "yaml" + ], + "nightly": [], + "no_cargo": [], + "strsim": [ + "dep:strsim" + ], + "suggestions": [ + "strsim" + ], + "term_size": [ + "dep:term_size" + ], + "unstable": [], + "vec_map": [ + "dep:vec_map" + ], + "wrap_help": [ + "term_size", + "textwrap/term_size" + ], + "yaml": [ + "yaml-rust" + ], + "yaml-rust": [ + "dep:yaml-rust" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/clap-2.34.0/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "features": [ + "doc" + ] + } + } + }, + "publish": null, + "authors": [ + "Kevin K. " + ], + "categories": [ + "command-line-interface" + ], + "keywords": [ + "argument", + "cli", + "arg", + "parser", + "parse" + ], + "readme": "README.md", + "repository": "https://github.com/clap-rs/clap", + "homepage": "https://clap.rs/", + "documentation": "https://docs.rs/clap/", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "crossbeam-channel", + "version": "0.5.8", + "id": "crossbeam-channel 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Multi-producer multi-consumer channels for message passing", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "cfg-if", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "crossbeam-utils", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "num_cpus", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.13.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rand", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "signal-hook", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "crossbeam-channel", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "fibonacci", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/examples/fibonacci.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "matching", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/examples/matching.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "stopwatch", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/examples/stopwatch.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "after", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/after.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "array", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/array.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "golang", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/golang.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "iter", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/iter.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "list", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/list.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "mpsc", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/mpsc.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "never", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/never.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "ready", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/ready.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "same_channel", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/same_channel.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "select", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/select.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "select_macro", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/select_macro.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "thread_locals", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/thread_locals.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "tick", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/tick.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "zero", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/zero.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "crossbeam", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/benches/crossbeam.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "crossbeam-utils": [ + "dep:crossbeam-utils" + ], + "default": [ + "std" + ], + "std": [ + "crossbeam-utils/std" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [], + "categories": [ + "algorithms", + "concurrency", + "data-structures" + ], + "keywords": [ + "channel", + "mpmc", + "select", + "golang", + "message" + ], + "readme": "README.md", + "repository": "https://github.com/crossbeam-rs/crossbeam", + "homepage": "https://github.com/crossbeam-rs/crossbeam/tree/master/crossbeam-channel", + "documentation": null, + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": "1.38" + }, + { + "name": "crossbeam-utils", + "version": "0.8.15", + "id": "crossbeam-utils 0.8.15 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Utilities for concurrent programming", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "cfg-if", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rand", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustversion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "loom", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.5", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": "cfg(crossbeam_loom)", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "crossbeam-utils", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "atomic_cell", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/tests/atomic_cell.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "cache_padded", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/tests/cache_padded.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "parker", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/tests/parker.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "sharded_lock", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/tests/sharded_lock.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "thread", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/tests/thread.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "wait_group", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/tests/wait_group.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "atomic_cell", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/benches/atomic_cell.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/build.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "default": [ + "std" + ], + "loom": [ + "dep:loom" + ], + "nightly": [], + "std": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [], + "categories": [ + "algorithms", + "concurrency", + "data-structures", + "no-std" + ], + "keywords": [ + "scoped", + "thread", + "atomic", + "cache" + ], + "readme": "README.md", + "repository": "https://github.com/crossbeam-rs/crossbeam", + "homepage": "https://github.com/crossbeam-rs/crossbeam/tree/master/crossbeam-utils", + "documentation": null, + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": "1.38" + }, + { + "name": "encoding_rs", + "version": "0.8.32", + "id": "encoding_rs 0.8.32 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "(Apache-2.0 OR MIT) AND BSD-3-Clause", + "license_file": null, + "description": "A Gecko-oriented implementation of the Encoding Standard", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "cfg-if", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "packed_simd_2", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.4", + "kind": null, + "rename": "packed_simd", + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "bincode", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde_derive", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde_json", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "encoding_rs", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/encoding_rs-0.8.32/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": { + "alloc": [], + "default": [ + "alloc" + ], + "fast-big5-hanzi-encode": [], + "fast-gb-hanzi-encode": [], + "fast-hangul-encode": [], + "fast-hanja-encode": [], + "fast-kanji-encode": [], + "fast-legacy-encode": [ + "fast-hangul-encode", + "fast-hanja-encode", + "fast-kanji-encode", + "fast-gb-hanzi-encode", + "fast-big5-hanzi-encode" + ], + "less-slow-big5-hanzi-encode": [], + "less-slow-gb-hanzi-encode": [], + "less-slow-kanji-encode": [], + "packed_simd": [ + "dep:packed_simd" + ], + "serde": [ + "dep:serde" + ], + "simd-accel": [ + "packed_simd", + "packed_simd/into_bits" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/encoding_rs-0.8.32/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Henri Sivonen " + ], + "categories": [ + "text-processing", + "encoding", + "web-programming", + "internationalization" + ], + "keywords": [ + "encoding", + "web", + "unicode", + "charset" + ], + "readme": "README.md", + "repository": "https://github.com/hsivonen/encoding_rs", + "homepage": "https://docs.rs/encoding_rs/", + "documentation": "https://docs.rs/encoding_rs/", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "encoding_rs_io", + "version": "0.1.7", + "id": "encoding_rs_io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Streaming transcoding for encoding_rs", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "encoding_rs", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "encoding_rs_io", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/encoding_rs_io-0.1.7/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/encoding_rs_io-0.1.7/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant " + ], + "categories": [ + "text-processing", + "encoding", + "web-programming", + "email" + ], + "keywords": [ + "encoding", + "transcoding", + "stream", + "io", + "read" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/encoding_rs_io", + "homepage": null, + "documentation": "https://docs.rs/encoding_rs_io", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "fnv", + "version": "1.0.7", + "id": "fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Apache-2.0 / MIT", + "license_file": null, + "description": "Fowler–Noll–Vo hash function", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "fnv", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/fnv-1.0.7/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": { + "default": [ + "std" + ], + "std": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/fnv-1.0.7/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Alex Crichton " + ], + "categories": [], + "keywords": [], + "readme": "README.md", + "repository": "https://github.com/servo/rust-fnv", + "homepage": null, + "documentation": "https://doc.servo.org/fnv/", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "glob", + "version": "0.3.1", + "id": "glob 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Support for matching file paths against Unix shell style patterns.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "doc-comment", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "tempdir", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "glob", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/glob-0.3.1/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "glob-std", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/glob-0.3.1/tests/glob-std.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/glob-0.3.1/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "The Rust Project Developers" + ], + "categories": [ + "filesystem" + ], + "keywords": [], + "readme": "README.md", + "repository": "https://github.com/rust-lang/glob", + "homepage": "https://github.com/rust-lang/glob", + "documentation": "https://docs.rs/glob/0.3.1", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "globset", + "version": "0.4.10", + "id": "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)", + "license": "Unlicense OR MIT", + "license_file": null, + "description": "Cross platform single glob and glob set matching. Glob set matching is the\nprocess of matching one or more glob patterns against a single candidate path\nsimultaneously, and returning all of the globs that matched.\n", + "source": null, + "dependencies": [ + { + "name": "aho-corasick", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.7.3", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "bstr", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [ + "std" + ], + "target": null, + "registry": null + }, + { + "name": "fnv", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.6", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "log", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4.5", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1.5", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [ + "perf", + "std" + ], + "target": null, + "registry": null + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.104", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "glob", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "lazy_static", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde_json", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.45", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "globset", + "src_path": "$ROOT$ripgrep/crates/globset/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "bench", + "src_path": "$ROOT$ripgrep/crates/globset/benches/bench.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "default": [ + "log" + ], + "log": [ + "dep:log" + ], + "serde": [ + "dep:serde" + ], + "serde1": [ + "serde" + ], + "simd-accel": [] + }, + "manifest_path": "$ROOT$ripgrep/crates/globset/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant " + ], + "categories": [], + "keywords": [ + "regex", + "glob", + "multiple", + "set", + "pattern" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/globset", + "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/globset", + "documentation": "https://docs.rs/globset", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "grep", + "version": "0.2.11", + "id": "grep 0.2.11 (path+file:///$ROOT$ripgrep/crates/grep)", + "license": "Unlicense OR MIT", + "license_file": null, + "description": "Fast line oriented regex searching as a library.\n", + "source": null, + "dependencies": [ + { + "name": "grep-cli", + "source": null, + "req": "^0.1.7", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/cli" + }, + { + "name": "grep-matcher", + "source": null, + "req": "^0.1.6", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/matcher" + }, + { + "name": "grep-pcre2", + "source": null, + "req": "^0.1.6", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/pcre2" + }, + { + "name": "grep-printer", + "source": null, + "req": "^0.1.7", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/printer" + }, + { + "name": "grep-regex", + "source": null, + "req": "^0.1.11", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/regex" + }, + { + "name": "grep-searcher", + "source": null, + "req": "^0.1.11", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/searcher" + }, + { + "name": "termcolor", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.4", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "walkdir", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.2.7", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "grep", + "src_path": "$ROOT$ripgrep/crates/grep/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "simplegrep", + "src_path": "$ROOT$ripgrep/crates/grep/examples/simplegrep.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "avx-accel": [], + "grep-pcre2": [ + "dep:grep-pcre2" + ], + "pcre2": [ + "grep-pcre2" + ], + "simd-accel": [ + "grep-searcher/simd-accel" + ] + }, + "manifest_path": "$ROOT$ripgrep/crates/grep/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant " + ], + "categories": [], + "keywords": [ + "regex", + "grep", + "egrep", + "search", + "pattern" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/grep", + "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/grep", + "documentation": "https://docs.rs/grep", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "grep-cli", + "version": "0.1.7", + "id": "grep-cli 0.1.7 (path+file:///$ROOT$ripgrep/crates/cli)", + "license": "Unlicense OR MIT", + "license_file": null, + "description": "Utilities for search oriented command line applications.\n", + "source": null, + "dependencies": [ + { + "name": "atty", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2.11", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "bstr", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "globset", + "source": null, + "req": "^0.4.10", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/globset" + }, + { + "name": "lazy_static", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "log", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4.5", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "same-file", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.4", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "termcolor", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.4", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "winapi-util", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": "cfg(windows)", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "grep-cli", + "src_path": "$ROOT$ripgrep/crates/cli/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": {}, + "manifest_path": "$ROOT$ripgrep/crates/cli/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant " + ], + "categories": [], + "keywords": [ + "regex", + "grep", + "cli", + "utility", + "util" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/cli", + "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/cli", + "documentation": "https://docs.rs/grep-cli", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "grep-matcher", + "version": "0.1.6", + "id": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", + "license": "Unlicense OR MIT", + "license_file": null, + "description": "A trait for regular expressions, with a focus on line oriented search.\n", + "source": null, + "dependencies": [ + { + "name": "memchr", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "grep-matcher", + "src_path": "$ROOT$ripgrep/crates/matcher/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "integration", + "src_path": "$ROOT$ripgrep/crates/matcher/tests/tests.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": {}, + "manifest_path": "$ROOT$ripgrep/crates/matcher/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant " + ], + "categories": [], + "keywords": [ + "regex", + "pattern", + "trait" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/matcher", + "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/matcher", + "documentation": "https://docs.rs/grep-matcher", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "grep-pcre2", + "version": "0.1.6", + "id": "grep-pcre2 0.1.6 (path+file:///$ROOT$ripgrep/crates/pcre2)", + "license": "Unlicense OR MIT", + "license_file": null, + "description": "Use PCRE2 with the 'grep' crate.\n", + "source": null, + "dependencies": [ + { + "name": "grep-matcher", + "source": null, + "req": "^0.1.6", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/matcher" + }, + { + "name": "pcre2", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2.3", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "grep-pcre2", + "src_path": "$ROOT$ripgrep/crates/pcre2/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": {}, + "manifest_path": "$ROOT$ripgrep/crates/pcre2/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant " + ], + "categories": [], + "keywords": [ + "regex", + "grep", + "pcre", + "backreference", + "look" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/pcre2", + "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/pcre2", + "documentation": "https://docs.rs/grep-pcre2", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "grep-printer", + "version": "0.1.7", + "id": "grep-printer 0.1.7 (path+file:///$ROOT$ripgrep/crates/printer)", + "license": "Unlicense OR MIT", + "license_file": null, + "description": "An implementation of the grep crate's Sink trait that provides standard\nprinting of search results, similar to grep itself.\n", + "source": null, + "dependencies": [ + { + "name": "base64", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.20.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "bstr", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "grep-matcher", + "source": null, + "req": "^0.1.6", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/matcher" + }, + { + "name": "grep-searcher", + "source": null, + "req": "^0.1.11", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/searcher" + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.77", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [ + "derive" + ], + "target": null, + "registry": null + }, + { + "name": "serde_json", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.27", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "termcolor", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.4", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "grep-regex", + "source": null, + "req": "^0.1.11", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/regex" + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "grep-printer", + "src_path": "$ROOT$ripgrep/crates/printer/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": { + "base64": [ + "dep:base64" + ], + "default": [ + "serde1" + ], + "serde": [ + "dep:serde" + ], + "serde1": [ + "base64", + "serde", + "serde_json" + ], + "serde_json": [ + "dep:serde_json" + ] + }, + "manifest_path": "$ROOT$ripgrep/crates/printer/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant " + ], + "categories": [], + "keywords": [ + "grep", + "pattern", + "print", + "printer", + "sink" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/printer", + "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/printer", + "documentation": "https://docs.rs/grep-printer", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "grep-regex", + "version": "0.1.11", + "id": "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)", + "license": "Unlicense OR MIT", + "license_file": null, + "description": "Use Rust's regex library with the 'grep' crate.\n", + "source": null, + "dependencies": [ + { + "name": "aho-corasick", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.7.3", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "bstr", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "grep-matcher", + "source": null, + "req": "^0.1.6", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/matcher" + }, + { + "name": "log", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4.5", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex-syntax", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.6.5", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "thread_local", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1.2", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "grep-regex", + "src_path": "$ROOT$ripgrep/crates/regex/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": {}, + "manifest_path": "$ROOT$ripgrep/crates/regex/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant " + ], + "categories": [], + "keywords": [ + "regex", + "grep", + "search", + "pattern", + "line" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/regex", + "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/regex", + "documentation": "https://docs.rs/grep-regex", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "grep-searcher", + "version": "0.1.11", + "id": "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)", + "license": "Unlicense OR MIT", + "license_file": null, + "description": "Fast line oriented regex searching as a library.\n", + "source": null, + "dependencies": [ + { + "name": "bstr", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [ + "std" + ], + "target": null, + "registry": null + }, + { + "name": "bytecount", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.6", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "encoding_rs", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8.14", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "encoding_rs_io", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.6", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "grep-matcher", + "source": null, + "req": "^0.1.6", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/matcher" + }, + { + "name": "log", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4.5", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "memmap2", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.5.3", + "kind": null, + "rename": "memmap", + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "grep-regex", + "source": null, + "req": "^0.1.11", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/regex" + }, + { + "name": "regex", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "grep-searcher", + "src_path": "$ROOT$ripgrep/crates/searcher/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "search-stdin", + "src_path": "$ROOT$ripgrep/crates/searcher/examples/search-stdin.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "avx-accel": [], + "default": [ + "bytecount/runtime-dispatch-simd" + ], + "simd-accel": [ + "encoding_rs/simd-accel" + ] + }, + "manifest_path": "$ROOT$ripgrep/crates/searcher/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant " + ], + "categories": [], + "keywords": [ + "regex", + "grep", + "egrep", + "search", + "pattern" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/searcher", + "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/searcher", + "documentation": "https://docs.rs/grep-searcher", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "hermit-abi", + "version": "0.1.19", + "id": "hermit-abi 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "hermit-abi is small interface to call functions from the unikernel RustyHermit.\nIt is used to build the target `x86_64-unknown-hermit`.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "compiler_builtins", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustc-std-workspace-core", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.0", + "kind": null, + "rename": "core", + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "libc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2.51", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "hermit-abi", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/hermit-abi-0.1.19/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": { + "compiler_builtins": [ + "dep:compiler_builtins" + ], + "core": [ + "dep:core" + ], + "default": [], + "docs": [], + "rustc-dep-of-std": [ + "core", + "compiler_builtins/rustc-dep-of-std", + "libc/rustc-dep-of-std" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/hermit-abi-0.1.19/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "default-target": "x86_64-unknown-hermit", + "features": [ + "docs" + ] + } + } + }, + "publish": null, + "authors": [ + "Stefan Lankes" + ], + "categories": [ + "os" + ], + "keywords": [ + "unikernel", + "libos" + ], + "readme": "README.md", + "repository": "https://github.com/hermitcore/libhermit-rs", + "homepage": null, + "documentation": "https://hermitcore.github.io/rusty-hermit/hermit_abi", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "ignore", + "version": "0.4.20", + "id": "ignore 0.4.20 (path+file:///$ROOT$ripgrep/crates/ignore)", + "license": "Unlicense OR MIT", + "license_file": null, + "description": "A fast library for efficiently matching ignore files such as `.gitignore`\nagainst file paths.\n", + "source": null, + "dependencies": [ + { + "name": "globset", + "source": null, + "req": "^0.4.10", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/globset" + }, + { + "name": "lazy_static", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "log", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4.5", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "memchr", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "same-file", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.4", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "thread_local", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "walkdir", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.2.7", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "crossbeam-channel", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.5.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "winapi-util", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.2", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": "cfg(windows)", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "ignore", + "src_path": "$ROOT$ripgrep/crates/ignore/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "walk", + "src_path": "$ROOT$ripgrep/crates/ignore/examples/walk.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "gitignore_matched_path_or_any_parents_tests", + "src_path": "$ROOT$ripgrep/crates/ignore/tests/gitignore_matched_path_or_any_parents_tests.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": { + "simd-accel": [ + "globset/simd-accel" + ] + }, + "manifest_path": "$ROOT$ripgrep/crates/ignore/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant " + ], + "categories": [], + "keywords": [ + "glob", + "ignore", + "gitignore", + "pattern", + "file" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/ignore", + "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/ignore", + "documentation": "https://docs.rs/ignore", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "itoa", + "version": "1.0.6", + "id": "itoa 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Fast integer primitive to string conversion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "no-panic", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "itoa", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/itoa-1.0.6/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/itoa-1.0.6/tests/test.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "bench", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/itoa-1.0.6/benches/bench.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "no-panic": [ + "dep:no-panic" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/itoa-1.0.6/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "targets": [ + "x86_64-unknown-linux-gnu" + ] + } + } + }, + "publish": null, + "authors": [ + "David Tolnay " + ], + "categories": [ + "value-formatting", + "no-std" + ], + "keywords": [ + "integer" + ], + "readme": "README.md", + "repository": "https://github.com/dtolnay/itoa", + "homepage": null, + "documentation": "https://docs.rs/itoa", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": "1.36" + }, + { + "name": "jemalloc-sys", + "version": "0.5.3+5.3.0-patched", + "id": "jemalloc-sys 0.5.3+5.3.0-patched (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "Rust FFI bindings to jemalloc\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "libc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2.8", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "cc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.13", + "kind": "build", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "jemalloc-sys", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemalloc-sys-0.5.3+5.3.0-patched/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "malloc_conf_empty", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemalloc-sys-0.5.3+5.3.0-patched/tests/malloc_conf_empty.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "malloc_conf_set", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemalloc-sys-0.5.3+5.3.0-patched/tests/malloc_conf_set.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "unprefixed_malloc", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemalloc-sys-0.5.3+5.3.0-patched/tests/unprefixed_malloc.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemalloc-sys-0.5.3+5.3.0-patched/build.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "background_threads": [ + "background_threads_runtime_support" + ], + "background_threads_runtime_support": [], + "debug": [], + "default": [ + "background_threads_runtime_support" + ], + "disable_initial_exec_tls": [], + "profiling": [], + "stats": [], + "unprefixed_malloc_on_supported_platforms": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemalloc-sys-0.5.3+5.3.0-patched/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "rustdoc-args": [ + "--cfg", + "jemallocator_docs" + ] + } + } + }, + "publish": null, + "authors": [ + "Alex Crichton ", + "Gonzalo Brito Gadeschi ", + "The TiKV Project Developers" + ], + "categories": [], + "keywords": [ + "allocator", + "jemalloc" + ], + "readme": "README.md", + "repository": "https://github.com/tikv/jemallocator", + "homepage": "https://github.com/tikv/jemallocator", + "documentation": "https://docs.rs/jemallocator-sys", + "edition": "2018", + "links": "jemalloc", + "default_run": null, + "rust_version": null + }, + { + "name": "jemallocator", + "version": "0.5.0", + "id": "jemallocator 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "A Rust allocator backed by jemalloc\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "jemalloc-sys", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.5.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "libc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2.8", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "paste", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "jemallocator", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": false + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "background_thread_defaults", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/background_thread_defaults.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "background_thread_enabled", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/background_thread_enabled.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "ffi", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/ffi.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "grow_in_place", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/grow_in_place.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "malloctl", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/malloctl.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "shrink_in_place", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/shrink_in_place.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "smoke", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/smoke.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "smoke_ffi", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/smoke_ffi.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "usable_size", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/usable_size.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "roundtrip", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/benches/roundtrip.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "alloc_trait": [], + "background_threads": [ + "jemalloc-sys/background_threads" + ], + "background_threads_runtime_support": [ + "jemalloc-sys/background_threads_runtime_support" + ], + "debug": [ + "jemalloc-sys/debug" + ], + "default": [ + "background_threads_runtime_support" + ], + "disable_initial_exec_tls": [ + "jemalloc-sys/disable_initial_exec_tls" + ], + "profiling": [ + "jemalloc-sys/profiling" + ], + "stats": [ + "jemalloc-sys/stats" + ], + "unprefixed_malloc_on_supported_platforms": [ + "jemalloc-sys/unprefixed_malloc_on_supported_platforms" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "features": [], + "rustdoc-args": [ + "--cfg", + "jemallocator_docs" + ] + } + } + }, + "publish": null, + "authors": [ + "Alex Crichton ", + "Gonzalo Brito Gadeschi ", + "Simon Sapin ", + "Steven Fackler ", + "The TiKV Project Developers" + ], + "categories": [ + "memory-management", + "api-bindings" + ], + "keywords": [ + "allocator", + "jemalloc" + ], + "readme": "README.md", + "repository": "https://github.com/tikv/jemallocator", + "homepage": "https://github.com/tikv/jemallocator", + "documentation": "https://docs.rs/jemallocator", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "jobserver", + "version": "0.1.26", + "id": "jobserver 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "An implementation of the GNU make jobserver for Rust\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "futures", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "num_cpus", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "tempfile", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "tokio-core", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "tokio-process", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "libc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2.50", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": "cfg(unix)", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "jobserver", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "client", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/tests/client.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "server", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/tests/server.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "client-of-myself", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/tests/client-of-myself.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "make-as-a-client", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/tests/make-as-a-client.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "helper", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/tests/helper.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Alex Crichton " + ], + "categories": [], + "keywords": [], + "readme": "README.md", + "repository": "https://github.com/alexcrichton/jobserver-rs", + "homepage": "https://github.com/alexcrichton/jobserver-rs", + "documentation": "https://docs.rs/jobserver", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "lazy_static", + "version": "1.4.0", + "id": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "A macro for declaring lazily evaluated statics in Rust.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "spin", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.5.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "doc-comment", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "lazy_static", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "no_std", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/tests/no_std.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/tests/test.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": { + "spin": [ + "dep:spin" + ], + "spin_no_std": [ + "spin" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Marvin Löbel " + ], + "categories": [ + "no-std", + "rust-patterns", + "memory-management" + ], + "keywords": [ + "macro", + "lazy", + "static" + ], + "readme": "README.md", + "repository": "https://github.com/rust-lang-nursery/lazy-static.rs", + "homepage": null, + "documentation": "https://docs.rs/lazy_static", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "libc", + "version": "0.2.142", + "id": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Raw FFI bindings to platform libraries like libc.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "rustc-std-workspace-core", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "libc", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "const_fn", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/tests/const_fn.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/build.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "align": [], + "const-extern-fn": [], + "default": [ + "std" + ], + "extra_traits": [], + "rustc-dep-of-std": [ + "align", + "rustc-std-workspace-core" + ], + "rustc-std-workspace-core": [ + "dep:rustc-std-workspace-core" + ], + "std": [], + "use_std": [ + "std" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "features": [ + "const-extern-fn", + "extra_traits" + ] + } + } + }, + "publish": null, + "authors": [ + "The Rust Project Developers" + ], + "categories": [ + "external-ffi-bindings", + "no-std", + "os" + ], + "keywords": [ + "libc", + "ffi", + "bindings", + "operating", + "system" + ], + "readme": "README.md", + "repository": "https://github.com/rust-lang/libc", + "homepage": "https://github.com/rust-lang/libc", + "documentation": "https://docs.rs/libc/", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "log", + "version": "0.4.17", + "id": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A lightweight logging facade for Rust\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "cfg-if", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "sval", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "=1.0.0-alpha.5", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "value-bag", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "=1.0.0-alpha.9", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustversion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "derive" + ], + "target": null, + "registry": null + }, + { + "name": "serde_test", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "sval", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "=1.0.0-alpha.5", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "derive" + ], + "target": null, + "registry": null + }, + { + "name": "value-bag", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "=1.0.0-alpha.9", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "test" + ], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "log", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/log-0.4.17/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "filters", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/log-0.4.17/tests/filters.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "macros", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/log-0.4.17/tests/macros.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "value", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/log-0.4.17/benches/value.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/log-0.4.17/build.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "kv_unstable": [ + "value-bag" + ], + "kv_unstable_serde": [ + "kv_unstable_std", + "value-bag/serde", + "serde" + ], + "kv_unstable_std": [ + "std", + "kv_unstable", + "value-bag/error" + ], + "kv_unstable_sval": [ + "kv_unstable", + "value-bag/sval", + "sval" + ], + "max_level_debug": [], + "max_level_error": [], + "max_level_info": [], + "max_level_off": [], + "max_level_trace": [], + "max_level_warn": [], + "release_max_level_debug": [], + "release_max_level_error": [], + "release_max_level_info": [], + "release_max_level_off": [], + "release_max_level_trace": [], + "release_max_level_warn": [], + "serde": [ + "dep:serde" + ], + "std": [], + "sval": [ + "dep:sval" + ], + "value-bag": [ + "dep:value-bag" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/log-0.4.17/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "features": [ + "std", + "serde", + "kv_unstable_std", + "kv_unstable_sval", + "kv_unstable_serde" + ] + } + } + }, + "publish": null, + "authors": [ + "The Rust Project Developers" + ], + "categories": [ + "development-tools::debugging" + ], + "keywords": [ + "logging" + ], + "readme": "README.md", + "repository": "https://github.com/rust-lang/log", + "homepage": null, + "documentation": "https://docs.rs/log", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "memchr", + "version": "2.5.0", + "id": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Unlicense/MIT", + "license_file": null, + "description": "Safe interface to memchr.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "compiler_builtins", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.2", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustc-std-workspace-core", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.0", + "kind": null, + "rename": "core", + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "libc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2.18", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "quickcheck", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "memchr", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memchr-2.5.0/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memchr-2.5.0/build.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "compiler_builtins": [ + "dep:compiler_builtins" + ], + "core": [ + "dep:core" + ], + "default": [ + "std" + ], + "libc": [ + "dep:libc" + ], + "rustc-dep-of-std": [ + "core", + "compiler_builtins" + ], + "std": [], + "use_std": [ + "std" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memchr-2.5.0/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant ", + "bluss" + ], + "categories": [], + "keywords": [ + "memchr", + "char", + "scan", + "strchr", + "string" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/memchr", + "homepage": "https://github.com/BurntSushi/memchr", + "documentation": "https://docs.rs/memchr/", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "memmap2", + "version": "0.5.10", + "id": "memmap2 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Cross-platform Rust API for memory-mapped file IO", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "stable_deref_trait", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "owning_ref", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4.1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "tempfile", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "libc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": "cfg(unix)", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "memmap2", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memmap2-0.5.10/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "cat", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memmap2-0.5.10/examples/cat.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "stable_deref_trait": [ + "dep:stable_deref_trait" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memmap2-0.5.10/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Dan Burkert ", + "Yevhenii Reizner " + ], + "categories": [], + "keywords": [ + "mmap", + "memory-map", + "io", + "file" + ], + "readme": "README.md", + "repository": "https://github.com/RazrFalcon/memmap2-rs", + "homepage": null, + "documentation": "https://docs.rs/memmap2", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "once_cell", + "version": "1.17.1", + "id": "once_cell 1.17.1 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Single assignment cells and lazy values.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "atomic-polyfill", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": null, + "rename": "atomic_polyfill", + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "critical-section", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": null, + "rename": "critical_section", + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "parking_lot_core", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.9.3", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "critical-section", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1.1", + "kind": "dev", + "rename": "critical_section", + "optional": false, + "uses_default_features": true, + "features": [ + "std" + ], + "target": null, + "registry": null + }, + { + "name": "crossbeam-utils", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8.7", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "lazy_static", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.2.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "once_cell", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/src/lib.rs", + "edition": "2021", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "bench", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/bench.rs", + "edition": "2021", + "required-features": [ + "std" + ], + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "bench_acquire", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/bench_acquire.rs", + "edition": "2021", + "required-features": [ + "std" + ], + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "bench_vs_lazy_static", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/bench_vs_lazy_static.rs", + "edition": "2021", + "required-features": [ + "std" + ], + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "lazy_static", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/lazy_static.rs", + "edition": "2021", + "required-features": [ + "std" + ], + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "reentrant_init_deadlocks", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/reentrant_init_deadlocks.rs", + "edition": "2021", + "required-features": [ + "std" + ], + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "regex", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/regex.rs", + "edition": "2021", + "required-features": [ + "std" + ], + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "test_synchronization", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/test_synchronization.rs", + "edition": "2021", + "required-features": [ + "std" + ], + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "it", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/tests/it.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": { + "alloc": [ + "race" + ], + "atomic-polyfill": [ + "critical-section" + ], + "atomic_polyfill": [ + "dep:atomic_polyfill" + ], + "critical-section": [ + "critical_section", + "atomic_polyfill" + ], + "critical_section": [ + "dep:critical_section" + ], + "default": [ + "std" + ], + "parking_lot": [ + "parking_lot_core" + ], + "parking_lot_core": [ + "dep:parking_lot_core" + ], + "race": [], + "std": [ + "alloc" + ], + "unstable": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "all-features": true + } + } + }, + "publish": null, + "authors": [ + "Aleksey Kladov " + ], + "categories": [ + "rust-patterns", + "memory-management" + ], + "keywords": [ + "lazy", + "static" + ], + "readme": "README.md", + "repository": "https://github.com/matklad/once_cell", + "homepage": null, + "documentation": "https://docs.rs/once_cell", + "edition": "2021", + "links": null, + "default_run": null, + "rust_version": "1.56" + }, + { + "name": "pcre2", + "version": "0.2.3", + "id": "pcre2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Unlicense/MIT", + "license_file": null, + "description": "High level wrapper library for PCRE2.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "libc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2.46", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "log", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4.5", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "pcre2-sys", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "thread_local", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "pcre2", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pcre2-0.2.3/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pcre2-0.2.3/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant " + ], + "categories": [ + "text-processing" + ], + "keywords": [ + "pcre", + "pcre2", + "regex", + "jit", + "perl" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/rust-pcre2", + "homepage": "https://github.com/BurntSushi/rust-pcre2", + "documentation": "https://docs.rs/pcre2", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "pcre2-sys", + "version": "0.2.5", + "id": "pcre2-sys 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Unlicense/MIT", + "license_file": null, + "description": "Low level bindings to PCRE2.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "libc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "cc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "build", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "parallel" + ], + "target": null, + "registry": null + }, + { + "name": "pkg-config", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.13", + "kind": "build", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "pcre2-sys", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pcre2-sys-0.2.5/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pcre2-sys-0.2.5/build.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pcre2-sys-0.2.5/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant " + ], + "categories": [ + "external-ffi-bindings" + ], + "keywords": [ + "pcre", + "pcre2", + "regex", + "jit" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/rust-pcre2", + "homepage": "https://github.com/BurntSushi/rust-pcre2", + "documentation": "https://docs.rs/pcre2-sys", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "pkg-config", + "version": "0.3.26", + "id": "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A library to run the pkg-config system tool at build time in order to be used in\nCargo build scripts.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "lazy_static", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "pkg-config", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pkg-config-0.3.26/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pkg-config-0.3.26/tests/test.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pkg-config-0.3.26/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Alex Crichton " + ], + "categories": [], + "keywords": [ + "build-dependencies" + ], + "readme": "README.md", + "repository": "https://github.com/rust-lang/pkg-config-rs", + "homepage": null, + "documentation": "https://docs.rs/pkg-config", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "proc-macro2", + "version": "1.0.56", + "id": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A substitute implementation of the compiler's `proc_macro` API to decouple token-based libraries from the procedural macro use case.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "unicode-ident", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "quote", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustversion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "proc-macro2", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "comments", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/comments.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "features", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/features.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "marker", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/marker.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/test.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_fmt", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/test_fmt.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_size", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/test_size.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/build.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "default": [ + "proc-macro" + ], + "nightly": [], + "proc-macro": [], + "span-locations": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "rustc-args": [ + "--cfg", + "procmacro2_semver_exempt" + ], + "rustdoc-args": [ + "--cfg", + "procmacro2_semver_exempt", + "--cfg", + "doc_cfg" + ], + "targets": [ + "x86_64-unknown-linux-gnu" + ] + } + }, + "playground": { + "features": [ + "span-locations" + ] + } + }, + "publish": null, + "authors": [ + "David Tolnay ", + "Alex Crichton " + ], + "categories": [ + "development-tools::procedural-macro-helpers" + ], + "keywords": [ + "macros", + "syn" + ], + "readme": "README.md", + "repository": "https://github.com/dtolnay/proc-macro2", + "homepage": null, + "documentation": "https://docs.rs/proc-macro2", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": "1.31" + }, + { + "name": "quote", + "version": "1.0.26", + "id": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Quasi-quoting macro quote!(...)", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "proc-macro2", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.52", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustversion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "trybuild", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.66", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "diff" + ], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "quote", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "compiletest", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/tests/compiletest.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/tests/test.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/build.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "default": [ + "proc-macro" + ], + "proc-macro": [ + "proc-macro2/proc-macro" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "targets": [ + "x86_64-unknown-linux-gnu" + ] + } + } + }, + "publish": null, + "authors": [ + "David Tolnay " + ], + "categories": [ + "development-tools::procedural-macro-helpers" + ], + "keywords": [ + "macros", + "syn" + ], + "readme": "README.md", + "repository": "https://github.com/dtolnay/quote", + "homepage": null, + "documentation": "https://docs.rs/quote/", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": "1.31" + }, + { + "name": "regex", + "version": "1.8.1", + "id": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "An implementation of regular expressions for Rust. This implementation uses\nfinite automata and guarantees linear time matching on all inputs.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "aho-corasick", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "memchr", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.5.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex-syntax", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.7.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "lazy_static", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "quickcheck", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rand", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [ + "getrandom", + "small_rng" + ], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "regex", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/src/lib.rs", + "edition": "2021", + "doc": true, + "doctest": false, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna-bytes", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-bytes.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna-cheat", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-cheat.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna-replace", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-replace.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna-single-cheat", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-single-cheat.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna-single", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-single.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "shootout-regex-dna", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "default", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_default.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "default-bytes", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_default_bytes.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "nfa", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_nfa.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "nfa-utf8bytes", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_nfa_utf8bytes.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "nfa-bytes", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_nfa_bytes.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "backtrack", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_backtrack.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "backtrack-utf8bytes", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_backtrack_utf8bytes.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "backtrack-bytes", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_backtrack_bytes.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "crates-regex", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_crates_regex.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": { + "aho-corasick": [ + "dep:aho-corasick" + ], + "default": [ + "std", + "perf", + "unicode", + "regex-syntax/default" + ], + "memchr": [ + "dep:memchr" + ], + "pattern": [], + "perf": [ + "perf-cache", + "perf-dfa", + "perf-inline", + "perf-literal" + ], + "perf-cache": [], + "perf-dfa": [], + "perf-inline": [], + "perf-literal": [ + "aho-corasick", + "memchr" + ], + "std": [], + "unicode": [ + "unicode-age", + "unicode-bool", + "unicode-case", + "unicode-gencat", + "unicode-perl", + "unicode-script", + "unicode-segment", + "regex-syntax/unicode" + ], + "unicode-age": [ + "regex-syntax/unicode-age" + ], + "unicode-bool": [ + "regex-syntax/unicode-bool" + ], + "unicode-case": [ + "regex-syntax/unicode-case" + ], + "unicode-gencat": [ + "regex-syntax/unicode-gencat" + ], + "unicode-perl": [ + "regex-syntax/unicode-perl" + ], + "unicode-script": [ + "regex-syntax/unicode-script" + ], + "unicode-segment": [ + "regex-syntax/unicode-segment" + ], + "unstable": [ + "pattern" + ], + "use_std": [ + "std" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "The Rust Project Developers" + ], + "categories": [ + "text-processing" + ], + "keywords": [], + "readme": "README.md", + "repository": "https://github.com/rust-lang/regex", + "homepage": "https://github.com/rust-lang/regex", + "documentation": "https://docs.rs/regex", + "edition": "2021", + "links": null, + "default_run": null, + "rust_version": "1.60.0" + }, + { + "name": "regex-automata", + "version": "0.1.10", + "id": "regex-automata 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Unlicense/MIT", + "license_file": null, + "description": "Automata construction and matching using regular expressions.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "fst", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex-syntax", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.6.16", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "bstr", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [ + "std" + ], + "target": null, + "registry": null + }, + { + "name": "lazy_static", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.2.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.82", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde_bytes", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.11", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde_derive", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.82", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "toml", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4.10", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "regex-automata", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-automata-0.1.10/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "default", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-automata-0.1.10/tests/tests.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": true + } + ], + "features": { + "default": [ + "std" + ], + "fst": [ + "dep:fst" + ], + "regex-syntax": [ + "dep:regex-syntax" + ], + "std": [ + "regex-syntax" + ], + "transducer": [ + "std", + "fst" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-automata-0.1.10/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant " + ], + "categories": [ + "text-processing" + ], + "keywords": [ + "regex", + "dfa", + "automata", + "automaton", + "nfa" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/regex-automata", + "homepage": "https://github.com/BurntSushi/regex-automata", + "documentation": "https://docs.rs/regex-automata", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "regex-syntax", + "version": "0.6.29", + "id": "regex-syntax 0.6.29 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A regular expression parser.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "regex-syntax", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.6.29/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "bench", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.6.29/benches/bench.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "default": [ + "unicode" + ], + "unicode": [ + "unicode-age", + "unicode-bool", + "unicode-case", + "unicode-gencat", + "unicode-perl", + "unicode-script", + "unicode-segment" + ], + "unicode-age": [], + "unicode-bool": [], + "unicode-case": [], + "unicode-gencat": [], + "unicode-perl": [], + "unicode-script": [], + "unicode-segment": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.6.29/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "The Rust Project Developers" + ], + "categories": [], + "keywords": [], + "readme": "README.md", + "repository": "https://github.com/rust-lang/regex", + "homepage": "https://github.com/rust-lang/regex", + "documentation": "https://docs.rs/regex-syntax", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "regex-syntax", + "version": "0.7.1", + "id": "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A regular expression parser.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "regex-syntax", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.7.1/src/lib.rs", + "edition": "2021", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "bench", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.7.1/benches/bench.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "default": [ + "std", + "unicode" + ], + "std": [], + "unicode": [ + "unicode-age", + "unicode-bool", + "unicode-case", + "unicode-gencat", + "unicode-perl", + "unicode-script", + "unicode-segment" + ], + "unicode-age": [], + "unicode-bool": [], + "unicode-case": [], + "unicode-gencat": [], + "unicode-perl": [], + "unicode-script": [], + "unicode-segment": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.7.1/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "all-features": true, + "rustdoc-args": [ + "--cfg", + "docsrs" + ] + } + } + }, + "publish": null, + "authors": [ + "The Rust Project Developers" + ], + "categories": [], + "keywords": [], + "readme": "README.md", + "repository": "https://github.com/rust-lang/regex", + "homepage": "https://github.com/rust-lang/regex", + "documentation": "https://docs.rs/regex-syntax", + "edition": "2021", + "links": null, + "default_run": null, + "rust_version": "1.60.0" + }, + { + "name": "ripgrep", + "version": "13.0.0", + "id": "ripgrep 13.0.0 (path+file:///$ROOT$ripgrep)", + "license": "Unlicense OR MIT", + "license_file": null, + "description": "ripgrep is a line-oriented search tool that recursively searches the current\ndirectory for a regex pattern while respecting gitignore rules. ripgrep has\nfirst class support on Windows, macOS and Linux.\n", + "source": null, + "dependencies": [ + { + "name": "bstr", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "clap", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.33.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [ + "suggestions" + ], + "target": null, + "registry": null + }, + { + "name": "grep", + "source": null, + "req": "^0.2.11", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/grep" + }, + { + "name": "ignore", + "source": null, + "req": "^0.4.19", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null, + "path": "$ROOT$ripgrep/crates/ignore" + }, + { + "name": "lazy_static", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "log", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4.5", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.3.5", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde_json", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.23", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "termcolor", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.77", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde_derive", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.77", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "walkdir", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "clap", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.33.0", + "kind": "build", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [ + "suggestions" + ], + "target": null, + "registry": null + }, + { + "name": "lazy_static", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.1.0", + "kind": "build", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "jemallocator", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.5.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": "cfg(all(target_env = \"musl\", target_pointer_width = \"64\"))", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "bin" + ], + "crate_types": [ + "bin" + ], + "name": "rg", + "src_path": "$ROOT$ripgrep/crates/core/main.rs", + "edition": "2018", + "doc": true, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "integration", + "src_path": "$ROOT$ripgrep/tests/tests.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$ripgrep/build.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "pcre2": [ + "grep/pcre2" + ], + "simd-accel": [ + "grep/simd-accel" + ] + }, + "manifest_path": "$ROOT$ripgrep/Cargo.toml", + "metadata": { + "deb": { + "assets": [ + [ + "target/release/rg", + "usr/bin/", + "755" + ], + [ + "COPYING", + "usr/share/doc/ripgrep/", + "644" + ], + [ + "LICENSE-MIT", + "usr/share/doc/ripgrep/", + "644" + ], + [ + "UNLICENSE", + "usr/share/doc/ripgrep/", + "644" + ], + [ + "CHANGELOG.md", + "usr/share/doc/ripgrep/CHANGELOG", + "644" + ], + [ + "README.md", + "usr/share/doc/ripgrep/README", + "644" + ], + [ + "FAQ.md", + "usr/share/doc/ripgrep/FAQ", + "644" + ], + [ + "deployment/deb/rg.1", + "usr/share/man/man1/rg.1", + "644" + ], + [ + "deployment/deb/rg.bash", + "usr/share/bash-completion/completions/rg", + "644" + ], + [ + "deployment/deb/rg.fish", + "usr/share/fish/vendor_completions.d/rg.fish", + "644" + ], + [ + "deployment/deb/_rg", + "usr/share/zsh/vendor-completions/", + "644" + ] + ], + "extended-description": "ripgrep (rg) recursively searches your current directory for a regex pattern.\nBy default, ripgrep will respect your .gitignore and automatically skip hidden\nfiles/directories and binary files.\n", + "features": [ + "pcre2" + ], + "section": "utils" + } + }, + "publish": null, + "authors": [ + "Andrew Gallant " + ], + "categories": [ + "command-line-utilities", + "text-processing" + ], + "keywords": [ + "regex", + "grep", + "egrep", + "search", + "pattern" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/ripgrep", + "homepage": "https://github.com/BurntSushi/ripgrep", + "documentation": "https://github.com/BurntSushi/ripgrep", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": "1.65" + }, + { + "name": "ryu", + "version": "1.0.13", + "id": "ryu 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Apache-2.0 OR BSL-1.0", + "license_file": null, + "description": "Fast floating point to string conversion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "no-panic", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "num_cpus", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.8", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rand", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rand_xorshift", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "ryu", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "upstream_benchmark", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/examples/upstream_benchmark.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "common_test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/common_test.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "d2s_table_test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/d2s_table_test.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "d2s_test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/d2s_test.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "exhaustive", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/exhaustive.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "f2s_test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/f2s_test.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "s2d_test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/s2d_test.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "s2f_test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/s2f_test.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "bench", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/benches/bench.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "no-panic": [ + "dep:no-panic" + ], + "small": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "targets": [ + "x86_64-unknown-linux-gnu" + ] + } + } + }, + "publish": null, + "authors": [ + "David Tolnay " + ], + "categories": [ + "value-formatting", + "no-std" + ], + "keywords": [ + "float" + ], + "readme": "README.md", + "repository": "https://github.com/dtolnay/ryu", + "homepage": null, + "documentation": "https://docs.rs/ryu", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": "1.36" + }, + { + "name": "same-file", + "version": "1.0.6", + "id": "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Unlicense/MIT", + "license_file": null, + "description": "A simple crate for determining whether two file paths point to the same file.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "doc-comment", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "winapi-util", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": "cfg(windows)", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "same-file", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/same-file-1.0.6/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "is_same_file", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/same-file-1.0.6/examples/is_same_file.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "is_stderr", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/same-file-1.0.6/examples/is_stderr.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/same-file-1.0.6/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant " + ], + "categories": [], + "keywords": [ + "same", + "file", + "equal", + "inode" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/same-file", + "homepage": "https://github.com/BurntSushi/same-file", + "documentation": "https://docs.rs/same-file", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "serde", + "version": "1.0.160", + "id": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A generic serialization/deserialization framework", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "serde_derive", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "=1.0.160", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde_derive", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "serde", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.160/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.160/build.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "alloc": [], + "default": [ + "std" + ], + "derive": [ + "serde_derive" + ], + "rc": [], + "serde_derive": [ + "dep:serde_derive" + ], + "std": [], + "unstable": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.160/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "features": [ + "derive" + ], + "targets": [ + "x86_64-unknown-linux-gnu" + ] + } + }, + "playground": { + "features": [ + "derive", + "rc" + ] + } + }, + "publish": null, + "authors": [ + "Erick Tryzelaar ", + "David Tolnay " + ], + "categories": [ + "encoding", + "no-std" + ], + "keywords": [ + "serde", + "serialization", + "no_std" + ], + "readme": "crates-io.md", + "repository": "https://github.com/serde-rs/serde", + "homepage": "https://serde.rs", + "documentation": "https://docs.rs/serde", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": "1.19" + }, + { + "name": "serde_derive", + "version": "1.0.160", + "id": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Macros 1.1 implementation of #[derive(Serialize, Deserialize)]", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "proc-macro2", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "quote", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "syn", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.0.3", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "proc-macro" + ], + "crate_types": [ + "proc-macro" + ], + "name": "serde_derive", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_derive-1.0.160/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_derive-1.0.160/build.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "default": [], + "deserialize_in_place": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_derive-1.0.160/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "targets": [ + "x86_64-unknown-linux-gnu" + ] + } + } + }, + "publish": null, + "authors": [ + "Erick Tryzelaar ", + "David Tolnay " + ], + "categories": [ + "no-std" + ], + "keywords": [ + "serde", + "serialization", + "no_std", + "derive" + ], + "readme": "crates-io.md", + "repository": "https://github.com/serde-rs/serde", + "homepage": "https://serde.rs", + "documentation": "https://serde.rs/derive.html", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": "1.56" + }, + { + "name": "serde_json", + "version": "1.0.96", + "id": "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "A JSON serialization file format", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "indexmap", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.5.2", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [ + "std" + ], + "target": null, + "registry": null + }, + { + "name": "itoa", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "ryu", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.100", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "automod", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "indoc", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "ref-cast", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustversion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.100", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "derive" + ], + "target": null, + "registry": null + }, + { + "name": "serde_bytes", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.11", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde_derive", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "serde_stacker", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "trybuild", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.49", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "diff" + ], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "serde_json", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "compiletest", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/compiletest.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "debug", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/debug.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "lexical", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/lexical.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "map", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/map.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "regression", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/regression.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "stream", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/stream.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/test.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/build.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "alloc": [ + "serde/alloc" + ], + "arbitrary_precision": [], + "default": [ + "std" + ], + "float_roundtrip": [], + "indexmap": [ + "dep:indexmap" + ], + "preserve_order": [ + "indexmap", + "std" + ], + "raw_value": [], + "std": [ + "serde/std" + ], + "unbounded_depth": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "features": [ + "raw_value", + "unbounded_depth" + ], + "rustdoc-args": [ + "--cfg", + "docsrs" + ], + "targets": [ + "x86_64-unknown-linux-gnu" + ] + } + }, + "playground": { + "features": [ + "raw_value" + ] + } + }, + "publish": null, + "authors": [ + "Erick Tryzelaar ", + "David Tolnay " + ], + "categories": [ + "encoding", + "parser-implementations", + "no-std" + ], + "keywords": [ + "json", + "serde", + "serialization" + ], + "readme": "README.md", + "repository": "https://github.com/serde-rs/json", + "homepage": null, + "documentation": "https://docs.rs/serde_json", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": "1.36" + }, + { + "name": "strsim", + "version": "0.8.0", + "id": "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT", + "license_file": null, + "description": "Implementations of string similarity metrics.\nIncludes Hamming, Levenshtein, OSA, Damerau-Levenshtein, Jaro, and Jaro-Winkler.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "strsim", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.8.0/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "lib", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.8.0/tests/lib.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "benches", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.8.0/benches/benches.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.8.0/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Danny Guo " + ], + "categories": [], + "keywords": [ + "string", + "similarity", + "Hamming", + "Levenshtein", + "Jaro" + ], + "readme": "README.md", + "repository": "https://github.com/dguo/strsim-rs", + "homepage": "https://github.com/dguo/strsim-rs", + "documentation": "https://docs.rs/strsim/", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "syn", + "version": "2.0.15", + "id": "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Parser for Rust source code", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "proc-macro2", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.55", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "quote", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.25", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "unicode-ident", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "anyhow", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "automod", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "flate2", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "insta", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rayon", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "ref-cast", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "regex", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "reqwest", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.11", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "blocking" + ], + "target": null, + "registry": null + }, + { + "name": "rustversion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "syn-test-suite", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "tar", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4.16", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "termcolor", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "walkdir", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^2.3.2", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "syn", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/src/lib.rs", + "edition": "2021", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "regression", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/regression.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_asyncness", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_asyncness.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_attribute", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_attribute.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_derive_input", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_derive_input.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_expr", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_expr.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_generics", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_generics.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_grouping", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_grouping.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_ident", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_ident.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_item", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_item.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_iterators", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_iterators.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_lit", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_lit.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_meta", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_meta.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_parse_buffer", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_parse_buffer.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_parse_stream", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_parse_stream.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_pat", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_pat.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_path", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_path.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_precedence", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_precedence.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_receiver", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_receiver.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_round_trip", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_round_trip.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_shebang", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_shebang.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_should_parse", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_should_parse.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_size", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_size.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_stmt", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_stmt.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_token_trees", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_token_trees.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_ty", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_ty.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "test_visibility", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_visibility.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "zzz_stable", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/zzz_stable.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "rust", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/benches/rust.rs", + "edition": "2021", + "required-features": [ + "full", + "parsing" + ], + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "file", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/benches/file.rs", + "edition": "2021", + "required-features": [ + "full", + "parsing" + ], + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "clone-impls": [], + "default": [ + "derive", + "parsing", + "printing", + "clone-impls", + "proc-macro" + ], + "derive": [], + "extra-traits": [], + "fold": [], + "full": [], + "parsing": [], + "printing": [ + "quote" + ], + "proc-macro": [ + "proc-macro2/proc-macro", + "quote/proc-macro" + ], + "quote": [ + "dep:quote" + ], + "test": [ + "syn-test-suite/all-features" + ], + "visit": [], + "visit-mut": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "all-features": true, + "rustdoc-args": [ + "--cfg", + "doc_cfg" + ], + "targets": [ + "x86_64-unknown-linux-gnu" + ] + } + }, + "playground": { + "features": [ + "full", + "visit", + "visit-mut", + "fold", + "extra-traits" + ] + } + }, + "publish": null, + "authors": [ + "David Tolnay " + ], + "categories": [ + "development-tools::procedural-macro-helpers", + "parser-implementations" + ], + "keywords": [ + "macros", + "syn" + ], + "readme": "README.md", + "repository": "https://github.com/dtolnay/syn", + "homepage": null, + "documentation": "https://docs.rs/syn", + "edition": "2021", + "links": null, + "default_run": null, + "rust_version": "1.56" + }, + { + "name": "termcolor", + "version": "1.2.0", + "id": "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Unlicense OR MIT", + "license_file": null, + "description": "A simple cross platform library for writing colored text to a terminal.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "winapi-util", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.3", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": "cfg(windows)", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "termcolor", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/termcolor-1.2.0/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/termcolor-1.2.0/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant " + ], + "categories": [], + "keywords": [ + "windows", + "win", + "color", + "ansi", + "console" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/termcolor", + "homepage": "https://github.com/BurntSushi/termcolor", + "documentation": "https://docs.rs/termcolor", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "textwrap", + "version": "0.11.0", + "id": "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT", + "license_file": null, + "description": "Textwrap is a small library for word wrapping, indenting, and\ndedenting strings.\n\nYou can use it to format strings (such as help and error messages) for\ndisplay in commandline applications. It is designed to be efficient\nand handle Unicode characters correctly.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "hyphenation", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.7.1", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [ + "embed_all" + ], + "target": null, + "registry": null + }, + { + "name": "term_size", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3.0", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "unicode-width", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.3", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "lipsum", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.6", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rand", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.6", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rand_xorshift", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "version-sync", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.6", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "textwrap", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/textwrap-0.11.0/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "layout", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/textwrap-0.11.0/examples/layout.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "example" + ], + "crate_types": [ + "bin" + ], + "name": "termwidth", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/textwrap-0.11.0/examples/termwidth.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "version-numbers", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/textwrap-0.11.0/tests/version-numbers.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "linear", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/textwrap-0.11.0/benches/linear.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "hyphenation": [ + "dep:hyphenation" + ], + "term_size": [ + "dep:term_size" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/textwrap-0.11.0/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "all-features": true + } + } + }, + "publish": null, + "authors": [ + "Martin Geisler " + ], + "categories": [ + "text-processing", + "command-line-interface" + ], + "keywords": [ + "text", + "formatting", + "wrap", + "typesetting", + "hyphenation" + ], + "readme": "README.md", + "repository": "https://github.com/mgeisler/textwrap", + "homepage": null, + "documentation": "https://docs.rs/textwrap/", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "thread_local", + "version": "1.1.7", + "id": "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT OR Apache-2.0", + "license_file": null, + "description": "Per-object thread-local storage", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "cfg-if", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.0", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "once_cell", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.5.2", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "criterion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4.0", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "thread_local", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/thread_local-1.1.7/src/lib.rs", + "edition": "2021", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "thread_local", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/thread_local-1.1.7/benches/thread_local.rs", + "edition": "2021", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "nightly": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/thread_local-1.1.7/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Amanieu d'Antras " + ], + "categories": [], + "keywords": [ + "thread_local", + "concurrent", + "thread" + ], + "readme": "README.md", + "repository": "https://github.com/Amanieu/thread_local-rs", + "homepage": null, + "documentation": "https://docs.rs/thread_local/", + "edition": "2021", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "unicode-ident", + "version": "1.0.8", + "id": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "(MIT OR Apache-2.0) AND Unicode-DFS-2016", + "license_file": null, + "description": "Determine whether characters have the XID_Start or XID_Continue properties according to Unicode Standard Annex #31", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "criterion", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "fst", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rand", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.8", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "small_rng" + ], + "target": null, + "registry": null + }, + { + "name": "roaring", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.10", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "ucd-trie", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": false, + "features": [], + "target": null, + "registry": null + }, + { + "name": "unicode-xid", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.2.4", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "unicode-ident", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "compare", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/tests/compare.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "test" + ], + "crate_types": [ + "bin" + ], + "name": "static_size", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/tests/static_size.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": true + }, + { + "kind": [ + "bench" + ], + "crate_types": [ + "bin" + ], + "name": "xid", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/benches/xid.rs", + "edition": "2018", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "targets": [ + "x86_64-unknown-linux-gnu" + ] + } + } + }, + "publish": null, + "authors": [ + "David Tolnay " + ], + "categories": [ + "development-tools::procedural-macro-helpers", + "no-std" + ], + "keywords": [ + "unicode", + "xid" + ], + "readme": "README.md", + "repository": "https://github.com/dtolnay/unicode-ident", + "homepage": null, + "documentation": "https://docs.rs/unicode-ident", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": "1.31" + }, + { + "name": "unicode-width", + "version": "0.1.10", + "id": "unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "Determine displayed width of `char` and `str` types\naccording to Unicode Standard Annex #11 rules.\n", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "compiler_builtins", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1", + "kind": null, + "rename": null, + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustc-std-workspace-core", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": "core", + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "rustc-std-workspace-std", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0", + "kind": null, + "rename": "std", + "optional": true, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "unicode-width", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-width-0.1.10/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": { + "bench": [], + "compiler_builtins": [ + "dep:compiler_builtins" + ], + "core": [ + "dep:core" + ], + "default": [], + "no_std": [], + "rustc-dep-of-std": [ + "std", + "core", + "compiler_builtins" + ], + "std": [ + "dep:std" + ] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-width-0.1.10/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "kwantam ", + "Manish Goregaokar " + ], + "categories": [], + "keywords": [ + "text", + "width", + "unicode" + ], + "readme": "README.md", + "repository": "https://github.com/unicode-rs/unicode-width", + "homepage": "https://github.com/unicode-rs/unicode-width", + "documentation": "https://unicode-rs.github.io/unicode-width", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "walkdir", + "version": "2.3.3", + "id": "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Unlicense/MIT", + "license_file": null, + "description": "Recursively walk a directory.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "same-file", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^1.0.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "doc-comment", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3", + "kind": "dev", + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": null, + "registry": null + }, + { + "name": "winapi-util", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.1.1", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": "cfg(windows)", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "walkdir", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/walkdir-2.3.3/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/walkdir-2.3.3/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Andrew Gallant " + ], + "categories": [ + "filesystem" + ], + "keywords": [ + "directory", + "recursive", + "walk", + "iterator" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/walkdir", + "homepage": "https://github.com/BurntSushi/walkdir", + "documentation": "https://docs.rs/walkdir/", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "winapi", + "version": "0.3.9", + "id": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "Raw FFI bindings for all of Windows API.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "winapi-i686-pc-windows-gnu", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": "i686-pc-windows-gnu", + "registry": null + }, + { + "name": "winapi-x86_64-pc-windows-gnu", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.4", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [], + "target": "x86_64-pc-windows-gnu", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "winapi", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-0.3.9/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-0.3.9/build.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": { + "accctrl": [], + "aclapi": [], + "activation": [], + "adhoc": [], + "appmgmt": [], + "audioclient": [], + "audiosessiontypes": [], + "avrt": [], + "basetsd": [], + "bcrypt": [], + "bits": [], + "bits10_1": [], + "bits1_5": [], + "bits2_0": [], + "bits2_5": [], + "bits3_0": [], + "bits4_0": [], + "bits5_0": [], + "bitscfg": [], + "bitsmsg": [], + "bluetoothapis": [], + "bluetoothleapis": [], + "bthdef": [], + "bthioctl": [], + "bthledef": [], + "bthsdpdef": [], + "bugcodes": [], + "cderr": [], + "cfg": [], + "cfgmgr32": [], + "cguid": [], + "combaseapi": [], + "coml2api": [], + "commapi": [], + "commctrl": [], + "commdlg": [], + "commoncontrols": [], + "consoleapi": [], + "corecrt": [], + "corsym": [], + "d2d1": [], + "d2d1_1": [], + "d2d1_2": [], + "d2d1_3": [], + "d2d1effectauthor": [], + "d2d1effects": [], + "d2d1effects_1": [], + "d2d1effects_2": [], + "d2d1svg": [], + "d2dbasetypes": [], + "d3d": [], + "d3d10": [], + "d3d10_1": [], + "d3d10_1shader": [], + "d3d10effect": [], + "d3d10misc": [], + "d3d10sdklayers": [], + "d3d10shader": [], + "d3d11": [], + "d3d11_1": [], + "d3d11_2": [], + "d3d11_3": [], + "d3d11_4": [], + "d3d11on12": [], + "d3d11sdklayers": [], + "d3d11shader": [], + "d3d11tokenizedprogramformat": [], + "d3d12": [], + "d3d12sdklayers": [], + "d3d12shader": [], + "d3d9": [], + "d3d9caps": [], + "d3d9types": [], + "d3dcommon": [], + "d3dcompiler": [], + "d3dcsx": [], + "d3dkmdt": [], + "d3dkmthk": [], + "d3dukmdt": [], + "d3dx10core": [], + "d3dx10math": [], + "d3dx10mesh": [], + "datetimeapi": [], + "davclnt": [], + "dbghelp": [], + "dbt": [], + "dcommon": [], + "dcomp": [], + "dcompanimation": [], + "dcomptypes": [], + "dde": [], + "ddraw": [], + "ddrawi": [], + "ddrawint": [], + "debug": [ + "impl-debug" + ], + "debugapi": [], + "devguid": [], + "devicetopology": [], + "devpkey": [], + "devpropdef": [], + "dinput": [], + "dinputd": [], + "dispex": [], + "dmksctl": [], + "dmusicc": [], + "docobj": [], + "documenttarget": [], + "dot1x": [], + "dpa_dsa": [], + "dpapi": [], + "dsgetdc": [], + "dsound": [], + "dsrole": [], + "dvp": [], + "dwmapi": [], + "dwrite": [], + "dwrite_1": [], + "dwrite_2": [], + "dwrite_3": [], + "dxdiag": [], + "dxfile": [], + "dxgi": [], + "dxgi1_2": [], + "dxgi1_3": [], + "dxgi1_4": [], + "dxgi1_5": [], + "dxgi1_6": [], + "dxgidebug": [], + "dxgiformat": [], + "dxgitype": [], + "dxva2api": [], + "dxvahd": [], + "eaptypes": [], + "enclaveapi": [], + "endpointvolume": [], + "errhandlingapi": [], + "everything": [], + "evntcons": [], + "evntprov": [], + "evntrace": [], + "excpt": [], + "exdisp": [], + "fibersapi": [], + "fileapi": [], + "functiondiscoverykeys_devpkey": [], + "gl-gl": [], + "guiddef": [], + "handleapi": [], + "heapapi": [], + "hidclass": [], + "hidpi": [], + "hidsdi": [], + "hidusage": [], + "highlevelmonitorconfigurationapi": [], + "hstring": [], + "http": [], + "ifdef": [], + "ifmib": [], + "imm": [], + "impl-debug": [], + "impl-default": [], + "in6addr": [], + "inaddr": [], + "inspectable": [], + "interlockedapi": [], + "intsafe": [], + "ioapiset": [], + "ipexport": [], + "iphlpapi": [], + "ipifcons": [], + "ipmib": [], + "iprtrmib": [], + "iptypes": [], + "jobapi": [], + "jobapi2": [], + "knownfolders": [], + "ks": [], + "ksmedia": [], + "ktmtypes": [], + "ktmw32": [], + "l2cmn": [], + "libloaderapi": [], + "limits": [], + "lmaccess": [], + "lmalert": [], + "lmapibuf": [], + "lmat": [], + "lmcons": [], + "lmdfs": [], + "lmerrlog": [], + "lmjoin": [], + "lmmsg": [], + "lmremutl": [], + "lmrepl": [], + "lmserver": [], + "lmshare": [], + "lmstats": [], + "lmsvc": [], + "lmuse": [], + "lmwksta": [], + "lowlevelmonitorconfigurationapi": [], + "lsalookup": [], + "memoryapi": [], + "minschannel": [], + "minwinbase": [], + "minwindef": [], + "mmdeviceapi": [], + "mmeapi": [], + "mmreg": [], + "mmsystem": [], + "mprapidef": [], + "msaatext": [], + "mscat": [], + "mschapp": [], + "mssip": [], + "mstcpip": [], + "mswsock": [], + "mswsockdef": [], + "namedpipeapi": [], + "namespaceapi": [], + "nb30": [], + "ncrypt": [], + "netioapi": [], + "nldef": [], + "ntddndis": [], + "ntddscsi": [], + "ntddser": [], + "ntdef": [], + "ntlsa": [], + "ntsecapi": [], + "ntstatus": [], + "oaidl": [], + "objbase": [], + "objidl": [], + "objidlbase": [], + "ocidl": [], + "ole2": [], + "oleauto": [], + "olectl": [], + "oleidl": [], + "opmapi": [], + "pdh": [], + "perflib": [], + "physicalmonitorenumerationapi": [], + "playsoundapi": [], + "portabledevice": [], + "portabledeviceapi": [], + "portabledevicetypes": [], + "powerbase": [], + "powersetting": [], + "powrprof": [], + "processenv": [], + "processsnapshot": [], + "processthreadsapi": [], + "processtopologyapi": [], + "profileapi": [], + "propidl": [], + "propkey": [], + "propkeydef": [], + "propsys": [], + "prsht": [], + "psapi": [], + "qos": [], + "realtimeapiset": [], + "reason": [], + "restartmanager": [], + "restrictederrorinfo": [], + "rmxfguid": [], + "roapi": [], + "robuffer": [], + "roerrorapi": [], + "rpc": [], + "rpcdce": [], + "rpcndr": [], + "rtinfo": [], + "sapi": [], + "sapi51": [], + "sapi53": [], + "sapiddk": [], + "sapiddk51": [], + "schannel": [], + "sddl": [], + "securityappcontainer": [], + "securitybaseapi": [], + "servprov": [], + "setupapi": [], + "shellapi": [], + "shellscalingapi": [], + "shlobj": [], + "shobjidl": [], + "shobjidl_core": [], + "shtypes": [], + "softpub": [], + "spapidef": [], + "spellcheck": [], + "sporder": [], + "sql": [], + "sqlext": [], + "sqltypes": [], + "sqlucode": [], + "sspi": [], + "std": [], + "stralign": [], + "stringapiset": [], + "strmif": [], + "subauth": [], + "synchapi": [], + "sysinfoapi": [], + "systemtopologyapi": [], + "taskschd": [], + "tcpestats": [], + "tcpmib": [], + "textstor": [], + "threadpoolapiset": [], + "threadpoollegacyapiset": [], + "timeapi": [], + "timezoneapi": [], + "tlhelp32": [], + "transportsettingcommon": [], + "tvout": [], + "udpmib": [], + "unknwnbase": [], + "urlhist": [], + "urlmon": [], + "usb": [], + "usbioctl": [], + "usbiodef": [], + "usbscan": [], + "usbspec": [], + "userenv": [], + "usp10": [], + "utilapiset": [], + "uxtheme": [], + "vadefs": [], + "vcruntime": [], + "vsbackup": [], + "vss": [], + "vsserror": [], + "vswriter": [], + "wbemads": [], + "wbemcli": [], + "wbemdisp": [], + "wbemprov": [], + "wbemtran": [], + "wct": [], + "werapi": [], + "winbase": [], + "wincodec": [], + "wincodecsdk": [], + "wincon": [], + "wincontypes": [], + "wincred": [], + "wincrypt": [], + "windef": [], + "windot11": [], + "windowsceip": [], + "windowsx": [], + "winefs": [], + "winerror": [], + "winevt": [], + "wingdi": [], + "winhttp": [], + "wininet": [], + "winineti": [], + "winioctl": [], + "winnetwk": [], + "winnls": [], + "winnt": [], + "winreg": [], + "winsafer": [], + "winscard": [], + "winsmcrd": [], + "winsock2": [], + "winspool": [], + "winstring": [], + "winsvc": [], + "wintrust": [], + "winusb": [], + "winusbio": [], + "winuser": [], + "winver": [], + "wlanapi": [], + "wlanihv": [], + "wlanihvtypes": [], + "wlantypes": [], + "wlclient": [], + "wmistr": [], + "wnnc": [], + "wow64apiset": [], + "wpdmtpextensions": [], + "ws2bth": [], + "ws2def": [], + "ws2ipdef": [], + "ws2spi": [], + "ws2tcpip": [], + "wtsapi32": [], + "wtypes": [], + "wtypesbase": [], + "xinput": [] + }, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-0.3.9/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "default-target": "x86_64-pc-windows-msvc", + "features": [ + "everything", + "impl-debug", + "impl-default" + ], + "targets": [ + "aarch64-pc-windows-msvc", + "i686-pc-windows-msvc", + "x86_64-pc-windows-msvc" + ] + } + } + }, + "publish": null, + "authors": [ + "Peter Atashian " + ], + "categories": [ + "external-ffi-bindings", + "no-std", + "os::windows-apis" + ], + "keywords": [ + "windows", + "ffi", + "win32", + "com", + "directx" + ], + "readme": "README.md", + "repository": "https://github.com/retep998/winapi-rs", + "homepage": null, + "documentation": "https://docs.rs/winapi/", + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "winapi-i686-pc-windows-gnu", + "version": "0.4.0", + "id": "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "Import libraries for the i686-pc-windows-gnu target. Please don't use this crate directly, depend on winapi instead.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "winapi-i686-pc-windows-gnu", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-i686-pc-windows-gnu-0.4.0/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-i686-pc-windows-gnu-0.4.0/build.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-i686-pc-windows-gnu-0.4.0/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Peter Atashian " + ], + "categories": [], + "keywords": [ + "windows" + ], + "readme": null, + "repository": "https://github.com/retep998/winapi-rs", + "homepage": null, + "documentation": null, + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "winapi-util", + "version": "0.1.5", + "id": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "Unlicense/MIT", + "license_file": null, + "description": "A dumping ground for high level safe wrappers over winapi.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [ + { + "name": "winapi", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "req": "^0.3", + "kind": null, + "rename": null, + "optional": false, + "uses_default_features": true, + "features": [ + "std", + "consoleapi", + "errhandlingapi", + "fileapi", + "minwindef", + "processenv", + "winbase", + "wincon", + "winerror", + "winnt" + ], + "target": "cfg(windows)", + "registry": null + } + ], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "winapi-util", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-util-0.1.5/src/lib.rs", + "edition": "2018", + "doc": true, + "doctest": true, + "test": true + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-util-0.1.5/Cargo.toml", + "metadata": { + "docs": { + "rs": { + "targets": [ + "x86_64-pc-windows-msvc" + ] + } + } + }, + "publish": null, + "authors": [ + "Andrew Gallant " + ], + "categories": [ + "os::windows-apis", + "external-ffi-bindings" + ], + "keywords": [ + "windows", + "winapi", + "util", + "win" + ], + "readme": "README.md", + "repository": "https://github.com/BurntSushi/winapi-util", + "homepage": "https://github.com/BurntSushi/winapi-util", + "documentation": "https://docs.rs/winapi-util", + "edition": "2018", + "links": null, + "default_run": null, + "rust_version": null + }, + { + "name": "winapi-x86_64-pc-windows-gnu", + "version": "0.4.0", + "id": "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "license": "MIT/Apache-2.0", + "license_file": null, + "description": "Import libraries for the x86_64-pc-windows-gnu target. Please don't use this crate directly, depend on winapi instead.", + "source": "registry+https://github.com/rust-lang/crates.io-index", + "dependencies": [], + "targets": [ + { + "kind": [ + "lib" + ], + "crate_types": [ + "lib" + ], + "name": "winapi-x86_64-pc-windows-gnu", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-x86_64-pc-windows-gnu-0.4.0/src/lib.rs", + "edition": "2015", + "doc": true, + "doctest": true, + "test": true + }, + { + "kind": [ + "custom-build" + ], + "crate_types": [ + "bin" + ], + "name": "build-script-build", + "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-x86_64-pc-windows-gnu-0.4.0/build.rs", + "edition": "2015", + "doc": false, + "doctest": false, + "test": false + } + ], + "features": {}, + "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-x86_64-pc-windows-gnu-0.4.0/Cargo.toml", + "metadata": null, + "publish": null, + "authors": [ + "Peter Atashian " + ], + "categories": [], + "keywords": [ + "windows" + ], + "readme": null, + "repository": "https://github.com/retep998/winapi-rs", + "homepage": null, + "documentation": null, + "edition": "2015", + "links": null, + "default_run": null, + "rust_version": null + } + ], + "workspace_members": [ + "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)", + "grep 0.2.11 (path+file:///$ROOT$ripgrep/crates/grep)", + "grep-cli 0.1.7 (path+file:///$ROOT$ripgrep/crates/cli)", + "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", + "grep-pcre2 0.1.6 (path+file:///$ROOT$ripgrep/crates/pcre2)", + "grep-printer 0.1.7 (path+file:///$ROOT$ripgrep/crates/printer)", + "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)", + "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)", + "ignore 0.4.20 (path+file:///$ROOT$ripgrep/crates/ignore)", + "ripgrep 13.0.0 (path+file:///$ROOT$ripgrep)" + ], + "resolve": { + "nodes": [ + { + "id": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "memchr", + "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "default", + "std" + ] + }, + { + "id": "aho-corasick 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "memchr", + "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "default", + "perf-literal", + "std" + ] + }, + { + "id": "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "hermit-abi 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "hermit_abi", + "pkg": "hermit-abi 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(target_os = \"hermit\")" + } + ] + }, + { + "name": "libc", + "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(unix)" + } + ] + }, + { + "name": "winapi", + "pkg": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(windows)" + } + ] + } + ], + "features": [] + }, + { + "id": "base64 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [ + "default", + "std" + ] + }, + { + "id": "bitflags 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [ + "default" + ] + }, + { + "id": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "once_cell 1.17.1 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-automata 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "memchr", + "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "once_cell", + "pkg": "once_cell 1.17.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "regex_automata", + "pkg": "regex-automata 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "serde", + "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "alloc", + "default", + "std", + "unicode" + ] + }, + { + "id": "bytecount 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [ + "runtime-dispatch-simd" + ] + }, + { + "id": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "jobserver 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "jobserver", + "pkg": "jobserver 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "jobserver", + "parallel" + ] + }, + { + "id": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "clap 2.34.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "bitflags 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "bitflags", + "pkg": "bitflags 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "strsim", + "pkg": "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "textwrap", + "pkg": "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "unicode_width", + "pkg": "unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "strsim", + "suggestions" + ] + }, + { + "id": "crossbeam-channel 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.8.15 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "cfg_if", + "pkg": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "crossbeam_utils", + "pkg": "crossbeam-utils 0.8.15 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "crossbeam-utils", + "default", + "std" + ] + }, + { + "id": "crossbeam-utils 0.8.15 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "cfg_if", + "pkg": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "std" + ] + }, + { + "id": "encoding_rs 0.8.32 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "cfg_if", + "pkg": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "alloc", + "default" + ] + }, + { + "id": "encoding_rs_io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "encoding_rs 0.8.32 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "encoding_rs", + "pkg": "encoding_rs 0.8.32 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [ + "default", + "std" + ] + }, + { + "id": "glob 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)", + "dependencies": [ + "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", + "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "glob 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "aho_corasick", + "pkg": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "bstr", + "pkg": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "fnv", + "pkg": "fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "glob", + "pkg": "glob 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ] + }, + { + "name": "lazy_static", + "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ] + }, + { + "name": "log", + "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "regex", + "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "serde_json", + "pkg": "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ] + } + ], + "features": [ + "default", + "log" + ] + }, + { + "id": "grep 0.2.11 (path+file:///$ROOT$ripgrep/crates/grep)", + "dependencies": [ + "grep-cli 0.1.7 (path+file:///$ROOT$ripgrep/crates/cli)", + "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", + "grep-printer 0.1.7 (path+file:///$ROOT$ripgrep/crates/printer)", + "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)", + "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)", + "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "grep_cli", + "pkg": "grep-cli 0.1.7 (path+file:///$ROOT$ripgrep/crates/cli)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "grep_matcher", + "pkg": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "grep_printer", + "pkg": "grep-printer 0.1.7 (path+file:///$ROOT$ripgrep/crates/printer)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "grep_regex", + "pkg": "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "grep_searcher", + "pkg": "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "termcolor", + "pkg": "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ] + }, + { + "name": "walkdir", + "pkg": "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "grep-cli 0.1.7 (path+file:///$ROOT$ripgrep/crates/cli)", + "dependencies": [ + "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", + "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "atty", + "pkg": "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "bstr", + "pkg": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "globset", + "pkg": "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "lazy_static", + "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "log", + "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "regex", + "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "same_file", + "pkg": "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "termcolor", + "pkg": "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "winapi_util", + "pkg": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(windows)" + } + ] + } + ], + "features": [] + }, + { + "id": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", + "dependencies": [ + "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "memchr", + "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "regex", + "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "grep-pcre2 0.1.6 (path+file:///$ROOT$ripgrep/crates/pcre2)", + "dependencies": [ + "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", + "pcre2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "grep_matcher", + "pkg": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "pcre2", + "pkg": "pcre2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "grep-printer 0.1.7 (path+file:///$ROOT$ripgrep/crates/printer)", + "dependencies": [ + "base64 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", + "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)", + "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)", + "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)", + "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "base64", + "pkg": "base64 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "bstr", + "pkg": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "grep_matcher", + "pkg": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "grep_regex", + "pkg": "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)", + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ] + }, + { + "name": "grep_searcher", + "pkg": "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "serde", + "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "serde_json", + "pkg": "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "termcolor", + "pkg": "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "base64", + "default", + "serde", + "serde1", + "serde_json" + ] + }, + { + "id": "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)", + "dependencies": [ + "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", + "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", + "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.6.29 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "aho_corasick", + "pkg": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "bstr", + "pkg": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "grep_matcher", + "pkg": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "log", + "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "regex", + "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "regex_syntax", + "pkg": "regex-syntax 0.6.29 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "thread_local", + "pkg": "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)", + "dependencies": [ + "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bytecount 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "encoding_rs 0.8.32 (registry+https://github.com/rust-lang/crates.io-index)", + "encoding_rs_io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", + "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)", + "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "memmap2 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "bstr", + "pkg": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "bytecount", + "pkg": "bytecount 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "encoding_rs", + "pkg": "encoding_rs 0.8.32 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "encoding_rs_io", + "pkg": "encoding_rs_io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "grep_matcher", + "pkg": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "grep_regex", + "pkg": "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)", + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ] + }, + { + "name": "log", + "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "memmap", + "pkg": "memmap2 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "regex", + "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ] + } + ], + "features": [ + "default" + ] + }, + { + "id": "hermit-abi 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "libc", + "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "default" + ] + }, + { + "id": "ignore 0.4.20 (path+file:///$ROOT$ripgrep/crates/ignore)", + "dependencies": [ + "crossbeam-channel 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", + "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "crossbeam_channel", + "pkg": "crossbeam-channel 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ] + }, + { + "name": "globset", + "pkg": "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "lazy_static", + "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "log", + "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "memchr", + "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "regex", + "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "same_file", + "pkg": "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "thread_local", + "pkg": "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "walkdir", + "pkg": "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "winapi_util", + "pkg": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(windows)" + } + ] + } + ], + "features": [] + }, + { + "id": "itoa 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "jemalloc-sys 0.5.3+5.3.0-patched (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "cc", + "pkg": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "build", + "target": null + } + ] + }, + { + "name": "libc", + "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "background_threads_runtime_support" + ] + }, + { + "id": "jemallocator 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "jemalloc-sys 0.5.3+5.3.0-patched (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "jemalloc_sys", + "pkg": "jemalloc-sys 0.5.3+5.3.0-patched (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "libc", + "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "background_threads_runtime_support", + "default" + ] + }, + { + "id": "jobserver 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "libc", + "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(unix)" + } + ] + } + ], + "features": [] + }, + { + "id": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [ + "default", + "std" + ] + }, + { + "id": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "cfg_if", + "pkg": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [ + "default", + "std" + ] + }, + { + "id": "memmap2 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "libc", + "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(unix)" + } + ] + } + ], + "features": [] + }, + { + "id": "once_cell 1.17.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [ + "alloc", + "default", + "race", + "std" + ] + }, + { + "id": "pcre2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "pcre2-sys 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "libc", + "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "log", + "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "pcre2_sys", + "pkg": "pcre2-sys 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "thread_local", + "pkg": "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "pcre2-sys 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "cc", + "pkg": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "build", + "target": null + } + ] + }, + { + "name": "libc", + "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "pkg_config", + "pkg": "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "build", + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "unicode_ident", + "pkg": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "default", + "proc-macro" + ] + }, + { + "id": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "proc_macro2", + "pkg": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "default", + "proc-macro" + ] + }, + { + "id": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "aho-corasick 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "aho_corasick", + "pkg": "aho-corasick 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "memchr", + "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "regex_syntax", + "pkg": "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "aho-corasick", + "default", + "memchr", + "perf", + "perf-cache", + "perf-dfa", + "perf-inline", + "perf-literal", + "std", + "unicode", + "unicode-age", + "unicode-bool", + "unicode-case", + "unicode-gencat", + "unicode-perl", + "unicode-script", + "unicode-segment" + ] + }, + { + "id": "regex-automata 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "regex-syntax 0.6.29 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [ + "default", + "unicode", + "unicode-age", + "unicode-bool", + "unicode-case", + "unicode-gencat", + "unicode-perl", + "unicode-script", + "unicode-segment" + ] + }, + { + "id": "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [ + "default", + "std", + "unicode", + "unicode-age", + "unicode-bool", + "unicode-case", + "unicode-gencat", + "unicode-perl", + "unicode-script", + "unicode-segment" + ] + }, + { + "id": "ripgrep 13.0.0 (path+file:///$ROOT$ripgrep)", + "dependencies": [ + "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "clap 2.34.0 (registry+https://github.com/rust-lang/crates.io-index)", + "grep 0.2.11 (path+file:///$ROOT$ripgrep/crates/grep)", + "ignore 0.4.20 (path+file:///$ROOT$ripgrep/crates/ignore)", + "jemallocator 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)", + "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "bstr", + "pkg": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "clap", + "pkg": "clap 2.34.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + }, + { + "kind": "build", + "target": null + } + ] + }, + { + "name": "grep", + "pkg": "grep 0.2.11 (path+file:///$ROOT$ripgrep/crates/grep)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "ignore", + "pkg": "ignore 0.4.20 (path+file:///$ROOT$ripgrep/crates/ignore)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "jemallocator", + "pkg": "jemallocator 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(all(target_env = \"musl\", target_pointer_width = \"64\"))" + } + ] + }, + { + "name": "lazy_static", + "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + }, + { + "kind": "build", + "target": null + } + ] + }, + { + "name": "log", + "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "regex", + "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "serde", + "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ] + }, + { + "name": "serde_derive", + "pkg": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ] + }, + { + "name": "serde_json", + "pkg": "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "termcolor", + "pkg": "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "walkdir", + "pkg": "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": "dev", + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "ryu 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "winapi_util", + "pkg": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(windows)" + } + ] + } + ], + "features": [] + }, + { + "id": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "serde_derive", + "pkg": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "alloc", + "default", + "derive", + "serde_derive", + "std" + ] + }, + { + "id": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "proc_macro2", + "pkg": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "quote", + "pkg": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "syn", + "pkg": "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "default" + ] + }, + { + "id": "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "itoa 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "itoa", + "pkg": "itoa 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "ryu", + "pkg": "ryu 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "serde", + "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "default", + "std" + ] + }, + { + "id": "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "proc_macro2", + "pkg": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "quote", + "pkg": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "unicode_ident", + "pkg": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [ + "clone-impls", + "default", + "derive", + "parsing", + "printing", + "proc-macro", + "quote" + ] + }, + { + "id": "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "winapi_util", + "pkg": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(windows)" + } + ] + } + ], + "features": [] + }, + { + "id": "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "unicode_width", + "pkg": "unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "once_cell 1.17.1 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "cfg_if", + "pkg": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "once_cell", + "pkg": "once_cell 1.17.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + } + ], + "features": [] + }, + { + "id": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [ + "default" + ] + }, + { + "id": "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "same_file", + "pkg": "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": null + } + ] + }, + { + "name": "winapi_util", + "pkg": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(windows)" + } + ] + } + ], + "features": [] + }, + { + "id": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "winapi_i686_pc_windows_gnu", + "pkg": "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "i686-pc-windows-gnu" + } + ] + }, + { + "name": "winapi_x86_64_pc_windows_gnu", + "pkg": "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "x86_64-pc-windows-gnu" + } + ] + } + ], + "features": [ + "consoleapi", + "errhandlingapi", + "fileapi", + "minwinbase", + "minwindef", + "processenv", + "std", + "winbase", + "wincon", + "winerror", + "winnt" + ] + }, + { + "id": "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + }, + { + "id": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [ + "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" + ], + "deps": [ + { + "name": "winapi", + "pkg": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "dep_kinds": [ + { + "kind": null, + "target": "cfg(windows)" + } + ] + } + ], + "features": [] + }, + { + "id": "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dependencies": [], + "deps": [], + "features": [] + } + ], + "root": "ripgrep 13.0.0 (path+file:///$ROOT$ripgrep)" + }, + "target_directory": "$ROOT$ripgrep/target", + "version": 1, + "workspace_root": "$ROOT$ripgrep", + "metadata": null +} diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml index f0f1900c78c56..77f02a83101e3 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml @@ -29,9 +29,8 @@ parking_lot = "0.12.1" xflags = "0.3.0" oorandom = "11.1.3" rustc-hash = "1.1.0" -serde = { version = "1.0.137", features = ["derive"] } -serde_json = { version = "1.0.81", features = ["preserve_order"] } -threadpool = "1.8.1" +serde_json = { workspace = true, features = ["preserve_order"] } +serde.workspace = true rayon = "1.6.1" num_cpus = "1.15.0" mimalloc = { version = "0.1.30", default-features = false, optional = true } @@ -45,8 +44,20 @@ tracing-subscriber = { version = "0.3.16", default-features = false, features = ] } tracing-log = "0.1.3" tracing-tree = "0.2.1" +triomphe.workspace = true +nohash-hasher.workspace = true always-assert = "0.1.2" +# These dependencies are unused, but we pin them to a version here to restrict them for our transitive dependencies +# so that we don't pull in duplicates of their dependencies like windows-sys and syn 1 vs 2 +# these would pull in serde 2 +thiserror = "=1.0.39" +serde_repr = "=0.1.11" +# these would pull in windows-sys 0.45.0 +mio = "=0.8.5" +filetime = "=0.2.19" +parking_lot_core = "=0.9.6" + cfg.workspace = true flycheck.workspace = true hir-def.workspace = true @@ -57,7 +68,7 @@ ide-db.workspace = true ide-ssr.workspace = true ide.workspace = true proc-macro-api.workspace = true -proc-macro-srv.workspace = true +proc-macro-srv-cli.workspace = true profile.workspace = true project-model.workspace = true stdx.workspace = true @@ -75,7 +86,6 @@ jemallocator = { version = "0.5.0", package = "tikv-jemallocator", optional = tr [dev-dependencies] expect-test = "1.4.0" -jod-thread = "0.1.2" xshell = "0.2.2" test-utils.workspace = true @@ -85,8 +95,5 @@ mbe.workspace = true [features] jemalloc = ["jemallocator", "profile/jemalloc"] force-always-assert = ["always-assert/force"] -in-rust-tree = [ - "proc-macro-srv/sysroot-abi", - "ide/in-rust-tree", - "syntax/in-rust-tree", -] +sysroot-abi = ["proc-macro-srv-cli/sysroot-abi"] +in-rust-tree = ["sysroot-abi", "ide/in-rust-tree", "syntax/in-rust-tree"] diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs index 4de022b6ed607..91911dd180962 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs @@ -7,7 +7,11 @@ mod logger; mod rustc_wrapper; -use std::{env, fs, path::Path, process}; +use std::{ + env, fs, + path::{Path, PathBuf}, + process, +}; use lsp_server::Connection; use rust_analyzer::{cli::flags, config::Config, from_json, Result}; @@ -74,10 +78,16 @@ fn try_main(flags: flags::RustAnalyzer) -> Result<()> { println!("rust-analyzer {}", rust_analyzer::version()); return Ok(()); } - with_extra_thread("LspServer", run_server)?; - } - flags::RustAnalyzerCmd::ProcMacro(flags::ProcMacro) => { - with_extra_thread("MacroExpander", || proc_macro_srv::cli::run().map_err(Into::into))?; + + // rust-analyzer’s “main thread” is actually + // a secondary latency-sensitive thread with an increased stack size. + // We use this thread intent because any delay in the main loop + // will make actions like hitting enter in the editor slow. + with_extra_thread( + "LspServer", + stdx::thread::ThreadIntent::LatencySensitive, + run_server, + )?; } flags::RustAnalyzerCmd::Parse(cmd) => cmd.run()?, flags::RustAnalyzerCmd::Symbols(cmd) => cmd.run()?, @@ -135,14 +145,17 @@ const STACK_SIZE: usize = 1024 * 1024 * 8; /// space. fn with_extra_thread( thread_name: impl Into, + thread_intent: stdx::thread::ThreadIntent, f: impl FnOnce() -> Result<()> + Send + 'static, ) -> Result<()> { - let handle = - std::thread::Builder::new().name(thread_name.into()).stack_size(STACK_SIZE).spawn(f)?; - match handle.join() { - Ok(res) => res, - Err(panic) => std::panic::resume_unwind(panic), - } + let handle = stdx::thread::Builder::new(thread_intent) + .name(thread_name.into()) + .stack_size(STACK_SIZE) + .spawn(f)?; + + handle.join()?; + + Ok(()) } fn run_server() -> Result<()> { @@ -152,12 +165,18 @@ fn run_server() -> Result<()> { let (initialize_id, initialize_params) = connection.initialize_start()?; tracing::info!("InitializeParams: {}", initialize_params); - let initialize_params = - from_json::("InitializeParams", &initialize_params)?; - - let root_path = match initialize_params - .root_uri + let lsp_types::InitializeParams { + root_uri, + capabilities, + workspace_folders, + initialization_options, + client_info, + .. + } = from_json::("InitializeParams", &initialize_params)?; + + let root_path = match root_uri .and_then(|it| it.to_file_path().ok()) + .map(patch_path_prefix) .and_then(|it| AbsPathBuf::try_from(it).ok()) { Some(it) => it, @@ -167,19 +186,19 @@ fn run_server() -> Result<()> { } }; - let workspace_roots = initialize_params - .workspace_folders + let workspace_roots = workspace_folders .map(|workspaces| { workspaces .into_iter() .filter_map(|it| it.uri.to_file_path().ok()) + .map(patch_path_prefix) .filter_map(|it| AbsPathBuf::try_from(it).ok()) .collect::>() }) .filter(|workspaces| !workspaces.is_empty()) .unwrap_or_else(|| vec![root_path.clone()]); - let mut config = Config::new(root_path, initialize_params.capabilities, workspace_roots); - if let Some(json) = initialize_params.initialization_options { + let mut config = Config::new(root_path, capabilities, workspace_roots); + if let Some(json) = initialization_options { if let Err(e) = config.update(json) { use lsp_types::{ notification::{Notification, ShowMessage}, @@ -208,7 +227,7 @@ fn run_server() -> Result<()> { connection.initialize_finish(initialize_id, initialize_result)?; - if let Some(client_info) = initialize_params.client_info { + if let Some(client_info) = client_info { tracing::info!("Client '{}' {}", client_info.name, client_info.version.unwrap_or_default()); } @@ -222,3 +241,42 @@ fn run_server() -> Result<()> { tracing::info!("server did shut down"); Ok(()) } + +fn patch_path_prefix(path: PathBuf) -> PathBuf { + use std::path::{Component, Prefix}; + if cfg!(windows) { + // VSCode might report paths with the file drive in lowercase, but this can mess + // with env vars set by tools and build scripts executed by r-a such that it invalidates + // cargo's compilations unnecessarily. https://github.com/rust-lang/rust-analyzer/issues/14683 + // So we just uppercase the drive letter here unconditionally. + // (doing it conditionally is a pain because std::path::Prefix always reports uppercase letters on windows) + let mut comps = path.components(); + match comps.next() { + Some(Component::Prefix(prefix)) => { + let prefix = match prefix.kind() { + Prefix::Disk(d) => { + format!("{}:", d.to_ascii_uppercase() as char) + } + Prefix::VerbatimDisk(d) => { + format!(r"\\?\{}:", d.to_ascii_uppercase() as char) + } + _ => return path, + }; + let mut path = PathBuf::new(); + path.push(prefix); + path.extend(comps); + path + } + _ => path, + } + } else { + path + } +} + +#[test] +#[cfg(windows)] +fn patch_path_prefix_works() { + assert_eq!(patch_path_prefix(r"c:\foo\bar".into()), PathBuf::from(r"C:\foo\bar")); + assert_eq!(patch_path_prefix(r"\\?\c:\foo\bar".into()), PathBuf::from(r"\\?\C:\foo\bar")); +} diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/caps.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/caps.rs index 3628670ac98b9..ab06b96814a29 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/caps.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/caps.rs @@ -23,13 +23,14 @@ use crate::semantic_tokens; pub fn server_capabilities(config: &Config) -> ServerCapabilities { ServerCapabilities { - position_encoding: Some(match negotiated_encoding(config.caps()) { - PositionEncoding::Utf8 => PositionEncodingKind::UTF8, + position_encoding: match negotiated_encoding(config.caps()) { + PositionEncoding::Utf8 => Some(PositionEncodingKind::UTF8), PositionEncoding::Wide(wide) => match wide { - WideEncoding::Utf16 => PositionEncodingKind::UTF16, - WideEncoding::Utf32 => PositionEncodingKind::UTF32, + WideEncoding::Utf16 => Some(PositionEncodingKind::UTF16), + WideEncoding::Utf32 => Some(PositionEncodingKind::UTF32), + _ => None, }, - }), + }, text_document_sync: Some(TextDocumentSyncCapability::Options(TextDocumentSyncOptions { open_close: Some(true), change: Some(TextDocumentSyncKind::INCREMENTAL), diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cargo_target_spec.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cargo_target_spec.rs index cf51cf15a0e1d..c7b84c41b33e9 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cargo_target_spec.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cargo_target_spec.rs @@ -3,8 +3,9 @@ use std::mem; use cfg::{CfgAtom, CfgExpr}; -use ide::{Cancellable, FileId, RunnableKind, TestId}; +use ide::{Cancellable, CrateId, FileId, RunnableKind, TestId}; use project_model::{self, CargoFeatures, ManifestPath, TargetKind}; +use rustc_hash::FxHashSet; use vfs::AbsPathBuf; use crate::global_state::GlobalStateSnapshot; @@ -20,7 +21,9 @@ pub(crate) struct CargoTargetSpec { pub(crate) package: String, pub(crate) target: String, pub(crate) target_kind: TargetKind, + pub(crate) crate_id: CrateId, pub(crate) required_features: Vec, + pub(crate) features: FxHashSet, } impl CargoTargetSpec { @@ -73,12 +76,13 @@ impl CargoTargetSpec { } } - let target_required_features = if let Some(mut spec) = spec { + let (allowed_features, target_required_features) = if let Some(mut spec) = spec { + let allowed_features = mem::take(&mut spec.features); let required_features = mem::take(&mut spec.required_features); spec.push_to(&mut args, kind); - required_features + (allowed_features, required_features) } else { - Vec::new() + (Default::default(), Default::default()) }; let cargo_config = snap.config.cargo(); @@ -97,7 +101,9 @@ impl CargoTargetSpec { required_features(cfg, &mut feats); } - feats.extend(features.iter().cloned()); + feats.extend( + features.iter().filter(|&feat| allowed_features.contains(feat)).cloned(), + ); feats.extend(target_required_features); feats.dedup(); @@ -136,6 +142,8 @@ impl CargoTargetSpec { target: target_data.name.clone(), target_kind: target_data.kind, required_features: target_data.required_features.clone(), + features: package_data.features.keys().cloned().collect(), + crate_id, }; Ok(Some(res)) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs index 6ce1de5d32bca..2c2a9a18d2e31 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -8,14 +8,14 @@ use std::{ use hir::{ db::{DefDatabase, ExpandDatabase, HirDatabase}, - AssocItem, Crate, Function, HasSource, HirDisplay, ModuleDef, + AssocItem, Crate, Function, HasCrate, HasSource, HirDisplay, ModuleDef, }; use hir_def::{ body::{BodySourceMap, SyntheticSyntax}, - expr::{ExprId, PatId}, + hir::{ExprId, PatId}, FunctionId, }; -use hir_ty::{Interner, TyExt, TypeFlags}; +use hir_ty::{Interner, Substitution, TyExt, TypeFlags}; use ide::{Analysis, AnalysisHost, LineCol, RootDatabase}; use ide_db::base_db::{ salsa::{self, debug::DebugQueryTable, ParallelDatabase}, @@ -121,14 +121,19 @@ impl flags::AnalysisStats { eprint!(" crates: {num_crates}"); let mut num_decls = 0; let mut funcs = Vec::new(); + let mut adts = Vec::new(); + let mut consts = Vec::new(); while let Some(module) = visit_queue.pop() { if visited_modules.insert(module) { visit_queue.extend(module.children(db)); for decl in module.declarations(db) { num_decls += 1; - if let ModuleDef::Function(f) = decl { - funcs.push(f); + match decl { + ModuleDef::Function(f) => funcs.push(f), + ModuleDef::Adt(a) => adts.push(a), + ModuleDef::Const(c) => consts.push(c), + _ => (), } } @@ -153,6 +158,13 @@ impl flags::AnalysisStats { self.run_inference(&host, db, &vfs, &funcs, verbosity); } + if !self.skip_mir_stats { + self.run_mir_lowering(db, &funcs, verbosity); + } + + self.run_data_layout(db, &adts, verbosity); + self.run_const_eval(db, &consts, verbosity); + let total_span = analysis_sw.elapsed(); eprintln!("{:<20} {total_span}", "Total:"); report_metric("total time", total_span.time.as_millis() as u64, "ms"); @@ -175,9 +187,8 @@ impl flags::AnalysisStats { let mut total_macro_file_size = Bytes::default(); for e in hir::db::ParseMacroExpansionQuery.in_db(db).entries::>() { - if let Some((val, _)) = db.parse_macro_expansion(e.key).value { - total_macro_file_size += syntax_len(val.syntax_node()) - } + let val = db.parse_macro_expansion(e.key).value.0; + total_macro_file_size += syntax_len(val.syntax_node()) } eprintln!("source files: {total_file_size}, macro files: {total_macro_file_size}"); } @@ -189,6 +200,93 @@ impl flags::AnalysisStats { Ok(()) } + fn run_data_layout(&self, db: &RootDatabase, adts: &[hir::Adt], verbosity: Verbosity) { + let mut sw = self.stop_watch(); + let mut all = 0; + let mut fail = 0; + for &a in adts { + if db.generic_params(a.into()).iter().next().is_some() { + // Data types with generics don't have layout. + continue; + } + all += 1; + let Err(e) = db.layout_of_adt(hir_def::AdtId::from(a).into(), Substitution::empty(Interner), a.krate(db).into()) else { + continue; + }; + if verbosity.is_spammy() { + let full_name = a + .module(db) + .path_to_root(db) + .into_iter() + .rev() + .filter_map(|it| it.name(db)) + .chain(Some(a.name(db))) + .map(|it| it.display(db).to_string()) + .join("::"); + println!("Data layout for {full_name} failed due {e:?}"); + } + fail += 1; + } + eprintln!("{:<20} {}", "Data layouts:", sw.elapsed()); + eprintln!("Failed data layouts: {fail} ({}%)", percentage(fail, all)); + report_metric("failed data layouts", fail, "#"); + } + + fn run_const_eval(&self, db: &RootDatabase, consts: &[hir::Const], verbosity: Verbosity) { + let mut sw = self.stop_watch(); + let mut all = 0; + let mut fail = 0; + for &c in consts { + all += 1; + let Err(e) = c.render_eval(db) else { + continue; + }; + if verbosity.is_spammy() { + let full_name = c + .module(db) + .path_to_root(db) + .into_iter() + .rev() + .filter_map(|it| it.name(db)) + .chain(c.name(db)) + .map(|it| it.display(db).to_string()) + .join("::"); + println!("Const eval for {full_name} failed due {e:?}"); + } + fail += 1; + } + eprintln!("{:<20} {}", "Const evaluation:", sw.elapsed()); + eprintln!("Failed const evals: {fail} ({}%)", percentage(fail, all)); + report_metric("failed const evals", fail, "#"); + } + + fn run_mir_lowering(&self, db: &RootDatabase, funcs: &[Function], verbosity: Verbosity) { + let mut sw = self.stop_watch(); + let all = funcs.len() as u64; + let mut fail = 0; + for f in funcs { + let Err(e) = db.mir_body(FunctionId::from(*f).into()) else { + continue; + }; + if verbosity.is_spammy() { + let full_name = f + .module(db) + .path_to_root(db) + .into_iter() + .rev() + .filter_map(|it| it.name(db)) + .chain(Some(f.name(db))) + .map(|it| it.display(db).to_string()) + .join("::"); + println!("Mir body for {full_name} failed due {e:?}"); + } + fail += 1; + } + eprintln!("{:<20} {}", "MIR lowering:", sw.elapsed()); + eprintln!("Mir failed bodies: {fail} ({}%)", percentage(fail, all)); + report_metric("mir failed bodies", fail, "#"); + } + fn run_inference( &self, host: &AnalysisHost, @@ -237,9 +335,10 @@ impl flags::AnalysisStats { .rev() .filter_map(|it| it.name(db)) .chain(Some(f.name(db))) + .map(|it| it.display(db).to_string()) .join("::"); if let Some(only_name) = self.only.as_deref() { - if name.to_string() != only_name && full_name != only_name { + if name.display(db).to_string() != only_name && full_name != only_name { continue; } } @@ -281,7 +380,7 @@ impl flags::AnalysisStats { end.col, )); } else { - bar.println(format!("{name}: Unknown type",)); + bar.println(format!("{}: Unknown type", name.display(db))); } } true @@ -336,7 +435,7 @@ impl flags::AnalysisStats { } else { bar.println(format!( "{}: Expected {}, got {}", - name, + name.display(db), mismatch.expected.display(db), mismatch.actual.display(db) )); @@ -384,7 +483,7 @@ impl flags::AnalysisStats { end.col, )); } else { - bar.println(format!("{name}: Unknown type",)); + bar.println(format!("{}: Unknown type", name.display(db))); } } true @@ -438,7 +537,7 @@ impl flags::AnalysisStats { } else { bar.println(format!( "{}: Expected {}, got {}", - name, + name.display(db), mismatch.expected.display(db), mismatch.actual.display(db) )); @@ -510,7 +609,7 @@ fn location_csv_expr( Ok(s) => s, Err(SyntheticSyntax) => return "synthetic,,".to_string(), }; - let root = db.parse_or_expand(src.file_id).unwrap(); + let root = db.parse_or_expand(src.file_id); let node = src.map(|e| e.to_node(&root).syntax().clone()); let original_range = node.as_ref().original_file_range(db); let path = vfs.file_path(original_range.file_id); @@ -532,7 +631,7 @@ fn location_csv_pat( Ok(s) => s, Err(SyntheticSyntax) => return "synthetic,,".to_string(), }; - let root = db.parse_or_expand(src.file_id).unwrap(); + let root = db.parse_or_expand(src.file_id); let node = src.map(|e| { e.either(|it| it.to_node(&root).syntax().clone(), |it| it.to_node(&root).syntax().clone()) }); @@ -554,7 +653,7 @@ fn expr_syntax_range( ) -> Option<(VfsPath, LineCol, LineCol)> { let src = sm.expr_syntax(expr_id); if let Ok(src) = src { - let root = db.parse_or_expand(src.file_id).unwrap(); + let root = db.parse_or_expand(src.file_id); let node = src.map(|e| e.to_node(&root).syntax().clone()); let original_range = node.as_ref().original_file_range(db); let path = vfs.file_path(original_range.file_id); @@ -576,7 +675,7 @@ fn pat_syntax_range( ) -> Option<(VfsPath, LineCol, LineCol)> { let src = sm.pat_syntax(pat_id); if let Ok(src) = src { - let root = db.parse_or_expand(src.file_id).unwrap(); + let root = db.parse_or_expand(src.file_id); let node = src.map(|e| { e.either( |it| it.to_node(&root).syntax().clone(), diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs index 770612cc9478d..31012c01b9530 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs @@ -66,6 +66,8 @@ xflags::xflags! { optional --memory-usage /// Print the total length of all source and macro files (whitespace is not counted). optional --source-stats + /// Only type check, skip lowering to mir + optional --skip-mir-stats /// Only analyze items matching this path. optional -o, --only path: String @@ -104,14 +106,15 @@ xflags::xflags! { optional --debug snippet: String } - cmd proc-macro {} - cmd lsif { required path: PathBuf } cmd scip { required path: PathBuf + + /// The output path where the SCIP file will be written to. Defaults to `index.scip`. + optional --output path: PathBuf } } } @@ -139,7 +142,6 @@ pub enum RustAnalyzerCmd { Diagnostics(Diagnostics), Ssr(Ssr), Search(Search), - ProcMacro(ProcMacro), Lsif(Lsif), Scip(Scip), } @@ -172,6 +174,7 @@ pub struct AnalysisStats { pub parallel: bool, pub memory_usage: bool, pub source_stats: bool, + pub skip_mir_stats: bool, pub only: Option, pub with_deps: bool, pub no_sysroot: bool, @@ -200,9 +203,6 @@ pub struct Search { pub debug: Option, } -#[derive(Debug)] -pub struct ProcMacro; - #[derive(Debug)] pub struct Lsif { pub path: PathBuf, @@ -211,6 +211,7 @@ pub struct Lsif { #[derive(Debug)] pub struct Scip { pub path: PathBuf, + pub output: Option, } impl RustAnalyzer { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/load_cargo.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/load_cargo.rs index 5a958d963e4b5..4e8f99971674a 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/load_cargo.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/load_cargo.rs @@ -1,14 +1,17 @@ //! Loads a Cargo project into a static instance of analysis, without support //! for incorporating changes. -use std::{convert::identity, path::Path, sync::Arc}; +use std::path::Path; -use anyhow::Result; +use anyhow::{anyhow, Result}; use crossbeam_channel::{unbounded, Receiver}; -use hir::db::DefDatabase; use ide::{AnalysisHost, Change}; -use ide_db::{base_db::CrateGraph, FxHashMap}; +use ide_db::{ + base_db::{CrateGraph, ProcMacros}, + FxHashMap, +}; use proc_macro_api::ProcMacroServer; use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace}; +use triomphe::Arc; use vfs::{loader::Handle, AbsPath, AbsPathBuf}; use crate::reload::{load_proc_macro, ProjectFolders, SourceRootConfig}; @@ -24,7 +27,7 @@ pub struct LoadCargoConfig { #[derive(Debug, Clone, PartialEq, Eq)] pub enum ProcMacroServerChoice { Sysroot, - Explicit(AbsPathBuf, Vec), + Explicit(AbsPathBuf), None, } @@ -66,23 +69,17 @@ pub fn load_workspace( Box::new(loader) }; - let proc_macro_client = match &load_config.with_proc_macro_server { + let proc_macro_server = match &load_config.with_proc_macro_server { ProcMacroServerChoice::Sysroot => ws .find_sysroot_proc_macro_srv() - .ok_or_else(|| "failed to find sysroot proc-macro server".to_owned()) - .and_then(|it| { - ProcMacroServer::spawn(it, identity::<&[&str]>(&[])).map_err(|e| e.to_string()) - }), - ProcMacroServerChoice::Explicit(path, args) => { - ProcMacroServer::spawn(path.clone(), args).map_err(|e| e.to_string()) + .and_then(|it| ProcMacroServer::spawn(it).map_err(Into::into)), + ProcMacroServerChoice::Explicit(path) => { + ProcMacroServer::spawn(path.clone()).map_err(Into::into) } - ProcMacroServerChoice::None => Err("proc macro server disabled".to_owned()), + ProcMacroServerChoice::None => Err(anyhow!("proc macro server disabled")), }; - let crate_graph = ws.to_crate_graph( - &mut |_, path: &AbsPath| { - load_proc_macro(proc_macro_client.as_ref().map_err(|e| &**e), path, &[]) - }, + let (crate_graph, proc_macros) = ws.to_crate_graph( &mut |path: &AbsPath| { let contents = loader.load_sync(path); let path = vfs::VfsPath::from(path.to_path_buf()); @@ -91,6 +88,28 @@ pub fn load_workspace( }, extra_env, ); + let proc_macros = { + let proc_macro_server = match &proc_macro_server { + Ok(it) => Ok(it), + Err(e) => Err(e.to_string()), + }; + proc_macros + .into_iter() + .map(|(crate_id, path)| { + ( + crate_id, + path.map_or_else( + |_| Err("proc macro crate is missing dylib".to_owned()), + |(_, path)| { + proc_macro_server.as_ref().map_err(Clone::clone).and_then( + |proc_macro_server| load_proc_macro(proc_macro_server, &path, &[]), + ) + }, + ), + ) + }) + .collect() + }; let project_folders = ProjectFolders::new(&[ws], &[]); loader.set_config(vfs::loader::Config { @@ -100,17 +119,23 @@ pub fn load_workspace( }); tracing::debug!("crate graph: {:?}", crate_graph); - let host = - load_crate_graph(crate_graph, project_folders.source_root_config, &mut vfs, &receiver); + let host = load_crate_graph( + crate_graph, + proc_macros, + project_folders.source_root_config, + &mut vfs, + &receiver, + ); if load_config.prefill_caches { host.analysis().parallel_prime_caches(1, |_| {})?; } - Ok((host, vfs, proc_macro_client.ok())) + Ok((host, vfs, proc_macro_server.ok())) } fn load_crate_graph( crate_graph: CrateGraph, + proc_macros: ProcMacros, source_root_config: SourceRootConfig, vfs: &mut vfs::Vfs, receiver: &Receiver, @@ -119,7 +144,7 @@ fn load_crate_graph( let mut host = AnalysisHost::new(lru_cap); let mut analysis_change = Change::new(); - host.raw_database_mut().set_enable_proc_attr_macros(true); + host.raw_database_mut().enable_proc_attr_macros(); // wait until Vfs has loaded all roots for task in receiver { @@ -139,9 +164,9 @@ fn load_crate_graph( let changes = vfs.take_changes(); for file in changes { if file.exists() { - let contents = vfs.file_contents(file.file_id).to_vec(); - if let Ok(text) = String::from_utf8(contents) { - analysis_change.change_file(file.file_id, Some(Arc::new(text))) + let contents = vfs.file_contents(file.file_id); + if let Ok(text) = std::str::from_utf8(contents) { + analysis_change.change_file(file.file_id, Some(Arc::from(text))) } } } @@ -149,6 +174,7 @@ fn load_crate_graph( analysis_change.set_roots(source_roots); analysis_change.set_crate_graph(crate_graph); + analysis_change.set_proc_macros(proc_macros); host.apply_change(analysis_change); host diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs index 3e5e40750e9ca..b0b724bdfe73d 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs @@ -2,6 +2,7 @@ use std::{ collections::{HashMap, HashSet}, + path::PathBuf, time::Instant, }; @@ -9,7 +10,6 @@ use crate::{ cli::load_cargo::ProcMacroServerChoice, line_index::{LineEndings, LineIndex, PositionEncoding}, }; -use hir::Name; use ide::{ LineCol, MonikerDescriptorKind, StaticIndex, StaticIndexedFile, TextRange, TokenId, TokenStaticData, @@ -66,7 +66,6 @@ impl flags::Scip { .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(), @@ -167,7 +166,8 @@ impl flags::Scip { special_fields: Default::default(), }; - scip::write_message_to_file("index.scip", index) + let out_path = self.output.unwrap_or_else(|| PathBuf::from(r"index.scip")); + scip::write_message_to_file(out_path, index) .map_err(|err| anyhow::anyhow!("Failed to write scip to file: {}", err))?; eprintln!("Generating SCIP finished {:?}", now.elapsed()); @@ -210,13 +210,12 @@ fn new_descriptor_str( } } -fn new_descriptor(name: Name, suffix: scip_types::descriptor::Suffix) -> scip_types::Descriptor { - let mut name = name.to_string(); - if name.contains("'") { - name = format!("`{name}`"); +fn new_descriptor(name: &str, suffix: scip_types::descriptor::Suffix) -> scip_types::Descriptor { + if name.contains('\'') { + new_descriptor_str(&format!("`{name}`"), suffix) + } else { + new_descriptor_str(&name, suffix) } - - new_descriptor_str(name.as_str(), suffix) } /// Loosely based on `def_to_moniker` @@ -236,7 +235,7 @@ fn token_to_symbol(token: &TokenStaticData) -> Option { .iter() .map(|desc| { new_descriptor( - desc.name.clone(), + &desc.name, match desc.desc { MonikerDescriptorKind::Namespace => Namespace, MonikerDescriptorKind::Type => Type, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index c35cce103fab6..6355c620f7802 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -7,13 +7,14 @@ //! configure the server itself, feature flags are passed into analysis, and //! tweak things like automatic insertion of `()` in completions. -use std::{fmt, iter, path::PathBuf}; +use std::{fmt, iter, ops::Not, path::PathBuf}; +use cfg::{CfgAtom, CfgDiff}; use flycheck::FlycheckConfig; use ide::{ AssistConfig, CallableSnippets, CompletionConfig, DiagnosticsConfig, ExprFillDefaultMode, HighlightConfig, HighlightRelatedConfig, HoverConfig, HoverDocFormat, InlayHintsConfig, - JoinLinesConfig, Snippet, SnippetScope, + JoinLinesConfig, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, Snippet, SnippetScope, }; use ide_db::{ imports::insert_use::{ImportGranularity, InsertUseConfig, PrefixKind}, @@ -23,11 +24,10 @@ use itertools::Itertools; use lsp_types::{ClientCapabilities, MarkupKind}; use project_model::{ CargoConfig, CargoFeatures, ProjectJson, ProjectJsonData, ProjectManifest, RustLibSource, - UnsetTestCrates, }; use rustc_hash::{FxHashMap, FxHashSet}; use serde::{de::DeserializeOwned, Deserialize}; -use vfs::AbsPathBuf; +use vfs::{AbsPath, AbsPathBuf}; use crate::{ caps::completion_item_edit_resolve, @@ -101,6 +101,8 @@ config_data! { /// Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to /// avoid checking unnecessary things. cargo_buildScripts_useRustcWrapper: bool = "true", + /// List of cfg options to enable with the given values. + cargo_cfgs: FxHashMap = "{}", /// Extra arguments that are passed to every cargo invocation. cargo_extraArgs: Vec = "[]", /// Extra environment variables that will be set when running cargo, rustc @@ -128,7 +130,7 @@ config_data! { // 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. + /// Unsets the implicit `#[cfg(test)]` for the specified crates. cargo_unsetTest: Vec = "[\"core\"]", /// Run the check command for diagnostics on save. @@ -281,6 +283,8 @@ config_data! { /// Enables highlighting of related references while the cursor is on `break`, `loop`, `while`, or `for` keywords. highlightRelated_breakPoints_enable: bool = "true", + /// Enables highlighting of all captures of a closure while the cursor is on the `|` or move keyword of a closure. + highlightRelated_closureCaptures_enable: bool = "true", /// Enables highlighting of all exit points while the cursor is on any `return`, `?`, `fn`, or return type arrow (`->`). highlightRelated_exitPoints_enable: bool = "true", /// Enables highlighting of related references while the cursor is on any identifier. @@ -311,8 +315,18 @@ config_data! { /// 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. + /// Use markdown syntax for links on hover. hover_links_enable: bool = "true", + /// How to render the align information in a memory layout hover. + hover_memoryLayout_alignment: Option = "\"hexadecimal\"", + /// Whether to show memory layout data on hover. + hover_memoryLayout_enable: bool = "true", + /// How to render the niche information in a memory layout hover. + hover_memoryLayout_niches: Option = "false", + /// How to render the offset information in a memory layout hover. + hover_memoryLayout_offset: Option = "\"hexadecimal\"", + /// How to render the size information in a memory layout hover. + hover_memoryLayout_size: Option = "\"both\"", /// Whether to enforce the import granularity setting for all files. If set to false rust-analyzer will try to keep import styles consistent per file. imports_granularity_enforce: bool = "false", @@ -336,8 +350,12 @@ config_data! { /// Minimum number of lines required before the `}` until the hint is shown (set to 0 or 1 /// to always show them). inlayHints_closingBraceHints_minLines: usize = "25", + /// Whether to show inlay hints for closure captures. + inlayHints_closureCaptureHints_enable: bool = "false", /// Whether to show inlay type hints for return types of closures. inlayHints_closureReturnTypeHints_enable: ClosureReturnTypeHintsDef = "\"never\"", + /// Closure notation in type and chaining inlay hints. + inlayHints_closureStyle: ClosureStyle = "\"impl_fn\"", /// Whether to show enum variant discriminant hints. inlayHints_discriminantHints_enable: DiscriminantHintsDef = "\"never\"", /// Whether to show inlay hints for type adjustments. @@ -418,6 +436,8 @@ config_data! { /// Number of syntax trees rust-analyzer keeps in memory. Defaults to 128. lru_capacity: Option = "null", + /// Sets the LRU capacity of the specified queries. + lru_query_capacities: FxHashMap, usize> = "{}", /// Whether to show `can't find Cargo.toml` error message. notifications_cargoTomlNotFound: bool = "true", @@ -433,8 +453,7 @@ config_data! { /// /// This config takes a map of crate names with the exported proc-macro names to ignore as values. procMacro_ignored: FxHashMap, Box<[Box]>> = "{}", - /// Internal config, path to proc-macro server executable (typically, - /// this is rust-analyzer itself, but we override this in tests). + /// Internal config, path to proc-macro server executable. procMacro_server: Option = "null", /// Exclude imports from find-all-references. @@ -474,6 +493,8 @@ config_data! { /// When enabled, rust-analyzer will highlight rust source in doc comments as well as intra /// doc links. semanticHighlighting_doc_comment_inject_enable: bool = "true", + /// Whether the server is allowed to emit non-standard tokens and modifiers. + semanticHighlighting_nonStandardTokens: bool = "true", /// Use semantic tokens for operators. /// /// When disabled, rust-analyzer will emit semantic tokens only for operator tokens when @@ -484,7 +505,7 @@ config_data! { /// When enabled, rust-analyzer will emit special token types for operator tokens instead /// of the generic `operator` token type. semanticHighlighting_operator_specialization_enable: bool = "false", - /// Use semantic tokens for punctuations. + /// Use semantic tokens for punctuation. /// /// When disabled, rust-analyzer will emit semantic tokens only for punctuation tokens when /// they are tagged with modifiers or have a special role. @@ -492,7 +513,7 @@ config_data! { /// When enabled, rust-analyzer will emit a punctuation semantic token for the `!` of macro /// calls. semanticHighlighting_punctuation_separate_macro_bang: bool = "false", - /// Use specialized semantic tokens for punctuations. + /// Use specialized semantic tokens for punctuation. /// /// When enabled, rust-analyzer will emit special token types for punctuation tokens instead /// of the generic `punctuation` token type. @@ -531,8 +552,9 @@ impl Default for ConfigData { #[derive(Debug, Clone)] pub struct Config { - pub discovered_projects: Option>, - pub workspace_roots: Vec, + discovered_projects: Vec, + /// The workspace roots as registered by the LSP client + workspace_roots: Vec, caps: lsp_types::ClientCapabilities, root_path: AbsPathBuf, data: ConfigData, @@ -570,6 +592,7 @@ pub struct LensConfig { // runnables pub run: bool, pub debug: bool, + pub interpret: bool, // implementations pub implementations: bool, @@ -707,11 +730,11 @@ pub struct ClientCommandsConfig { } #[derive(Debug)] -pub struct ConfigUpdateError { +pub struct ConfigError { errors: Vec<(String, serde_json::Error)>, } -impl fmt::Display for ConfigUpdateError { +impl fmt::Display for ConfigError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let errors = self.errors.iter().format_with("\n", |(key, e), f| { f(key)?; @@ -720,8 +743,7 @@ impl fmt::Display for ConfigUpdateError { }); write!( f, - "rust-analyzer found {} invalid config value{}:\n{}", - self.errors.len(), + "invalid config value{}:\n{}", if self.errors.len() == 1 { "" } else { "s" }, errors ) @@ -738,7 +760,7 @@ impl Config { caps, data: ConfigData::default(), detached_files: Vec::new(), - discovered_projects: None, + discovered_projects: Vec::new(), root_path, snippets: Default::default(), workspace_roots, @@ -751,10 +773,20 @@ impl Config { if discovered.is_empty() { tracing::error!("failed to find any projects in {:?}", &self.workspace_roots); } - self.discovered_projects = Some(discovered); + self.discovered_projects = discovered; } - pub fn update(&mut self, mut json: serde_json::Value) -> Result<(), ConfigUpdateError> { + pub fn remove_workspace(&mut self, path: &AbsPath) { + if let Some(position) = self.workspace_roots.iter().position(|it| it == path) { + self.workspace_roots.remove(position); + } + } + + pub fn add_workspaces(&mut self, paths: impl Iterator) { + self.workspace_roots.extend(paths); + } + + pub fn update(&mut self, mut json: serde_json::Value) -> Result<(), ConfigError> { tracing::info!("updating config from JSON: {:#}", json); if json.is_null() || json.as_object().map_or(false, |it| it.is_empty()) { return Ok(()); @@ -801,7 +833,7 @@ impl Config { if errors.is_empty() { Ok(()) } else { - Err(ConfigUpdateError { errors }) + Err(ConfigError { errors }) } } @@ -856,25 +888,19 @@ impl Config { pub fn linked_projects(&self) -> Vec { match self.data.linkedProjects.as_slice() { [] => { - match self.discovered_projects.as_ref() { - Some(discovered_projects) => { - let exclude_dirs: Vec<_> = self - .data - .files_excludeDirs - .iter() - .map(|p| self.root_path.join(p)) - .collect(); - discovered_projects - .iter() - .filter(|(ProjectManifest::ProjectJson(path) | ProjectManifest::CargoToml(path))| { + let exclude_dirs: Vec<_> = + self.data.files_excludeDirs.iter().map(|p| self.root_path.join(p)).collect(); + self.discovered_projects + .iter() + .filter( + |(ProjectManifest::ProjectJson(path) + | ProjectManifest::CargoToml(path))| { !exclude_dirs.iter().any(|p| path.starts_with(p)) - }) - .cloned() - .map(LinkedProject::from) - .collect() - } - None => Vec::new(), - } + }, + ) + .cloned() + .map(LinkedProject::from) + .collect() } linked_projects => linked_projects .iter() @@ -1013,6 +1039,11 @@ impl Config { .is_some() } + pub fn semantics_tokens_augments_syntax_tokens(&self) -> bool { + try_!(self.caps.text_document.as_ref()?.semantic_tokens.as_ref()?.augments_syntax_tokens?) + .unwrap_or(false) + } + pub fn position_encoding(&self) -> PositionEncoding { negotiated_encoding(&self.caps) } @@ -1025,6 +1056,10 @@ impl Config { self.experimental("codeActionGroup") } + pub fn local_docs(&self) -> bool { + self.experimental("localDocs") + } + pub fn open_server_logs(&self) -> bool { self.experimental("openServerLogs") } @@ -1085,27 +1120,27 @@ impl Config { extra_env } - pub fn lru_capacity(&self) -> Option { + pub fn lru_parse_query_capacity(&self) -> Option { self.data.lru_capacity } - pub fn proc_macro_srv(&self) -> Option<(AbsPathBuf, /* is path explicitly set */ bool)> { - if !self.data.procMacro_enable { - return None; - } - Some(match &self.data.procMacro_server { - Some(it) => ( - AbsPathBuf::try_from(it.clone()).unwrap_or_else(|path| self.root_path.join(path)), - true, - ), - None => (AbsPathBuf::assert(std::env::current_exe().ok()?), false), - }) + pub fn lru_query_capacities(&self) -> Option<&FxHashMap, usize>> { + self.data.lru_query_capacities.is_empty().not().then(|| &self.data.lru_query_capacities) + } + + pub fn proc_macro_srv(&self) -> Option { + let path = self.data.procMacro_server.clone()?; + Some(AbsPathBuf::try_from(path).unwrap_or_else(|path| self.root_path.join(&path))) } pub fn dummy_replacements(&self) -> &FxHashMap, Box<[Box]>> { &self.data.procMacro_ignored } + pub fn expand_proc_macros(&self) -> bool { + self.data.procMacro_enable + } + pub fn expand_proc_attr_macros(&self) -> bool { self.data.procMacro_enable && self.data.procMacro_attributes_enable } @@ -1164,7 +1199,34 @@ impl Config { sysroot, sysroot_src, rustc_source, - unset_test_crates: UnsetTestCrates::Only(self.data.cargo_unsetTest.clone()), + cfg_overrides: project_model::CfgOverrides { + global: CfgDiff::new( + self.data + .cargo_cfgs + .iter() + .map(|(key, val)| { + if val.is_empty() { + CfgAtom::Flag(key.into()) + } else { + CfgAtom::KeyValue { key: key.into(), value: val.into() } + } + }) + .collect(), + vec![], + ) + .unwrap(), + selective: self + .data + .cargo_unsetTest + .iter() + .map(|it| { + ( + it.clone(), + CfgDiff::new(vec![], vec![CfgAtom::Flag("test".into())]).unwrap(), + ) + }) + .collect(), + }, wrap_rustc_in_build_scripts: self.data.cargo_buildScripts_useRustcWrapper, invocation_strategy: match self.data.cargo_buildScripts_invocationStrategy { InvocationStrategy::Once => project_model::InvocationStrategy::Once, @@ -1291,6 +1353,13 @@ impl Config { hide_closure_initialization_hints: self .data .inlayHints_typeHints_hideClosureInitialization, + closure_style: match self.data.inlayHints_closureStyle { + ClosureStyle::ImplFn => hir::ClosureStyle::ImplFn, + ClosureStyle::RustAnalyzer => hir::ClosureStyle::RANotation, + ClosureStyle::WithId => hir::ClosureStyle::ClosureWithId, + ClosureStyle::Hide => hir::ClosureStyle::Hide, + }, + closure_capture_hints: self.data.inlayHints_closureCaptureHints_enable, adjustment_hints: match self.data.inlayHints_expressionAdjustmentHints_enable { AdjustmentHintsDef::Always => ide::AdjustmentHints::Always, AdjustmentHintsDef::Never => match self.data.inlayHints_reborrowHints_enable { @@ -1409,6 +1478,9 @@ impl Config { LensConfig { run: self.data.lens_enable && self.data.lens_run_enable, debug: self.data.lens_enable && self.data.lens_debug_enable, + interpret: self.data.lens_enable + && self.data.lens_run_enable + && self.data.interpret_tests, implementations: self.data.lens_enable && self.data.lens_implementations_enable, method_refs: self.data.lens_enable && self.data.lens_references_method_enable, refs_adt: self.data.lens_enable && self.data.lens_references_adt_enable, @@ -1430,6 +1502,10 @@ impl Config { } } + pub fn highlighting_non_standard_tokens(&self) -> bool { + self.data.semanticHighlighting_nonStandardTokens + } + pub fn highlighting_config(&self) -> HighlightConfig { HighlightConfig { strings: self.data.semanticHighlighting_strings_enable, @@ -1446,8 +1522,19 @@ impl Config { } pub fn hover(&self) -> HoverConfig { + let mem_kind = |kind| match kind { + MemoryLayoutHoverRenderKindDef::Both => MemoryLayoutHoverRenderKind::Both, + MemoryLayoutHoverRenderKindDef::Decimal => MemoryLayoutHoverRenderKind::Decimal, + MemoryLayoutHoverRenderKindDef::Hexadecimal => MemoryLayoutHoverRenderKind::Hexadecimal, + }; HoverConfig { links_in_hover: self.data.hover_links_enable, + memory_layout: self.data.hover_memoryLayout_enable.then_some(MemoryLayoutHoverConfig { + size: self.data.hover_memoryLayout_size.map(mem_kind), + offset: self.data.hover_memoryLayout_offset.map(mem_kind), + alignment: self.data.hover_memoryLayout_alignment.map(mem_kind), + niches: self.data.hover_memoryLayout_niches.unwrap_or_default(), + }), documentation: self.data.hover_documentation_enable, format: { let is_markdown = try_or_def!(self @@ -1467,7 +1554,6 @@ impl Config { } }, keywords: self.data.hover_documentation_keywords_enable, - interpret_tests: self.data.interpret_tests, } } @@ -1537,6 +1623,7 @@ impl Config { break_points: self.data.highlightRelated_breakPoints_enable, exit_points: self.data.highlightRelated_exitPoints_enable, yield_points: self.data.highlightRelated_yieldPoints_enable, + closure_captures: self.data.highlightRelated_closureCaptures_enable, } } @@ -1657,6 +1744,9 @@ mod de_unit_v { named_unit_variant!(reborrow); named_unit_variant!(fieldless); named_unit_variant!(with_block); + named_unit_variant!(decimal); + named_unit_variant!(hexadecimal); + named_unit_variant!(both); } #[derive(Deserialize, Debug, Clone, Copy)] @@ -1797,6 +1887,15 @@ enum ClosureReturnTypeHintsDef { WithBlock, } +#[derive(Deserialize, Debug, Clone)] +#[serde(rename_all = "snake_case")] +enum ClosureStyle { + ImplFn, + RustAnalyzer, + WithId, + Hide, +} + #[derive(Deserialize, Debug, Clone)] #[serde(untagged)] enum ReborrowHintsDef { @@ -1878,6 +1977,18 @@ enum WorkspaceSymbolSearchKindDef { AllSymbols, } +#[derive(Deserialize, Debug, Copy, Clone)] +#[serde(rename_all = "snake_case")] +#[serde(untagged)] +pub enum MemoryLayoutHoverRenderKindDef { + #[serde(deserialize_with = "de_unit_v::decimal")] + Decimal, + #[serde(deserialize_with = "de_unit_v::hexadecimal")] + Hexadecimal, + #[serde(deserialize_with = "de_unit_v::both")] + Both, +} + macro_rules! _config_data { (struct $name:ident { $( @@ -1940,7 +2051,7 @@ fn get_field( alias: Option<&'static str>, default: &str, ) -> T { - // XXX: check alias first, to work-around the VS Code where it pre-fills the + // XXX: check alias first, to work around the VS Code where it pre-fills the // defaults instead of sending an empty object. alias .into_iter() @@ -1960,7 +2071,9 @@ fn get_field( None } }) - .unwrap_or_else(|| serde_json::from_str(default).unwrap()) + .unwrap_or_else(|| { + serde_json::from_str(default).unwrap_or_else(|e| panic!("{e} on: `{default}`")) + }) } fn schema(fields: &[(&'static str, &'static str, &[&str], &str)]) -> serde_json::Value { @@ -2020,6 +2133,9 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json "FxHashMap" => set! { "type": "object", }, + "FxHashMap, usize>" => set! { + "type": "object", + }, "Option" => set! { "type": ["null", "integer"], "minimum": 0, @@ -2169,8 +2285,8 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json "enumDescriptions": [ "Always show adjustment hints as prefix (`*expr`).", "Always show adjustment hints as postfix (`expr.*`).", - "Show prefix or postfix depending on which uses less parenthesis, prefering prefix.", - "Show prefix or postfix depending on which uses less parenthesis, prefering postfix.", + "Show prefix or postfix depending on which uses less parenthesis, preferring prefix.", + "Show prefix or postfix depending on which uses less parenthesis, preferring postfix.", ] }, "CargoFeaturesDef" => set! { @@ -2275,6 +2391,32 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json }, ], }, + "ClosureStyle" => set! { + "type": "string", + "enum": ["impl_fn", "rust_analyzer", "with_id", "hide"], + "enumDescriptions": [ + "`impl_fn`: `impl FnMut(i32, u64) -> i8`", + "`rust_analyzer`: `|i32, u64| -> i8`", + "`with_id`: `{closure#14352}`, where that id is the unique number of the closure in r-a internals", + "`hide`: Shows `...` for every closure type", + ], + }, + "Option" => set! { + "anyOf": [ + { + "type": "null" + }, + { + "type": "string", + "enum": ["both", "decimal", "hexadecimal", ], + "enumDescriptions": [ + "Render as 12 (0xC)", + "Render as 12", + "Render as 0xC" + ], + }, + ], + }, _ => panic!("missing entry for {ty}: {default}"), } @@ -2384,4 +2526,43 @@ mod tests { fn remove_ws(text: &str) -> String { text.replace(char::is_whitespace, "") } + + #[test] + fn proc_macro_srv_null() { + let mut config = + Config::new(AbsPathBuf::try_from(project_root()).unwrap(), Default::default(), vec![]); + config + .update(serde_json::json!({ + "procMacro_server": null, + })) + .unwrap(); + assert_eq!(config.proc_macro_srv(), None); + } + + #[test] + fn proc_macro_srv_abs() { + let mut config = + Config::new(AbsPathBuf::try_from(project_root()).unwrap(), Default::default(), vec![]); + config + .update(serde_json::json!({ + "procMacro": {"server": project_root().display().to_string()} + })) + .unwrap(); + assert_eq!(config.proc_macro_srv(), Some(AbsPathBuf::try_from(project_root()).unwrap())); + } + + #[test] + fn proc_macro_srv_rel() { + let mut config = + Config::new(AbsPathBuf::try_from(project_root()).unwrap(), Default::default(), vec![]); + config + .update(serde_json::json!({ + "procMacro": {"server": "./server"} + })) + .unwrap(); + assert_eq!( + config.proc_macro_srv(), + Some(AbsPathBuf::try_from(project_root().join("./server")).unwrap()) + ); + } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs index 83b03fe473621..33422fd058ef0 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs @@ -1,15 +1,16 @@ //! Book keeping for keeping diagnostics easily in sync with the client. pub(crate) mod to_proto; -use std::{mem, sync::Arc}; +use std::mem; use ide::FileId; use ide_db::FxHashMap; -use stdx::hash::{NoHashHashMap, NoHashHashSet}; +use nohash_hasher::{IntMap, IntSet}; +use triomphe::Arc; use crate::lsp_ext; -pub(crate) type CheckFixes = Arc>>>; +pub(crate) type CheckFixes = Arc>>>; #[derive(Debug, Default, Clone)] pub struct DiagnosticsMapConfig { @@ -20,12 +21,12 @@ pub struct DiagnosticsMapConfig { #[derive(Debug, Default, Clone)] pub(crate) struct DiagnosticCollection { - // FIXME: should be NoHashHashMap> - pub(crate) native: NoHashHashMap>, + // FIXME: should be IntMap> + pub(crate) native: IntMap>, // FIXME: should be Vec - pub(crate) check: NoHashHashMap>>, + pub(crate) check: IntMap>>, pub(crate) check_fixes: CheckFixes, - changes: NoHashHashSet, + changes: IntSet, } #[derive(Debug, Clone)] @@ -105,7 +106,7 @@ impl DiagnosticCollection { native.chain(check) } - pub(crate) fn take_changes(&mut self) -> Option> { + pub(crate) fn take_changes(&mut self) -> Option> { if self.changes.is_empty() { return None; } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs index 415fa4e02f20c..e1d1130ff1b87 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs @@ -3,7 +3,6 @@ use std::collections::HashMap; use flycheck::{Applicability, DiagnosticLevel, DiagnosticSpan}; -use ide_db::line_index::WideEncoding; use itertools::Itertools; use stdx::format_to; use vfs::{AbsPath, AbsPathBuf}; @@ -80,37 +79,33 @@ fn position( position_encoding: &PositionEncoding, span: &DiagnosticSpan, line_offset: usize, - column_offset: usize, + column_offset_utf32: usize, ) -> lsp_types::Position { let line_index = line_offset - span.line_start; - let mut true_column_offset = column_offset; - if let Some(line) = span.text.get(line_index) { - if line.text.chars().count() == line.text.len() { - // all one byte utf-8 char - return lsp_types::Position { - line: (line_offset as u32).saturating_sub(1), - character: (column_offset as u32).saturating_sub(1), - }; - } - let mut char_offset = 0; - let len_func = match position_encoding { - PositionEncoding::Utf8 => char::len_utf8, - PositionEncoding::Wide(WideEncoding::Utf16) => char::len_utf16, - PositionEncoding::Wide(WideEncoding::Utf32) => |_| 1, - }; - for c in line.text.chars() { - char_offset += 1; - if char_offset > column_offset { - break; + let column_offset_encoded = match span.text.get(line_index) { + // Fast path. + Some(line) if line.text.is_ascii() => column_offset_utf32, + Some(line) => { + let line_prefix_len = line + .text + .char_indices() + .take(column_offset_utf32) + .last() + .map(|(pos, c)| pos + c.len_utf8()) + .unwrap_or(0); + let line_prefix = &line.text[..line_prefix_len]; + match position_encoding { + PositionEncoding::Utf8 => line_prefix.len(), + PositionEncoding::Wide(enc) => enc.measure(line_prefix), } - true_column_offset += len_func(c) - 1; } - } + None => column_offset_utf32, + }; lsp_types::Position { line: (line_offset as u32).saturating_sub(1), - character: (true_column_offset as u32).saturating_sub(1), + character: (column_offset_encoded as u32).saturating_sub(1), } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/dispatch.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/dispatch.rs index 313bb2ec8dffa..ebe77b8dfe721 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/dispatch.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/dispatch.rs @@ -4,6 +4,7 @@ use std::{fmt, panic, thread}; use ide::Cancelled; use lsp_server::ExtractError; use serde::{de::DeserializeOwned, Serialize}; +use stdx::thread::ThreadIntent; use crate::{ global_state::{GlobalState, GlobalStateSnapshot}, @@ -87,7 +88,8 @@ impl<'a> RequestDispatcher<'a> { self } - /// Dispatches the request onto thread pool + /// Dispatches a non-latency-sensitive request onto the thread pool + /// without retrying it if it panics. pub(crate) fn on_no_retry( &mut self, f: fn(GlobalStateSnapshot, R::Params) -> Result, @@ -102,7 +104,7 @@ impl<'a> RequestDispatcher<'a> { None => return self, }; - self.global_state.task_pool.handle.spawn({ + self.global_state.task_pool.handle.spawn(ThreadIntent::Worker, { let world = self.global_state.snapshot(); move || { let result = panic::catch_unwind(move || { @@ -123,11 +125,49 @@ impl<'a> RequestDispatcher<'a> { self } - /// Dispatches the request onto thread pool + /// Dispatches a non-latency-sensitive request onto the thread pool. pub(crate) fn on( &mut self, f: fn(GlobalStateSnapshot, R::Params) -> Result, ) -> &mut Self + where + R: lsp_types::request::Request + 'static, + R::Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug, + R::Result: Serialize, + { + self.on_with_thread_intent::(ThreadIntent::Worker, f) + } + + /// Dispatches a latency-sensitive request onto the thread pool. + pub(crate) fn on_latency_sensitive( + &mut self, + f: fn(GlobalStateSnapshot, R::Params) -> Result, + ) -> &mut Self + where + R: lsp_types::request::Request + 'static, + R::Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug, + R::Result: Serialize, + { + self.on_with_thread_intent::(ThreadIntent::LatencySensitive, f) + } + + pub(crate) fn finish(&mut self) { + if let Some(req) = self.req.take() { + tracing::error!("unknown request: {:?}", req); + let response = lsp_server::Response::new_err( + req.id, + lsp_server::ErrorCode::MethodNotFound as i32, + "unknown request".to_string(), + ); + self.global_state.respond(response); + } + } + + fn on_with_thread_intent( + &mut self, + intent: ThreadIntent, + f: fn(GlobalStateSnapshot, R::Params) -> Result, + ) -> &mut Self where R: lsp_types::request::Request + 'static, R::Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug, @@ -138,7 +178,7 @@ impl<'a> RequestDispatcher<'a> { None => return self, }; - self.global_state.task_pool.handle.spawn({ + self.global_state.task_pool.handle.spawn(intent, { let world = self.global_state.snapshot(); move || { let result = panic::catch_unwind(move || { @@ -155,18 +195,6 @@ impl<'a> RequestDispatcher<'a> { self } - pub(crate) fn finish(&mut self) { - if let Some(req) = self.req.take() { - tracing::error!("unknown request: {:?}", req); - let response = lsp_server::Response::new_err( - req.id, - lsp_server::ErrorCode::MethodNotFound as i32, - "unknown request".to_string(), - ); - self.global_state.respond(response); - } - } - fn parse(&mut self) -> Option<(lsp_server::Request, R::Params, String)> where R: lsp_types::request::Request, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/from_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/from_proto.rs index 50af38cd6fe37..cd74a5500d0a9 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/from_proto.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/from_proto.rs @@ -31,7 +31,10 @@ pub(crate) fn offset(line_index: &LineIndex, position: lsp_types::Position) -> R PositionEncoding::Utf8 => LineCol { line: position.line, col: position.character }, PositionEncoding::Wide(enc) => { let line_col = WideLineCol { line: position.line, col: position.character }; - line_index.index.to_utf8(enc, line_col) + line_index + .index + .to_utf8(enc, line_col) + .ok_or_else(|| format_err!("Invalid wide col offset"))? } }; let text_size = @@ -98,13 +101,18 @@ pub(crate) fn assist_kind(kind: lsp_types::CodeActionKind) -> Option pub(crate) fn annotation( snap: &GlobalStateSnapshot, code_lens: lsp_types::CodeLens, -) -> Result { +) -> Result> { let data = code_lens.data.ok_or_else(|| invalid_params_error("code lens without data".to_string()))?; let resolve = from_json::("CodeLensResolveData", &data)?; - match resolve { - lsp_ext::CodeLensResolveData::Impls(params) => { + match resolve.kind { + lsp_ext::CodeLensResolveDataKind::Impls(params) => { + if snap.url_file_version(¶ms.text_document_position_params.text_document.uri) + != Some(resolve.version) + { + return Ok(None); + } let pos @ FilePosition { file_id, .. } = file_position(snap, params.text_document_position_params)?; let line_index = snap.file_line_index(file_id)?; @@ -114,7 +122,10 @@ pub(crate) fn annotation( kind: AnnotationKind::HasImpls { pos, data: None }, }) } - lsp_ext::CodeLensResolveData::References(params) => { + lsp_ext::CodeLensResolveDataKind::References(params) => { + if snap.url_file_version(¶ms.text_document.uri) != Some(resolve.version) { + return Ok(None); + } let pos @ FilePosition { file_id, .. } = file_position(snap, params)?; let line_index = snap.file_line_index(file_id)?; @@ -124,4 +135,5 @@ pub(crate) fn annotation( }) } } + .map(Some) } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs index aca6c92357070..9f4dc4440202d 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs @@ -3,22 +3,23 @@ //! //! Each tick provides an immutable snapshot of the state as `WorldSnapshot`. -use std::{sync::Arc, time::Instant}; +use std::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, ProcMacroPaths, SourceDatabase}; use lsp_types::{SemanticTokens, Url}; +use nohash_hasher::IntMap; use parking_lot::{Mutex, RwLock}; use proc_macro_api::ProcMacroServer; use project_model::{CargoWorkspace, ProjectWorkspace, Target, WorkspaceBuildScripts}; use rustc_hash::FxHashMap; -use stdx::hash::NoHashHashMap; +use triomphe::Arc; use vfs::AnchoredPathBuf; use crate::{ - config::Config, + config::{Config, ConfigError}, diagnostics::{CheckFixes, DiagnosticCollection}, from_proto, line_index::{LineEndings, LineIndex}, @@ -51,24 +52,34 @@ pub(crate) type ReqQueue = lsp_server::ReqQueue<(String, Instant), ReqHandler>; pub(crate) struct GlobalState { sender: Sender, req_queue: ReqQueue, + pub(crate) task_pool: Handle, Receiver>, - pub(crate) loader: Handle, Receiver>, + pub(crate) config: Arc, + pub(crate) config_errors: Option, pub(crate) analysis_host: AnalysisHost, pub(crate) diagnostics: DiagnosticCollection, pub(crate) mem_docs: MemDocs, + pub(crate) source_root_config: SourceRootConfig, pub(crate) semantic_tokens_cache: Arc>>, + + // status pub(crate) shutdown_requested: bool, - pub(crate) proc_macro_changed: bool, pub(crate) last_reported_status: Option, - pub(crate) source_root_config: SourceRootConfig, - pub(crate) proc_macro_clients: Vec>, + // proc macros + pub(crate) proc_macro_changed: bool, + pub(crate) proc_macro_clients: Arc<[anyhow::Result]>, + + // Flycheck pub(crate) flycheck: Arc<[FlycheckHandle]>, pub(crate) flycheck_sender: Sender, pub(crate) flycheck_receiver: Receiver, + pub(crate) last_flycheck_error: Option, - pub(crate) vfs: Arc)>>, + // VFS + pub(crate) loader: Handle, Receiver>, + pub(crate) vfs: Arc)>>, pub(crate) vfs_config_version: u32, pub(crate) vfs_progress_config_version: u32, pub(crate) vfs_progress_n_total: usize, @@ -92,7 +103,7 @@ pub(crate) struct GlobalState { /// first phase is much faster, and is much less likely to fail. /// /// This creates a complication -- by the time the second phase completes, - /// the results of the fist phase could be invalid. That is, while we run + /// the results of the first phase could be invalid. That is, while we run /// `cargo check`, the user edits `Cargo.toml`, we notice this, and the new /// `cargo metadata` completes before `cargo check`. /// @@ -100,11 +111,13 @@ 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_build_data_queue: - OpQueue<(Arc>, Vec>)>, - pub(crate) prime_caches_queue: OpQueue<()>, + // op queues + pub(crate) fetch_workspaces_queue: OpQueue<(), Option>>>, + pub(crate) fetch_build_data_queue: + OpQueue<(), (Arc>, Vec>)>, + pub(crate) fetch_proc_macros_queue: OpQueue, bool>, + pub(crate) prime_caches_queue: OpQueue, } /// An immutable snapshot of the world's state at a point in time. @@ -114,8 +127,9 @@ pub(crate) struct GlobalStateSnapshot { pub(crate) check_fixes: CheckFixes, mem_docs: MemDocs, pub(crate) semantic_tokens_cache: Arc>>, - vfs: Arc)>>, + vfs: Arc)>>, pub(crate) workspaces: Arc>, + // used to signal semantic highlighting to fall back to syntax based highlighting until proc-macros have been loaded pub(crate) proc_macros_loaded: bool, pub(crate) flycheck: Arc<[FlycheckHandle]>, } @@ -138,7 +152,10 @@ impl GlobalState { Handle { handle, receiver } }; - let analysis_host = AnalysisHost::new(config.lru_capacity()); + let mut analysis_host = AnalysisHost::new(config.lru_parse_query_capacity()); + if let Some(capacities) = config.lru_query_capacities() { + analysis_host.update_lru_capacities(capacities); + } let (flycheck_sender, flycheck_receiver) = unbounded(); let mut this = GlobalState { sender, @@ -151,16 +168,21 @@ impl GlobalState { mem_docs: MemDocs::default(), semantic_tokens_cache: Arc::new(Default::default()), shutdown_requested: false, - proc_macro_changed: false, last_reported_status: None, source_root_config: SourceRootConfig::default(), - proc_macro_clients: vec![], + config_errors: Default::default(), + + proc_macro_changed: false, + // FIXME: use `Arc::from_iter` when it becomes available + proc_macro_clients: Arc::from(Vec::new()), - flycheck: Arc::new([]), + // FIXME: use `Arc::from_iter` when it becomes available + flycheck: Arc::from(Vec::new()), flycheck_sender, flycheck_receiver, + last_flycheck_error: None, - vfs: Arc::new(RwLock::new((vfs::Vfs::default(), NoHashHashMap::default()))), + vfs: Arc::new(RwLock::new((vfs::Vfs::default(), IntMap::default()))), vfs_config_version: 0, vfs_progress_config_version: 0, vfs_progress_n_total: 0, @@ -168,9 +190,10 @@ impl GlobalState { workspaces: Arc::new(Vec::new()), fetch_workspaces_queue: OpQueue::default(), - prime_caches_queue: OpQueue::default(), - fetch_build_data_queue: OpQueue::default(), + fetch_proc_macros_queue: OpQueue::default(), + + prime_caches_queue: OpQueue::default(), }; // Apply any required database inputs from the config. this.update_configuration(config); @@ -185,7 +208,7 @@ impl GlobalState { let (change, changed_files) = { let mut change = Change::new(); let (vfs, line_endings_map) = &mut *self.vfs.write(); - let mut changed_files = vfs.take_changes(); + let changed_files = vfs.take_changes(); if changed_files.is_empty() { return false; } @@ -193,7 +216,7 @@ impl GlobalState { // We need to fix up the changed events a bit. If we have a create or modify for a file // id that is followed by a delete we actually skip observing the file text from the // earlier event, to avoid problems later on. - for changed_file in &changed_files { + for changed_file in changed_files { use vfs::ChangeKind::*; file_changes @@ -229,14 +252,13 @@ impl GlobalState { )); } - changed_files.extend( - file_changes - .into_iter() - .filter(|(_, (change_kind, just_created))| { - !matches!((change_kind, just_created), (vfs::ChangeKind::Delete, true)) - }) - .map(|(file_id, (change_kind, _))| vfs::ChangedFile { file_id, change_kind }), - ); + let changed_files: Vec<_> = file_changes + .into_iter() + .filter(|(_, (change_kind, just_created))| { + !matches!((change_kind, just_created), (vfs::ChangeKind::Delete, true)) + }) + .map(|(file_id, (change_kind, _))| vfs::ChangedFile { file_id, change_kind }) + .collect(); // A file was added or deleted let mut has_structure_changes = false; @@ -261,7 +283,7 @@ impl GlobalState { String::from_utf8(bytes).ok().and_then(|text| { let (text, line_endings) = LineEndings::normalize(text); line_endings_map.insert(file.file_id, line_endings); - Some(Arc::new(text)) + Some(Arc::from(text)) }) } else { None @@ -280,11 +302,11 @@ impl GlobalState { { let raw_database = self.analysis_host.raw_database(); // 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 + // but something's 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())); + .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| { @@ -307,7 +329,8 @@ impl GlobalState { check_fixes: Arc::clone(&self.diagnostics.check_fixes), mem_docs: self.mem_docs.clone(), semantic_tokens_cache: Arc::clone(&self.semantic_tokens_cache), - proc_macros_loaded: !self.fetch_build_data_queue.last_op_result().0.is_empty(), + proc_macros_loaded: !self.config.expand_proc_macros() + || *self.fetch_proc_macros_queue.last_op_result(), flycheck: self.flycheck.clone(), } } @@ -331,7 +354,7 @@ impl GlobalState { } pub(crate) fn send_notification( - &mut self, + &self, params: N::Params, ) { let not = lsp_server::Notification::new(N::METHOD.to_string(), params); @@ -372,7 +395,7 @@ impl GlobalState { self.req_queue.incoming.is_completed(&request.id) } - fn send(&mut self, message: lsp_server::Message) { + fn send(&self, message: lsp_server::Message) { self.sender.send(message).unwrap() } } @@ -431,6 +454,10 @@ impl GlobalStateSnapshot { ProjectWorkspace::DetachedFiles { .. } => None, }) } + + pub(crate) fn vfs_memory_usage(&self) -> usize { + self.vfs.read().0.memory_usage() + } } pub(crate) fn file_id_to_url(vfs: &vfs::Vfs, id: FileId) -> Url { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs new file mode 100644 index 0000000000000..09de6900c8fb5 --- /dev/null +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs @@ -0,0 +1,334 @@ +//! This module is responsible for implementing handlers for Language Server +//! Protocol. This module specifically handles notifications. + +use std::ops::Deref; + +use itertools::Itertools; +use lsp_types::{ + CancelParams, DidChangeConfigurationParams, DidChangeTextDocumentParams, + DidChangeWatchedFilesParams, DidChangeWorkspaceFoldersParams, DidCloseTextDocumentParams, + DidOpenTextDocumentParams, DidSaveTextDocumentParams, WorkDoneProgressCancelParams, +}; +use triomphe::Arc; +use vfs::{AbsPathBuf, ChangeKind, VfsPath}; + +use crate::{ + config::Config, from_proto, global_state::GlobalState, lsp_ext::RunFlycheckParams, + lsp_utils::apply_document_changes, mem_docs::DocumentData, reload, Result, +}; + +pub(crate) fn handle_cancel(state: &mut GlobalState, params: CancelParams) -> Result<()> { + let id: lsp_server::RequestId = match params.id { + lsp_types::NumberOrString::Number(id) => id.into(), + lsp_types::NumberOrString::String(id) => id.into(), + }; + state.cancel(id); + Ok(()) +} + +pub(crate) fn handle_work_done_progress_cancel( + state: &mut GlobalState, + params: WorkDoneProgressCancelParams, +) -> Result<()> { + if let lsp_types::NumberOrString::String(s) = ¶ms.token { + if let Some(id) = s.strip_prefix("rust-analyzer/flycheck/") { + if let Ok(id) = u32::from_str_radix(id, 10) { + if let Some(flycheck) = state.flycheck.get(id as usize) { + flycheck.cancel(); + } + } + } + } + + // Just ignore this. It is OK to continue sending progress + // notifications for this token, as the client can't know when + // we accepted notification. + Ok(()) +} + +pub(crate) fn handle_did_open_text_document( + state: &mut GlobalState, + params: DidOpenTextDocumentParams, +) -> Result<()> { + let _p = profile::span("handle_did_open_text_document"); + + if let Ok(path) = from_proto::vfs_path(¶ms.text_document.uri) { + let already_exists = state + .mem_docs + .insert(path.clone(), DocumentData::new(params.text_document.version)) + .is_err(); + if already_exists { + tracing::error!("duplicate DidOpenTextDocument: {}", path); + } + state.vfs.write().0.set_file_contents(path, Some(params.text_document.text.into_bytes())); + } + Ok(()) +} + +pub(crate) fn handle_did_change_text_document( + state: &mut GlobalState, + params: DidChangeTextDocumentParams, +) -> Result<()> { + let _p = profile::span("handle_did_change_text_document"); + + if let Ok(path) = from_proto::vfs_path(¶ms.text_document.uri) { + match state.mem_docs.get_mut(&path) { + Some(doc) => { + // The version passed in DidChangeTextDocument is the version after all edits are applied + // so we should apply it before the vfs is notified. + doc.version = params.text_document.version; + } + None => { + tracing::error!("unexpected DidChangeTextDocument: {}", path); + return Ok(()); + } + }; + + let vfs = &mut state.vfs.write().0; + let file_id = vfs.file_id(&path).unwrap(); + let text = apply_document_changes( + state.config.position_encoding(), + || std::str::from_utf8(vfs.file_contents(file_id)).unwrap().into(), + params.content_changes, + ); + + vfs.set_file_contents(path, Some(text.into_bytes())); + } + Ok(()) +} + +pub(crate) fn handle_did_close_text_document( + state: &mut GlobalState, + params: DidCloseTextDocumentParams, +) -> Result<()> { + let _p = profile::span("handle_did_close_text_document"); + + if let Ok(path) = from_proto::vfs_path(¶ms.text_document.uri) { + if state.mem_docs.remove(&path).is_err() { + tracing::error!("orphan DidCloseTextDocument: {}", path); + } + + state.semantic_tokens_cache.lock().remove(¶ms.text_document.uri); + + if let Some(path) = path.as_path() { + state.loader.handle.invalidate(path.to_path_buf()); + } + } + Ok(()) +} + +pub(crate) fn handle_did_save_text_document( + state: &mut GlobalState, + params: DidSaveTextDocumentParams, +) -> Result<()> { + if let Ok(vfs_path) = from_proto::vfs_path(¶ms.text_document.uri) { + // 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) { + state + .fetch_workspaces_queue + .request_op(format!("DidSaveTextDocument {}", abs_path.display()), ()); + } + } + + if !state.config.check_on_save() || run_flycheck(state, vfs_path) { + return Ok(()); + } + } else if state.config.check_on_save() { + // No specific flycheck was triggered, so let's trigger all of them. + for flycheck in state.flycheck.iter() { + flycheck.restart(); + } + } + Ok(()) +} + +pub(crate) fn handle_did_change_configuration( + state: &mut GlobalState, + _params: DidChangeConfigurationParams, +) -> Result<()> { + // As stated in https://github.com/microsoft/language-server-protocol/issues/676, + // this notification's parameters should be ignored and the actual config queried separately. + state.send_request::( + lsp_types::ConfigurationParams { + items: vec![lsp_types::ConfigurationItem { + scope_uri: None, + section: Some("rust-analyzer".to_string()), + }], + }, + |this, resp| { + tracing::debug!("config update response: '{:?}", resp); + let lsp_server::Response { error, result, .. } = resp; + + match (error, result) { + (Some(err), _) => { + tracing::error!("failed to fetch the server settings: {:?}", err) + } + (None, Some(mut configs)) => { + if let Some(json) = configs.get_mut(0) { + // Note that json can be null according to the spec if the client can't + // provide a configuration. This is handled in Config::update below. + let mut config = Config::clone(&*this.config); + this.config_errors = config.update(json.take()).err(); + this.update_configuration(config); + } + } + (None, None) => { + tracing::error!("received empty server settings response from the client") + } + } + }, + ); + + Ok(()) +} + +pub(crate) fn handle_did_change_workspace_folders( + state: &mut GlobalState, + params: DidChangeWorkspaceFoldersParams, +) -> Result<()> { + let config = Arc::make_mut(&mut state.config); + + for workspace in params.event.removed { + let Ok(path) = workspace.uri.to_file_path() else { continue }; + let Ok(path) = AbsPathBuf::try_from(path) else { continue }; + config.remove_workspace(&path); + } + + let added = params + .event + .added + .into_iter() + .filter_map(|it| it.uri.to_file_path().ok()) + .filter_map(|it| AbsPathBuf::try_from(it).ok()); + config.add_workspaces(added); + + if !config.has_linked_projects() && config.detached_files().is_empty() { + config.rediscover_workspaces(); + state.fetch_workspaces_queue.request_op("client workspaces changed".to_string(), ()) + } + + Ok(()) +} + +pub(crate) fn handle_did_change_watched_files( + state: &mut GlobalState, + params: DidChangeWatchedFilesParams, +) -> Result<()> { + for change in params.changes { + if let Ok(path) = from_proto::abs_path(&change.uri) { + state.loader.handle.invalidate(path); + } + } + Ok(()) +} + +fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { + let _p = profile::span("run_flycheck"); + + let file_id = state.vfs.read().0.file_id(&vfs_path); + if let Some(file_id) = file_id { + let world = state.snapshot(); + let mut updated = false; + let task = move || -> std::result::Result<(), ide::Cancelled> { + // Trigger flychecks for all workspaces that depend on the saved file + // Crates containing or depending on the saved file + let crate_ids: Vec<_> = world + .analysis + .crates_for(file_id)? + .into_iter() + .flat_map(|id| world.analysis.transitive_rev_deps(id)) + .flatten() + .sorted() + .unique() + .collect(); + + let crate_root_paths: Vec<_> = crate_ids + .iter() + .filter_map(|&crate_id| { + world + .analysis + .crate_root(crate_id) + .map(|file_id| { + world.file_id_to_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 = world.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 world.flycheck.iter() { + for (id, _) in workspace_ids.clone() { + if id == flycheck.id() { + updated = true; + flycheck.restart(); + continue; + } + } + } + // No specific flycheck was triggered, so let's trigger all of them. + if !updated { + for flycheck in world.flycheck.iter() { + flycheck.restart(); + } + } + Ok(()) + }; + state.task_pool.handle.spawn_with_sender(stdx::thread::ThreadIntent::Worker, move |_| { + if let Err(e) = std::panic::catch_unwind(task) { + tracing::error!("flycheck task panicked: {e:?}") + } + }); + true + } else { + false + } +} + +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_clear_flycheck(state: &mut GlobalState, _: ()) -> Result<()> { + let _p = profile::span("handle_clear_flycheck"); + state.diagnostics.clear_check_all(); + Ok(()) +} + +pub(crate) fn handle_run_flycheck( + state: &mut GlobalState, + params: RunFlycheckParams, +) -> Result<()> { + let _p = profile::span("handle_run_flycheck"); + if let Some(text_document) = params.text_document { + if let Ok(vfs_path) = from_proto::vfs_path(&text_document.uri) { + if run_flycheck(state, vfs_path) { + return Ok(()); + } + } + } + // No specific flycheck was triggered, so let's trigger all of them. + for flycheck in state.flycheck.iter() { + flycheck.restart(); + } + Ok(()) +} diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs similarity index 91% rename from src/tools/rust-analyzer/crates/rust-analyzer/src/handlers.rs rename to src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs index 2fca2ab851d41..3f365c05942f0 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs @@ -1,35 +1,36 @@ //! This module is responsible for implementing handlers for Language Server -//! Protocol. The majority of requests are fulfilled by calling into the -//! `ide` crate. +//! Protocol. This module specifically handles requests. use std::{ + fs, io::Write as _, process::{self, Stdio}, }; use anyhow::Context; use ide::{ - AnnotationConfig, AssistKind, AssistResolveStrategy, Cancellable, FileId, FilePosition, - FileRange, HoverAction, HoverGotoTypeData, Query, RangeInfo, ReferenceCategory, Runnable, - RunnableKind, SingleResolve, SourceChange, TextEdit, + AnnotationConfig, AssistKind, AssistResolveStrategy, Cancellable, FilePosition, FileRange, + HoverAction, HoverGotoTypeData, Query, RangeInfo, ReferenceCategory, Runnable, RunnableKind, + SingleResolve, SourceChange, TextEdit, }; use ide_db::SymbolKind; use lsp_server::ErrorCode; use lsp_types::{ CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem, CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams, - CodeLens, CompletionItem, Diagnostic, DiagnosticTag, DocumentFormattingParams, FoldingRange, - FoldingRangeParams, HoverContents, InlayHint, InlayHintParams, Location, LocationLink, - NumberOrString, Position, PrepareRenameResponse, Range, RenameParams, - SemanticTokensDeltaParams, SemanticTokensFullDeltaResult, SemanticTokensParams, - SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, - SymbolTag, TextDocumentIdentifier, Url, WorkspaceEdit, + CodeLens, CompletionItem, DocumentFormattingParams, FoldingRange, FoldingRangeParams, + HoverContents, InlayHint, InlayHintParams, Location, LocationLink, Position, + PrepareRenameResponse, Range, RenameParams, SemanticTokensDeltaParams, + SemanticTokensFullDeltaResult, SemanticTokensParams, SemanticTokensRangeParams, + SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, SymbolTag, + TextDocumentIdentifier, Url, WorkspaceEdit, }; use project_model::{ManifestPath, ProjectWorkspace, TargetKind}; use serde_json::json; use stdx::{format_to, never}; use syntax::{algo, ast, AstNode, TextRange, TextSize}; -use vfs::{AbsPath, AbsPathBuf}; +use triomphe::Arc; +use vfs::{AbsPath, AbsPathBuf, VfsPath}; use crate::{ cargo_target_spec::CargoTargetSpec, @@ -38,23 +39,29 @@ use crate::{ from_proto, global_state::{GlobalState, GlobalStateSnapshot}, line_index::LineEndings, - lsp_ext::{self, PositionOrRange, ViewCrateGraphParams, WorkspaceSymbolParams}, + lsp_ext::{ + self, CrateInfoResult, ExternalDocsPair, ExternalDocsResponse, FetchDependencyListParams, + FetchDependencyListResult, PositionOrRange, ViewCrateGraphParams, WorkspaceSymbolParams, + }, lsp_utils::{all_edits_are_disjoint, invalid_params_error}, to_proto, LspError, Result, }; pub(crate) fn handle_workspace_reload(state: &mut GlobalState, _: ()) -> Result<()> { - state.proc_macro_clients.clear(); + // FIXME: use `Arc::from_iter` when it becomes available + state.proc_macro_clients = Arc::from(Vec::new()); 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()); + state.fetch_workspaces_queue.request_op("reload workspace request".to_string(), ()); 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()); +pub(crate) fn handle_proc_macros_rebuild(state: &mut GlobalState, _: ()) -> Result<()> { + // FIXME: use `Arc::from_iter` when it becomes available + state.proc_macro_clients = Arc::from(Vec::new()); + state.proc_macro_changed = false; + + state.fetch_build_data_queue.request_op("rebuild proc macros request".to_string(), ()); Ok(()) } @@ -95,6 +102,7 @@ pub(crate) fn handle_analyzer_status( .collect::>() ); } + format_to!(buf, "\nVfs memory usage: {}\n", profile::Bytes::new(snap.vfs_memory_usage() as _)); buf.push_str("\nAnalysis:\n"); buf.push_str( &snap @@ -154,6 +162,16 @@ pub(crate) fn handle_view_mir( Ok(res) } +pub(crate) fn handle_interpret_function( + snap: GlobalStateSnapshot, + params: lsp_types::TextDocumentPositionParams, +) -> Result { + let _p = profile::span("handle_interpret_function"); + let position = from_proto::file_position(&snap, params)?; + let res = snap.analysis.interpret_function(position)?; + Ok(res) +} + pub(crate) fn handle_view_file_text( snap: GlobalStateSnapshot, params: lsp_types::TextDocumentIdentifier, @@ -502,7 +520,10 @@ pub(crate) fn handle_workspace_symbol( #[allow(deprecated)] let info = SymbolInformation { - name: nav.name.to_string(), + name: match &nav.alias { + Some(alias) => format!("{} (alias for {})", alias, nav.name), + None => format!("{}", nav.name), + }, kind: nav .kind .map(to_proto::symbol_kind) @@ -747,20 +768,25 @@ pub(crate) fn handle_runnables( let config = snap.config.runnables(); match cargo_spec { Some(spec) => { + let all_targets = !snap.analysis.is_crate_no_std(spec.crate_id)?; for cmd in ["check", "test"] { + let mut cargo_args = + vec![cmd.to_owned(), "--package".to_owned(), spec.package.clone()]; + if all_targets { + cargo_args.push("--all-targets".to_owned()); + } res.push(lsp_ext::Runnable { - label: format!("cargo {cmd} -p {} --all-targets", spec.package), + label: format!( + "cargo {cmd} -p {}{all_targets}", + spec.package, + all_targets = if all_targets { " --all-targets" } else { "" } + ), location: None, kind: lsp_ext::RunnableKind::Cargo, args: lsp_ext::CargoRunnable { workspace_root: Some(spec.workspace_root.clone().into()), override_cargo: config.override_cargo.clone(), - cargo_args: vec![ - cmd.to_string(), - "--package".to_string(), - spec.package.clone(), - "--all-targets".to_string(), - ], + cargo_args, cargo_extra_args: config.cargo_extra_args.clone(), executable_args: Vec::new(), expect_test: None, @@ -769,14 +795,7 @@ pub(crate) fn handle_runnables( } } None => { - if !snap.config.linked_projects().is_empty() - || !snap - .config - .discovered_projects - .as_ref() - .map(|projects| projects.is_empty()) - .unwrap_or(true) - { + if !snap.config.linked_projects().is_empty() { res.push(lsp_ext::Runnable { label: "cargo check --workspace".to_string(), location: None, @@ -1262,7 +1281,7 @@ pub(crate) fn handle_code_lens_resolve( snap: GlobalStateSnapshot, code_lens: CodeLens, ) -> Result { - let annotation = from_proto::annotation(&snap, code_lens.clone())?; + let Some(annotation) = from_proto::annotation(&snap, code_lens.clone())? else { return Ok(code_lens) }; let annotation = snap.analysis.resolve_annotation(annotation)?; let mut acc = Vec::new(); @@ -1321,38 +1340,6 @@ pub(crate) fn handle_ssr( to_proto::workspace_edit(&snap, source_change).map_err(Into::into) } -pub(crate) fn publish_diagnostics( - snap: &GlobalStateSnapshot, - file_id: FileId, -) -> Result> { - let _p = profile::span("publish_diagnostics"); - let line_index = snap.file_line_index(file_id)?; - - let diagnostics: Vec = snap - .analysis - .diagnostics(&snap.config.diagnostics(), AssistResolveStrategy::None, file_id)? - .into_iter() - .map(|d| Diagnostic { - range: to_proto::range(&line_index, d.range), - severity: Some(to_proto::diagnostic_severity(d.severity)), - code: Some(NumberOrString::String(d.code.as_str().to_string())), - code_description: Some(lsp_types::CodeDescription { - href: lsp_types::Url::parse(&format!( - "https://rust-analyzer.github.io/manual.html#{}", - d.code.as_str() - )) - .unwrap(), - }), - source: Some("rust-analyzer".to_string()), - message: d.message, - related_information: None, - tags: if d.unused { Some(vec![DiagnosticTag::UNNECESSARY]) } else { None }, - data: None, - }) - .collect(); - Ok(diagnostics) -} - pub(crate) fn handle_inlay_hints( snap: GlobalStateSnapshot, params: InlayHintParams, @@ -1370,9 +1357,7 @@ pub(crate) fn handle_inlay_hints( snap.analysis .inlay_hints(&inlay_hints_config, file_id, Some(range))? .into_iter() - .map(|it| { - to_proto::inlay_hint(&snap, &line_index, inlay_hints_config.render_colons, it) - }) + .map(|it| to_proto::inlay_hint(&snap, &line_index, it)) .collect::>>()?, )) } @@ -1493,7 +1478,13 @@ pub(crate) fn handle_semantic_tokens_full( snap.workspaces.is_empty() || !snap.proc_macros_loaded; let highlights = snap.analysis.highlight(highlight_config, file_id)?; - let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights); + let semantic_tokens = to_proto::semantic_tokens( + &text, + &line_index, + highlights, + snap.config.semantics_tokens_augments_syntax_tokens(), + snap.config.highlighting_non_standard_tokens(), + ); // Unconditionally cache the tokens snap.semantic_tokens_cache.lock().insert(params.text_document.uri, semantic_tokens.clone()); @@ -1517,7 +1508,13 @@ pub(crate) fn handle_semantic_tokens_full_delta( snap.workspaces.is_empty() || !snap.proc_macros_loaded; let highlights = snap.analysis.highlight(highlight_config, file_id)?; - let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights); + let semantic_tokens = to_proto::semantic_tokens( + &text, + &line_index, + highlights, + snap.config.semantics_tokens_augments_syntax_tokens(), + snap.config.highlighting_non_standard_tokens(), + ); let mut cache = snap.semantic_tokens_cache.lock(); let cached_tokens = cache.entry(params.text_document.uri).or_default(); @@ -1551,20 +1548,53 @@ pub(crate) fn handle_semantic_tokens_range( snap.workspaces.is_empty() || !snap.proc_macros_loaded; let highlights = snap.analysis.highlight_range(highlight_config, frange)?; - let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights); + let semantic_tokens = to_proto::semantic_tokens( + &text, + &line_index, + highlights, + snap.config.semantics_tokens_augments_syntax_tokens(), + snap.config.highlighting_non_standard_tokens(), + ); Ok(Some(semantic_tokens.into())) } pub(crate) fn handle_open_docs( snap: GlobalStateSnapshot, params: lsp_types::TextDocumentPositionParams, -) -> Result> { +) -> Result { let _p = profile::span("handle_open_docs"); let position = from_proto::file_position(&snap, params)?; - let remote = snap.analysis.external_docs(position)?; + let ws_and_sysroot = snap.workspaces.iter().find_map(|ws| match ws { + ProjectWorkspace::Cargo { cargo, sysroot, .. } => Some((cargo, sysroot.as_ref().ok())), + ProjectWorkspace::Json { .. } => None, + ProjectWorkspace::DetachedFiles { .. } => None, + }); + + let (cargo, sysroot) = match ws_and_sysroot { + Some((ws, sysroot)) => (Some(ws), sysroot), + _ => (None, None), + }; + + let sysroot = sysroot.map(|p| p.root().as_os_str()); + let target_dir = cargo.map(|cargo| cargo.target_directory()).map(|p| p.as_os_str()); + + let Ok(remote_urls) = snap.analysis.external_docs(position, target_dir, sysroot) else { + return if snap.config.local_docs() { + Ok(ExternalDocsResponse::WithLocal(Default::default())) + } else { + Ok(ExternalDocsResponse::Simple(None)) + } + }; - Ok(remote.and_then(|remote| Url::parse(&remote).ok())) + let web = remote_urls.web_url.and_then(|it| Url::parse(&it).ok()); + let local = remote_urls.local_url.and_then(|it| Url::parse(&it).ok()); + + if snap.config.local_docs() { + Ok(ExternalDocsResponse::WithLocal(ExternalDocsPair { web, local })) + } else { + Ok(ExternalDocsResponse::Simple(web)) + } } pub(crate) fn handle_open_cargo_toml( @@ -1908,3 +1938,52 @@ fn run_rustfmt( Ok(Some(to_proto::text_edit_vec(&line_index, diff(&file, &new_text)))) } } + +pub(crate) fn fetch_dependency_list( + state: GlobalStateSnapshot, + _params: FetchDependencyListParams, +) -> Result { + let crates = state.analysis.fetch_crates()?; + let crate_infos = crates + .into_iter() + .filter_map(|it| { + let root_file_path = state.file_id_to_file_path(it.root_file_id); + crate_path(root_file_path).and_then(to_url).map(|path| CrateInfoResult { + name: it.name, + version: it.version, + path, + }) + }) + .collect(); + Ok(FetchDependencyListResult { crates: crate_infos }) +} + +/// Searches for the directory of a Rust crate given this crate's root file path. +/// +/// # Arguments +/// +/// * `root_file_path`: The path to the root file of the crate. +/// +/// # Returns +/// +/// An `Option` value representing the path to the directory of the crate with the given +/// name, if such a crate is found. If no crate with the given name is found, this function +/// returns `None`. +fn crate_path(root_file_path: VfsPath) -> Option { + let mut current_dir = root_file_path.parent(); + while let Some(path) = current_dir { + let cargo_toml_path = path.join("../Cargo.toml")?; + if fs::metadata(cargo_toml_path.as_path()?).is_ok() { + let crate_path = cargo_toml_path.parent()?; + return Some(crate_path); + } + current_dir = path.parent(); + } + None +} + +fn to_url(path: VfsPath) -> Option { + let path = path.as_path()?; + let str_path = path.as_os_str().to_str()?; + Url::from_file_path(str_path).ok() +} diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs index e8912b90796ac..bd9f471a46d11 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs @@ -10,8 +10,6 @@ //! 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; - use ide::{CallableSnippets, Change, CompletionConfig, FilePosition, TextSize}; use ide_db::{ imports::insert_use::{ImportGranularity, InsertUseConfig}, @@ -19,6 +17,7 @@ use ide_db::{ }; use project_model::CargoConfig; use test_utils::project_root; +use triomphe::Arc; use vfs::{AbsPathBuf, VfsPath}; use crate::cli::load_cargo::{load_workspace_at, LoadCargoConfig, ProcMacroServerChoice}; @@ -65,7 +64,7 @@ fn integrated_highlighting_benchmark() { let mut text = host.analysis().file_text(file_id).unwrap().to_string(); text.push_str("\npub fn _dummy() {}\n"); let mut change = Change::new(); - change.change_file(file_id, Some(Arc::new(text))); + change.change_file(file_id, Some(Arc::from(text))); host.apply_change(change); } @@ -121,7 +120,7 @@ fn integrated_completion_benchmark() { patch(&mut text, "db.struct_data(self.id)", "sel;\ndb.struct_data(self.id)") + "sel".len(); let mut change = Change::new(); - change.change_file(file_id, Some(Arc::new(text))); + change.change_file(file_id, Some(Arc::from(text))); host.apply_change(change); completion_offset }; @@ -160,7 +159,7 @@ fn integrated_completion_benchmark() { patch(&mut text, "sel;\ndb.struct_data(self.id)", "self.;\ndb.struct_data(self.id)") + "self.".len(); let mut change = Change::new(); - change.change_file(file_id, Some(Arc::new(text))); + change.change_file(file_id, Some(Arc::from(text))); host.apply_change(change); completion_offset }; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs index 32dc3750fdf6b..65de4366e9f61 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs @@ -25,7 +25,6 @@ mod diff; mod dispatch; mod from_proto; mod global_state; -mod handlers; mod line_index; mod lsp_utils; mod main_loop; @@ -38,6 +37,11 @@ mod task_pool; mod to_proto; mod version; +mod handlers { + pub(crate) mod notification; + pub(crate) mod request; +} + pub mod config; pub mod lsp_ext; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/line_index.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/line_index.rs index 791cd931d42a6..15450303ff250 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/line_index.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/line_index.rs @@ -5,9 +5,8 @@ //! This module does line ending conversion and detection (so that we can //! convert back to `\r\n` on the way out). -use std::sync::Arc; - use ide_db::line_index::WideEncoding; +use triomphe::Arc; #[derive(Clone, Copy)] pub enum PositionEncoding { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_ext.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_ext.rs index c7b513db981ea..4d67c8b305fe3 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_ext.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_ext.rs @@ -4,11 +4,11 @@ use std::{collections::HashMap, path::PathBuf}; use ide_db::line_index::WideEncoding; use lsp_types::request::Request; -use lsp_types::PositionEncodingKind; use lsp_types::{ notification::Notification, CodeActionKind, DocumentOnTypeFormattingParams, PartialResultParams, Position, Range, TextDocumentIdentifier, WorkDoneProgressParams, }; +use lsp_types::{PositionEncodingKind, Url}; use serde::{Deserialize, Serialize}; use crate::line_index::PositionEncoding; @@ -27,6 +27,31 @@ pub struct AnalyzerStatusParams { pub text_document: Option, } +#[derive(Deserialize, Serialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct CrateInfoResult { + pub name: Option, + pub version: Option, + pub path: Url, +} +pub enum FetchDependencyList {} + +impl Request for FetchDependencyList { + type Params = FetchDependencyListParams; + type Result = FetchDependencyListResult; + const METHOD: &'static str = "rust-analyzer/fetchDependencyList"; +} + +#[derive(Deserialize, Serialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct FetchDependencyListParams {} + +#[derive(Deserialize, Serialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct FetchDependencyListResult { + pub crates: Vec, +} + pub enum MemoryUsage {} impl Request for MemoryUsage { @@ -51,6 +76,14 @@ impl Request for ReloadWorkspace { const METHOD: &'static str = "rust-analyzer/reloadWorkspace"; } +pub enum RebuildProcMacros {} + +impl Request for RebuildProcMacros { + type Params = (); + type Result = (); + const METHOD: &'static str = "rust-analyzer/rebuildProcMacros"; +} + pub enum SyntaxTree {} impl Request for SyntaxTree { @@ -82,6 +115,14 @@ impl Request for ViewMir { const METHOD: &'static str = "rust-analyzer/viewMir"; } +pub enum InterpretFunction {} + +impl Request for InterpretFunction { + type Params = lsp_types::TextDocumentPositionParams; + type Result = String; + const METHOD: &'static str = "rust-analyzer/interpretFunction"; +} + pub enum ViewFileText {} impl Request for ViewFileText { @@ -343,6 +384,7 @@ impl Request for CodeActionRequest { } pub enum CodeActionResolveRequest {} + impl Request for CodeActionResolveRequest { type Params = CodeAction; type Result = CodeAction; @@ -418,7 +460,7 @@ pub enum HoverRequest {} impl Request for HoverRequest { type Params = HoverParams; type Result = Option; - const METHOD: &'static str = "textDocument/hover"; + const METHOD: &'static str = lsp_types::request::HoverRequest::METHOD; } #[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)] @@ -466,10 +508,24 @@ pub enum ExternalDocs {} impl Request for ExternalDocs { type Params = lsp_types::TextDocumentPositionParams; - type Result = Option; + type Result = ExternalDocsResponse; const METHOD: &'static str = "experimental/externalDocs"; } +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] +#[serde(untagged)] +pub enum ExternalDocsResponse { + Simple(Option), + WithLocal(ExternalDocsPair), +} + +#[derive(Debug, Default, PartialEq, Serialize, Deserialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct ExternalDocsPair { + pub web: Option, + pub local: Option, +} + pub enum OpenCargoToml {} impl Request for OpenCargoToml { @@ -487,7 +543,14 @@ pub struct OpenCargoTomlParams { /// Information about CodeLens, that is to be resolved. #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -pub(crate) enum CodeLensResolveData { +pub struct CodeLensResolveData { + pub version: i32, + pub kind: CodeLensResolveDataKind, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub enum CodeLensResolveDataKind { Impls(lsp_types::request::GotoImplementationParams), References(lsp_types::TextDocumentPositionParams), } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_utils.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_utils.rs index 12e5caf2cc9e4..74e79e8e6050a 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_utils.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_utils.rs @@ -1,8 +1,9 @@ //! Utilities for LSP-related boilerplate code. -use std::{mem, ops::Range, sync::Arc}; +use std::{mem, ops::Range}; use lsp_server::Notification; use lsp_types::request::Request; +use triomphe::Arc; use crate::{ from_proto, @@ -80,39 +81,14 @@ impl GlobalState { match additional_info { Some(additional_info) => { tracing::error!("{}:\n{}", &message, &additional_info); - match self.config.open_server_logs() && tracing::enabled!(tracing::Level::ERROR) { - true => self.send_request::( - lsp_types::ShowMessageRequestParams { - typ: lsp_types::MessageType::ERROR, - message, - actions: Some(vec![lsp_types::MessageActionItem { - title: "Open server logs".to_owned(), - properties: Default::default(), - }]), - }, - |this, resp| { - let lsp_server::Response { error: None, result: Some(result), .. } = resp - else { return }; - if let Ok(Some(_item)) = crate::from_json::< - ::Result, - >( - lsp_types::request::ShowMessageRequest::METHOD, &result - ) { - this.send_notification::(()); - } - }, - ), - false => self.send_notification::( - lsp_types::ShowMessageParams { - typ: lsp_types::MessageType::ERROR, - message, - }, - ), - } + self.show_message( + lsp_types::MessageType::ERROR, + message, + tracing::enabled!(tracing::Level::ERROR), + ); } None => { tracing::error!("{}", &message); - self.send_notification::( lsp_types::ShowMessageParams { typ: lsp_types::MessageType::ERROR, message }, ); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index 67a54cde68c6f..12bc638929d66 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -2,8 +2,6 @@ //! requests/replies and notifications back to the client. use std::{ fmt, - ops::Deref, - sync::Arc, time::{Duration, Instant}, }; @@ -11,20 +9,19 @@ use always_assert::always; use crossbeam_channel::{select, Receiver}; use flycheck::FlycheckHandle; use ide_db::base_db::{SourceDatabaseExt, VfsPath}; -use itertools::Itertools; use lsp_server::{Connection, Notification, Request}; use lsp_types::notification::Notification as _; -use vfs::{AbsPathBuf, ChangeKind, FileId}; +use triomphe::Arc; +use vfs::FileId; use crate::{ config::Config, dispatch::{NotificationDispatcher, RequestDispatcher}, from_proto, global_state::{file_id_to_url, url_to_file_id, GlobalState}, - handlers, lsp_ext, - lsp_utils::{apply_document_changes, notification_is, Progress}, - mem_docs::DocumentData, - reload::{self, BuildDataProgress, ProjectWorkspaceProgress}, + lsp_ext, + lsp_utils::{notification_is, Progress}, + reload::{BuildDataProgress, ProcMacroProgress, ProjectWorkspaceProgress}, Result, }; @@ -36,7 +33,7 @@ pub fn main_loop(config: Config, connection: Connection) -> Result<()> { // temporary bumped. This optimization backfires in our case: each time the // `main_loop` schedules a task to run on a threadpool, the worker threads // gets a higher priority, and (on a machine with fewer cores) displaces the - // main loop! We work-around this by marking the main loop as a + // main loop! We work around this by marking the main loop as a // higher-priority thread. // // https://docs.microsoft.com/en-us/windows/win32/procthread/scheduling-priorities @@ -68,6 +65,7 @@ pub(crate) enum Task { PrimeCaches(PrimeCachesProgress), FetchWorkspace(ProjectWorkspaceProgress), FetchBuildData(BuildDataProgress), + LoadProcMacros(ProcMacroProgress), } #[derive(Debug)] @@ -79,7 +77,7 @@ pub(crate) enum PrimeCachesProgress { impl fmt::Debug for Event { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let debug_verbose_not = |not: &Notification, f: &mut fmt::Formatter<'_>| { + let debug_non_verbose = |not: &Notification, f: &mut fmt::Formatter<'_>| { f.debug_struct("Notification").field("method", ¬.method).finish() }; @@ -88,7 +86,7 @@ impl fmt::Debug for Event { if notification_is::(not) || notification_is::(not) { - return debug_verbose_not(not, f); + return debug_non_verbose(not, f); } } Event::Task(Task::Response(resp)) => { @@ -114,57 +112,63 @@ impl GlobalState { self.update_status_or_notify(); if self.config.did_save_text_document_dynamic_registration() { - let save_registration_options = lsp_types::TextDocumentSaveRegistrationOptions { - include_text: Some(false), - text_document_registration_options: lsp_types::TextDocumentRegistrationOptions { - document_selector: Some(vec![ - lsp_types::DocumentFilter { - language: None, - scheme: None, - pattern: Some("**/*.rs".into()), - }, - lsp_types::DocumentFilter { - language: None, - scheme: None, - pattern: Some("**/Cargo.toml".into()), - }, - lsp_types::DocumentFilter { - language: None, - scheme: None, - pattern: Some("**/Cargo.lock".into()), - }, - ]), - }, - }; - - let registration = lsp_types::Registration { - id: "textDocument/didSave".to_string(), - method: "textDocument/didSave".to_string(), - register_options: Some(serde_json::to_value(save_registration_options).unwrap()), - }; - self.send_request::( - lsp_types::RegistrationParams { registrations: vec![registration] }, - |_, _| (), - ); + self.register_did_save_capability(); } - self.fetch_workspaces_queue.request_op("startup".to_string()); - if let Some(cause) = self.fetch_workspaces_queue.should_start_op() { + self.fetch_workspaces_queue.request_op("startup".to_string(), ()); + if let Some((cause, ())) = self.fetch_workspaces_queue.should_start_op() { self.fetch_workspaces(cause); } while let Some(event) = self.next_event(&inbox) { - if let Event::Lsp(lsp_server::Message::Notification(not)) = &event { - if not.method == lsp_types::notification::Exit::METHOD { - return Ok(()); - } + if matches!( + &event, + Event::Lsp(lsp_server::Message::Notification(Notification { method, .. })) + if method == lsp_types::notification::Exit::METHOD + ) { + return Ok(()); } - self.handle_event(event)? + self.handle_event(event)?; } Err("client exited without proper shutdown sequence".into()) } + fn register_did_save_capability(&mut self) { + let save_registration_options = lsp_types::TextDocumentSaveRegistrationOptions { + include_text: Some(false), + text_document_registration_options: lsp_types::TextDocumentRegistrationOptions { + document_selector: Some(vec![ + lsp_types::DocumentFilter { + language: None, + scheme: None, + pattern: Some("**/*.rs".into()), + }, + lsp_types::DocumentFilter { + language: None, + scheme: None, + pattern: Some("**/Cargo.toml".into()), + }, + lsp_types::DocumentFilter { + language: None, + scheme: None, + pattern: Some("**/Cargo.lock".into()), + }, + ]), + }, + }; + + let registration = lsp_types::Registration { + id: "textDocument/didSave".to_string(), + method: "textDocument/didSave".to_string(), + register_options: Some(serde_json::to_value(save_registration_options).unwrap()), + }; + self.send_request::( + lsp_types::RegistrationParams { registrations: vec![registration] }, + |_, _| (), + ); + } + fn next_event(&self, inbox: &Receiver) -> Option { select! { recv(inbox) -> msg => @@ -186,19 +190,20 @@ impl GlobalState { // NOTE: don't count blocking select! call as a loop-turn time let _p = profile::span("GlobalState::handle_event"); - tracing::debug!("{:?} handle_event({:?})", loop_start, event); - let task_queue_len = self.task_pool.handle.len(); - if task_queue_len > 0 { - tracing::info!("task queue len: {}", task_queue_len); + let event_dbg_msg = format!("{event:?}"); + tracing::debug!("{:?} handle_event({})", loop_start, event_dbg_msg); + if tracing::enabled!(tracing::Level::INFO) { + let task_queue_len = self.task_pool.handle.len(); + if task_queue_len > 0 { + tracing::info!("task queue len: {}", task_queue_len); + } } let was_quiescent = self.is_quiescent(); match event { Event::Lsp(msg) => match msg { lsp_server::Message::Request(req) => self.on_new_request(loop_start, req), - lsp_server::Message::Notification(not) => { - self.on_notification(not)?; - } + lsp_server::Message::Notification(not) => self.on_notification(not)?, lsp_server::Message::Response(resp) => self.complete_request(resp), }, Event::Task(task) => { @@ -247,7 +252,7 @@ impl GlobalState { self.prime_caches_queue.op_completed(()); if cancelled { self.prime_caches_queue - .request_op("restart after cancellation".to_string()); + .request_op("restart after cancellation".to_string(), ()); } } }; @@ -279,7 +284,8 @@ impl GlobalState { if self.is_quiescent() { let became_quiescent = !(was_quiescent || self.fetch_workspaces_queue.op_requested() - || self.fetch_build_data_queue.op_requested()); + || self.fetch_build_data_queue.op_requested() + || self.fetch_proc_macros_queue.op_requested()); if became_quiescent { if self.config.check_on_save() { @@ -287,11 +293,12 @@ impl GlobalState { self.flycheck.iter().for_each(FlycheckHandle::restart); } if self.config.prefill_caches() { - self.prime_caches_queue.request_op("became quiescent".to_string()); + self.prime_caches_queue.request_op("became quiescent".to_string(), ()); } } - if !was_quiescent || state_changed { + let client_refresh = !was_quiescent || state_changed; + if client_refresh { // Refresh semantic tokens if the client supports it. if self.config.semantic_tokens_refresh() { self.semantic_tokens_cache.lock().clear(); @@ -309,9 +316,9 @@ impl GlobalState { } } - if (!was_quiescent || state_changed || memdocs_added_or_removed) - && self.config.publish_diagnostics() - { + let update_diagnostics = (!was_quiescent || state_changed || memdocs_added_or_removed) + && self.config.publish_diagnostics(); + if update_diagnostics { self.update_diagnostics() } } @@ -357,48 +364,54 @@ impl GlobalState { } if self.config.cargo_autoreload() { - if let Some(cause) = self.fetch_workspaces_queue.should_start_op() { + if let Some((cause, ())) = self.fetch_workspaces_queue.should_start_op() { self.fetch_workspaces(cause); } } if !self.fetch_workspaces_queue.op_in_progress() { - if let Some(cause) = self.fetch_build_data_queue.should_start_op() { + if let Some((cause, ())) = self.fetch_build_data_queue.should_start_op() { self.fetch_build_data(cause); + } else if let Some((cause, paths)) = self.fetch_proc_macros_queue.should_start_op() { + self.fetch_proc_macros(cause, paths); } } - if let Some(cause) = self.prime_caches_queue.should_start_op() { - tracing::debug!(%cause, "will prime caches"); - let num_worker_threads = self.config.prime_caches_num_threads(); - - self.task_pool.handle.spawn_with_sender({ - let analysis = self.snapshot().analysis; - move |sender| { - sender.send(Task::PrimeCaches(PrimeCachesProgress::Begin)).unwrap(); - let res = analysis.parallel_prime_caches(num_worker_threads, |progress| { - let report = PrimeCachesProgress::Report(progress); - sender.send(Task::PrimeCaches(report)).unwrap(); - }); - sender - .send(Task::PrimeCaches(PrimeCachesProgress::End { - cancelled: res.is_err(), - })) - .unwrap(); - } - }); + if let Some((cause, ())) = self.prime_caches_queue.should_start_op() { + self.prime_caches(cause); } self.update_status_or_notify(); let loop_duration = loop_start.elapsed(); if loop_duration > Duration::from_millis(100) && was_quiescent { - tracing::warn!("overly long loop turn: {:?}", loop_duration); - self.poke_rust_analyzer_developer(format!("overly long loop turn: {loop_duration:?}")); + tracing::warn!("overly long loop turn took {loop_duration:?}: {event_dbg_msg}"); + self.poke_rust_analyzer_developer(format!( + "overly long loop turn took {loop_duration:?}: {event_dbg_msg}" + )); } Ok(()) } + fn prime_caches(&mut self, cause: String) { + tracing::debug!(%cause, "will prime caches"); + let num_worker_threads = self.config.prime_caches_num_threads(); + + self.task_pool.handle.spawn_with_sender(stdx::thread::ThreadIntent::Worker, { + let analysis = self.snapshot().analysis; + move |sender| { + sender.send(Task::PrimeCaches(PrimeCachesProgress::Begin)).unwrap(); + let res = analysis.parallel_prime_caches(num_worker_threads, |progress| { + let report = PrimeCachesProgress::Report(progress); + sender.send(Task::PrimeCaches(report)).unwrap(); + }); + sender + .send(Task::PrimeCaches(PrimeCachesProgress::End { cancelled: res.is_err() })) + .unwrap(); + } + }); + } + fn update_status_or_notify(&mut self) { let status = self.current_status(); if self.last_reported_status.as_ref() != Some(&status) { @@ -406,7 +419,11 @@ impl GlobalState { if self.config.server_status_notification() { self.send_notification::(status); - } else if let (health, Some(message)) = (status.health, &status.message) { + } else if let ( + health @ (lsp_ext::Health::Warning | lsp_ext::Health::Error), + Some(message), + ) = (status.health, &status.message) + { let open_log_button = tracing::enabled!(tracing::Level::ERROR) && (self.fetch_build_data_error().is_err() || self.fetch_workspace_error().is_err()); @@ -462,7 +479,8 @@ impl GlobalState { 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")); + self.fetch_build_data_queue + .request_op(format!("workspace updated"), ()); } (Progress::End, None) @@ -487,6 +505,22 @@ impl GlobalState { } }; + if let Some(state) = state { + self.report_progress("Building", state, msg, None, None); + } + } + Task::LoadProcMacros(progress) => { + let (state, msg) = match progress { + ProcMacroProgress::Begin => (Some(Progress::Begin), None), + ProcMacroProgress::Report(msg) => (Some(Progress::Report), Some(msg)), + ProcMacroProgress::End(proc_macro_load_result) => { + self.fetch_proc_macros_queue.op_completed(true); + self.set_proc_macros(proc_macro_load_result); + + (Some(Progress::End), None) + } + }; + if let Some(state) = state { self.report_progress("Loading", state, msg, None, None); } @@ -568,21 +602,18 @@ impl GlobalState { (Progress::Begin, None) } flycheck::Progress::DidCheckCrate(target) => (Progress::Report, Some(target)), - flycheck::Progress::DidCancel => (Progress::End, None), + flycheck::Progress::DidCancel => { + self.last_flycheck_error = None; + (Progress::End, None) + } flycheck::Progress::DidFailToRestart(err) => { - self.show_and_log_error( - "cargo check failed to start".to_string(), - Some(err), - ); + self.last_flycheck_error = + Some(format!("cargo check failed to start: {err}")); return; } flycheck::Progress::DidFinish(result) => { - if let Err(err) = result { - self.show_and_log_error( - "cargo check failed".to_string(), - Some(err.to_string()), - ); - } + self.last_flycheck_error = + result.err().map(|err| format!("cargo check failed to start: {err}")); (Progress::End, None) } }; @@ -631,18 +662,54 @@ impl GlobalState { _ => (), } + use crate::handlers::request as handlers; + dispatcher + // Request handlers that must run on the main thread + // because they mutate GlobalState: .on_sync_mut::(handlers::handle_workspace_reload) + .on_sync_mut::(handlers::handle_proc_macros_rebuild) .on_sync_mut::(handlers::handle_memory_usage) .on_sync_mut::(handlers::handle_shuffle_crate_graph) + // Request handlers which are related to the user typing + // are run on the main thread to reduce latency: .on_sync::(handlers::handle_join_lines) .on_sync::(handlers::handle_on_enter) .on_sync::(handlers::handle_selection_range) .on_sync::(handlers::handle_matching_brace) + .on_sync::(handlers::handle_on_type_formatting) + // We can’t run latency-sensitive request handlers which do semantic + // analysis on the main thread because that would block other + // requests. Instead, we run these request handlers on higher priority + // threads in the threadpool. + .on_latency_sensitive::(handlers::handle_completion) + .on_latency_sensitive::( + handlers::handle_completion_resolve, + ) + .on_latency_sensitive::( + handlers::handle_semantic_tokens_full, + ) + .on_latency_sensitive::( + handlers::handle_semantic_tokens_full_delta, + ) + .on_latency_sensitive::( + handlers::handle_semantic_tokens_range, + ) + // Formatting is not caused by the user typing, + // but it does qualify as latency-sensitive + // because a delay before formatting is applied + // can be confusing for the user. + .on_latency_sensitive::(handlers::handle_formatting) + .on_latency_sensitive::( + handlers::handle_range_formatting, + ) + // All other request handlers + .on::(handlers::fetch_dependency_list) .on::(handlers::handle_analyzer_status) .on::(handlers::handle_syntax_tree) .on::(handlers::handle_view_hir) .on::(handlers::handle_view_mir) + .on::(handlers::handle_interpret_function) .on::(handlers::handle_view_file_text) .on::(handlers::handle_view_crate_graph) .on::(handlers::handle_view_item_tree) @@ -657,7 +724,6 @@ impl GlobalState { .on::(handlers::handle_open_cargo_toml) .on::(handlers::handle_move_item) .on::(handlers::handle_workspace_symbol) - .on::(handlers::handle_on_type_formatting) .on::(handlers::handle_document_symbol) .on::(handlers::handle_goto_definition) .on::(handlers::handle_goto_declaration) @@ -665,8 +731,6 @@ impl GlobalState { .on::(handlers::handle_goto_type_definition) .on_no_retry::(handlers::handle_inlay_hints) .on::(handlers::handle_inlay_hints_resolve) - .on::(handlers::handle_completion) - .on::(handlers::handle_completion_resolve) .on::(handlers::handle_code_lens) .on::(handlers::handle_code_lens_resolve) .on::(handlers::handle_folding_range) @@ -674,8 +738,6 @@ impl GlobalState { .on::(handlers::handle_prepare_rename) .on::(handlers::handle_rename) .on::(handlers::handle_references) - .on::(handlers::handle_formatting) - .on::(handlers::handle_range_formatting) .on::(handlers::handle_document_highlight) .on::(handlers::handle_call_hierarchy_prepare) .on::( @@ -684,15 +746,6 @@ impl GlobalState { .on::( handlers::handle_call_hierarchy_outgoing, ) - .on::( - handlers::handle_semantic_tokens_full, - ) - .on::( - handlers::handle_semantic_tokens_full_delta, - ) - .on::( - handlers::handle_semantic_tokens_range, - ) .on::(handlers::handle_will_rename_files) .on::(handlers::handle_ssr) .finish(); @@ -700,282 +753,22 @@ impl GlobalState { /// Handles an incoming notification. fn on_notification(&mut self, not: Notification) -> Result<()> { - // FIXME: Move these implementations out into a module similar to on_request - fn run_flycheck(this: &mut GlobalState, vfs_path: VfsPath) -> bool { - let file_id = this.vfs.read().0.file_id(&vfs_path); - if let Some(file_id) = file_id { - let world = this.snapshot(); - let mut updated = false; - let task = move || -> std::result::Result<(), ide::Cancelled> { - // Trigger flychecks for all workspaces that depend on the saved file - // Crates containing or depending on the saved file - let crate_ids: Vec<_> = world - .analysis - .crates_for(file_id)? - .into_iter() - .flat_map(|id| world.analysis.transitive_rev_deps(id)) - .flatten() - .sorted() - .unique() - .collect(); - - let crate_root_paths: Vec<_> = crate_ids - .iter() - .filter_map(|&crate_id| { - world - .analysis - .crate_root(crate_id) - .map(|file_id| { - world - .file_id_to_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 = - world.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 world.flycheck.iter() { - for (id, _) in workspace_ids.clone() { - if id == flycheck.id() { - updated = true; - flycheck.restart(); - continue; - } - } - } - // No specific flycheck was triggered, so let's trigger all of them. - if !updated { - for flycheck in world.flycheck.iter() { - flycheck.restart(); - } - } - Ok(()) - }; - this.task_pool.handle.spawn_with_sender(move |_| { - if let Err(e) = std::panic::catch_unwind(task) { - tracing::error!("flycheck task panicked: {e:?}") - } - }); - true - } else { - false - } - } + use crate::handlers::notification as handlers; + use lsp_types::notification as notifs; NotificationDispatcher { not: Some(not), global_state: self } - .on::(|this, params| { - let id: lsp_server::RequestId = match params.id { - lsp_types::NumberOrString::Number(id) => id.into(), - lsp_types::NumberOrString::String(id) => id.into(), - }; - this.cancel(id); - Ok(()) - })? - .on::(|this, params| { - if let lsp_types::NumberOrString::String(s) = ¶ms.token { - if let Some(id) = s.strip_prefix("rust-analyzer/flycheck/") { - if let Ok(id) = u32::from_str_radix(id, 10) { - if let Some(flycheck) = this.flycheck.get(id as usize) { - flycheck.cancel(); - } - } - } - } - // Just ignore this. It is OK to continue sending progress - // notifications for this token, as the client can't know when - // we accepted notification. - Ok(()) - })? - .on::(|this, params| { - if let Ok(path) = from_proto::vfs_path(¶ms.text_document.uri) { - let already_exists = this - .mem_docs - .insert(path.clone(), DocumentData::new(params.text_document.version)) - .is_err(); - if already_exists { - tracing::error!("duplicate DidOpenTextDocument: {}", path); - } - this.vfs - .write() - .0 - .set_file_contents(path, Some(params.text_document.text.into_bytes())); - } - Ok(()) - })? + .on::(handlers::handle_cancel)? + .on::(handlers::handle_work_done_progress_cancel)? + .on::(handlers::handle_did_open_text_document)? + .on::(handlers::handle_did_change_text_document)? + .on::(handlers::handle_did_close_text_document)? + .on::(handlers::handle_did_save_text_document)? + .on::(handlers::handle_did_change_configuration)? + .on::(handlers::handle_did_change_workspace_folders)? + .on::(handlers::handle_did_change_watched_files)? .on::(handlers::handle_cancel_flycheck)? - .on::(|this, params| { - if let Ok(path) = from_proto::vfs_path(¶ms.text_document.uri) { - match this.mem_docs.get_mut(&path) { - Some(doc) => { - // The version passed in DidChangeTextDocument is the version after all edits are applied - // so we should apply it before the vfs is notified. - doc.version = params.text_document.version; - } - None => { - tracing::error!("unexpected DidChangeTextDocument: {}", path); - return Ok(()); - } - }; - - let vfs = &mut this.vfs.write().0; - let file_id = vfs.file_id(&path).unwrap(); - let text = apply_document_changes( - this.config.position_encoding(), - || std::str::from_utf8(vfs.file_contents(file_id)).unwrap().into(), - params.content_changes, - ); - - vfs.set_file_contents(path, Some(text.into_bytes())); - } - Ok(()) - })? - .on::(|this, params| { - if let Ok(path) = from_proto::vfs_path(¶ms.text_document.uri) { - if this.mem_docs.remove(&path).is_err() { - tracing::error!("orphan DidCloseTextDocument: {}", path); - } - - this.semantic_tokens_cache.lock().remove(¶ms.text_document.uri); - - if let Some(path) = path.as_path() { - this.loader.handle.invalidate(path.to_path_buf()); - } - } - Ok(()) - })? - .on::(|this, ()| { - this.diagnostics.clear_check_all(); - Ok(()) - })? - .on::(|this, params| { - if let Some(text_document) = params.text_document { - if let Ok(vfs_path) = from_proto::vfs_path(&text_document.uri) { - if run_flycheck(this, vfs_path) { - return Ok(()); - } - } - } - // No specific flycheck was triggered, so let's trigger all of them. - for flycheck in this.flycheck.iter() { - flycheck.restart(); - } - Ok(()) - })? - .on::(|this, params| { - if let Ok(vfs_path) = from_proto::vfs_path(¶ms.text_document.uri) { - // 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 - .request_op(format!("DidSaveTextDocument {}", abs_path.display())); - } - } - - if !this.config.check_on_save() || run_flycheck(this, vfs_path) { - return Ok(()); - } - } else if this.config.check_on_save() { - // No specific flycheck was triggered, so let's trigger all of them. - for flycheck in this.flycheck.iter() { - flycheck.restart(); - } - } - Ok(()) - })? - .on::(|this, _params| { - // As stated in https://github.com/microsoft/language-server-protocol/issues/676, - // this notification's parameters should be ignored and the actual config queried separately. - this.send_request::( - lsp_types::ConfigurationParams { - items: vec![lsp_types::ConfigurationItem { - scope_uri: None, - section: Some("rust-analyzer".to_string()), - }], - }, - |this, resp| { - tracing::debug!("config update response: '{:?}", resp); - let lsp_server::Response { error, result, .. } = resp; - - match (error, result) { - (Some(err), _) => { - tracing::error!("failed to fetch the server settings: {:?}", err) - } - (None, Some(mut configs)) => { - if let Some(json) = configs.get_mut(0) { - // Note that json can be null according to the spec if the client can't - // provide a configuration. This is handled in Config::update below. - let mut config = Config::clone(&*this.config); - if let Err(error) = config.update(json.take()) { - this.show_message( - lsp_types::MessageType::WARNING, - error.to_string(), - false, - ); - } - this.update_configuration(config); - } - } - (None, None) => tracing::error!( - "received empty server settings response from the client" - ), - } - }, - ); - - Ok(()) - })? - .on::(|this, params| { - let config = Arc::make_mut(&mut this.config); - - for workspace in params.event.removed { - let Ok(path) = workspace.uri.to_file_path() else { continue }; - let Ok(path) = AbsPathBuf::try_from(path) else { continue }; - let Some(position) = config.workspace_roots.iter().position(|it| it == &path) else { continue }; - config.workspace_roots.remove(position); - } - - let added = params - .event - .added - .into_iter() - .filter_map(|it| it.uri.to_file_path().ok()) - .filter_map(|it| AbsPathBuf::try_from(it).ok()); - config.workspace_roots.extend(added); - if !config.has_linked_projects() && config.detached_files().is_empty() { - config.rediscover_workspaces(); - this.fetch_workspaces_queue.request_op("client workspaces changed".to_string()) - } - - Ok(()) - })? - .on::(|this, params| { - for change in params.changes { - if let Ok(path) = from_proto::abs_path(&change.uri) { - this.loader.handle.invalidate(path); - } - } - Ok(()) - })? + .on::(handlers::handle_clear_flycheck)? + .on::(handlers::handle_run_flycheck)? .finish(); Ok(()) } @@ -1000,16 +793,60 @@ impl GlobalState { tracing::trace!("updating notifications for {:?}", subscriptions); let snapshot = self.snapshot(); - self.task_pool.handle.spawn(move || { + + // Diagnostics are triggered by the user typing + // so we run them on a latency sensitive thread. + self.task_pool.handle.spawn(stdx::thread::ThreadIntent::LatencySensitive, move || { + let _p = profile::span("publish_diagnostics"); + let _ctx = stdx::panic_context::enter("publish_diagnostics".to_owned()); let diagnostics = subscriptions .into_iter() .filter_map(|file_id| { - handlers::publish_diagnostics(&snapshot, file_id) - .ok() - .map(|diags| (file_id, diags)) + let line_index = snapshot.file_line_index(file_id).ok()?; + Some(( + file_id, + line_index, + snapshot + .analysis + .diagnostics( + &snapshot.config.diagnostics(), + ide::AssistResolveStrategy::None, + file_id, + ) + .ok()?, + )) }) - .collect::>(); - Task::Diagnostics(diagnostics) - }) + .map(|(file_id, line_index, it)| { + ( + file_id, + it.into_iter() + .map(move |d| lsp_types::Diagnostic { + range: crate::to_proto::range(&line_index, d.range), + severity: Some(crate::to_proto::diagnostic_severity(d.severity)), + code: Some(lsp_types::NumberOrString::String( + d.code.as_str().to_string(), + )), + code_description: Some(lsp_types::CodeDescription { + href: lsp_types::Url::parse(&format!( + "https://rust-analyzer.github.io/manual.html#{}", + d.code.as_str() + )) + .unwrap(), + }), + source: Some("rust-analyzer".to_string()), + message: d.message, + related_information: None, + tags: if d.unused { + Some(vec![lsp_types::DiagnosticTag::UNNECESSARY]) + } else { + None + }, + data: None, + }) + .collect::>(), + ) + }); + Task::Diagnostics(diagnostics.collect()) + }); } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/markdown.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/markdown.rs index 912ed1e764275..58426c66a85c1 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/markdown.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/markdown.rs @@ -28,7 +28,7 @@ pub(crate) fn format_docs(src: &str) -> String { if in_code_block { let trimmed = line.trim_start(); - if trimmed.starts_with("##") { + if is_rust && trimmed.starts_with("##") { line = &trimmed[1..]; } } @@ -154,4 +154,12 @@ let s = "foo assert_eq!(format_docs(comment), "```rust\nlet s = \"foo\n# bar # baz\";\n```"); } + + #[test] + fn test_format_docs_handles_double_hashes_non_rust() { + let comment = r#"```markdown +## A second-level heading +```"#; + assert_eq!(format_docs(comment), "```markdown\n## A second-level heading\n```"); + } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/op_queue.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/op_queue.rs index 97aca0161873e..932730fc234b4 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/op_queue.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/op_queue.rs @@ -3,23 +3,23 @@ pub(crate) type Cause = String; -pub(crate) struct OpQueue { - op_requested: Option, +pub(crate) struct OpQueue { + op_requested: Option<(Cause, Args)>, op_in_progress: bool, last_op_result: Output, } -impl Default for OpQueue { +impl Default for OpQueue { fn default() -> Self { Self { op_requested: None, op_in_progress: false, last_op_result: Default::default() } } } -impl OpQueue { - pub(crate) fn request_op(&mut self, reason: Cause) { - self.op_requested = Some(reason); +impl OpQueue { + pub(crate) fn request_op(&mut self, reason: Cause, args: Args) { + self.op_requested = Some((reason, args)); } - pub(crate) fn should_start_op(&mut self) -> Option { + pub(crate) fn should_start_op(&mut self) -> Option<(Cause, Args)> { if self.op_in_progress { return None; } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs index 1a6e1af2eb7ed..6e8c8ea91a1b3 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs @@ -12,22 +12,24 @@ //! correct. Instead, we try to provide a best-effort service. Even if the //! project is currently loading and we don't have a full project model, we //! still want to respond to various requests. -use std::{collections::hash_map::Entry, mem, sync::Arc}; +use std::{collections::hash_map::Entry, iter, mem, sync}; use flycheck::{FlycheckConfig, FlycheckHandle}; use hir::db::DefDatabase; use ide::Change; use ide_db::{ base_db::{ - CrateGraph, Env, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind, - ProcMacroLoadResult, SourceRoot, VfsPath, + salsa::Durability, CrateGraph, Env, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, + ProcMacroKind, ProcMacroLoadResult, ProcMacroPaths, ProcMacros, SourceRoot, VfsPath, }, FxHashMap, }; use itertools::Itertools; use proc_macro_api::{MacroDylib, ProcMacroServer}; use project_model::{PackageRoot, ProjectWorkspace, WorkspaceBuildScripts}; +use stdx::{format_to, thread::ThreadIntent}; use syntax::SmolStr; +use triomphe::Arc; use vfs::{file_set::FileSetConfig, AbsPath, AbsPathBuf, ChangeKind}; use crate::{ @@ -54,11 +56,19 @@ pub(crate) enum BuildDataProgress { End((Arc>, Vec>)), } +#[derive(Debug)] +pub(crate) enum ProcMacroProgress { + Begin, + Report(String), + End(ProcMacros), +} + impl GlobalState { pub(crate) fn is_quiescent(&self) -> bool { !(self.last_reported_status.is_none() || self.fetch_workspaces_queue.op_in_progress() || self.fetch_build_data_queue.op_in_progress() + || self.fetch_proc_macros_queue.op_in_progress() || self.vfs_progress_config_version < self.vfs_config_version || self.vfs_progress_n_done < self.vfs_progress_n_total) } @@ -66,21 +76,27 @@ impl GlobalState { pub(crate) fn update_configuration(&mut self, config: Config) { let _p = profile::span("GlobalState::update_configuration"); let old_config = mem::replace(&mut self.config, Arc::new(config)); - if self.config.lru_capacity() != old_config.lru_capacity() { - self.analysis_host.update_lru_capacity(self.config.lru_capacity()); + if self.config.lru_parse_query_capacity() != old_config.lru_parse_query_capacity() { + self.analysis_host.update_lru_capacity(self.config.lru_parse_query_capacity()); + } + if self.config.lru_query_capacities() != old_config.lru_query_capacities() { + self.analysis_host.update_lru_capacities( + &self.config.lru_query_capacities().cloned().unwrap_or_default(), + ); } if self.config.linked_projects() != old_config.linked_projects() { - self.fetch_workspaces_queue.request_op("linked projects changed".to_string()) + self.fetch_workspaces_queue.request_op("linked projects changed".to_string(), ()) } else if self.config.flycheck() != old_config.flycheck() { self.reload_flycheck(); } - if self.analysis_host.raw_database().enable_proc_attr_macros() + if self.analysis_host.raw_database().expand_proc_attr_macros() != self.config.expand_proc_attr_macros() { - self.analysis_host - .raw_database_mut() - .set_enable_proc_attr_macros(self.config.expand_proc_attr_macros()); + self.analysis_host.raw_database_mut().set_expand_proc_attr_macros_with_durability( + self.config.expand_proc_attr_macros(), + Durability::HIGH, + ); } } @@ -94,12 +110,16 @@ impl GlobalState { if self.proc_macro_changed { status.health = lsp_ext::Health::Warning; - message.push_str("Reload required due to source changes of a procedural macro.\n\n"); + message.push_str("Proc-macros have changed and need to be rebuild.\n\n"); } if let Err(_) = self.fetch_build_data_error() { status.health = lsp_ext::Health::Warning; message.push_str("Failed to run build scripts of some packages.\n\n"); } + if self.proc_macro_clients.iter().any(|it| it.is_err()) { + status.health = lsp_ext::Health::Warning; + message.push_str("Failed to spawn one or more proc-macro servers.\n\n"); + } if !self.config.cargo_autoreload() && self.is_quiescent() && self.fetch_workspaces_queue.op_requested() @@ -112,17 +132,37 @@ impl GlobalState { && self.config.notifications().cargo_toml_not_found { status.health = lsp_ext::Health::Warning; - message.push_str("Failed to discover workspace.\n\n"); + message.push_str("Failed to discover workspace.\n"); + message.push_str("Consider adding the `Cargo.toml` of the workspace to the [`linkedProjects`](https://rust-analyzer.github.io/manual.html#rust-analyzer.linkedProjects) setting.\n\n"); + } + if let Some(err) = &self.config_errors { + status.health = lsp_ext::Health::Warning; + format_to!(message, "{err}\n"); + } + if let Some(err) = &self.last_flycheck_error { + status.health = lsp_ext::Health::Warning; + message.push_str(err); + message.push('\n'); } for ws in self.workspaces.iter() { let (ProjectWorkspace::Cargo { sysroot, .. } | ProjectWorkspace::Json { sysroot, .. } | ProjectWorkspace::DetachedFiles { sysroot, .. }) = ws; - if let Err(Some(e)) = sysroot { - status.health = lsp_ext::Health::Warning; - message.push_str(e); - message.push_str("\n\n"); + match sysroot { + Err(None) => (), + Err(Some(e)) => { + status.health = lsp_ext::Health::Warning; + message.push_str(e); + message.push_str("\n\n"); + } + Ok(s) => { + if let Some(e) = s.loading_warning() { + status.health = lsp_ext::Health::Warning; + message.push_str(&e); + message.push_str("\n\n"); + } + } } if let ProjectWorkspace::Cargo { rustc: Err(Some(e)), .. } = ws { status.health = lsp_ext::Health::Warning; @@ -145,7 +185,7 @@ impl GlobalState { pub(crate) fn fetch_workspaces(&mut self, cause: Cause) { tracing::info!(%cause, "will fetch workspaces"); - self.task_pool.handle.spawn_with_sender({ + self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, { let linked_projects = self.config.linked_projects(); let detached_files = self.config.detached_files().to_vec(); let cargo_config = self.config.cargo(); @@ -177,11 +217,30 @@ impl GlobalState { it.clone(), cargo_config.target.as_deref(), &cargo_config.extra_env, + None, )) } }) .collect::>(); + let mut i = 0; + while i < workspaces.len() { + if let Ok(w) = &workspaces[i] { + let dupes: Vec<_> = workspaces + .iter() + .enumerate() + .skip(i + 1) + .filter_map(|(i, it)| { + it.as_ref().ok().filter(|ws| ws.eq_ignore_build_data(w)).map(|_| i) + }) + .collect(); + dupes.into_iter().rev().for_each(|d| { + _ = workspaces.remove(d); + }); + } + i += 1; + } + if !detached_files.is_empty() { workspaces.push(project_model::ProjectWorkspace::load_detached_files( detached_files, @@ -201,7 +260,7 @@ impl GlobalState { tracing::info!(%cause, "will fetch build data"); let workspaces = Arc::clone(&self.workspaces); let config = self.config.cargo(); - self.task_pool.handle.spawn_with_sender(move |sender| { + self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, move |sender| { sender.send(Task::FetchBuildData(BuildDataProgress::Begin)).unwrap(); let progress = { @@ -216,6 +275,63 @@ impl GlobalState { }); } + pub(crate) fn fetch_proc_macros(&mut self, cause: Cause, paths: Vec) { + tracing::info!(%cause, "will load proc macros"); + let dummy_replacements = self.config.dummy_replacements().clone(); + let proc_macro_clients = self.proc_macro_clients.clone(); + + self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, move |sender| { + sender.send(Task::LoadProcMacros(ProcMacroProgress::Begin)).unwrap(); + + let dummy_replacements = &dummy_replacements; + let progress = { + let sender = sender.clone(); + &move |msg| { + sender.send(Task::LoadProcMacros(ProcMacroProgress::Report(msg))).unwrap() + } + }; + + let mut res = FxHashMap::default(); + let chain = proc_macro_clients + .iter() + .map(|res| res.as_ref().map_err(|e| e.to_string())) + .chain(iter::repeat_with(|| Err("Proc macros servers are not running".into()))); + for (client, paths) in chain.zip(paths) { + res.extend(paths.into_iter().map(move |(crate_id, res)| { + ( + crate_id, + res.map_or_else( + |_| Err("proc macro crate is missing dylib".to_owned()), + |(crate_name, path)| { + progress(path.display().to_string()); + client.as_ref().map_err(Clone::clone).and_then(|client| { + load_proc_macro( + client, + &path, + crate_name + .as_deref() + .and_then(|crate_name| { + dummy_replacements.get(crate_name).map(|v| &**v) + }) + .unwrap_or_default(), + ) + }) + }, + ), + ) + })); + } + + sender.send(Task::LoadProcMacros(ProcMacroProgress::End(res))).unwrap(); + }); + } + + pub(crate) fn set_proc_macros(&mut self, proc_macros: ProcMacros) { + let mut change = Change::new(); + change.set_proc_macros(proc_macros); + self.analysis_host.apply_change(change); + } + pub(crate) fn switch_workspaces(&mut self, cause: Cause) { let _p = profile::span("GlobalState::switch_workspaces"); tracing::info!(%cause, "will switch workspaces"); @@ -303,43 +419,39 @@ impl GlobalState { ); } - let mut change = Change::new(); - let files_config = self.config.files(); let project_folders = ProjectFolders::new(&self.workspaces, &files_config.exclude); - if self.proc_macro_clients.is_empty() { - if let Some((path, path_manually_set)) = self.config.proc_macro_srv() { + if self.proc_macro_clients.is_empty() || !same_workspaces { + if self.config.expand_proc_macros() { tracing::info!("Spawning proc-macro servers"); - self.proc_macro_clients = self - .workspaces - .iter() - .map(|ws| { - let (path, args): (_, &[_]) = if path_manually_set { - tracing::debug!( - "Pro-macro server path explicitly set: {}", - path.display() - ); - (path.clone(), &[]) - } else { - match ws.find_sysroot_proc_macro_srv() { - Some(server_path) => (server_path, &[]), - None => (path.clone(), &["proc-macro"]), - } - }; - - tracing::info!(?args, "Using proc-macro server at {}", path.display(),); - ProcMacroServer::spawn(path.clone(), args).map_err(|err| { - let error = format!( - "Failed to run proc-macro server from path {}, error: {:?}", - path.display(), - err - ); - tracing::error!(error); - error + + // FIXME: use `Arc::from_iter` when it becomes available + self.proc_macro_clients = Arc::from( + self.workspaces + .iter() + .map(|ws| { + let path = match self.config.proc_macro_srv() { + Some(path) => path, + None => ws.find_sysroot_proc_macro_srv()?, + }; + + tracing::info!("Using proc-macro server at {}", path.display(),); + ProcMacroServer::spawn(path.clone()).map_err(|err| { + tracing::error!( + "Failed to run proc-macro server from path {}, error: {:?}", + path.display(), + err + ); + anyhow::anyhow!( + "Failed to run proc-macro server from path {}, error: {:?}", + path.display(), + err + ) + }) }) - }) - .collect() + .collect::>(), + ) }; } @@ -353,56 +465,48 @@ impl GlobalState { watch, version: self.vfs_config_version, }); + self.source_root_config = project_folders.source_root_config; // Create crate graph from all the workspaces - let crate_graph = { - let dummy_replacements = self.config.dummy_replacements(); - + let (crate_graph, proc_macro_paths) = { let vfs = &mut self.vfs.write().0; let loader = &mut self.loader; - let mem_docs = &self.mem_docs; - let mut load = move |path: &AbsPath| { + let mut load = |path: &AbsPath| { let _p = profile::span("switch_workspaces::load"); let vfs_path = vfs::VfsPath::from(path.to_path_buf()); - if !mem_docs.contains(&vfs_path) { - let contents = loader.handle.load_sync(path); - vfs.set_file_contents(vfs_path.clone(), contents); - } - let res = vfs.file_id(&vfs_path); - if res.is_none() { - tracing::warn!("failed to load {}", path.display()) + match vfs.file_id(&vfs_path) { + Some(file_id) => Some(file_id), + None => { + if !self.mem_docs.contains(&vfs_path) { + let contents = loader.handle.load_sync(path); + vfs.set_file_contents(vfs_path.clone(), contents); + } + vfs.file_id(&vfs_path) + } } - res }; let mut crate_graph = CrateGraph::default(); - for (idx, ws) in self.workspaces.iter().enumerate() { - 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, - 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, - &self.config.cargo().extra_env, - )); + let mut proc_macros = Vec::default(); + for ws in &**self.workspaces { + let (other, mut crate_proc_macros) = + ws.to_crate_graph(&mut load, &self.config.extra_env()); + crate_graph.extend(other, &mut crate_proc_macros); + proc_macros.push(crate_proc_macros); } - crate_graph + (crate_graph, proc_macros) }; - change.set_crate_graph(crate_graph); - - self.source_root_config = project_folders.source_root_config; + let mut change = Change::new(); + if self.config.expand_proc_macros() { + self.fetch_proc_macros_queue.request_op(cause, proc_macro_paths); + } + change.set_crate_graph(crate_graph); self.analysis_host.apply_change(change); self.process_changes(); + self.reload_flycheck(); + tracing::info!("did switch workspaces"); } @@ -642,14 +746,12 @@ 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, &str>, + server: &ProcMacroServer, path: &AbsPath, dummy_replace: &[Box], ) -> ProcMacroLoadResult { - let server = server.map_err(ToOwned::to_owned)?; let res: Result, String> = (|| { - let dylib = MacroDylib::new(path.to_path_buf()) - .map_err(|io| format!("Proc-macro dylib loading failed: {io}"))?; + let dylib = MacroDylib::new(path.to_path_buf()); 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()); @@ -684,14 +786,14 @@ pub(crate) fn load_proc_macro( proc_macro_api::ProcMacroKind::FuncLike => ProcMacroKind::FuncLike, proc_macro_api::ProcMacroKind::Attr => ProcMacroKind::Attr, }; - let expander: Arc = + let expander: sync::Arc = if dummy_replace.iter().any(|replace| &**replace == name) { match kind { - ProcMacroKind::Attr => Arc::new(IdentityExpander), - _ => Arc::new(EmptyExpander), + ProcMacroKind::Attr => sync::Arc::new(IdentityExpander), + _ => sync::Arc::new(EmptyExpander), } } else { - Arc::new(Expander(expander)) + sync::Arc::new(Expander(expander)) }; ProcMacro { name, kind, expander } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/semantic_tokens.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/semantic_tokens.rs index c2cc3f422d206..d4bb20c8f448e 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/semantic_tokens.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/semantic_tokens.rs @@ -13,7 +13,7 @@ macro_rules! define_semantic_token_types { $($standard:ident),*$(,)? } custom { - $(($custom:ident, $string:literal)),*$(,)? + $(($custom:ident, $string:literal) $(=> $fallback:ident)?),*$(,)? } ) => { @@ -24,6 +24,15 @@ macro_rules! define_semantic_token_types { $(SemanticTokenType::$standard,)* $($custom),* ]; + + pub(crate) fn standard_fallback_type(token: SemanticTokenType) -> Option { + $( + if token == $custom { + None $(.or(Some(SemanticTokenType::$fallback)))? + } else + )* + { Some(token )} + } }; } @@ -51,42 +60,46 @@ define_semantic_token_types![ custom { (ANGLE, "angle"), - (ARITHMETIC, "arithmetic"), - (ATTRIBUTE, "attribute"), - (ATTRIBUTE_BRACKET, "attributeBracket"), - (BITWISE, "bitwise"), + (ARITHMETIC, "arithmetic") => OPERATOR, + (ATTRIBUTE, "attribute") => DECORATOR, + (ATTRIBUTE_BRACKET, "attributeBracket") => DECORATOR, + (BITWISE, "bitwise") => OPERATOR, (BOOLEAN, "boolean"), (BRACE, "brace"), (BRACKET, "bracket"), - (BUILTIN_ATTRIBUTE, "builtinAttribute"), + (BUILTIN_ATTRIBUTE, "builtinAttribute") => DECORATOR, (BUILTIN_TYPE, "builtinType"), - (CHAR, "character"), + (CHAR, "character") => STRING, (COLON, "colon"), (COMMA, "comma"), - (COMPARISON, "comparison"), + (COMPARISON, "comparison") => OPERATOR, (CONST_PARAMETER, "constParameter"), - (DERIVE, "derive"), - (DERIVE_HELPER, "deriveHelper"), + (DERIVE, "derive") => DECORATOR, + (DERIVE_HELPER, "deriveHelper") => DECORATOR, (DOT, "dot"), - (ESCAPE_SEQUENCE, "escapeSequence"), - (FORMAT_SPECIFIER, "formatSpecifier"), - (GENERIC, "generic"), + (ESCAPE_SEQUENCE, "escapeSequence") => STRING, + (FORMAT_SPECIFIER, "formatSpecifier") => STRING, + (GENERIC, "generic") => TYPE_PARAMETER, (LABEL, "label"), (LIFETIME, "lifetime"), - (LOGICAL, "logical"), - (MACRO_BANG, "macroBang"), + (LOGICAL, "logical") => OPERATOR, + (MACRO_BANG, "macroBang") => MACRO, (PARENTHESIS, "parenthesis"), (PUNCTUATION, "punctuation"), - (SELF_KEYWORD, "selfKeyword"), - (SELF_TYPE_KEYWORD, "selfTypeKeyword"), + (SELF_KEYWORD, "selfKeyword") => KEYWORD, + (SELF_TYPE_KEYWORD, "selfTypeKeyword") => KEYWORD, (SEMICOLON, "semicolon"), (TYPE_ALIAS, "typeAlias"), - (TOOL_MODULE, "toolModule"), + (TOOL_MODULE, "toolModule") => DECORATOR, (UNION, "union"), (UNRESOLVED_REFERENCE, "unresolvedReference"), } ]; +macro_rules! count_tts { + () => {0usize}; + ($_head:tt $($tail:tt)*) => {1usize + count_tts!($($tail)*)}; +} macro_rules! define_semantic_token_modifiers { ( standard { @@ -105,6 +118,8 @@ macro_rules! define_semantic_token_modifiers { $(SemanticTokenModifier::$standard,)* $($custom),* ]; + + const LAST_STANDARD_MOD: usize = count_tts!($($standard)*); }; } @@ -126,6 +141,7 @@ define_semantic_token_modifiers![ (INJECTED, "injected"), (INTRA_DOC_LINK, "intraDocLink"), (LIBRARY, "library"), + (MACRO_MODIFIER, "macro"), (MUTABLE, "mutable"), (PUBLIC, "public"), (REFERENCE, "reference"), @@ -137,6 +153,13 @@ define_semantic_token_modifiers![ #[derive(Default)] pub(crate) struct ModifierSet(pub(crate) u32); +impl ModifierSet { + pub(crate) fn standard_fallback(&mut self) { + // Remove all non standard modifiers + self.0 = self.0 & !(!0u32 << LAST_STANDARD_MOD) + } +} + impl ops::BitOrAssign for ModifierSet { fn bitor_assign(&mut self, rhs: SemanticTokenModifier) { let idx = SUPPORTED_MODIFIERS.iter().position(|it| it == &rhs).unwrap(); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/task_pool.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/task_pool.rs index 616e449984ae6..a5a10e86914e0 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/task_pool.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/task_pool.rs @@ -1,53 +1,42 @@ -//! A thin wrapper around `ThreadPool` to make sure that we join all things -//! properly. +//! A thin wrapper around [`stdx::thread::Pool`] which threads a sender through spawned jobs. +//! It is used in [`crate::global_state::GlobalState`] throughout the main loop. + use crossbeam_channel::Sender; +use stdx::thread::{Pool, ThreadIntent}; pub(crate) struct TaskPool { sender: Sender, - inner: threadpool::ThreadPool, + pool: Pool, } impl TaskPool { pub(crate) fn new_with_threads(sender: Sender, threads: usize) -> TaskPool { - const STACK_SIZE: usize = 8 * 1024 * 1024; - - let inner = threadpool::Builder::new() - .thread_name("Worker".into()) - .thread_stack_size(STACK_SIZE) - .num_threads(threads) - .build(); - TaskPool { sender, inner } + TaskPool { sender, pool: Pool::new(threads) } } - pub(crate) fn spawn(&mut self, task: F) + pub(crate) fn spawn(&mut self, intent: ThreadIntent, task: F) where F: FnOnce() -> T + Send + 'static, T: Send + 'static, { - self.inner.execute({ + self.pool.spawn(intent, { let sender = self.sender.clone(); move || sender.send(task()).unwrap() }) } - pub(crate) fn spawn_with_sender(&mut self, task: F) + pub(crate) fn spawn_with_sender(&mut self, intent: ThreadIntent, task: F) where F: FnOnce(Sender) + Send + 'static, T: Send + 'static, { - self.inner.execute({ + self.pool.spawn(intent, { let sender = self.sender.clone(); move || task(sender) }) } pub(crate) fn len(&self) -> usize { - self.inner.queued_count() - } -} - -impl Drop for TaskPool { - fn drop(&mut self) { - self.inner.join() + self.pool.len() } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs index 7d97b69f8ea05..8a9e947ded0ad 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs +++ b/src/tools/rust-analyzer/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, + semantic_tokens::{self, standard_fallback_type}, }; pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position { @@ -32,7 +32,7 @@ pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::P match line_index.encoding { PositionEncoding::Utf8 => lsp_types::Position::new(line_col.line, line_col.col), PositionEncoding::Wide(enc) => { - let line_col = line_index.index.to_wide(enc, line_col); + let line_col = line_index.index.to_wide(enc, line_col).unwrap(); lsp_types::Position::new(line_col.line, line_col.col) } } @@ -279,7 +279,7 @@ fn completion_item( let mut lsp_item = lsp_types::CompletionItem { label: item.label.to_string(), - detail: item.detail.map(|it| it.to_string()), + detail: item.detail, filter_text: Some(lookup), kind: Some(completion_item_kind(item.kind)), text_edit: Some(text_edit), @@ -306,12 +306,10 @@ fn completion_item( let imports: Vec<_> = item .import_to_add .into_iter() - .filter_map(|import_edit| { - let import_path = &import_edit.import_path; - let import_name = import_path.segments().last()?; + .filter_map(|(import_path, import_name)| { Some(lsp_ext::CompletionImport { - full_import_path: import_path.to_string(), - imported_name: import_name.to_string(), + full_import_path: import_path, + imported_name: import_name, }) }) .collect(); @@ -434,83 +432,23 @@ pub(crate) fn signature_help( pub(crate) fn inlay_hint( snap: &GlobalStateSnapshot, line_index: &LineIndex, - render_colons: bool, - mut inlay_hint: InlayHint, + inlay_hint: InlayHint, ) -> Cancellable { - match inlay_hint.kind { - InlayKind::Parameter if render_colons => inlay_hint.label.append_str(":"), - InlayKind::Type if render_colons => inlay_hint.label.prepend_str(": "), - InlayKind::ClosureReturnType => inlay_hint.label.prepend_str(" -> "), - InlayKind::Discriminant => inlay_hint.label.prepend_str(" = "), - _ => {} - } - let (label, tooltip) = inlay_hint_label(snap, inlay_hint.label)?; Ok(lsp_types::InlayHint { - position: match inlay_hint.kind { - // before annotated thing - InlayKind::OpeningParenthesis - | InlayKind::Parameter - | InlayKind::Adjustment - | InlayKind::BindingMode => position(line_index, inlay_hint.range.start()), - // after annotated thing - InlayKind::ClosureReturnType - | InlayKind::Type - | InlayKind::Discriminant - | InlayKind::Chaining - | InlayKind::GenericParamList - | InlayKind::ClosingParenthesis - | InlayKind::AdjustmentPostfix - | InlayKind::Lifetime - | InlayKind::ClosingBrace => position(line_index, inlay_hint.range.end()), + position: match inlay_hint.position { + ide::InlayHintPosition::Before => position(line_index, inlay_hint.range.start()), + ide::InlayHintPosition::After => position(line_index, inlay_hint.range.end()), }, - padding_left: Some(match inlay_hint.kind { - InlayKind::Type => !render_colons, - InlayKind::Chaining | InlayKind::ClosingBrace => true, - InlayKind::ClosingParenthesis - | InlayKind::Discriminant - | InlayKind::OpeningParenthesis - | InlayKind::BindingMode - | InlayKind::ClosureReturnType - | InlayKind::GenericParamList - | InlayKind::Adjustment - | InlayKind::AdjustmentPostfix - | InlayKind::Lifetime - | InlayKind::Parameter => false, - }), - padding_right: Some(match inlay_hint.kind { - InlayKind::ClosingParenthesis - | InlayKind::OpeningParenthesis - | InlayKind::Chaining - | InlayKind::ClosureReturnType - | InlayKind::GenericParamList - | InlayKind::Adjustment - | InlayKind::AdjustmentPostfix - | InlayKind::Type - | InlayKind::Discriminant - | InlayKind::ClosingBrace => false, - InlayKind::BindingMode => { - matches!(&label, lsp_types::InlayHintLabel::String(s) if s != "&") - } - InlayKind::Parameter | InlayKind::Lifetime => true, - }), + padding_left: Some(inlay_hint.pad_left), + padding_right: Some(inlay_hint.pad_right), kind: match inlay_hint.kind { InlayKind::Parameter => Some(lsp_types::InlayHintKind::PARAMETER), - InlayKind::ClosureReturnType | InlayKind::Type | InlayKind::Chaining => { - Some(lsp_types::InlayHintKind::TYPE) - } - InlayKind::ClosingParenthesis - | InlayKind::Discriminant - | InlayKind::OpeningParenthesis - | InlayKind::BindingMode - | InlayKind::GenericParamList - | InlayKind::Lifetime - | InlayKind::Adjustment - | InlayKind::AdjustmentPostfix - | InlayKind::ClosingBrace => None, + InlayKind::Type | InlayKind::Chaining => Some(lsp_types::InlayHintKind::TYPE), + _ => None, }, - text_edits: None, + text_edits: inlay_hint.text_edit.map(|it| text_edit_vec(line_index, it)), data: None, tooltip, label, @@ -580,6 +518,8 @@ pub(crate) fn semantic_tokens( text: &str, line_index: &LineIndex, highlights: Vec, + semantics_tokens_augments_syntax_tokens: bool, + non_standard_tokens: bool, ) -> lsp_types::SemanticTokens { let id = TOKEN_RESULT_COUNTER.fetch_add(1, Ordering::SeqCst).to_string(); let mut builder = semantic_tokens::SemanticTokensBuilder::new(id); @@ -589,7 +529,35 @@ pub(crate) fn semantic_tokens( continue; } - let (ty, mods) = semantic_token_type_and_modifiers(highlight_range.highlight); + if semantics_tokens_augments_syntax_tokens { + match highlight_range.highlight.tag { + HlTag::BoolLiteral + | HlTag::ByteLiteral + | HlTag::CharLiteral + | HlTag::Comment + | HlTag::Keyword + | HlTag::NumericLiteral + | HlTag::Operator(_) + | HlTag::Punctuation(_) + | HlTag::StringLiteral + | HlTag::None + if highlight_range.highlight.mods.is_empty() => + { + continue + } + _ => (), + } + } + + let (mut ty, mut mods) = semantic_token_type_and_modifiers(highlight_range.highlight); + + if !non_standard_tokens { + ty = match standard_fallback_type(ty) { + Some(ty) => ty, + None => continue, + }; + mods.standard_fallback(); + } let token_index = semantic_tokens::type_index(ty); let modifier_bitset = mods.0; @@ -710,6 +678,7 @@ fn semantic_token_type_and_modifiers( HlMod::Injected => semantic_tokens::INJECTED, HlMod::IntraDocLink => semantic_tokens::INTRA_DOC_LINK, HlMod::Library => semantic_tokens::LIBRARY, + HlMod::Macro => semantic_tokens::MACRO_MODIFIER, HlMod::Mutable => semantic_tokens::MUTABLE, HlMod::Public => semantic_tokens::PUBLIC, HlMod::Reference => semantic_tokens::REFERENCE, @@ -1215,6 +1184,14 @@ pub(crate) fn code_lens( data: None, }) } + if lens_config.interpret { + let command = command::interpret_single(&r); + acc.push(lsp_types::CodeLens { + range: annotation_range, + command: Some(command), + data: None, + }) + } } AnnotationKind::HasImpls { pos: file_range, data } => { if !client_commands_config.show_reference { @@ -1257,7 +1234,16 @@ pub(crate) fn code_lens( acc.push(lsp_types::CodeLens { range: annotation_range, command, - data: Some(to_value(lsp_ext::CodeLensResolveData::Impls(goto_params)).unwrap()), + data: (|| { + let version = snap.url_file_version(&url)?; + Some( + to_value(lsp_ext::CodeLensResolveData { + version, + kind: lsp_ext::CodeLensResolveDataKind::Impls(goto_params), + }) + .unwrap(), + ) + })(), }) } AnnotationKind::HasReferences { pos: file_range, data } => { @@ -1287,7 +1273,16 @@ pub(crate) fn code_lens( acc.push(lsp_types::CodeLens { range: annotation_range, command, - data: Some(to_value(lsp_ext::CodeLensResolveData::References(doc_pos)).unwrap()), + data: (|| { + let version = snap.url_file_version(&url)?; + Some( + to_value(lsp_ext::CodeLensResolveData { + version, + kind: lsp_ext::CodeLensResolveDataKind::References(doc_pos), + }) + .unwrap(), + ) + })(), }) } } @@ -1341,6 +1336,15 @@ pub(crate) mod command { } } + pub(crate) fn interpret_single(_runnable: &lsp_ext::Runnable) -> lsp_types::Command { + lsp_types::Command { + title: "Interpret".into(), + command: "rust-analyzer.interpretFunction".into(), + // FIXME: use the `_runnable` here. + arguments: Some(vec![]), + } + } + pub(crate) fn goto_location( snap: &GlobalStateSnapshot, nav: &NavigationTarget, @@ -1406,9 +1410,8 @@ pub(crate) fn rename_error(err: RenameError) -> crate::LspError { #[cfg(test)] mod tests { - use std::sync::Arc; - use ide::Analysis; + use triomphe::Arc; use super::*; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs index 587d640969aac..e130c762fc48a 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs @@ -59,7 +59,7 @@ use std::collections::Spam; "#, ) .with_config(serde_json::json!({ - "cargo": { "sysroot": "discover" } + "cargo": { "sysroot": "discover" }, })) .server() .wait_until_workspace_is_loaded(); @@ -508,7 +508,7 @@ fn main() {} #[test] fn test_missing_module_code_action_in_json_project() { if skip_slow_tests() { - // return; + return; } let tmp_dir = TestDir::new(); @@ -612,7 +612,7 @@ fn main() {{}} "# )) .with_config(serde_json::json!({ - "cargo": { "sysroot": "discover" } + "cargo": { "sysroot": "discover" }, })) .server() .wait_until_workspace_is_loaded(); @@ -685,7 +685,7 @@ version = \"0.0.0\" #[test] fn out_dirs_check() { if skip_slow_tests() { - // return; + return; } let server = Project::with_fixture( @@ -711,10 +711,21 @@ fn main() { println!("cargo:rerun-if-changed=build.rs"); } //- /src/main.rs -#[rustc_builtin_macro] macro_rules! include {} -#[rustc_builtin_macro] macro_rules! include_str {} -#[rustc_builtin_macro] macro_rules! concat {} -#[rustc_builtin_macro] macro_rules! env {} +#![allow(warnings)] +#![feature(rustc_attrs)] +#[rustc_builtin_macro] macro_rules! include { + ($file:expr $(,)?) => {{ /* compiler built-in */ }}; +} +#[rustc_builtin_macro] macro_rules! include_str { + ($file:expr $(,)?) => {{ /* compiler built-in */ }}; +} +#[rustc_builtin_macro] macro_rules! concat { + ($($e:ident),+ $(,)?) => {{ /* compiler built-in */ }}; +} +#[rustc_builtin_macro] macro_rules! env { + ($name:expr $(,)?) => {{ /* compiler built-in */ }}; + ($name:expr, $error_msg:expr $(,)?) => {{ /* compiler built-in */ }}; +} include!(concat!(env!("OUT_DIR"), "/hello.rs")); @@ -741,6 +752,9 @@ fn main() { "enable": true }, "sysroot": null, + "extraEnv": { + "RUSTC_BOOTSTRAP": "1" + } } })) .server() @@ -749,7 +763,7 @@ fn main() { let res = server.send_request::(HoverParams { text_document_position_params: TextDocumentPositionParams::new( server.doc_id("src/main.rs"), - Position::new(19, 10), + Position::new(30, 10), ), work_done_progress_params: Default::default(), }); @@ -758,7 +772,7 @@ fn main() { let res = server.send_request::(HoverParams { text_document_position_params: TextDocumentPositionParams::new( server.doc_id("src/main.rs"), - Position::new(20, 10), + Position::new(31, 10), ), work_done_progress_params: Default::default(), }); @@ -768,23 +782,23 @@ fn main() { GotoDefinitionParams { text_document_position_params: TextDocumentPositionParams::new( server.doc_id("src/main.rs"), - Position::new(17, 9), + Position::new(28, 9), ), work_done_progress_params: Default::default(), partial_result_params: Default::default(), }, json!([{ "originSelectionRange": { - "end": { "character": 10, "line": 17 }, - "start": { "character": 8, "line": 17 } + "end": { "character": 10, "line": 28 }, + "start": { "character": 8, "line": 28 } }, "targetRange": { - "end": { "character": 9, "line": 8 }, - "start": { "character": 0, "line": 7 } + "end": { "character": 9, "line": 19 }, + "start": { "character": 0, "line": 18 } }, "targetSelectionRange": { - "end": { "character": 8, "line": 8 }, - "start": { "character": 7, "line": 8 } + "end": { "character": 8, "line": 19 }, + "start": { "character": 7, "line": 19 } }, "targetUri": "file:///[..]src/main.rs" }]), @@ -794,23 +808,23 @@ fn main() { GotoDefinitionParams { text_document_position_params: TextDocumentPositionParams::new( server.doc_id("src/main.rs"), - Position::new(18, 9), + Position::new(29, 9), ), work_done_progress_params: Default::default(), partial_result_params: Default::default(), }, json!([{ "originSelectionRange": { - "end": { "character": 10, "line": 18 }, - "start": { "character": 8, "line": 18 } + "end": { "character": 10, "line": 29 }, + "start": { "character": 8, "line": 29 } }, "targetRange": { - "end": { "character": 9, "line": 12 }, - "start": { "character": 0, "line":11 } + "end": { "character": 9, "line": 23 }, + "start": { "character": 0, "line": 22 } }, "targetSelectionRange": { - "end": { "character": 8, "line": 12 }, - "start": { "character": 7, "line": 12 } + "end": { "character": 8, "line": 23 }, + "start": { "character": 7, "line": 23 } }, "targetUri": "file:///[..]src/main.rs" }]), @@ -818,8 +832,7 @@ fn main() { } #[test] -// FIXME: Re-enable once we can run proc-macro tests on rust-lang/rust-analyzer again -#[cfg(any())] +#[cfg(feature = "sysroot-abi")] fn resolve_proc_macro() { use expect_test::expect; if skip_slow_tests() { @@ -837,6 +850,7 @@ edition = "2021" bar = {path = "../bar"} //- /foo/src/main.rs +#![feature(rustc_attrs, decl_macro)] use bar::Bar; #[rustc_builtin_macro] @@ -913,7 +927,7 @@ pub fn foo(_input: TokenStream) -> TokenStream { let res = server.send_request::(HoverParams { text_document_position_params: TextDocumentPositionParams::new( server.doc_id("foo/src/main.rs"), - Position::new(10, 9), + Position::new(11, 9), ), work_done_progress_params: Default::default(), }); @@ -1083,10 +1097,18 @@ version = "0.0.0" //- /bar/src/lib.rs pub fn bar() {} + +//- /baz/Cargo.toml +[package] +name = "baz" +version = "0.0.0" + +//- /baz/src/lib.rs "#, ) .root("foo") .root("bar") + .root("baz") .with_config(json!({ "files": { "excludeDirs": ["foo", "bar"] diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs index 037fc89ace090..b2a8041ae9b57 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs @@ -9,11 +9,10 @@ use std::{ use crossbeam_channel::{after, select, Receiver}; use lsp_server::{Connection, Message, Notification, Request}; use lsp_types::{notification::Exit, request::Shutdown, TextDocumentIdentifier, Url}; -use project_model::ProjectManifest; use rust_analyzer::{config::Config, lsp_ext, main_loop}; use serde::Serialize; use serde_json::{json, to_string_pretty, Value}; -use test_utils::Fixture; +use test_utils::FixtureWithProjectMeta; use vfs::AbsPathBuf; use crate::testdir::TestDir; @@ -37,8 +36,12 @@ impl<'a> Project<'a> { "sysroot": null, // Can't use test binary as rustc wrapper. "buildScripts": { - "useRustcWrapper": false + "useRustcWrapper": false, + "enable": false, }, + }, + "procMacro": { + "enable": false, } }), } @@ -80,10 +83,12 @@ impl<'a> Project<'a> { profile::init_from(crate::PROFILE); }); - let (mini_core, proc_macros, fixtures) = Fixture::parse(self.fixture); - assert!(proc_macros.is_empty()); + let FixtureWithProjectMeta { fixture, mini_core, proc_macro_names, toolchain } = + FixtureWithProjectMeta::parse(self.fixture); + assert!(proc_macro_names.is_empty()); assert!(mini_core.is_none()); - for entry in fixtures { + assert!(toolchain.is_none()); + for entry in fixture { let path = tmp_dir.path().join(&entry.path['/'.len_utf8()..]); fs::create_dir_all(path.parent().unwrap()).unwrap(); fs::write(path.as_path(), entry.text.as_bytes()).unwrap(); @@ -95,10 +100,6 @@ impl<'a> Project<'a> { if roots.is_empty() { roots.push(tmp_dir_path.clone()); } - let discovered_projects = roots - .into_iter() - .map(|it| ProjectManifest::discover_single(&it).unwrap()) - .collect::>(); let mut config = Config::new( tmp_dir_path, @@ -138,10 +139,10 @@ impl<'a> Project<'a> { })), ..Default::default() }, - Vec::new(), + roots, ); - config.discovered_projects = Some(discovered_projects); config.update(self.config).expect("invalid config"); + config.rediscover_workspaces(); Server::new(tmp_dir, config) } @@ -154,7 +155,7 @@ pub(crate) fn project(fixture: &str) -> Server { pub(crate) struct Server { req_id: Cell, messages: RefCell>, - _thread: jod_thread::JoinHandle<()>, + _thread: stdx::thread::JoinHandle, client: Connection, /// XXX: remove the tempdir last dir: TestDir, @@ -164,7 +165,7 @@ impl Server { fn new(dir: TestDir, config: Config) -> Server { let (connection, client) = Connection::memory(); - let _thread = jod_thread::Builder::new() + let _thread = stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker) .name("test server".to_string()) .spawn(move || main_loop(config, connection).unwrap()) .expect("failed to spawn a thread"); @@ -251,6 +252,9 @@ impl Server { .clone() .extract::("experimental/serverStatus") .unwrap(); + if status.health != lsp_ext::Health::Ok { + panic!("server errored/warned while loading workspace: {:?}", status.message); + } status.quiescent } _ => false, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/tidy.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/tidy.rs index 8e3097fce4230..f230cba2bf828 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/tidy.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/tidy.rs @@ -257,6 +257,8 @@ fn check_dbg(path: &Path, text: &str) { "ide-db/src/generated/lints.rs", // test for doc test for remove_dbg "src/tests/generated.rs", + // `expect!` string can contain `dbg!` (due to .dbg postfix) + "ide-completion/src/tests/special.rs", ]; if need_dbg.iter().any(|p| path.ends_with(p)) { return; diff --git a/src/tools/rust-analyzer/crates/sourcegen/src/lib.rs b/src/tools/rust-analyzer/crates/sourcegen/src/lib.rs index 72d26635c3369..c5da6ceb4d198 100644 --- a/src/tools/rust-analyzer/crates/sourcegen/src/lib.rs +++ b/src/tools/rust-analyzer/crates/sourcegen/src/lib.rs @@ -58,21 +58,19 @@ impl CommentBlock { assert!(tag.starts_with(char::is_uppercase)); let tag = format!("{tag}:"); - // Would be nice if we had `.retain_mut` here! - CommentBlock::extract_untagged(text) - .into_iter() - .filter_map(|mut block| { - let first = block.contents.remove(0); - first.strip_prefix(&tag).map(|id| { - if block.is_doc { - panic!("Use plain (non-doc) comments with tags like {tag}:\n {first}"); - } + let mut blocks = CommentBlock::extract_untagged(text); + blocks.retain_mut(|block| { + let first = block.contents.remove(0); + let Some(id) = first.strip_prefix(&tag) else { return false; }; + + if block.is_doc { + panic!("Use plain (non-doc) comments with tags like {tag}:\n {first}"); + } - block.id = id.trim().to_string(); - block - }) - }) - .collect() + block.id = id.trim().to_string(); + true + }); + blocks } pub fn extract_untagged(text: &str) -> Vec { diff --git a/src/tools/rust-analyzer/crates/stdx/Cargo.toml b/src/tools/rust-analyzer/crates/stdx/Cargo.toml index c881f2fd3f45e..a67f36ae9006a 100644 --- a/src/tools/rust-analyzer/crates/stdx/Cargo.toml +++ b/src/tools/rust-analyzer/crates/stdx/Cargo.toml @@ -15,6 +15,8 @@ doctest = false libc = "0.2.135" backtrace = { version = "0.3.65", optional = true } always-assert = { version = "0.1.2", features = ["log"] } +jod-thread = "0.1.2" +crossbeam-channel = "0.5.5" # Think twice before adding anything here [target.'cfg(windows)'.dependencies] diff --git a/src/tools/rust-analyzer/crates/stdx/src/hash.rs b/src/tools/rust-analyzer/crates/stdx/src/hash.rs deleted file mode 100644 index 0c21d2674b1a0..0000000000000 --- a/src/tools/rust-analyzer/crates/stdx/src/hash.rs +++ /dev/null @@ -1,80 +0,0 @@ -//! A none hashing [`Hasher`] implementation. -use std::{ - hash::{BuildHasher, Hasher}, - marker::PhantomData, -}; - -pub type NoHashHashMap = std::collections::HashMap>; -pub type NoHashHashSet = std::collections::HashSet>; - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct NoHashHasherBuilder(PhantomData); - -impl Default for NoHashHasherBuilder { - fn default() -> Self { - Self(Default::default()) - } -} - -pub trait NoHashHashable {} -impl NoHashHashable for usize {} -impl NoHashHashable for u32 {} - -pub struct NoHashHasher(u64); - -impl BuildHasher for NoHashHasherBuilder { - type Hasher = NoHashHasher; - fn build_hasher(&self) -> Self::Hasher { - NoHashHasher(0) - } -} - -impl Hasher for NoHashHasher { - fn finish(&self) -> u64 { - self.0 - } - - fn write(&mut self, _: &[u8]) { - unimplemented!("NoHashHasher should only be used for hashing primitive integers") - } - - fn write_u8(&mut self, i: u8) { - self.0 = i as u64; - } - - fn write_u16(&mut self, i: u16) { - self.0 = i as u64; - } - - fn write_u32(&mut self, i: u32) { - self.0 = i as u64; - } - - fn write_u64(&mut self, i: u64) { - self.0 = i; - } - - fn write_usize(&mut self, i: usize) { - self.0 = i as u64; - } - - fn write_i8(&mut self, i: i8) { - self.0 = i as u64; - } - - fn write_i16(&mut self, i: i16) { - self.0 = i as u64; - } - - fn write_i32(&mut self, i: i32) { - self.0 = i as u64; - } - - fn write_i64(&mut self, i: i64) { - self.0 = i as u64; - } - - fn write_isize(&mut self, i: isize) { - self.0 = i as u64; - } -} diff --git a/src/tools/rust-analyzer/crates/stdx/src/lib.rs b/src/tools/rust-analyzer/crates/stdx/src/lib.rs index 5639aaf57cd97..24990d6a0e714 100644 --- a/src/tools/rust-analyzer/crates/stdx/src/lib.rs +++ b/src/tools/rust-analyzer/crates/stdx/src/lib.rs @@ -7,11 +7,11 @@ use std::process::Command; use std::{cmp::Ordering, ops, time::Instant}; mod macros; -pub mod hash; pub mod process; pub mod panic_context; pub mod non_empty_vec; pub mod rand; +pub mod thread; pub use always_assert::{always, never}; diff --git a/src/tools/rust-analyzer/crates/stdx/src/thread.rs b/src/tools/rust-analyzer/crates/stdx/src/thread.rs new file mode 100644 index 0000000000000..e577eb4313714 --- /dev/null +++ b/src/tools/rust-analyzer/crates/stdx/src/thread.rs @@ -0,0 +1,102 @@ +//! A utility module for working with threads that automatically joins threads upon drop +//! and abstracts over operating system quality of service (QoS) APIs +//! through the concept of a “thread intent”. +//! +//! The intent of a thread is frozen at thread creation time, +//! i.e. there is no API to change the intent of a thread once it has been spawned. +//! +//! As a system, rust-analyzer should have the property that +//! old manual scheduling APIs are replaced entirely by QoS. +//! To maintain this invariant, we panic when it is clear that +//! old scheduling APIs have been used. +//! +//! Moreover, we also want to ensure that every thread has an intent set explicitly +//! to force a decision about its importance to the system. +//! Thus, [`ThreadIntent`] has no default value +//! and every entry point to creating a thread requires a [`ThreadIntent`] upfront. + +use std::fmt; + +mod intent; +mod pool; + +pub use intent::ThreadIntent; +pub use pool::Pool; + +pub fn spawn(intent: ThreadIntent, f: F) -> JoinHandle +where + F: FnOnce() -> T, + F: Send + 'static, + T: Send + 'static, +{ + Builder::new(intent).spawn(f).expect("failed to spawn thread") +} + +pub struct Builder { + intent: ThreadIntent, + inner: jod_thread::Builder, + allow_leak: bool, +} + +impl Builder { + pub fn new(intent: ThreadIntent) -> Builder { + Builder { intent, inner: jod_thread::Builder::new(), allow_leak: false } + } + + pub fn name(self, name: String) -> Builder { + Builder { inner: self.inner.name(name), ..self } + } + + pub fn stack_size(self, size: usize) -> Builder { + Builder { inner: self.inner.stack_size(size), ..self } + } + + pub fn allow_leak(self, b: bool) -> Builder { + Builder { allow_leak: b, ..self } + } + + pub fn spawn(self, f: F) -> std::io::Result> + where + F: FnOnce() -> T, + F: Send + 'static, + T: Send + 'static, + { + let inner_handle = self.inner.spawn(move || { + self.intent.apply_to_current_thread(); + f() + })?; + + Ok(JoinHandle { inner: Some(inner_handle), allow_leak: self.allow_leak }) + } +} + +pub struct JoinHandle { + // `inner` is an `Option` so that we can + // take ownership of the contained `JoinHandle`. + inner: Option>, + allow_leak: bool, +} + +impl JoinHandle { + pub fn join(mut self) -> T { + self.inner.take().unwrap().join() + } +} + +impl Drop for JoinHandle { + fn drop(&mut self) { + if !self.allow_leak { + return; + } + + if let Some(join_handle) = self.inner.take() { + join_handle.detach(); + } + } +} + +impl fmt::Debug for JoinHandle { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("JoinHandle { .. }") + } +} diff --git a/src/tools/rust-analyzer/crates/stdx/src/thread/intent.rs b/src/tools/rust-analyzer/crates/stdx/src/thread/intent.rs new file mode 100644 index 0000000000000..7b65db30cc5b0 --- /dev/null +++ b/src/tools/rust-analyzer/crates/stdx/src/thread/intent.rs @@ -0,0 +1,287 @@ +//! An opaque façade around platform-specific QoS APIs. + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +// Please maintain order from least to most priority for the derived `Ord` impl. +pub enum ThreadIntent { + /// Any thread which does work that isn’t in the critical path of the user typing + /// (e.g. processing Go To Definition). + Worker, + + /// Any thread which does work caused by the user typing + /// (e.g. processing syntax highlighting). + LatencySensitive, +} + +impl ThreadIntent { + // These APIs must remain private; + // we only want consumers to set thread intent + // either during thread creation or using our pool impl. + + pub(super) fn apply_to_current_thread(self) { + let class = thread_intent_to_qos_class(self); + set_current_thread_qos_class(class); + } + + pub(super) fn assert_is_used_on_current_thread(self) { + if IS_QOS_AVAILABLE { + let class = thread_intent_to_qos_class(self); + assert_eq!(get_current_thread_qos_class(), Some(class)); + } + } +} + +use imp::QoSClass; + +const IS_QOS_AVAILABLE: bool = imp::IS_QOS_AVAILABLE; + +fn set_current_thread_qos_class(class: QoSClass) { + imp::set_current_thread_qos_class(class) +} + +fn get_current_thread_qos_class() -> Option { + imp::get_current_thread_qos_class() +} + +fn thread_intent_to_qos_class(intent: ThreadIntent) -> QoSClass { + imp::thread_intent_to_qos_class(intent) +} + +// All Apple platforms use XNU as their kernel +// and thus have the concept of QoS. +#[cfg(target_vendor = "apple")] +mod imp { + use super::ThreadIntent; + + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] + // Please maintain order from least to most priority for the derived `Ord` impl. + pub(super) enum QoSClass { + // Documentation adapted from https://github.com/apple-oss-distributions/libpthread/blob/67e155c94093be9a204b69637d198eceff2c7c46/include/sys/qos.h#L55 + // + /// TLDR: invisible maintenance tasks + /// + /// Contract: + /// + /// * **You do not care about how long it takes for work to finish.** + /// * **You do not care about work being deferred temporarily.** + /// (e.g. if the device’s battery is in a critical state) + /// + /// Examples: + /// + /// * in a video editor: + /// creating periodic backups of project files + /// * in a browser: + /// cleaning up cached sites which have not been accessed in a long time + /// * in a collaborative word processor: + /// creating a searchable index of all documents + /// + /// Use this QoS class for background tasks + /// which the user did not initiate themselves + /// and which are invisible to the user. + /// It is expected that this work will take significant time to complete: + /// minutes or even hours. + /// + /// This QoS class provides the most energy and thermally-efficient execution possible. + /// All other work is prioritized over background tasks. + Background, + + /// TLDR: tasks that don’t block using your app + /// + /// Contract: + /// + /// * **Your app remains useful even as the task is executing.** + /// + /// Examples: + /// + /// * in a video editor: + /// exporting a video to disk – + /// the user can still work on the timeline + /// * in a browser: + /// automatically extracting a downloaded zip file – + /// the user can still switch tabs + /// * in a collaborative word processor: + /// downloading images embedded in a document – + /// the user can still make edits + /// + /// Use this QoS class for tasks which + /// may or may not be initiated by the user, + /// but whose result is visible. + /// It is expected that this work will take a few seconds to a few minutes. + /// Typically your app will include a progress bar + /// for tasks using this class. + /// + /// This QoS class provides a balance between + /// performance, responsiveness and efficiency. + Utility, + + /// TLDR: tasks that block using your app + /// + /// Contract: + /// + /// * **You need this work to complete + /// before the user can keep interacting with your app.** + /// * **Your work will not take more than a few seconds to complete.** + /// + /// Examples: + /// + /// * in a video editor: + /// opening a saved project + /// * in a browser: + /// loading a list of the user’s bookmarks and top sites + /// when a new tab is created + /// * in a collaborative word processor: + /// running a search on the document’s content + /// + /// Use this QoS class for tasks which were initiated by the user + /// and block the usage of your app while they are in progress. + /// It is expected that this work will take a few seconds or less to complete; + /// not long enough to cause the user to switch to something else. + /// Your app will likely indicate progress on these tasks + /// through the display of placeholder content or modals. + /// + /// This QoS class is not energy-efficient. + /// Rather, it provides responsiveness + /// by prioritizing work above other tasks on the system + /// except for critical user-interactive work. + UserInitiated, + + /// TLDR: render loops and nothing else + /// + /// Contract: + /// + /// * **You absolutely need this work to complete immediately + /// or your app will appear to freeze.** + /// * **Your work will always complete virtually instantaneously.** + /// + /// Examples: + /// + /// * the main thread in a GUI application + /// * the update & render loop in a game + /// * a secondary thread which progresses an animation + /// + /// Use this QoS class for any work which, if delayed, + /// will make your user interface unresponsive. + /// It is expected that this work will be virtually instantaneous. + /// + /// This QoS class is not energy-efficient. + /// Specifying this class is a request to run with + /// nearly all available system CPU and I/O bandwidth even under contention. + UserInteractive, + } + + pub(super) const IS_QOS_AVAILABLE: bool = true; + + pub(super) fn set_current_thread_qos_class(class: QoSClass) { + let c = match class { + QoSClass::UserInteractive => libc::qos_class_t::QOS_CLASS_USER_INTERACTIVE, + QoSClass::UserInitiated => libc::qos_class_t::QOS_CLASS_USER_INITIATED, + QoSClass::Utility => libc::qos_class_t::QOS_CLASS_UTILITY, + QoSClass::Background => libc::qos_class_t::QOS_CLASS_BACKGROUND, + }; + + let code = unsafe { libc::pthread_set_qos_class_self_np(c, 0) }; + + if code == 0 { + return; + } + + let errno = unsafe { *libc::__error() }; + + match errno { + libc::EPERM => { + // This thread has been excluded from the QoS system + // due to a previous call to a function such as `pthread_setschedparam` + // which is incompatible with QoS. + // + // Panic instead of returning an error + // to maintain the invariant that we only use QoS APIs. + panic!("tried to set QoS of thread which has opted out of QoS (os error {errno})") + } + + libc::EINVAL => { + // This is returned if we pass something other than a qos_class_t + // to `pthread_set_qos_class_self_np`. + // + // This is impossible, so again panic. + unreachable!( + "invalid qos_class_t value was passed to pthread_set_qos_class_self_np" + ) + } + + _ => { + // `pthread_set_qos_class_self_np`’s documentation + // does not mention any other errors. + unreachable!("`pthread_set_qos_class_self_np` returned unexpected error {errno}") + } + } + } + + pub(super) fn get_current_thread_qos_class() -> Option { + let current_thread = unsafe { libc::pthread_self() }; + let mut qos_class_raw = libc::qos_class_t::QOS_CLASS_UNSPECIFIED; + let code = unsafe { + libc::pthread_get_qos_class_np(current_thread, &mut qos_class_raw, std::ptr::null_mut()) + }; + + if code != 0 { + // `pthread_get_qos_class_np`’s documentation states that + // an error value is placed into errno if the return code is not zero. + // However, it never states what errors are possible. + // Inspecting the source[0] shows that, as of this writing, it always returns zero. + // + // Whatever errors the function could report in future are likely to be + // ones which we cannot handle anyway + // + // 0: https://github.com/apple-oss-distributions/libpthread/blob/67e155c94093be9a204b69637d198eceff2c7c46/src/qos.c#L171-L177 + let errno = unsafe { *libc::__error() }; + unreachable!("`pthread_get_qos_class_np` failed unexpectedly (os error {errno})"); + } + + match qos_class_raw { + libc::qos_class_t::QOS_CLASS_USER_INTERACTIVE => Some(QoSClass::UserInteractive), + libc::qos_class_t::QOS_CLASS_USER_INITIATED => Some(QoSClass::UserInitiated), + libc::qos_class_t::QOS_CLASS_DEFAULT => None, // QoS has never been set + libc::qos_class_t::QOS_CLASS_UTILITY => Some(QoSClass::Utility), + libc::qos_class_t::QOS_CLASS_BACKGROUND => Some(QoSClass::Background), + + libc::qos_class_t::QOS_CLASS_UNSPECIFIED => { + // Using manual scheduling APIs causes threads to “opt out” of QoS. + // At this point they become incompatible with QoS, + // and as such have the “unspecified” QoS class. + // + // Panic instead of returning an error + // to maintain the invariant that we only use QoS APIs. + panic!("tried to get QoS of thread which has opted out of QoS") + } + } + } + + pub(super) fn thread_intent_to_qos_class(intent: ThreadIntent) -> QoSClass { + match intent { + ThreadIntent::Worker => QoSClass::Utility, + ThreadIntent::LatencySensitive => QoSClass::UserInitiated, + } + } +} + +// FIXME: Windows has QoS APIs, we should use them! +#[cfg(not(target_vendor = "apple"))] +mod imp { + use super::ThreadIntent; + + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] + pub(super) enum QoSClass { + Default, + } + + pub(super) const IS_QOS_AVAILABLE: bool = false; + + pub(super) fn set_current_thread_qos_class(_: QoSClass) {} + + pub(super) fn get_current_thread_qos_class() -> Option { + None + } + + pub(super) fn thread_intent_to_qos_class(_: ThreadIntent) -> QoSClass { + QoSClass::Default + } +} diff --git a/src/tools/rust-analyzer/crates/stdx/src/thread/pool.rs b/src/tools/rust-analyzer/crates/stdx/src/thread/pool.rs new file mode 100644 index 0000000000000..2ddd7da74c291 --- /dev/null +++ b/src/tools/rust-analyzer/crates/stdx/src/thread/pool.rs @@ -0,0 +1,92 @@ +//! [`Pool`] implements a basic custom thread pool +//! inspired by the [`threadpool` crate](http://docs.rs/threadpool). +//! When you spawn a task you specify a thread intent +//! so the pool can schedule it to run on a thread with that intent. +//! rust-analyzer uses this to prioritize work based on latency requirements. +//! +//! The thread pool is implemented entirely using +//! the threading utilities in [`crate::thread`]. + +use std::sync::{ + atomic::{AtomicUsize, Ordering}, + Arc, +}; + +use crossbeam_channel::{Receiver, Sender}; + +use super::{Builder, JoinHandle, ThreadIntent}; + +pub struct Pool { + // `_handles` is never read: the field is present + // only for its `Drop` impl. + + // The worker threads exit once the channel closes; + // make sure to keep `job_sender` above `handles` + // so that the channel is actually closed + // before we join the worker threads! + job_sender: Sender, + _handles: Vec, + extant_tasks: Arc, +} + +struct Job { + requested_intent: ThreadIntent, + f: Box, +} + +impl Pool { + pub fn new(threads: usize) -> Pool { + const STACK_SIZE: usize = 8 * 1024 * 1024; + const INITIAL_INTENT: ThreadIntent = ThreadIntent::Worker; + + let (job_sender, job_receiver) = crossbeam_channel::unbounded(); + let extant_tasks = Arc::new(AtomicUsize::new(0)); + + let mut handles = Vec::with_capacity(threads); + for _ in 0..threads { + let handle = Builder::new(INITIAL_INTENT) + .stack_size(STACK_SIZE) + .name("Worker".into()) + .spawn({ + let extant_tasks = Arc::clone(&extant_tasks); + let job_receiver: Receiver = job_receiver.clone(); + move || { + let mut current_intent = INITIAL_INTENT; + for job in job_receiver { + if job.requested_intent != current_intent { + job.requested_intent.apply_to_current_thread(); + current_intent = job.requested_intent; + } + extant_tasks.fetch_add(1, Ordering::SeqCst); + (job.f)(); + extant_tasks.fetch_sub(1, Ordering::SeqCst); + } + } + }) + .expect("failed to spawn thread"); + + handles.push(handle); + } + + Pool { _handles: handles, extant_tasks, job_sender } + } + + pub fn spawn(&self, intent: ThreadIntent, f: F) + where + F: FnOnce() + Send + 'static, + { + let f = Box::new(move || { + if cfg!(debug_assertions) { + intent.assert_is_used_on_current_thread(); + } + f() + }); + + let job = Job { requested_intent: intent, f }; + self.job_sender.send(job).unwrap(); + } + + pub fn len(&self) -> usize { + self.extant_tasks.load(Ordering::SeqCst) + } +} diff --git a/src/tools/rust-analyzer/crates/syntax/Cargo.toml b/src/tools/rust-analyzer/crates/syntax/Cargo.toml index 305cf2d394b44..fb38d25ab541e 100644 --- a/src/tools/rust-analyzer/crates/syntax/Cargo.toml +++ b/src/tools/rust-analyzer/crates/syntax/Cargo.toml @@ -16,12 +16,14 @@ doctest = false cov-mark = "2.0.0-pre.1" either = "1.7.0" itertools = "0.10.5" -rowan = "0.15.10" -rustc_lexer = { version = "727.0.0", package = "rustc-ap-rustc_lexer" } +rowan = "0.15.11" rustc-hash = "1.1.0" once_cell = "1.17.0" indexmap = "1.9.1" -smol_str = "0.1.23" +smol_str.workspace = true +triomphe.workspace = true + +rustc_lexer.workspace = true parser.workspace = true profile.workspace = true diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram index 548b5ba8b8b6d..4c9027dec68e9 100644 --- a/src/tools/rust-analyzer/crates/syntax/rust.ungram +++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram @@ -51,7 +51,9 @@ TypeArg = Type AssocTypeArg = - NameRef GenericArgList? (':' TypeBoundList | ('=' Type | ConstArg)) + NameRef + (GenericArgList | ParamList RetType?)? + (':' TypeBoundList | ('=' Type | ConstArg)) LifetimeArg = Lifetime @@ -581,7 +583,7 @@ ImplTraitType = 'impl' TypeBoundList DynTraitType = - 'dyn' TypeBoundList + 'dyn'? TypeBoundList TypeBoundList = bounds:(TypeBound ('+' TypeBound)* '+'?) @@ -613,7 +615,7 @@ Pat = | ConstBlockPat LiteralPat = - Literal + '-'? Literal IdentPat = Attr* 'ref'? 'mut'? Name ('@' Pat)? diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs index a493c92e7dae9..b3ea6ca8d46a1 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs @@ -236,6 +236,21 @@ impl ast::GenericParamList { } } + /// Removes the existing generic param + pub fn remove_generic_param(&self, generic_param: ast::GenericParam) { + if let Some(previous) = generic_param.syntax().prev_sibling() { + if let Some(next_token) = previous.next_sibling_or_token() { + ted::remove_all(next_token..=generic_param.syntax().clone().into()); + } + } else if let Some(next) = generic_param.syntax().next_sibling() { + if let Some(next_token) = next.prev_sibling_or_token() { + ted::remove_all(generic_param.syntax().clone().into()..=next_token); + } + } else { + ted::remove(generic_param.syntax()); + } + } + /// Constructs a matching [`ast::GenericArgList`] pub fn to_generic_args(&self) -> ast::GenericArgList { let args = self.generic_params().filter_map(|param| match param { @@ -465,6 +480,8 @@ impl ast::Impl { } impl ast::AssocItemList { + /// Attention! This function does align the first line of `item` with respect to `self`, + /// but it does _not_ change indentation of other lines (if any). pub fn add_item(&self, item: ast::AssocItem) { let (indent, position, whitespace) = match self.assoc_items().last() { Some(last_item) => ( diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs index c43d0830b9e24..36980b146ef56 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs @@ -288,6 +288,7 @@ impl ast::ArrayExpr { pub enum LiteralKind { String(ast::String), ByteString(ast::ByteString), + CString(ast::CString), IntNumber(ast::IntNumber), FloatNumber(ast::FloatNumber), Char(ast::Char), @@ -319,6 +320,9 @@ impl ast::Literal { if let Some(t) = ast::ByteString::cast(token.clone()) { return LiteralKind::ByteString(t); } + if let Some(t) = ast::CString::cast(token.clone()) { + return LiteralKind::CString(t); + } if let Some(t) = ast::Char::cast(token.clone()) { return LiteralKind::Char(t); } @@ -366,8 +370,7 @@ impl ast::BlockExpr { match parent.kind() { FOR_EXPR | IF_EXPR => parent .children() - .filter(|it| ast::Expr::can_cast(it.kind())) - .next() + .find(|it| ast::Expr::can_cast(it.kind())) .map_or(true, |it| it == *self.syntax()), LET_ELSE | FN | WHILE_EXPR | LOOP_EXPR | CONST_BLOCK_PAT => false, _ => true, diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs index fe324845360d7..61f6a04c98d38 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs @@ -121,6 +121,8 @@ impl ast::HasTypeBounds for AssocTypeArg {} impl AssocTypeArg { pub fn name_ref(&self) -> Option { support::child(&self.syntax) } pub fn generic_arg_list(&self) -> Option { support::child(&self.syntax) } + pub fn param_list(&self) -> Option { support::child(&self.syntax) } + pub fn ret_type(&self) -> Option { support::child(&self.syntax) } pub fn eq_token(&self) -> Option { support::token(&self.syntax, T![=]) } pub fn ty(&self) -> Option { support::child(&self.syntax) } pub fn const_arg(&self) -> Option { support::child(&self.syntax) } @@ -1375,6 +1377,7 @@ pub struct LiteralPat { pub(crate) syntax: SyntaxNode, } impl LiteralPat { + pub fn minus_token(&self) -> Option { support::token(&self.syntax, T![-]) } pub fn literal(&self) -> Option { support::child(&self.syntax) } } diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/tokens.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/tokens.rs index a3209c5abd23e..f5863e9efe0da 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/tokens.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/tokens.rs @@ -90,6 +90,27 @@ impl AstToken for ByteString { fn syntax(&self) -> &SyntaxToken { &self.syntax } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct CString { + pub(crate) syntax: SyntaxToken, +} +impl std::fmt::Display for CString { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(&self.syntax, f) + } +} +impl AstToken for CString { + fn can_cast(kind: SyntaxKind) -> bool { kind == C_STRING } + fn cast(syntax: SyntaxToken) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxToken { &self.syntax } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct IntNumber { pub(crate) syntax: SyntaxToken, diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs index 5aebe4cd9f53a..a07561e79a28b 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs @@ -158,34 +158,148 @@ fn ty_from_text(text: &str) -> ast::Type { ast_from_text(&format!("type _T = {text};")) } +pub fn ty_alias( + ident: &str, + generic_param_list: Option, + type_param_bounds: Option, + where_clause: Option, + assignment: Option<(ast::Type, Option)>, +) -> ast::TypeAlias { + let mut s = String::new(); + s.push_str(&format!("type {} ", ident)); + + if let Some(list) = generic_param_list { + s.push_str(&list.to_string()); + } + + if let Some(list) = type_param_bounds { + s.push_str(&format!(" : {}", &list)); + } + + if let Some(cl) = where_clause { + s.push_str(&format!(" {}", &cl.to_string())); + } + + if let Some(exp) = assignment { + if let Some(cl) = exp.1 { + s.push_str(&format!("= {} {}", &exp.0.to_string(), &cl.to_string())); + } else { + s.push_str(&format!("= {}", &exp.0.to_string())); + } + } + + s.push(';'); + ast_from_text(&s) +} + pub fn assoc_item_list() -> ast::AssocItemList { ast_from_text("impl C for D {}") } -// FIXME: `ty_params` should be `ast::GenericArgList` +fn merge_gen_params( + ps: Option, + bs: Option, +) -> Option { + match (ps, bs) { + (None, None) => None, + (None, Some(bs)) => Some(bs), + (Some(ps), None) => Some(ps), + (Some(ps), Some(bs)) => { + for b in bs.generic_params() { + ps.add_generic_param(b); + } + Some(ps) + } + } +} + pub fn impl_( - ty: ast::Path, - params: Option, - ty_params: Option, + generic_params: Option, + generic_args: Option, + path_type: ast::Type, + where_clause: Option, + body: Option>>, ) -> ast::Impl { - let params = match params { - Some(params) => params.to_string(), - None => String::new(), + let (gen_params, tr_gen_args) = match (generic_params, generic_args) { + (None, None) => (String::new(), String::new()), + (None, Some(args)) => (String::new(), args.to_generic_args().to_string()), + (Some(params), None) => (params.to_string(), params.to_generic_args().to_string()), + (Some(params), Some(args)) => match merge_gen_params(Some(params.clone()), Some(args)) { + Some(merged) => (params.to_string(), merged.to_generic_args().to_string()), + None => (params.to_string(), String::new()), + }, }; - let ty_params = match ty_params { - Some(params) => params.to_string(), + + let where_clause = match where_clause { + Some(pr) => pr.to_string(), + None => " ".to_string(), + }; + + let body = match body { + Some(bd) => bd.iter().map(|elem| elem.to_string()).join(""), None => String::new(), }; - ast_from_text(&format!("impl{params} {ty}{ty_params} {{}}")) + + ast_from_text(&format!("impl{gen_params} {path_type}{tr_gen_args}{where_clause}{{{}}}", body)) } +// FIXME : We must make *_gen_args' type ast::GenericArgList but in order to do so we must implement in `edit_in_place.rs` +// `add_generic_arg()` just like `add_generic_param()` +// is implemented for `ast::GenericParamList` pub fn impl_trait( - trait_: ast::Path, - ty: ast::Path, - ty_params: Option, + is_unsafe: bool, + trait_gen_params: Option, + trait_gen_args: Option, + type_gen_params: Option, + type_gen_args: Option, + is_negative: bool, + path_type: ast::Type, + ty: ast::Type, + trait_where_clause: Option, + ty_where_clause: Option, + body: Option>>, ) -> ast::Impl { - let ty_params = ty_params.map_or_else(String::new, |params| params.to_string()); - ast_from_text(&format!("impl{ty_params} {trait_} for {ty}{ty_params} {{}}")) + let is_unsafe = if is_unsafe { "unsafe " } else { "" }; + let ty_gen_args = match merge_gen_params(type_gen_params.clone(), type_gen_args) { + Some(pars) => pars.to_generic_args().to_string(), + None => String::new(), + }; + + let tr_gen_args = match merge_gen_params(trait_gen_params.clone(), trait_gen_args) { + Some(pars) => pars.to_generic_args().to_string(), + None => String::new(), + }; + + let gen_params = match merge_gen_params(trait_gen_params, type_gen_params) { + Some(pars) => pars.to_string(), + None => String::new(), + }; + + let is_negative = if is_negative { "! " } else { "" }; + + let where_clause = match (ty_where_clause, trait_where_clause) { + (None, None) => " ".to_string(), + (None, Some(tr)) => format!("\n{}\n", tr).to_string(), + (Some(ty), None) => format!("\n{}\n", ty).to_string(), + (Some(ty), Some(tr)) => { + let updated = ty.clone_for_update(); + tr.predicates().for_each(|p| { + ty.add_predicate(p); + }); + format!("\n{}\n", updated).to_string() + } + }; + + let body = match body { + Some(bd) => bd.iter().map(|elem| elem.to_string()).join(""), + None => String::new(), + }; + + ast_from_text(&format!("{is_unsafe}impl{gen_params} {is_negative}{path_type}{tr_gen_args} for {ty}{ty_gen_args}{where_clause}{{{}}}" , body)) +} + +pub fn impl_trait_type(bounds: ast::TypeBoundList) -> ast::ImplTraitType { + ast_from_text(&format!("fn f(x: impl {bounds}) {{}}")) } pub fn path_segment(name_ref: ast::NameRef) -> ast::PathSegment { @@ -355,7 +469,7 @@ pub fn hacky_block_expr( format_to!(buf, " {t}\n") } else if kind == SyntaxKind::WHITESPACE { let content = t.text().trim_matches(|c| c != '\n'); - if content.len() >= 1 { + if !content.is_empty() { format_to!(buf, "{}", &content[1..]) } } @@ -827,6 +941,8 @@ pub fn fn_( body: ast::BlockExpr, ret_type: Option, is_async: bool, + is_const: bool, + is_unsafe: bool, ) -> ast::Fn { let type_params = match type_params { Some(type_params) => format!("{type_params}"), @@ -846,12 +962,13 @@ pub fn fn_( }; let async_literal = if is_async { "async " } else { "" }; + let const_literal = if is_const { "const " } else { "" }; + let unsafe_literal = if is_unsafe { "unsafe " } else { "" }; ast_from_text(&format!( - "{visibility}{async_literal}fn {fn_name}{type_params}{params} {ret_type}{where_clause}{body}", + "{visibility}{async_literal}{const_literal}{unsafe_literal}fn {fn_name}{type_params}{params} {ret_type}{where_clause}{body}", )) } - pub fn struct_( visibility: Option, strukt_name: ast::Name, @@ -901,7 +1018,7 @@ pub mod tokens { pub(super) static SOURCE_FILE: Lazy> = Lazy::new(|| { SourceFile::parse( - "const C: <()>::Item = (1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p)\n;\n\n", + "const C: <()>::Item = (1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p, &p , &mut p)\n;\n\n", ) }); diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs index 2cd312e7f4f8c..090eb89f47040 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs @@ -145,6 +145,10 @@ impl QuoteOffsets { } pub trait IsString: AstToken { + const RAW_PREFIX: &'static str; + fn is_raw(&self) -> bool { + self.text().starts_with(Self::RAW_PREFIX) + } fn quote_offsets(&self) -> Option { let text = self.text(); let offsets = QuoteOffsets::new(text)?; @@ -183,20 +187,18 @@ pub trait IsString: AstToken { cb(text_range + offset, unescaped_char); }); } -} - -impl IsString for ast::String {} - -impl ast::String { - pub fn is_raw(&self) -> bool { - self.text().starts_with('r') - } - pub fn map_range_up(&self, range: TextRange) -> Option { + fn map_range_up(&self, range: TextRange) -> Option { let contents_range = self.text_range_between_quotes()?; assert!(TextRange::up_to(contents_range.len()).contains_range(range)); Some(range + contents_range.start()) } +} +impl IsString for ast::String { + const RAW_PREFIX: &'static str = "r"; +} + +impl ast::String { pub fn value(&self) -> Option> { if self.is_raw() { let text = self.text(); @@ -235,13 +237,11 @@ impl ast::String { } } -impl IsString for ast::ByteString {} +impl IsString for ast::ByteString { + const RAW_PREFIX: &'static str = "br"; +} impl ast::ByteString { - pub fn is_raw(&self) -> bool { - self.text().starts_with("br") - } - pub fn value(&self) -> Option> { if self.is_raw() { let text = self.text(); @@ -280,6 +280,49 @@ impl ast::ByteString { } } +impl IsString for ast::CString { + const RAW_PREFIX: &'static str = "cr"; +} + +impl ast::CString { + pub fn value(&self) -> Option> { + if self.is_raw() { + let text = self.text(); + let text = + &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; + return Some(Cow::Borrowed(text)); + } + + let text = self.text(); + let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; + + let mut buf = String::new(); + 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(_), 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[..prev_end]); + buf.push(c); + } + (Err(_), _) => has_error = true, + }); + + match (has_error, buf.capacity() == 0) { + (true, _) => None, + (false, true) => Some(Cow::Borrowed(text)), + (false, false) => Some(Cow::Owned(buf)), + } + } +} + impl ast::IntNumber { pub fn radix(&self) -> Radix { match self.text().get(..2).unwrap_or_default() { diff --git a/src/tools/rust-analyzer/crates/syntax/src/lib.rs b/src/tools/rust-analyzer/crates/syntax/src/lib.rs index 6f57cbad66b16..efbf87966448a 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/lib.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/lib.rs @@ -43,10 +43,11 @@ pub mod utils; pub mod ted; pub mod hacks; -use std::{marker::PhantomData, sync::Arc}; +use std::marker::PhantomData; use stdx::format_to; use text_edit::Indel; +use triomphe::Arc; pub use crate::{ ast::{AstNode, AstToken}, diff --git a/src/tools/rust-analyzer/crates/syntax/src/parsing/reparsing.rs b/src/tools/rust-analyzer/crates/syntax/src/parsing/reparsing.rs index 701e6232d5898..45e5916098282 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/parsing/reparsing.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/parsing/reparsing.rs @@ -39,7 +39,7 @@ fn reparse_token( let prev_token = root.covering_element(edit.delete).as_token()?.clone(); let prev_token_kind = prev_token.kind(); match prev_token_kind { - WHITESPACE | COMMENT | IDENT | STRING => { + WHITESPACE | COMMENT | IDENT | STRING | BYTE_STRING | C_STRING => { if prev_token_kind == WHITESPACE || prev_token_kind == COMMENT { // removing a new line may extends previous token let deleted_range = edit.delete - prev_token.text_range().start(); @@ -166,8 +166,8 @@ fn merge_errors( } res.extend(new_errors.into_iter().map(|new_err| { // fighting borrow checker with a variable ;) - let offseted_range = new_err.range() + range_before_reparse.start(); - new_err.with_range(offseted_range) + let offsetted_range = new_err.range() + range_before_reparse.start(); + new_err.with_range(offsetted_range) })); res } @@ -408,7 +408,7 @@ enum Foo { #[test] fn reparse_str_token_with_error_fixed() { - do_check(r#""unterinated$0$0"#, "\"", 12); + do_check(r#""unterminated$0$0"#, "\"", 13); } #[test] diff --git a/src/tools/rust-analyzer/crates/syntax/src/tests/ast_src.rs b/src/tools/rust-analyzer/crates/syntax/src/tests/ast_src.rs index ccce71966ff88..c5783b91a0fdb 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/tests/ast_src.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/tests/ast_src.rs @@ -71,7 +71,7 @@ pub(crate) const KINDS_SRC: KindsSrc<'_> = KindsSrc { "super", "trait", "true", "try", "type", "unsafe", "use", "where", "while", "yield", ], contextual_keywords: &["auto", "default", "existential", "union", "raw", "macro_rules", "yeet"], - literals: &["INT_NUMBER", "FLOAT_NUMBER", "CHAR", "BYTE", "STRING", "BYTE_STRING"], + literals: &["INT_NUMBER", "FLOAT_NUMBER", "CHAR", "BYTE", "STRING", "BYTE_STRING", "C_STRING"], tokens: &["ERROR", "IDENT", "WHITESPACE", "LIFETIME_IDENT", "COMMENT", "SHEBANG"], nodes: &[ "SOURCE_FILE", @@ -199,6 +199,7 @@ pub(crate) const KINDS_SRC: KindsSrc<'_> = KindsSrc { "GENERIC_PARAM", "LIFETIME_PARAM", "TYPE_PARAM", + "RETURN_TYPE_ARG", "CONST_PARAM", "GENERIC_ARG_LIST", "LIFETIME", diff --git a/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs b/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs index e954b58251faa..c49c5fa108b62 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs @@ -535,6 +535,7 @@ impl Field { "!" => "excl", "*" => "star", "&" => "amp", + "-" => "minus", "_" => "underscore", "." => "dot", ".." => "dotdot", @@ -572,10 +573,11 @@ impl Field { fn lower(grammar: &Grammar) -> AstSrc { let mut res = AstSrc { - tokens: "Whitespace Comment String ByteString IntNumber FloatNumber Char Byte Ident" - .split_ascii_whitespace() - .map(|it| it.to_string()) - .collect::>(), + tokens: + "Whitespace Comment String ByteString CString IntNumber FloatNumber Char Byte Ident" + .split_ascii_whitespace() + .map(|it| it.to_string()) + .collect::>(), ..Default::default() }; diff --git a/src/tools/rust-analyzer/crates/syntax/src/token_text.rs b/src/tools/rust-analyzer/crates/syntax/src/token_text.rs index 913b24d42bcfd..09c080c0c2301 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/token_text.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/token_text.rs @@ -3,6 +3,7 @@ use std::{cmp::Ordering, fmt, ops}; use rowan::GreenToken; +use smol_str::SmolStr; pub struct TokenText<'a>(pub(crate) Repr<'a>); @@ -47,6 +48,12 @@ impl From> for String { } } +impl From> for SmolStr { + fn from(token_text: TokenText<'_>) -> Self { + SmolStr::new(token_text.as_str()) + } +} + impl PartialEq<&'_ str> for TokenText<'_> { fn eq(&self, other: &&str) -> bool { self.as_str() == *other diff --git a/src/tools/rust-analyzer/crates/syntax/src/validation.rs b/src/tools/rust-analyzer/crates/syntax/src/validation.rs index fb2381110bfe2..e0ec6a242ffa7 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/validation.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/validation.rs @@ -5,11 +5,11 @@ mod block; use rowan::Direction; -use rustc_lexer::unescape::{self, unescape_byte, unescape_char, unescape_literal, Mode}; +use rustc_lexer::unescape::{self, unescape_literal, Mode}; use crate::{ algo, - ast::{self, HasAttrs, HasVisibility}, + ast::{self, HasAttrs, HasVisibility, IsString}, match_ast, AstNode, SyntaxError, SyntaxKind::{CONST, FN, INT_NUMBER, TYPE_ALIAS}, SyntaxNode, SyntaxToken, TextSize, T, @@ -44,7 +44,7 @@ pub(crate) fn validate(root: &SyntaxNode) -> Vec { errors } -fn rustc_unescape_error_to_string(err: unescape::EscapeError) -> &'static str { +fn rustc_unescape_error_to_string(err: unescape::EscapeError) -> (&'static str, bool) { use unescape::EscapeError as EE; #[rustfmt::skip] @@ -103,12 +103,15 @@ fn rustc_unescape_error_to_string(err: unescape::EscapeError) -> &'static str { EE::UnicodeEscapeInByte => { "Byte literals must not contain unicode escapes" } - EE::NonAsciiCharInByte | EE::NonAsciiCharInByteString => { + EE::NonAsciiCharInByte => { "Byte literals must not contain non-ASCII characters" } + EE::UnskippedWhitespaceWarning => "Whitespace after this escape is not skipped", + EE::MultipleSkippedLinesWarning => "Multiple lines are skipped by this escape", + }; - err_message + (err_message, err.is_fatal()) } fn validate_literal(literal: ast::Literal, acc: &mut Vec) { @@ -121,9 +124,13 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec) { let text = token.text(); // FIXME: lift this lambda refactor to `fn` (https://github.com/rust-lang/rust-analyzer/pull/2834#discussion_r366199205) - let mut push_err = |prefix_len, (off, err): (usize, unescape::EscapeError)| { + let mut push_err = |prefix_len, off, err: unescape::EscapeError| { let off = token.text_range().start() + TextSize::try_from(off + prefix_len).unwrap(); - acc.push(SyntaxError::new_at_offset(rustc_unescape_error_to_string(err), off)); + let (message, is_err) = rustc_unescape_error_to_string(err); + // FIXME: Emit lexer warnings + if is_err { + acc.push(SyntaxError::new_at_offset(message, off)); + } }; match literal.kind() { @@ -132,7 +139,7 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec) { if let Some(without_quotes) = unquote(text, 1, '"') { unescape_literal(without_quotes, Mode::Str, &mut |range, char| { if let Err(err) = char { - push_err(1, (range.start, err)); + push_err(1, range.start, err); } }); } @@ -143,20 +150,39 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec) { if let Some(without_quotes) = unquote(text, 2, '"') { unescape_literal(without_quotes, Mode::ByteStr, &mut |range, char| { if let Err(err) = char { - push_err(2, (range.start, err)); + push_err(1, range.start, err); + } + }); + } + } + } + ast::LiteralKind::CString(s) => { + if !s.is_raw() { + if let Some(without_quotes) = unquote(text, 2, '"') { + unescape_literal(without_quotes, Mode::ByteStr, &mut |range, char| { + if let Err(err) = char { + push_err(1, range.start, err); } }); } } } ast::LiteralKind::Char(_) => { - if let Some(Err(e)) = unquote(text, 1, '\'').map(unescape_char) { - push_err(1, e); + if let Some(without_quotes) = unquote(text, 1, '\'') { + unescape_literal(without_quotes, Mode::Char, &mut |range, char| { + if let Err(err) = char { + push_err(1, range.start, err); + } + }); } } ast::LiteralKind::Byte(_) => { - if let Some(Err(e)) = unquote(text, 2, '\'').map(unescape_byte) { - push_err(2, e); + if let Some(without_quotes) = unquote(text, 2, '\'') { + unescape_literal(without_quotes, Mode::Byte, &mut |range, char| { + if let Err(err) = char { + push_err(2, range.start, err); + } + }); } } ast::LiteralKind::IntNumber(_) @@ -175,14 +201,14 @@ pub(crate) fn validate_block_structure(root: &SyntaxNode) { assert_eq!( node.parent(), pair.parent(), - "\nunpaired curlys:\n{}\n{:#?}\n", + "\nunpaired curlies:\n{}\n{:#?}\n", root.text(), root, ); assert!( node.next_sibling_or_token().is_none() && pair.prev_sibling_or_token().is_none(), - "\nfloating curlys at {:?}\nfile:\n{}\nerror:\n{}\n", + "\nfloating curlies at {:?}\nfile:\n{}\nerror:\n{}\n", node, root.text(), node, diff --git a/src/tools/rust-analyzer/crates/syntax/test_data/parser/fuzz-failures/0000.rs b/src/tools/rust-analyzer/crates/syntax/test_data/parser/fuzz-failures/0000.rs index f977d23c48090..13852aa78b84f 100644 --- a/src/tools/rust-analyzer/crates/syntax/test_data/parser/fuzz-failures/0000.rs +++ b/src/tools/rust-analyzer/crates/syntax/test_data/parser/fuzz-failures/0000.rs @@ -39,13 +39,13 @@ ast::Root::cast(self.syntax()).unwrap() } pub fn syntax(&self) -> SyntaxNodeRef { - self.root.brroowed() + self.root.borrowed() } mp_tree(root), ); assert!( node.next_sibling().is_none() && pair.prev_sibling().is_none(), - "\nfloating curlys at {:?}\nfile:\n{}\nerror:\n{}\n", + "\nfloating curlies at {:?}\nfile:\n{}\nerror:\n{}\n", node, root.text(), node.text(), diff --git a/src/tools/rust-analyzer/crates/test-utils/Cargo.toml b/src/tools/rust-analyzer/crates/test-utils/Cargo.toml index 92b1ef23e69ce..2b5b6f4956166 100644 --- a/src/tools/rust-analyzer/crates/test-utils/Cargo.toml +++ b/src/tools/rust-analyzer/crates/test-utils/Cargo.toml @@ -14,7 +14,7 @@ doctest = false [dependencies] # Avoid adding deps here, this crate is widely used in tests it should compile fast! dissimilar = "1.0.4" -text-size = "1.1.0" +text-size.workspace = true rustc-hash = "1.1.0" stdx.workspace = true diff --git a/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs b/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs index cd1235fa6dc4e..05f32f8e51ea4 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs @@ -86,7 +86,14 @@ pub struct MiniCore { valid_flags: Vec, } -impl Fixture { +pub struct FixtureWithProjectMeta { + pub fixture: Vec, + pub mini_core: Option, + pub proc_macro_names: Vec, + pub toolchain: Option, +} + +impl FixtureWithProjectMeta { /// Parses text which looks like this: /// /// ```not_rust @@ -96,37 +103,41 @@ impl Fixture { /// //- other meta /// ``` /// - /// Fixture can also start with a proc_macros and minicore declaration(in that order): + /// Fixture can also start with a proc_macros and minicore declaration (in that order): /// /// ``` + /// //- toolchain: nightly /// //- proc_macros: identity /// //- minicore: sized /// ``` /// - /// That will include predefined proc macros and a subset of `libcore` into the fixture, see - /// `minicore.rs` for what's available. - pub fn parse(ra_fixture: &str) -> (Option, Vec, Vec) { + /// That will set toolchain to nightly and include predefined proc macros and a subset of + /// `libcore` into the fixture, see `minicore.rs` for what's available. Note that toolchain + /// defaults to stable. + pub fn parse(ra_fixture: &str) -> Self { let fixture = trim_indent(ra_fixture); let mut fixture = fixture.as_str(); + let mut toolchain = None; let mut mini_core = None; let mut res: Vec = Vec::new(); - let mut test_proc_macros = vec![]; - - if fixture.starts_with("//- proc_macros:") { - let first_line = fixture.split_inclusive('\n').next().unwrap(); - test_proc_macros = first_line - .strip_prefix("//- proc_macros:") - .unwrap() - .split(',') - .map(|it| it.trim().to_string()) - .collect(); - fixture = &fixture[first_line.len()..]; + let mut proc_macro_names = vec![]; + + if let Some(meta) = fixture.strip_prefix("//- toolchain:") { + let (meta, remain) = meta.split_once('\n').unwrap(); + toolchain = Some(meta.trim().to_string()); + fixture = remain; + } + + if let Some(meta) = fixture.strip_prefix("//- proc_macros:") { + let (meta, remain) = meta.split_once('\n').unwrap(); + proc_macro_names = meta.split(',').map(|it| it.trim().to_string()).collect(); + fixture = remain; } - if fixture.starts_with("//- minicore:") { - let first_line = fixture.split_inclusive('\n').next().unwrap(); - mini_core = Some(MiniCore::parse(first_line)); - fixture = &fixture[first_line.len()..]; + if let Some(meta) = fixture.strip_prefix("//- minicore:") { + let (meta, remain) = meta.split_once('\n').unwrap(); + mini_core = Some(MiniCore::parse(meta)); + fixture = remain; } let default = if fixture.contains("//-") { None } else { Some("//- /main.rs") }; @@ -142,7 +153,7 @@ impl Fixture { } if line.starts_with("//-") { - let meta = Fixture::parse_meta_line(line); + let meta = Self::parse_meta_line(line); res.push(meta); } else { if line.starts_with("// ") @@ -160,7 +171,7 @@ impl Fixture { } } - (mini_core, test_proc_macros, res) + Self { fixture: res, mini_core, proc_macro_names, toolchain } } //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b env:OUTDIR=path/to,OTHER=foo @@ -243,10 +254,19 @@ impl Fixture { } impl MiniCore { + const RAW_SOURCE: &str = include_str!("./minicore.rs"); + fn has_flag(&self, flag: &str) -> bool { self.activated_flags.iter().any(|it| it == flag) } + pub fn from_flags<'a>(flags: impl IntoIterator) -> Self { + MiniCore { + activated_flags: flags.into_iter().map(|x| x.to_owned()).collect(), + valid_flags: Vec::new(), + } + } + #[track_caller] fn assert_valid_flag(&self, flag: &str) { if !self.valid_flags.iter().any(|it| it == flag) { @@ -257,8 +277,7 @@ impl MiniCore { fn parse(line: &str) -> MiniCore { let mut res = MiniCore { activated_flags: Vec::new(), valid_flags: Vec::new() }; - let line = line.strip_prefix("//- minicore:").unwrap().trim(); - for entry in line.split(", ") { + for entry in line.trim().split(", ") { if res.has_flag(entry) { panic!("duplicate minicore flag: {entry:?}"); } @@ -268,13 +287,21 @@ impl MiniCore { res } + pub fn available_flags() -> impl Iterator { + let lines = MiniCore::RAW_SOURCE.split_inclusive('\n'); + lines + .map_while(|x| x.strip_prefix("//!")) + .skip_while(|line| !line.contains("Available flags:")) + .skip(1) + .map(|x| x.split_once(':').unwrap().0.trim()) + } + /// Strips parts of minicore.rs which are flagged by inactive flags. /// /// This is probably over-engineered to support flags dependencies. pub fn source_code(mut self) -> String { let mut buf = String::new(); - let raw_mini_core = include_str!("./minicore.rs"); - let mut lines = raw_mini_core.split_inclusive('\n'); + let mut lines = MiniCore::RAW_SOURCE.split_inclusive('\n'); let mut implications = Vec::new(); @@ -372,7 +399,7 @@ impl MiniCore { #[test] #[should_panic] fn parse_fixture_checks_further_indented_metadata() { - Fixture::parse( + FixtureWithProjectMeta::parse( r" //- /lib.rs mod bar; @@ -386,15 +413,18 @@ fn parse_fixture_checks_further_indented_metadata() { #[test] fn parse_fixture_gets_full_meta() { - let (mini_core, proc_macros, parsed) = Fixture::parse( - r#" + let FixtureWithProjectMeta { fixture: parsed, mini_core, proc_macro_names, toolchain } = + FixtureWithProjectMeta::parse( + r#" +//- toolchain: nightly //- proc_macros: identity //- minicore: coerce_unsized //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b,atom env:OUTDIR=path/to,OTHER=foo mod m; "#, - ); - assert_eq!(proc_macros, vec!["identity".to_string()]); + ); + assert_eq!(toolchain, Some("nightly".to_string())); + assert_eq!(proc_macro_names, vec!["identity".to_string()]); assert_eq!(mini_core.unwrap().activated_flags, vec!["coerce_unsized".to_string()]); assert_eq!(1, parsed.len()); diff --git a/src/tools/rust-analyzer/crates/test-utils/src/lib.rs b/src/tools/rust-analyzer/crates/test-utils/src/lib.rs index a7a52e08e75a8..fd3e68e2d2c63 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/lib.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/lib.rs @@ -27,7 +27,7 @@ pub use rustc_hash::FxHashMap; pub use crate::{ assert_linear::AssertLinear, - fixture::{Fixture, MiniCore}, + fixture::{Fixture, FixtureWithProjectMeta, MiniCore}, }; pub const CURSOR_MARKER: &str = "$0"; @@ -95,7 +95,7 @@ fn try_extract_range(text: &str) -> Option<(TextRange, String)> { Some((TextRange::new(start, end), text)) } -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug)] pub enum RangeOrOffset { Range(TextRange), Offset(TextSize), diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs index ca6de4061a4b8..c79b4e966d67e 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -11,6 +11,8 @@ //! add: //! as_ref: sized //! bool_impl: option, fn +//! builtin_impls: +//! cell: copy, drop //! clone: sized //! coerce_unsized: unsize //! copy: clone @@ -21,19 +23,23 @@ //! drop: //! eq: sized //! error: fmt -//! fmt: result +//! fmt: result, transmute, coerce_unsized //! fn: //! from: sized //! future: pin //! generator: pin //! hash: +//! include: //! index: sized //! infallible: //! iterator: option //! iterators: iterator, fn +//! manually_drop: drop //! non_zero: -//! option: +//! option: panic //! ord: eq, option +//! panic: fmt +//! phantom_data: //! pin: //! range: //! result: @@ -41,6 +47,7 @@ //! sized: //! slice: //! sync: sized +//! transmute: //! try: infallible //! unsize: sized @@ -106,6 +113,7 @@ pub mod marker { impl Copy for *const T {} impl Copy for *mut T {} impl Copy for &T {} + impl Copy for ! {} } // endregion:copy @@ -113,6 +121,11 @@ pub mod marker { #[lang = "tuple_trait"] pub trait Tuple {} // endregion:fn + + // region:phantom_data + #[lang = "phantom_data"] + pub struct PhantomData; + // endregion:phantom_data } // region:default @@ -124,6 +137,27 @@ pub mod default { #[rustc_builtin_macro(Default, attributes(default))] pub macro Default($item:item) {} // endregion:derive + + // region:builtin_impls + macro_rules! impl_default { + ($v:literal; $($t:ty)*) => { + $( + impl const Default for $t { + fn default() -> Self { + $v + } + } + )* + } + } + + impl_default! { + 0; usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 + } + impl_default! { + 0.0; f32 f64 + } + // endregion:builtin_impls } // endregion:default @@ -134,15 +168,100 @@ pub mod hash { pub trait Hash { fn hash(&self, state: &mut H); } + + // region:derive + #[rustc_builtin_macro] + pub macro Hash($item:item) {} + // endregion:derive } // endregion:hash +// region:cell +pub mod cell { + use crate::mem; + + #[lang = "unsafe_cell"] + pub struct UnsafeCell { + value: T, + } + + impl UnsafeCell { + pub const fn new(value: T) -> UnsafeCell { + UnsafeCell { value } + } + + pub const fn get(&self) -> *mut T { + self as *const UnsafeCell as *const T as *mut T + } + } + + pub struct Cell { + value: UnsafeCell, + } + + impl Cell { + pub const fn new(value: T) -> Cell { + Cell { value: UnsafeCell::new(value) } + } + + pub fn set(&self, val: T) { + let old = self.replace(val); + mem::drop(old); + } + + pub fn replace(&self, val: T) -> T { + mem::replace(unsafe { &mut *self.value.get() }, val) + } + } + + impl Cell { + pub fn get(&self) -> T { + unsafe { *self.value.get() } + } + } +} +// endregion:cell + // region:clone pub mod clone { #[lang = "clone"] pub trait Clone: Sized { fn clone(&self) -> Self; } + + impl Clone for &T { + fn clone(&self) -> Self { + *self + } + } + + // region:builtin_impls + macro_rules! impl_clone { + ($($t:ty)*) => { + $( + impl const Clone for $t { + fn clone(&self) -> Self { + *self + } + } + )* + } + } + + impl_clone! { + usize u8 u16 u32 u64 u128 + isize i8 i16 i32 i64 i128 + f32 f64 + bool char + } + + impl Clone for ! { + fn clone(&self) { + *self + } + } + // endregion:builtin_impls + // region:derive #[rustc_builtin_macro] pub macro Clone($item:item) {} @@ -181,10 +300,68 @@ pub mod convert { } // endregion:as_ref // region:infallible - pub enum Infallibe {} + pub enum Infallible {} // endregion:infallible } +pub mod mem { + // region:drop + // region:manually_drop + #[lang = "manually_drop"] + #[repr(transparent)] + pub struct ManuallyDrop { + value: T, + } + + impl ManuallyDrop { + pub const fn new(value: T) -> ManuallyDrop { + ManuallyDrop { value } + } + } + + // region:deref + impl crate::ops::Deref for ManuallyDrop { + type Target = T; + fn deref(&self) -> &T { + &self.value + } + } + // endregion:deref + + // endregion:manually_drop + + pub fn drop(_x: T) {} + pub const fn replace(dest: &mut T, src: T) -> T { + unsafe { + let result = crate::ptr::read(dest); + crate::ptr::write(dest, src); + result + } + } + // endregion:drop + + // region:transmute + extern "rust-intrinsic" { + pub fn transmute(src: Src) -> Dst; + } + // endregion:transmute +} + +pub mod ptr { + // region:drop + #[lang = "drop_in_place"] + pub unsafe fn drop_in_place(to_drop: *mut T) { + unsafe { drop_in_place(to_drop) } + } + pub const unsafe fn read(src: *const T) -> T { + *src + } + pub const unsafe fn write(dst: *mut T, src: T) { + *dst = src; + } + // endregion:drop +} + pub mod ops { // region:coerce_unsized mod unsize { @@ -309,12 +486,6 @@ pub mod ops { pub use self::index::{Index, IndexMut}; // endregion:index - // region:drop - pub mod mem { - pub fn drop(_x: T) {} - } - // endregion:drop - // region:range mod range { #[lang = "RangeFull"] @@ -375,16 +546,82 @@ pub mod ops { type Output; extern "rust-call" fn call_once(self, args: Args) -> Self::Output; } + + mod impls { + use crate::marker::Tuple; + + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")] + impl const Fn
for &F + where + F: ~const Fn, + { + extern "rust-call" fn call(&self, args: A) -> F::Output { + (**self).call(args) + } + } + + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")] + impl const FnMut for &F + where + F: ~const Fn, + { + extern "rust-call" fn call_mut(&mut self, args: A) -> F::Output { + (**self).call(args) + } + } + + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")] + impl const FnOnce for &F + where + F: ~const Fn, + { + type Output = F::Output; + + extern "rust-call" fn call_once(self, args: A) -> F::Output { + (*self).call(args) + } + } + + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")] + impl const FnMut for &mut F + where + F: ~const FnMut, + { + extern "rust-call" fn call_mut(&mut self, args: A) -> F::Output { + (*self).call_mut(args) + } + } + + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")] + impl const FnOnce for &mut F + where + F: ~const FnMut, + { + type Output = F::Output; + extern "rust-call" fn call_once(self, args: A) -> F::Output { + (*self).call_mut(args) + } + } + } } pub use self::function::{Fn, FnMut, FnOnce}; // endregion:fn // region:try mod try_ { + use super::super::convert::Infallible; + pub enum ControlFlow { + #[lang = "Continue"] Continue(C), + #[lang = "Break"] Break(B), } - pub trait FromResidual { + pub trait FromResidual::Residual> { #[lang = "from_residual"] fn from_residual(residual: R) -> Self; } @@ -400,14 +637,80 @@ pub mod ops { impl Try for ControlFlow { type Output = C; - type Residual = ControlFlow; - fn from_output(output: Self::Output) -> Self {} - fn branch(self) -> ControlFlow {} + type Residual = ControlFlow; + fn from_output(output: Self::Output) -> Self { + ControlFlow::Continue(output) + } + fn branch(self) -> ControlFlow { + match self { + ControlFlow::Continue(x) => ControlFlow::Continue(x), + ControlFlow::Break(x) => ControlFlow::Break(ControlFlow::Break(x)), + } + } } impl FromResidual for ControlFlow { - fn from_residual(residual: ControlFlow) -> Self {} + fn from_residual(residual: ControlFlow) -> Self { + match residual { + ControlFlow::Break(b) => ControlFlow::Break(b), + ControlFlow::Continue(_) => loop {}, + } + } + } + // region:option + impl Try for Option { + type Output = T; + type Residual = Option; + fn from_output(output: Self::Output) -> Self { + Some(output) + } + fn branch(self) -> ControlFlow { + match self { + Some(x) => ControlFlow::Continue(x), + None => ControlFlow::Break(None), + } + } + } + + impl FromResidual for Option { + fn from_residual(x: Option) -> Self { + match x { + None => None, + Some(_) => loop {}, + } + } + } + // endregion:option + // region:result + // region:from + use super::super::convert::From; + + impl Try for Result { + type Output = T; + type Residual = Result; + + fn from_output(output: Self::Output) -> Self { + Ok(output) + } + + fn branch(self) -> ControlFlow { + match self { + Ok(v) => ControlFlow::Continue(v), + Err(e) => ControlFlow::Break(Err(e)), + } + } } + + impl> FromResidual> for Result { + fn from_residual(residual: Result) -> Self { + match residual { + Err(e) => Err(From::from(e)), + Ok(_) => loop {}, + } + } + } + // endregion:from + // endregion:result } pub use self::try_::{ControlFlow, FromResidual, Try}; // endregion:try @@ -424,6 +727,19 @@ pub mod ops { pub trait AddAssign { fn add_assign(&mut self, rhs: Rhs); } + + // region:builtin_impls + macro_rules! add_impl { + ($($t:ty)*) => ($( + impl const Add for $t { + type Output = $t; + fn add(self, other: $t) -> $t { self + other } + } + )*) + } + + add_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 } + // endregion:builtin_impls // endregion:add // region:generator @@ -499,12 +815,114 @@ pub mod fmt { pub struct Error; pub type Result = Result<(), Error>; pub struct Formatter<'a>; + pub struct DebugTuple; + pub struct DebugStruct; + impl Formatter<'_> { + pub fn debug_tuple(&mut self, name: &str) -> DebugTuple { + DebugTuple + } + + pub fn debug_struct(&mut self, name: &str) -> DebugStruct { + DebugStruct + } + } + + impl DebugTuple { + pub fn field(&mut self, value: &dyn Debug) -> &mut Self { + self + } + + pub fn finish(&mut self) -> Result { + Ok(()) + } + } + + impl DebugStruct { + pub fn field(&mut self, name: &str, value: &dyn Debug) -> &mut Self { + self + } + + pub fn finish(&mut self) -> Result { + Ok(()) + } + } + pub trait Debug { fn fmt(&self, f: &mut Formatter<'_>) -> Result; } pub trait Display { fn fmt(&self, f: &mut Formatter<'_>) -> Result; } + + extern "C" { + type Opaque; + } + + #[lang = "format_argument"] + pub struct ArgumentV1<'a> { + value: &'a Opaque, + formatter: fn(&Opaque, &mut Formatter<'_>) -> Result, + } + + impl<'a> ArgumentV1<'a> { + pub fn new<'b, T>(x: &'b T, f: fn(&T, &mut Formatter<'_>) -> Result) -> ArgumentV1<'b> { + use crate::mem::transmute; + unsafe { ArgumentV1 { formatter: transmute(f), value: transmute(x) } } + } + } + + #[lang = "format_arguments"] + pub struct Arguments<'a> { + pieces: &'a [&'static str], + args: &'a [ArgumentV1<'a>], + } + + impl<'a> Arguments<'a> { + pub const fn new_v1( + pieces: &'a [&'static str], + args: &'a [ArgumentV1<'a>], + ) -> Arguments<'a> { + Arguments { pieces, args } + } + } + + // region:derive + #[rustc_builtin_macro] + pub macro Debug($item:item) {} + // endregion:derive + + // region:builtin_impls + macro_rules! impl_debug { + ($($t:ty)*) => { + $( + impl const Debug for $t { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + Ok(()) + } + } + )* + } + } + + impl_debug! { + usize u8 u16 u32 u64 u128 + isize i8 i16 i32 i64 i128 + f32 f64 + bool char + } + + impl Debug for [T] { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + Ok(()) + } + } + + impl Debug for &T { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + (&**self).fmt(f) + } + } + // endregion:builtin_impls } // endregion:fmt @@ -537,12 +955,30 @@ pub mod option { } } + pub const fn as_ref(&self) -> Option<&T> { + match self { + Some(x) => Some(x), + None => None, + } + } + pub fn and(self, optb: Option) -> Option { loop {} } pub fn unwrap_or(self, default: T) -> T { - loop {} + match self { + Some(val) => val, + None => default, + } } + // region:result + pub const fn ok_or(self, err: E) -> Result { + match self { + Some(v) => Ok(v), + None => Err(err), + } + } + // endregion:result // region:fn pub fn and_then(self, f: F) -> Option where @@ -713,8 +1149,6 @@ pub mod iter { mod traits { mod iterator { - use super::super::Take; - pub trait Iterator { type Item; #[lang = "next"] @@ -764,12 +1198,19 @@ pub mod iter { self } } - pub struct IntoIter([T; N]); + struct IndexRange { + start: usize, + end: usize, + } + pub struct IntoIter { + data: [T; N], + range: IndexRange, + } impl IntoIterator for [T; N] { type Item = T; type IntoIter = IntoIter; fn into_iter(self) -> I { - IntoIter(self) + IntoIter { data: self, range: IndexRange { start: 0, end: loop {} } } } } impl Iterator for IntoIter { @@ -785,16 +1226,64 @@ pub mod iter { } // endregion:iterator -// region:derive +// region:panic +mod panic { + pub macro panic_2021 { + ($($t:tt)+) => ( + $crate::panicking::panic_fmt($crate::const_format_args!($($t)+)) + ), + } +} + +mod panicking { + #[lang = "panic_fmt"] + pub const fn panic_fmt(fmt: crate::fmt::Arguments<'_>) -> ! { + loop {} + } +} +// endregion:panic + mod macros { + // region:panic + #[macro_export] + #[rustc_builtin_macro(std_panic)] + macro_rules! panic { + ($($arg:tt)*) => { + /* compiler built-in */ + }; + } + + pub(crate) use panic; + // endregion:panic + + // region:fmt + #[macro_export] + #[rustc_builtin_macro] + macro_rules! const_format_args { + ($fmt:expr) => {{ /* compiler built-in */ }}; + ($fmt:expr, $($args:tt)*) => {{ /* compiler built-in */ }}; + } + + pub(crate) use const_format_args; + // endregion:fmt + + // region:derive pub(crate) mod builtin { #[rustc_builtin_macro] pub macro derive($item:item) { /* compiler built-in */ } } + // endregion:derive + + // region:include + #[rustc_builtin_macro] + #[macro_export] + macro_rules! include { + ($file:expr $(,)?) => {{ /* compiler built-in */ }}; + } + // endregion:include } -// endregion:derive // region:non_zero pub mod num { @@ -848,6 +1337,7 @@ pub mod prelude { ops::Drop, // :drop ops::{Fn, FnMut, FnOnce}, // :fn option::Option::{self, None, Some}, // :option + panic, // :panic result::Result::{self, Err, Ok}, // :result }; } diff --git a/src/tools/rust-analyzer/crates/text-edit/Cargo.toml b/src/tools/rust-analyzer/crates/text-edit/Cargo.toml index 337cd234739c8..76d0ca5ccb601 100644 --- a/src/tools/rust-analyzer/crates/text-edit/Cargo.toml +++ b/src/tools/rust-analyzer/crates/text-edit/Cargo.toml @@ -13,4 +13,4 @@ doctest = false [dependencies] itertools = "0.10.5" -text-size = "1.1.0" +text-size.workspace = true diff --git a/src/tools/rust-analyzer/crates/text-edit/src/lib.rs b/src/tools/rust-analyzer/crates/text-edit/src/lib.rs index 9bb4271b65f7f..4705d18187af8 100644 --- a/src/tools/rust-analyzer/crates/text-edit/src/lib.rs +++ b/src/tools/rust-analyzer/crates/text-edit/src/lib.rs @@ -176,6 +176,7 @@ impl TextEditBuilder { pub fn finish(self) -> TextEdit { let mut indels = self.indels; assert_disjoint_or_equal(&mut indels); + indels = coalesce_indels(indels); TextEdit { indels } } pub fn invalidates_offset(&self, offset: TextSize) -> bool { @@ -205,6 +206,21 @@ where indels.clone().zip(indels.skip(1)).all(|(l, r)| l.delete.end() <= r.delete.start() || l == r) } +fn coalesce_indels(indels: Vec) -> Vec { + indels + .into_iter() + .coalesce(|mut a, b| { + if a.delete.end() == b.delete.start() { + a.insert.push_str(&b.insert); + a.delete = TextRange::new(a.delete.start(), b.delete.end()); + Ok(a) + } else { + Err((a, b)) + } + }) + .collect_vec() +} + #[cfg(test)] mod tests { use super::{TextEdit, TextEditBuilder, TextRange}; @@ -261,4 +277,40 @@ mod tests { let edit2 = TextEdit::delete(range(9, 13)); assert!(edit1.union(edit2).is_err()); } + + #[test] + fn test_coalesce_disjoint() { + let mut builder = TextEditBuilder::default(); + builder.replace(range(1, 3), "aa".into()); + builder.replace(range(5, 7), "bb".into()); + let edit = builder.finish(); + + assert_eq!(edit.indels.len(), 2); + } + + #[test] + fn test_coalesce_adjacent() { + let mut builder = TextEditBuilder::default(); + builder.replace(range(1, 3), "aa".into()); + builder.replace(range(3, 5), "bb".into()); + + let edit = builder.finish(); + assert_eq!(edit.indels.len(), 1); + assert_eq!(edit.indels[0].insert, "aabb"); + assert_eq!(edit.indels[0].delete, range(1, 5)); + } + + #[test] + fn test_coalesce_adjacent_series() { + let mut builder = TextEditBuilder::default(); + builder.replace(range(1, 3), "au".into()); + builder.replace(range(3, 5), "www".into()); + builder.replace(range(5, 8), "".into()); + builder.replace(range(8, 9), "ub".into()); + + let edit = builder.finish(); + assert_eq!(edit.indels.len(), 1); + assert_eq!(edit.indels[0].insert, "auwwwub"); + assert_eq!(edit.indels[0].delete, range(1, 9)); + } } diff --git a/src/tools/rust-analyzer/crates/tt/Cargo.toml b/src/tools/rust-analyzer/crates/tt/Cargo.toml index b8469383183b6..a28ee5f1ca2bb 100644 --- a/src/tools/rust-analyzer/crates/tt/Cargo.toml +++ b/src/tools/rust-analyzer/crates/tt/Cargo.toml @@ -12,6 +12,6 @@ rust-version.workspace = true doctest = false [dependencies] -smol_str = "0.1.23" +smol_str.workspace = true stdx.workspace = true diff --git a/src/tools/rust-analyzer/crates/tt/src/lib.rs b/src/tools/rust-analyzer/crates/tt/src/lib.rs index b7dbc82e1d66e..c2ebf03746a87 100644 --- a/src/tools/rust-analyzer/crates/tt/src/lib.rs +++ b/src/tools/rust-analyzer/crates/tt/src/lib.rs @@ -153,6 +153,12 @@ pub struct Ident { pub span: Span, } +impl Ident { + pub fn new(text: impl Into, span: S) -> Self { + Ident { text: text.into(), span } + } +} + fn print_debug_subtree( f: &mut fmt::Formatter<'_>, subtree: &Subtree, diff --git a/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml b/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml index e06b98d8118b6..5d61a227284ed 100644 --- a/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml +++ b/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml @@ -13,10 +13,10 @@ doctest = false [dependencies] tracing = "0.1.35" -jod-thread = "0.1.2" walkdir = "2.3.2" crossbeam-channel = "0.5.5" notify = "5.0" +stdx.workspace = true vfs.workspace = true paths.workspace = true diff --git a/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs b/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs index c95304e55ac10..abfc51dfec66f 100644 --- a/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs +++ b/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs @@ -21,7 +21,7 @@ use walkdir::WalkDir; pub struct NotifyHandle { // Relative order of fields below is significant. sender: Sender, - _thread: jod_thread::JoinHandle, + _thread: stdx::thread::JoinHandle, } #[derive(Debug)] @@ -34,7 +34,7 @@ impl loader::Handle for NotifyHandle { fn spawn(sender: loader::Sender) -> NotifyHandle { let actor = NotifyActor::new(sender); let (sender, receiver) = unbounded::(); - let thread = jod_thread::Builder::new() + let thread = stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker) .name("VfsLoader".to_owned()) .spawn(move || actor.run(receiver)) .expect("failed to spawn thread"); diff --git a/src/tools/rust-analyzer/crates/vfs/Cargo.toml b/src/tools/rust-analyzer/crates/vfs/Cargo.toml index 802a300060fa1..3ae3dc83ca9b3 100644 --- a/src/tools/rust-analyzer/crates/vfs/Cargo.toml +++ b/src/tools/rust-analyzer/crates/vfs/Cargo.toml @@ -15,6 +15,7 @@ doctest = false rustc-hash = "1.1.0" fst = "0.4.7" indexmap = "1.9.1" +nohash-hasher.workspace = true paths.workspace = true stdx.workspace = true diff --git a/src/tools/rust-analyzer/crates/vfs/src/file_set.rs b/src/tools/rust-analyzer/crates/vfs/src/file_set.rs index 700aebe0b34f1..0392ef3cebe96 100644 --- a/src/tools/rust-analyzer/crates/vfs/src/file_set.rs +++ b/src/tools/rust-analyzer/crates/vfs/src/file_set.rs @@ -5,8 +5,8 @@ use std::fmt; use fst::{IntoStreamer, Streamer}; +use nohash_hasher::IntMap; use rustc_hash::FxHashMap; -use stdx::hash::NoHashHashMap; use crate::{AnchoredPath, FileId, Vfs, VfsPath}; @@ -14,7 +14,7 @@ use crate::{AnchoredPath, FileId, Vfs, VfsPath}; #[derive(Default, Clone, Eq, PartialEq)] pub struct FileSet { files: FxHashMap, - paths: NoHashHashMap, + paths: IntMap, } impl FileSet { diff --git a/src/tools/rust-analyzer/crates/vfs/src/lib.rs b/src/tools/rust-analyzer/crates/vfs/src/lib.rs index 14972d2907416..fe3dfe6196864 100644 --- a/src/tools/rust-analyzer/crates/vfs/src/lib.rs +++ b/src/tools/rust-analyzer/crates/vfs/src/lib.rs @@ -62,7 +62,8 @@ pub use paths::{AbsPath, AbsPathBuf}; #[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] pub struct FileId(pub u32); -impl stdx::hash::NoHashHashable for FileId {} +/// safe because `FileId` is a newtype of `u32` +impl nohash_hasher::IsEnabled for FileId {} /// Storage for all files read by rust-analyzer. /// @@ -108,13 +109,6 @@ pub enum ChangeKind { } impl Vfs { - /// Amount of files currently stored. - /// - /// Note that this includes deleted files. - pub fn len(&self) -> usize { - self.data.len() - } - /// Id of the given path if it exists in the `Vfs` and is not deleted. pub fn file_id(&self, path: &VfsPath) -> Option { self.interner.get(path).filter(|&it| self.get(it).is_some()) @@ -139,6 +133,11 @@ impl Vfs { self.get(file_id).as_deref().unwrap() } + /// Returns the overall memory usage for the stored files. + pub fn memory_usage(&self) -> usize { + self.data.iter().flatten().map(|d| d.capacity()).sum() + } + /// Returns an iterator over the stored ids and their corresponding paths. /// /// This will skip deleted files. @@ -158,16 +157,18 @@ impl Vfs { /// /// If the path does not currently exists in the `Vfs`, allocates a new /// [`FileId`] for it. - pub fn set_file_contents(&mut self, path: VfsPath, contents: Option>) -> bool { + pub fn set_file_contents(&mut self, path: VfsPath, mut contents: Option>) -> bool { let file_id = self.alloc_file_id(path); - let change_kind = match (&self.get(file_id), &contents) { + let change_kind = match (self.get(file_id), &contents) { (None, None) => return false, (Some(old), Some(new)) if old == new => return false, (None, Some(_)) => ChangeKind::Create, (Some(_), None) => ChangeKind::Delete, (Some(_), Some(_)) => ChangeKind::Modify, }; - + if let Some(contents) = &mut contents { + contents.shrink_to_fit(); + } *self.get_mut(file_id) = contents; self.changes.push(ChangedFile { file_id, change_kind }); true diff --git a/src/tools/rust-analyzer/crates/vfs/src/vfs_path.rs b/src/tools/rust-analyzer/crates/vfs/src/vfs_path.rs index 38501a8ba5a34..d327f2edf1444 100644 --- a/src/tools/rust-analyzer/crates/vfs/src/vfs_path.rs +++ b/src/tools/rust-analyzer/crates/vfs/src/vfs_path.rs @@ -107,10 +107,7 @@ impl VfsPath { /// Returns `self`'s base name and file extension. pub fn name_and_extension(&self) -> Option<(&str, Option<&str>)> { match &self.0 { - VfsPathRepr::PathBuf(p) => Some(( - p.file_stem()?.to_str()?, - p.extension().and_then(|extension| extension.to_str()), - )), + VfsPathRepr::PathBuf(p) => p.name_and_extension(), VfsPathRepr::VirtualPath(p) => p.name_and_extension(), } } diff --git a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md index de1422032088f..bc58aa7220dc0 100644 --- a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md +++ b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md @@ -1,5 +1,5 @@ [^\n]+\n/m, ""); } - let value; - if (errorCode) { - if (typeof diag.code === "string" || typeof diag.code === "number") { - value = diag.code; - } else { - value = diag.code?.value; - } - } diag.code = { target: vscode.Uri.from({ scheme: diagnostics.URI_SCHEME, @@ -153,7 +210,8 @@ export async function createClient( fragment: uri.toString(), query: idx.toString(), }), - value: value ?? "Click for full compiler diagnostic", + value: + errorCode && value ? value : "Click for full compiler diagnostic", }; } }); @@ -308,6 +366,7 @@ export async function createClient( // To turn on all proposed features use: client.registerProposedFeatures(); client.registerFeature(new ExperimentalFeatures()); + client.registerFeature(new OverrideFeatures()); return client; } @@ -343,6 +402,25 @@ class ExperimentalFeatures implements lc.StaticFeature { dispose(): void {} } +class OverrideFeatures implements lc.StaticFeature { + getState(): lc.FeatureState { + return { kind: "static" }; + } + fillClientCapabilities(capabilities: lc.ClientCapabilities): void { + // Force disable `augmentsSyntaxTokens`, VSCode's textmate grammar is somewhat incomplete + // making the experience generally worse + const caps = capabilities.textDocument?.semanticTokens; + if (caps) { + caps.augmentsSyntaxTokens = false; + } + } + initialize( + _capabilities: lc.ServerCapabilities, + _documentSelector: lc.DocumentSelector | undefined + ): void {} + dispose(): void {} +} + function isCodeActionWithoutEditsAndCommands(value: any): boolean { const candidate: lc.CodeAction = value; return ( diff --git a/src/tools/rust-analyzer/editors/code/src/commands.ts b/src/tools/rust-analyzer/editors/code/src/commands.ts index 8a953577e99d3..98ccd50dc0406 100644 --- a/src/tools/rust-analyzer/editors/code/src/commands.ts +++ b/src/tools/rust-analyzer/editors/code/src/commands.ts @@ -8,10 +8,18 @@ import { applySnippetWorkspaceEdit, applySnippetTextEdits } from "./snippets"; import { spawnSync } from "child_process"; import { RunnableQuickPick, selectRunnable, createTask, createArgs } from "./run"; import { AstInspector } from "./ast_inspector"; -import { isRustDocument, isCargoTomlDocument, sleep, isRustEditor } from "./util"; +import { + isRustDocument, + isCargoTomlDocument, + sleep, + isRustEditor, + RustEditor, + RustDocument, +} from "./util"; import { startDebugSession, makeDebugConfig } from "./debug"; import { LanguageClient } from "vscode-languageclient/node"; import { LINKED_COMMANDS } from "./client"; +import { DependencyId } from "./dependencies_provider"; export * from "./ast_inspector"; export * from "./run"; @@ -89,7 +97,13 @@ export function shuffleCrateGraph(ctx: CtxInit): Cmd { export function triggerParameterHints(_: CtxInit): Cmd { return async () => { - await vscode.commands.executeCommand("editor.action.triggerParameterHints"); + const parameterHintsEnabled = vscode.workspace + .getConfiguration("editor") + .get("parameterHints.enabled"); + + if (parameterHintsEnabled) { + await vscode.commands.executeCommand("editor.action.triggerParameterHints"); + } }; } @@ -260,6 +274,71 @@ export function openCargoToml(ctx: CtxInit): Cmd { }; } +export function revealDependency(ctx: CtxInit): Cmd { + return async (editor: RustEditor) => { + if (!ctx.dependencies?.isInitialized()) { + return; + } + const documentPath = editor.document.uri.fsPath; + const dep = ctx.dependencies?.getDependency(documentPath); + if (dep) { + await ctx.treeView?.reveal(dep, { select: true, expand: true }); + } else { + await revealParentChain(editor.document, ctx); + } + }; +} + +/** + * This function calculates the parent chain of a given file until it reaches it crate root contained in ctx.dependencies. + * This is need because the TreeView is Lazy, so at first it only has the root dependencies: For example if we have the following crates: + * - core + * - alloc + * - std + * + * if I want to reveal alloc/src/str.rs, I have to: + + * 1. reveal every children of alloc + * - core + * - alloc\ + *  |-beches\ + *  |-src\ + *  |- ... + * - std + * 2. reveal every children of src: + * core + * alloc\ + *  |-beches\ + *  |-src\ + *   |- lib.rs\ + *   |- str.rs <------- FOUND IT!\ + *   |- ...\ + *  |- ...\ + * std + */ +async function revealParentChain(document: RustDocument, ctx: CtxInit) { + let documentPath = document.uri.fsPath; + const maxDepth = documentPath.split(path.sep).length - 1; + const parentChain: DependencyId[] = [{ id: documentPath.toLowerCase() }]; + do { + documentPath = path.dirname(documentPath); + parentChain.push({ id: documentPath.toLowerCase() }); + if (parentChain.length >= maxDepth) { + // this is an odd case that can happen when we change a crate version but we'd still have + // a open file referencing the old version + return; + } + } while (!ctx.dependencies?.contains(documentPath)); + parentChain.reverse(); + for (const idx in parentChain) { + await ctx.treeView?.reveal(parentChain[idx], { select: true, expand: true }); + } +} + +export async function execRevealDependency(e: RustEditor): Promise { + await vscode.commands.executeCommand("rust-analyzer.revealDependency", e); +} + export function ssr(ctx: CtxInit): Cmd { return async () => { const editor = vscode.window.activeTextEditor; @@ -416,8 +495,20 @@ export function syntaxTree(ctx: CtxInit): Cmd { function viewHirOrMir(ctx: CtxInit, xir: "hir" | "mir"): Cmd { const viewXir = xir === "hir" ? "viewHir" : "viewMir"; const requestType = xir === "hir" ? ra.viewHir : ra.viewMir; + const uri = `rust-analyzer-${xir}://${viewXir}/${xir}.rs`; + const scheme = `rust-analyzer-${xir}`; + return viewFileUsingTextDocumentContentProvider(ctx, requestType, uri, scheme, true); +} + +function viewFileUsingTextDocumentContentProvider( + ctx: CtxInit, + requestType: lc.RequestType, + uri: string, + scheme: string, + shouldUpdate: boolean +): Cmd { const tdcp = new (class implements vscode.TextDocumentContentProvider { - readonly uri = vscode.Uri.parse(`rust-analyzer-${xir}://${viewXir}/${xir}.rs`); + readonly uri = vscode.Uri.parse(uri); readonly eventEmitter = new vscode.EventEmitter(); constructor() { vscode.workspace.onDidChangeTextDocument( @@ -433,14 +524,14 @@ function viewHirOrMir(ctx: CtxInit, xir: "hir" | "mir"): Cmd { } private onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) { - if (isRustDocument(event.document)) { + if (isRustDocument(event.document) && shouldUpdate) { // We need to order this after language server updates, but there's no API for that. // Hence, good old sleep(). void sleep(10).then(() => this.eventEmitter.fire(this.uri)); } } private onDidChangeActiveTextEditor(editor: vscode.TextEditor | undefined) { - if (editor && isRustEditor(editor)) { + if (editor && isRustEditor(editor) && shouldUpdate) { this.eventEmitter.fire(this.uri); } } @@ -467,9 +558,7 @@ function viewHirOrMir(ctx: CtxInit, xir: "hir" | "mir"): Cmd { } })(); - ctx.pushExtCleanup( - vscode.workspace.registerTextDocumentContentProvider(`rust-analyzer-${xir}`, tdcp) - ); + ctx.pushExtCleanup(vscode.workspace.registerTextDocumentContentProvider(scheme, tdcp)); return async () => { const document = await vscode.workspace.openTextDocument(tdcp.uri); @@ -495,6 +584,20 @@ export function viewMir(ctx: CtxInit): Cmd { return viewHirOrMir(ctx, "mir"); } +// Opens the virtual file that will show the MIR of the function containing the cursor position +// +// The contents of the file come from the `TextDocumentContentProvider` +export function interpretFunction(ctx: CtxInit): Cmd { + const uri = `rust-analyzer-interpret-function://interpretFunction/result.log`; + return viewFileUsingTextDocumentContentProvider( + ctx, + ra.interpretFunction, + uri, + `rust-analyzer-interpret-function`, + false + ); +} + export function viewFileText(ctx: CtxInit): Cmd { const tdcp = new (class implements vscode.TextDocumentContentProvider { readonly uri = vscode.Uri.parse("rust-analyzer-file-text://viewFileText/file.rs"); @@ -663,21 +766,26 @@ function crateGraph(ctx: CtxInit, full: boolean): Cmd { - +
`; @@ -699,7 +807,7 @@ export function viewFullCrateGraph(ctx: CtxInit): Cmd { // The contents of the file come from the `TextDocumentContentProvider` export function expandMacro(ctx: CtxInit): Cmd { function codeFormat(expanded: ra.ExpandedMacro): string { - let result = `// Recursive expansion of ${expanded.name}! macro\n`; + let result = `// Recursive expansion of ${expanded.name} macro\n`; result += "// " + "=".repeat(result.length - 3); result += "\n\n"; result += expanded.expansion; @@ -749,6 +857,10 @@ export function reloadWorkspace(ctx: CtxInit): Cmd { return async () => ctx.client.sendRequest(ra.reloadWorkspace); } +export function rebuildProcMacros(ctx: CtxInit): Cmd { + return async () => ctx.client.sendRequest(ra.rebuildProcMacros); +} + export function addProject(ctx: CtxInit): Cmd { return async () => { const discoverProjectCommand = ctx.config.discoverProjectCommand; @@ -757,12 +869,13 @@ export function addProject(ctx: CtxInit): Cmd { } const workspaces: JsonProject[] = await Promise.all( - vscode.workspace.workspaceFolders!.map(async (folder): Promise => { - const rustDocuments = vscode.workspace.textDocuments.filter(isRustDocument); - return discoverWorkspace(rustDocuments, discoverProjectCommand, { - cwd: folder.uri.fsPath, - }); - }) + vscode.workspace.textDocuments + .filter(isRustDocument) + .map(async (file): Promise => { + return discoverWorkspace([file], discoverProjectCommand, { + cwd: path.dirname(file.uri.fsPath), + }); + }) ); ctx.addToDiscoveredWorkspaces(workspaces); diff --git a/src/tools/rust-analyzer/editors/code/src/config.ts b/src/tools/rust-analyzer/editors/code/src/config.ts index da7c74c28bae9..c6d2bcc2b2a97 100644 --- a/src/tools/rust-analyzer/editors/code/src/config.ts +++ b/src/tools/rust-analyzer/editors/code/src/config.ts @@ -21,7 +21,6 @@ export class Config { "serverPath", "server", "files", - "lens", // works as lens.* ].map((opt) => `${this.rootSection}.${opt}`); readonly package: { @@ -70,7 +69,7 @@ export class Config { if (!requiresReloadOpt) return; if (this.restartServerOnConfigChange) { - await vscode.commands.executeCommand("rust-analyzer.reload"); + await vscode.commands.executeCommand("rust-analyzer.restartServer"); return; } @@ -78,7 +77,7 @@ export class Config { const userResponse = await vscode.window.showInformationMessage(message, "Restart now"); if (userResponse) { - const command = "rust-analyzer.reload"; + const command = "rust-analyzer.restartServer"; await vscode.commands.executeCommand(command); } } @@ -285,6 +284,10 @@ export class Config { get useRustcErrorCode() { return this.get("diagnostics.useRustcErrorCode"); } + + get showDependenciesExplorer() { + return this.get("showDependenciesExplorer"); + } } // the optional `cb?` parameter is meant to be used to add additional diff --git a/src/tools/rust-analyzer/editors/code/src/ctx.ts b/src/tools/rust-analyzer/editors/code/src/ctx.ts index c2dca733df8f5..a72b5391ff130 100644 --- a/src/tools/rust-analyzer/editors/code/src/ctx.ts +++ b/src/tools/rust-analyzer/editors/code/src/ctx.ts @@ -1,11 +1,13 @@ import * as vscode from "vscode"; import * as lc from "vscode-languageclient/node"; import * as ra from "./lsp_ext"; +import * as path from "path"; import { Config, prepareVSCodeConfig } from "./config"; import { createClient } from "./client"; import { executeDiscoverProject, + isDocumentInWorkspace, isRustDocument, isRustEditor, LazyOutputChannel, @@ -13,6 +15,13 @@ import { RustEditor, } from "./util"; import { ServerStatusParams } from "./lsp_ext"; +import { + Dependency, + DependencyFile, + RustDependenciesProvider, + DependencyId, +} from "./dependencies_provider"; +import { execRevealDependency } from "./commands"; import { PersistentState } from "./persistent_state"; import { bootstrap } from "./bootstrap"; import { ExecOptions } from "child_process"; @@ -82,11 +91,22 @@ export class Ctx { private state: PersistentState; private commandFactories: Record; private commandDisposables: Disposable[]; + private unlinkedFiles: vscode.Uri[]; + private _dependencies: RustDependenciesProvider | undefined; + private _treeView: vscode.TreeView | undefined; get client() { return this._client; } + get treeView() { + return this._treeView; + } + + get dependencies() { + return this._dependencies; + } + constructor( readonly extCtx: vscode.ExtensionContext, commandFactories: Record, @@ -94,12 +114,11 @@ export class Ctx { ) { extCtx.subscriptions.push(this); this.statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left); - this.statusBar.show(); this.workspace = workspace; this.clientSubscriptions = []; this.commandDisposables = []; this.commandFactories = commandFactories; - + this.unlinkedFiles = []; this.state = new PersistentState(extCtx.globalState); this.config = new Config(extCtx); @@ -191,12 +210,13 @@ export class Ctx { const discoverProjectCommand = this.config.discoverProjectCommand; if (discoverProjectCommand) { const workspaces: JsonProject[] = await Promise.all( - vscode.workspace.workspaceFolders!.map(async (folder): Promise => { - const rustDocuments = vscode.workspace.textDocuments.filter(isRustDocument); - return discoverWorkspace(rustDocuments, discoverProjectCommand, { - cwd: folder.uri.fsPath, - }); - }) + vscode.workspace.textDocuments + .filter(isRustDocument) + .map(async (file): Promise => { + return discoverWorkspace([file], discoverProjectCommand, { + cwd: path.dirname(file.uri.fsPath), + }); + }) ); this.addToDiscoveredWorkspaces(workspaces); @@ -218,7 +238,8 @@ export class Ctx { this.outputChannel, initializationOptions, serverOptions, - this.config + this.config, + this.unlinkedFiles ); this.pushClientCleanup( this._client.onNotification(ra.serverStatus, (params) => @@ -242,6 +263,56 @@ export class Ctx { } await client.start(); this.updateCommands(); + + if (this.config.showDependenciesExplorer) { + this.prepareTreeDependenciesView(client); + } + } + + private prepareTreeDependenciesView(client: lc.LanguageClient) { + const ctxInit: CtxInit = { + ...this, + client: client, + }; + this._dependencies = new RustDependenciesProvider(ctxInit); + this._treeView = vscode.window.createTreeView("rustDependencies", { + treeDataProvider: this._dependencies, + showCollapseAll: true, + }); + + this.pushExtCleanup(this._treeView); + vscode.window.onDidChangeActiveTextEditor(async (e) => { + // we should skip documents that belong to the current workspace + if (this.shouldRevealDependency(e)) { + try { + await execRevealDependency(e); + } catch (reason) { + await vscode.window.showErrorMessage(`Dependency error: ${reason}`); + } + } + }); + + this.treeView?.onDidChangeVisibility(async (e) => { + if (e.visible) { + const activeEditor = vscode.window.activeTextEditor; + if (this.shouldRevealDependency(activeEditor)) { + try { + await execRevealDependency(activeEditor); + } catch (reason) { + await vscode.window.showErrorMessage(`Dependency error: ${reason}`); + } + } + } + }); + } + + private shouldRevealDependency(e: vscode.TextEditor | undefined): e is RustEditor { + return ( + e !== undefined && + isRustEditor(e) && + !isDocumentInWorkspace(e.document) && + (this.treeView?.visible || false) + ); } async restart() { @@ -335,6 +406,7 @@ export class Ctx { setServerStatus(status: ServerStatusParams | { health: "stopped" }) { let icon = ""; const statusBar = this.statusBar; + statusBar.show(); statusBar.tooltip = new vscode.MarkdownString("", true); statusBar.tooltip.isTrusted = true; switch (status.health) { @@ -343,6 +415,7 @@ export class Ctx { statusBar.color = undefined; statusBar.backgroundColor = undefined; statusBar.command = "rust-analyzer.stopServer"; + this.dependencies?.refresh(); break; case "warning": if (status.message) { @@ -378,12 +451,17 @@ export class Ctx { if (statusBar.tooltip.value) { statusBar.tooltip.appendText("\n\n"); } + statusBar.tooltip.appendMarkdown("\n\n[Open logs](command:rust-analyzer.openLogs)"); statusBar.tooltip.appendMarkdown( "\n\n[Reload Workspace](command:rust-analyzer.reloadWorkspace)" ); - statusBar.tooltip.appendMarkdown("\n\n[Open logs](command:rust-analyzer.openLogs)"); - statusBar.tooltip.appendMarkdown("\n\n[Restart server](command:rust-analyzer.startServer)"); - statusBar.tooltip.appendMarkdown("[Stop server](command:rust-analyzer.stopServer)"); + statusBar.tooltip.appendMarkdown( + "\n\n[Rebuild Proc Macros](command:rust-analyzer.rebuildProcMacros)" + ); + statusBar.tooltip.appendMarkdown( + "\n\n[Restart server](command:rust-analyzer.restartServer)" + ); + statusBar.tooltip.appendMarkdown("\n\n[Stop server](command:rust-analyzer.stopServer)"); if (!status.quiescent) icon = "$(sync~spin) "; statusBar.text = `${icon}rust-analyzer`; } @@ -400,4 +478,5 @@ export class Ctx { export interface Disposable { dispose(): void; } + export type Cmd = (...args: any[]) => unknown; diff --git a/src/tools/rust-analyzer/editors/code/src/debug.ts b/src/tools/rust-analyzer/editors/code/src/debug.ts index 268b70b4fbbc7..cffee1de6af7b 100644 --- a/src/tools/rust-analyzer/editors/code/src/debug.ts +++ b/src/tools/rust-analyzer/editors/code/src/debug.ts @@ -118,8 +118,8 @@ async function getDebugConfiguration( return path.normalize(p).replace(wsFolder, "${workspaceFolder" + workspaceQualifier + "}"); } - const executable = await getDebugExecutable(runnable); const env = prepareEnv(runnable, ctx.config.runnableEnv); + const executable = await getDebugExecutable(runnable, env); let sourceFileMap = debugOptions.sourceFileMap; if (sourceFileMap === "auto") { // let's try to use the default toolchain @@ -156,8 +156,11 @@ async function getDebugConfiguration( return debugConfig; } -async function getDebugExecutable(runnable: ra.Runnable): Promise { - const cargo = new Cargo(runnable.args.workspaceRoot || ".", debugOutput); +async function getDebugExecutable( + runnable: ra.Runnable, + env: Record +): Promise { + const cargo = new Cargo(runnable.args.workspaceRoot || ".", debugOutput, env); const executable = await cargo.executableFromArgs(runnable.args.cargoArgs); // if we are here, there were no compilation errors. diff --git a/src/tools/rust-analyzer/editors/code/src/dependencies_provider.ts b/src/tools/rust-analyzer/editors/code/src/dependencies_provider.ts new file mode 100644 index 0000000000000..8900aa9a5f35f --- /dev/null +++ b/src/tools/rust-analyzer/editors/code/src/dependencies_provider.ts @@ -0,0 +1,148 @@ +import * as vscode from "vscode"; +import * as fspath from "path"; +import * as fs from "fs"; +import { CtxInit } from "./ctx"; +import * as ra from "./lsp_ext"; +import { FetchDependencyListResult } from "./lsp_ext"; + +export class RustDependenciesProvider + implements vscode.TreeDataProvider +{ + dependenciesMap: { [id: string]: Dependency | DependencyFile }; + ctx: CtxInit; + + constructor(ctx: CtxInit) { + this.dependenciesMap = {}; + this.ctx = ctx; + } + + private _onDidChangeTreeData: vscode.EventEmitter< + Dependency | DependencyFile | undefined | null | void + > = new vscode.EventEmitter(); + + readonly onDidChangeTreeData: vscode.Event< + Dependency | DependencyFile | undefined | null | void + > = this._onDidChangeTreeData.event; + + getDependency(filePath: string): Dependency | DependencyFile | undefined { + return this.dependenciesMap[filePath.toLowerCase()]; + } + + contains(filePath: string): boolean { + return filePath.toLowerCase() in this.dependenciesMap; + } + + isInitialized(): boolean { + return Object.keys(this.dependenciesMap).length !== 0; + } + + refresh(): void { + this.dependenciesMap = {}; + this._onDidChangeTreeData.fire(); + } + + getParent?( + element: Dependency | DependencyFile + ): vscode.ProviderResult { + if (element instanceof Dependency) return undefined; + return element.parent; + } + + getTreeItem(element: Dependency | DependencyFile): vscode.TreeItem | Thenable { + if (element.id! in this.dependenciesMap) return this.dependenciesMap[element.id!]; + return element; + } + + getChildren( + element?: Dependency | DependencyFile + ): vscode.ProviderResult { + return new Promise((resolve, _reject) => { + if (!vscode.workspace.workspaceFolders) { + void vscode.window.showInformationMessage("No dependency in empty workspace"); + return Promise.resolve([]); + } + if (element) { + const files = fs.readdirSync(element.dependencyPath).map((fileName) => { + const filePath = fspath.join(element.dependencyPath, fileName); + const collapsibleState = fs.lstatSync(filePath).isDirectory() + ? vscode.TreeItemCollapsibleState.Collapsed + : vscode.TreeItemCollapsibleState.None; + const dep = new DependencyFile(fileName, filePath, element, collapsibleState); + this.dependenciesMap[dep.dependencyPath.toLowerCase()] = dep; + return dep; + }); + return resolve(files); + } else { + return resolve(this.getRootDependencies()); + } + }); + } + + private async getRootDependencies(): Promise { + const dependenciesResult: FetchDependencyListResult = await this.ctx.client.sendRequest( + ra.fetchDependencyList, + {} + ); + const crates = dependenciesResult.crates; + + return crates + .map((crate) => { + const dep = this.toDep(crate.name || "unknown", crate.version || "", crate.path); + this.dependenciesMap[dep.dependencyPath.toLowerCase()] = dep; + return dep; + }) + .sort((a, b) => { + return a.label.localeCompare(b.label); + }); + } + + private toDep(moduleName: string, version: string, path: string): Dependency { + return new Dependency( + moduleName, + version, + vscode.Uri.parse(path).fsPath, + vscode.TreeItemCollapsibleState.Collapsed + ); + } +} + +export class Dependency extends vscode.TreeItem { + constructor( + public readonly label: string, + private version: string, + readonly dependencyPath: string, + public readonly collapsibleState: vscode.TreeItemCollapsibleState + ) { + super(label, collapsibleState); + this.resourceUri = vscode.Uri.file(dependencyPath); + this.id = this.resourceUri.fsPath.toLowerCase(); + this.description = this.version; + if (this.version) { + this.tooltip = `${this.label}-${this.version}`; + } else { + this.tooltip = this.label; + } + } +} + +export class DependencyFile extends vscode.TreeItem { + constructor( + readonly label: string, + readonly dependencyPath: string, + readonly parent: Dependency | DependencyFile, + public readonly collapsibleState: vscode.TreeItemCollapsibleState + ) { + super(vscode.Uri.file(dependencyPath), collapsibleState); + this.id = this.resourceUri!.fsPath.toLowerCase(); + const isDir = fs.lstatSync(this.resourceUri!.fsPath).isDirectory(); + if (!isDir) { + this.command = { + command: "vscode.open", + title: "Open File", + arguments: [this.resourceUri], + }; + } + } +} + +export type DependencyId = { id: string }; diff --git a/src/tools/rust-analyzer/editors/code/src/lsp_ext.ts b/src/tools/rust-analyzer/editors/code/src/lsp_ext.ts index 872d7199b838a..b72804e510ce4 100644 --- a/src/tools/rust-analyzer/editors/code/src/lsp_ext.ts +++ b/src/tools/rust-analyzer/editors/code/src/lsp_ext.ts @@ -10,12 +10,9 @@ export const hover = new lc.RequestType< HoverParams, (lc.Hover & { actions: CommandLinkGroup[] }) | null, void ->("textDocument/hover"); -export type HoverParams = { position: lc.Position | lc.Range } & Omit< - lc.TextDocumentPositionParams, - "position" -> & - lc.WorkDoneProgressParams; +>(lc.HoverRequest.method); +export type HoverParams = { position: lc.Position | lc.Range } & Omit; + export type CommandLink = { /** * A tooltip for the command, when represented in the UI. @@ -43,6 +40,7 @@ export const relatedTests = new lc.RequestType("rust-analyzer/reloadWorkspace"); +export const rebuildProcMacros = new lc.RequestType0("rust-analyzer/rebuildProcMacros"); export const runFlycheck = new lc.NotificationType<{ textDocument: lc.TextDocumentIdentifier | null; @@ -63,12 +61,47 @@ export const viewHir = new lc.RequestType( "rust-analyzer/viewMir" ); +export const interpretFunction = new lc.RequestType( + "rust-analyzer/interpretFunction" +); export const viewItemTree = new lc.RequestType( "rust-analyzer/viewItemTree" ); export type AnalyzerStatusParams = { textDocument?: lc.TextDocumentIdentifier }; +export interface FetchDependencyListParams {} + +export interface FetchDependencyListResult { + crates: { + name: string | undefined; + version: string | undefined; + path: string; + }[]; +} + +export const fetchDependencyList = new lc.RequestType< + FetchDependencyListParams, + FetchDependencyListResult, + void +>("rust-analyzer/fetchDependencyList"); + +export interface FetchDependencyGraphParams {} + +export interface FetchDependencyGraphResult { + crates: { + name: string; + version: string; + path: string; + }[]; +} + +export const fetchDependencyGraph = new lc.RequestType< + FetchDependencyGraphParams, + FetchDependencyGraphResult, + void +>("rust-analyzer/fetchDependencyGraph"); + export type ExpandMacroParams = { textDocument: lc.TextDocumentIdentifier; position: lc.Position; diff --git a/src/tools/rust-analyzer/editors/code/src/main.ts b/src/tools/rust-analyzer/editors/code/src/main.ts index d5de00561b123..be9bc9d363ce6 100644 --- a/src/tools/rust-analyzer/editors/code/src/main.ts +++ b/src/tools/rust-analyzer/editors/code/src/main.ts @@ -120,13 +120,11 @@ function createCommands(): Record { enabled: commands.onEnter, disabled: (_) => () => vscode.commands.executeCommand("default:type", { text: "\n" }), }, - reload: { + restartServer: { enabled: (ctx) => async () => { - void vscode.window.showInformationMessage("Reloading rust-analyzer..."); await ctx.restart(); }, disabled: (ctx) => async () => { - void vscode.window.showInformationMessage("Reloading rust-analyzer..."); await ctx.start(); }, }, @@ -153,6 +151,7 @@ function createCommands(): Record { memoryUsage: { enabled: commands.memoryUsage }, shuffleCrateGraph: { enabled: commands.shuffleCrateGraph }, reloadWorkspace: { enabled: commands.reloadWorkspace }, + rebuildProcMacros: { enabled: commands.rebuildProcMacros }, addProject: { enabled: commands.addProject }, matchingBrace: { enabled: commands.matchingBrace }, joinLines: { enabled: commands.joinLines }, @@ -160,6 +159,7 @@ function createCommands(): Record { syntaxTree: { enabled: commands.syntaxTree }, viewHir: { enabled: commands.viewHir }, viewMir: { enabled: commands.viewMir }, + interpretFunction: { enabled: commands.interpretFunction }, viewFileText: { enabled: commands.viewFileText }, viewItemTree: { enabled: commands.viewItemTree }, viewCrateGraph: { enabled: commands.viewCrateGraph }, @@ -190,5 +190,6 @@ function createCommands(): Record { showReferences: { enabled: commands.showReferences }, triggerParameterHints: { enabled: commands.triggerParameterHints }, openLogs: { enabled: commands.openLogs }, + revealDependency: { enabled: commands.revealDependency }, }; } diff --git a/src/tools/rust-analyzer/editors/code/src/run.ts b/src/tools/rust-analyzer/editors/code/src/run.ts index 35627e2fc6beb..623fb102953bc 100644 --- a/src/tools/rust-analyzer/editors/code/src/run.ts +++ b/src/tools/rust-analyzer/editors/code/src/run.ts @@ -157,7 +157,7 @@ export async function createTask(runnable: ra.Runnable, config: Config): Promise cargoTask.presentationOptions.clear = true; // Sadly, this doesn't prevent focus stealing if the terminal is currently - // hidden, and will become revealed due to task exucution. + // hidden, and will become revealed due to task execution. cargoTask.presentationOptions.focus = false; return cargoTask; diff --git a/src/tools/rust-analyzer/editors/code/src/tasks.ts b/src/tools/rust-analyzer/editors/code/src/tasks.ts index e6239deeb21af..d6509d9aa6e71 100644 --- a/src/tools/rust-analyzer/editors/code/src/tasks.ts +++ b/src/tools/rust-analyzer/editors/code/src/tasks.ts @@ -128,7 +128,7 @@ export async function buildCargoTask( name, TASK_SOURCE, exec, - ["$rustc"] + ["$rustc", "$rust-panic"] ); } diff --git a/src/tools/rust-analyzer/editors/code/src/toolchain.ts b/src/tools/rust-analyzer/editors/code/src/toolchain.ts index eb70b88871e3d..917a1d6b0997b 100644 --- a/src/tools/rust-analyzer/editors/code/src/toolchain.ts +++ b/src/tools/rust-analyzer/editors/code/src/toolchain.ts @@ -18,7 +18,11 @@ export interface ArtifactSpec { } export class Cargo { - constructor(readonly rootFolder: string, readonly output: vscode.OutputChannel) {} + constructor( + readonly rootFolder: string, + readonly output: vscode.OutputChannel, + readonly env: Record + ) {} // Made public for testing purposes static artifactSpec(args: readonly string[]): ArtifactSpec { @@ -102,6 +106,7 @@ export class Cargo { const cargo = cp.spawn(path, cargoArgs, { stdio: ["ignore", "pipe", "pipe"], cwd: this.rootFolder, + env: this.env, }); cargo.on("error", (err) => reject(new Error(`could not launch cargo: ${err}`))); diff --git a/src/tools/rust-analyzer/editors/code/src/util.ts b/src/tools/rust-analyzer/editors/code/src/util.ts index 922fbcbcf35a5..b6b779e266013 100644 --- a/src/tools/rust-analyzer/editors/code/src/util.ts +++ b/src/tools/rust-analyzer/editors/code/src/util.ts @@ -112,6 +112,19 @@ export function isRustEditor(editor: vscode.TextEditor): editor is RustEditor { return isRustDocument(editor.document); } +export function isDocumentInWorkspace(document: RustDocument): boolean { + const workspaceFolders = vscode.workspace.workspaceFolders; + if (!workspaceFolders) { + return false; + } + for (const folder of workspaceFolders) { + if (document.uri.fsPath.startsWith(folder.uri.fsPath)) { + return true; + } + } + return false; +} + export function isValidExecutable(path: string): boolean { log.debug("Checking availability of a binary at", path); diff --git a/src/tools/rust-analyzer/lib/README.md b/src/tools/rust-analyzer/lib/README.md index 6b2eeac2c0d76..ed55e31d6bb7b 100644 --- a/src/tools/rust-analyzer/lib/README.md +++ b/src/tools/rust-analyzer/lib/README.md @@ -1,2 +1,5 @@ -Crates in this directory are published to crates.io and obey semver. -They *could* live in a separate repo, but we want to experiment with a monorepo setup. +# lib + +Crates in this directory are published to [crates.io](https://crates.io) and obey semver. + +They _could_ live in a separate repo, but we want to experiment with a monorepo setup. diff --git a/src/tools/rust-analyzer/lib/la-arena/src/lib.rs b/src/tools/rust-analyzer/lib/la-arena/src/lib.rs index ccaaf3991769e..5107f294394bc 100644 --- a/src/tools/rust-analyzer/lib/la-arena/src/lib.rs +++ b/src/tools/rust-analyzer/lib/la-arena/src/lib.rs @@ -4,8 +4,9 @@ #![warn(missing_docs)] use std::{ - fmt, + cmp, fmt, hash::{Hash, Hasher}, + iter::{Enumerate, FusedIterator}, marker::PhantomData, ops::{Index, IndexMut, Range, RangeInclusive}, }; @@ -17,13 +18,27 @@ pub use map::{ArenaMap, Entry, OccupiedEntry, VacantEntry}; #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct RawIdx(u32); +impl RawIdx { + /// Constructs a [`RawIdx`] from a u32. + pub const fn from_u32(u32: u32) -> Self { + RawIdx(u32) + } + + /// Deconstructs a [`RawIdx`] into the underlying u32. + pub const fn into_u32(self) -> u32 { + self.0 + } +} + impl From for u32 { + #[inline] fn from(raw: RawIdx) -> u32 { raw.0 } } impl From for RawIdx { + #[inline] fn from(idx: u32) -> RawIdx { RawIdx(idx) } @@ -47,6 +62,18 @@ pub struct Idx { _ty: PhantomData T>, } +impl Ord for Idx { + fn cmp(&self, other: &Self) -> cmp::Ordering { + self.raw.cmp(&other.raw) + } +} + +impl PartialOrd for Idx { + fn partial_cmp(&self, other: &Self) -> Option { + self.raw.partial_cmp(&other.raw) + } +} + impl Clone for Idx { fn clone(&self) -> Self { *self @@ -79,12 +106,12 @@ impl fmt::Debug for Idx { impl Idx { /// Creates a new index from a [`RawIdx`]. - pub fn from_raw(raw: RawIdx) -> Self { + pub const fn from_raw(raw: RawIdx) -> Self { Idx { raw, _ty: PhantomData } } /// Converts this index into the underlying [`RawIdx`]. - pub fn into_raw(self) -> RawIdx { + pub const fn into_raw(self) -> RawIdx { self.raw } } @@ -147,13 +174,46 @@ impl IdxRange { pub fn is_empty(&self) -> bool { self.range.is_empty() } + + /// Returns the start of the index range. + pub fn start(&self) -> Idx { + Idx::from_raw(RawIdx::from(self.range.start)) + } + + /// Returns the end of the index range. + pub fn end(&self) -> Idx { + Idx::from_raw(RawIdx::from(self.range.end)) + } } impl Iterator for IdxRange { type Item = Idx; + fn next(&mut self) -> Option { self.range.next().map(|raw| Idx::from_raw(raw.into())) } + + fn size_hint(&self) -> (usize, Option) { + self.range.size_hint() + } + + fn count(self) -> usize + where + Self: Sized, + { + self.range.count() + } + + fn last(self) -> Option + where + Self: Sized, + { + self.range.last().map(|raw| Idx::from_raw(raw.into())) + } + + fn nth(&mut self, n: usize) -> Option { + self.range.nth(n).map(|raw| Idx::from_raw(raw.into())) + } } impl DoubleEndedIterator for IdxRange { @@ -162,6 +222,10 @@ impl DoubleEndedIterator for IdxRange { } } +impl ExactSizeIterator for IdxRange {} + +impl FusedIterator for IdxRange {} + impl fmt::Debug for IdxRange { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple(&format!("IdxRange::<{}>", std::any::type_name::())) @@ -280,6 +344,21 @@ impl Arena { idx } + /// Densely allocates multiple values, returning the values’ index range. + /// + /// ``` + /// let mut arena = la_arena::Arena::new(); + /// let range = arena.alloc_many(0..4); + /// + /// assert_eq!(arena[range], [0, 1, 2, 3]); + /// ``` + pub fn alloc_many>(&mut self, iter: II) -> IdxRange { + let start = self.next_idx(); + self.extend(iter); + let end = self.next_idx(); + IdxRange::new(start..end) + } + /// Returns an iterator over the arena’s elements. /// /// ``` @@ -295,7 +374,7 @@ impl Arena { /// ``` pub fn iter( &self, - ) -> impl Iterator, &T)> + ExactSizeIterator + DoubleEndedIterator { + ) -> impl Iterator, &T)> + ExactSizeIterator + DoubleEndedIterator + Clone { self.data.iter().enumerate().map(|(idx, value)| (Idx::from_raw(RawIdx(idx as u32)), value)) } @@ -335,7 +414,7 @@ impl Arena { /// assert_eq!(iterator.next(), Some(&40)); /// assert_eq!(iterator.next(), Some(&60)); /// ``` - pub fn values(&mut self) -> impl Iterator + ExactSizeIterator + DoubleEndedIterator { + pub fn values(&self) -> impl Iterator + ExactSizeIterator + DoubleEndedIterator { self.data.iter() } @@ -410,3 +489,32 @@ impl FromIterator for Arena { Arena { data: Vec::from_iter(iter) } } } + +/// An iterator over the arena’s elements. +pub struct IntoIter(Enumerate< as IntoIterator>::IntoIter>); + +impl Iterator for IntoIter { + type Item = (Idx, T); + + fn next(&mut self) -> Option { + self.0.next().map(|(idx, value)| (Idx::from_raw(RawIdx(idx as u32)), value)) + } +} + +impl IntoIterator for Arena { + type Item = (Idx, T); + + type IntoIter = IntoIter; + + fn into_iter(self) -> Self::IntoIter { + IntoIter(self.data.into_iter().enumerate()) + } +} + +impl Extend for Arena { + fn extend>(&mut self, iter: II) { + for t in iter { + self.alloc(t); + } + } +} diff --git a/src/tools/rust-analyzer/lib/la-arena/src/map.rs b/src/tools/rust-analyzer/lib/la-arena/src/map.rs index 7fff2b09c97b5..750f345b5398d 100644 --- a/src/tools/rust-analyzer/lib/la-arena/src/map.rs +++ b/src/tools/rust-analyzer/lib/la-arena/src/map.rs @@ -1,3 +1,4 @@ +use std::iter::Enumerate; use std::marker::PhantomData; use crate::Idx; @@ -72,17 +73,17 @@ impl ArenaMap, V> { } /// Returns an iterator over the values in the map. - pub fn values(&self) -> impl Iterator { + pub fn values(&self) -> impl Iterator + DoubleEndedIterator { self.v.iter().filter_map(|o| o.as_ref()) } /// Returns an iterator over mutable references to the values in the map. - pub fn values_mut(&mut self) -> impl Iterator { + pub fn values_mut(&mut self) -> impl Iterator + DoubleEndedIterator { self.v.iter_mut().filter_map(|o| o.as_mut()) } /// Returns an iterator over the arena indexes and values in the map. - pub fn iter(&self) -> impl Iterator, &V)> { + pub fn iter(&self) -> impl Iterator, &V)> + DoubleEndedIterator { self.v.iter().enumerate().filter_map(|(idx, o)| Some((Self::from_idx(idx), o.as_ref()?))) } @@ -94,12 +95,6 @@ impl ArenaMap, V> { .filter_map(|(idx, o)| Some((Self::from_idx(idx), o.as_mut()?))) } - /// Returns an iterator over the arena indexes and values in the map. - // FIXME: Implement `IntoIterator` trait. - pub fn into_iter(self) -> impl Iterator, V)> { - self.v.into_iter().enumerate().filter_map(|(idx, o)| Some((Self::from_idx(idx), o?))) - } - /// 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); @@ -154,6 +149,63 @@ impl FromIterator<(Idx, T)> for ArenaMap, T> { } } +pub struct ArenaMapIter { + iter: Enumerate>>, + _ty: PhantomData, +} + +impl IntoIterator for ArenaMap, V> { + type Item = (Idx, V); + + type IntoIter = ArenaMapIter, V>; + + fn into_iter(self) -> Self::IntoIter { + let iter = self.v.into_iter().enumerate(); + Self::IntoIter { iter, _ty: PhantomData } + } +} + +impl ArenaMapIter, V> { + fn mapper((idx, o): (usize, Option)) -> Option<(Idx, V)> { + Some((ArenaMap::, V>::from_idx(idx), o?)) + } +} + +impl Iterator for ArenaMapIter, V> { + type Item = (Idx, V); + + #[inline] + fn next(&mut self) -> Option { + for next in self.iter.by_ref() { + match Self::mapper(next) { + Some(r) => return Some(r), + None => continue, + } + } + + None + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +impl DoubleEndedIterator for ArenaMapIter, V> { + #[inline] + fn next_back(&mut self) -> Option { + while let Some(next_back) = self.iter.next_back() { + match Self::mapper(next_back) { + Some(r) => return Some(r), + None => continue, + } + } + + None + } +} + /// 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`]. diff --git a/src/tools/rust-analyzer/lib/line-index/Cargo.toml b/src/tools/rust-analyzer/lib/line-index/Cargo.toml new file mode 100644 index 0000000000000..019ad3a53ba0e --- /dev/null +++ b/src/tools/rust-analyzer/lib/line-index/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "line-index" +version = "0.1.0-pre.1" +description = "Maps flat `TextSize` offsets to/from `(line, column)` representation." +license = "MIT OR Apache-2.0" +repository = "https://github.com/rust-lang/rust-analyzer/tree/master/lib/line-index" +edition = "2021" + +[dependencies] +text-size.workspace = true +nohash-hasher.workspace = true diff --git a/src/tools/rust-analyzer/lib/line-index/src/lib.rs b/src/tools/rust-analyzer/lib/line-index/src/lib.rs new file mode 100644 index 0000000000000..ad67d3f246eca --- /dev/null +++ b/src/tools/rust-analyzer/lib/line-index/src/lib.rs @@ -0,0 +1,237 @@ +//! See [`LineIndex`]. + +#![deny(missing_debug_implementations, missing_docs, rust_2018_idioms)] + +#[cfg(test)] +mod tests; + +use nohash_hasher::IntMap; + +pub use text_size::{TextRange, TextSize}; + +/// `(line, column)` information in the native, UTF-8 encoding. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct LineCol { + /// Zero-based. + pub line: u32, + /// Zero-based UTF-8 offset. + pub col: u32, +} + +/// A kind of wide character encoding. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[non_exhaustive] +pub enum WideEncoding { + /// UTF-16. + Utf16, + /// UTF-32. + Utf32, +} + +impl WideEncoding { + /// Returns the number of code units it takes to encode `text` in this encoding. + pub fn measure(&self, text: &str) -> usize { + match self { + WideEncoding::Utf16 => text.encode_utf16().count(), + WideEncoding::Utf32 => text.chars().count(), + } + } +} + +/// `(line, column)` information in wide encodings. +/// +/// See [`WideEncoding`] for the kinds of wide encodings available. +// +// Deliberately not a generic type and different from `LineCol`. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct WideLineCol { + /// Zero-based. + pub line: u32, + /// Zero-based. + pub col: u32, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +struct WideChar { + /// Start offset of a character inside a line, zero-based. + start: TextSize, + /// End offset of a character inside a line, zero-based. + end: TextSize, +} + +impl WideChar { + /// Returns the length in 8-bit UTF-8 code units. + fn len(&self) -> TextSize { + self.end - self.start + } + + /// Returns the length in UTF-16 or UTF-32 code units. + fn wide_len(&self, enc: WideEncoding) -> u32 { + match enc { + WideEncoding::Utf16 => { + if self.len() == TextSize::from(4) { + 2 + } else { + 1 + } + } + WideEncoding::Utf32 => 1, + } + } +} + +/// Maps flat [`TextSize`] offsets to/from `(line, column)` representation. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct LineIndex { + /// Offset the beginning of each line (except the first, which always has offset 0). + newlines: Box<[TextSize]>, + /// List of non-ASCII characters on each line. + line_wide_chars: IntMap>, + /// The length of the entire text. + len: TextSize, +} + +impl LineIndex { + /// Returns a `LineIndex` for the `text`. + pub fn new(text: &str) -> LineIndex { + let mut newlines = Vec::::with_capacity(16); + let mut line_wide_chars = IntMap::>::default(); + + let mut wide_chars = Vec::::new(); + let mut cur_row = TextSize::from(0); + let mut cur_col = TextSize::from(0); + let mut line = 0u32; + + for c in text.chars() { + let c_len = TextSize::of(c); + cur_row += c_len; + if c == '\n' { + newlines.push(cur_row); + + // Save any wide characters seen in the previous line + if !wide_chars.is_empty() { + let cs = std::mem::take(&mut wide_chars).into_boxed_slice(); + line_wide_chars.insert(line, cs); + } + + // Prepare for processing the next line + cur_col = TextSize::from(0); + line += 1; + continue; + } + + if !c.is_ascii() { + wide_chars.push(WideChar { start: cur_col, end: cur_col + c_len }); + } + + cur_col += c_len; + } + + // Save any wide characters seen in the last line + if !wide_chars.is_empty() { + line_wide_chars.insert(line, wide_chars.into_boxed_slice()); + } + + LineIndex { + newlines: newlines.into_boxed_slice(), + line_wide_chars, + len: TextSize::of(text), + } + } + + /// Transforms the `TextSize` into a `LineCol`. + /// + /// # Panics + /// + /// If the offset is invalid. See [`Self::try_line_col`]. + pub fn line_col(&self, offset: TextSize) -> LineCol { + self.try_line_col(offset).expect("invalid offset") + } + + /// Transforms the `TextSize` into a `LineCol`. + /// + /// Returns `None` if the `offset` was invalid, e.g. if it extends past the end of the text or + /// points to the middle of a multi-byte character. + pub fn try_line_col(&self, offset: TextSize) -> Option { + if offset > self.len { + return None; + } + let line = self.newlines.partition_point(|&it| it <= offset); + let start = self.start_offset(line)?; + let col = offset - start; + let ret = LineCol { line: line as u32, col: col.into() }; + self.line_wide_chars + .get(&ret.line) + .into_iter() + .flat_map(|it| it.iter()) + .all(|it| col <= it.start || it.end <= col) + .then_some(ret) + } + + /// Transforms the `LineCol` into a `TextSize`. + pub fn offset(&self, line_col: LineCol) -> Option { + self.start_offset(line_col.line as usize).map(|start| start + TextSize::from(line_col.col)) + } + + fn start_offset(&self, line: usize) -> Option { + match line.checked_sub(1) { + None => Some(TextSize::from(0)), + Some(it) => self.newlines.get(it).copied(), + } + } + + /// Transforms the `LineCol` with the given `WideEncoding` into a `WideLineCol`. + pub fn to_wide(&self, enc: WideEncoding, line_col: LineCol) -> Option { + let mut col = line_col.col; + if let Some(wide_chars) = self.line_wide_chars.get(&line_col.line) { + for c in wide_chars.iter() { + if u32::from(c.end) <= line_col.col { + col = col.checked_sub(u32::from(c.len()) - c.wide_len(enc))?; + } else { + // From here on, all utf16 characters come *after* the character we are mapping, + // so we don't need to take them into account + break; + } + } + } + Some(WideLineCol { line: line_col.line, col }) + } + + /// Transforms the `WideLineCol` with the given `WideEncoding` into a `LineCol`. + pub fn to_utf8(&self, enc: WideEncoding, line_col: WideLineCol) -> Option { + let mut col = line_col.col; + if let Some(wide_chars) = self.line_wide_chars.get(&line_col.line) { + for c in wide_chars.iter() { + if col > u32::from(c.start) { + col = col.checked_add(u32::from(c.len()) - c.wide_len(enc))?; + } else { + // From here on, all utf16 characters come *after* the character we are mapping, + // so we don't need to take them into account + break; + } + } + } + Some(LineCol { line: line_col.line, col }) + } + + /// Given a range [start, end), returns a sorted iterator of non-empty ranges [start, x1), [x1, + /// x2), ..., [xn, end) where all the xi, which are positions of newlines, are inside the range + /// [start, end). + pub fn lines(&self, range: TextRange) -> impl Iterator + '_ { + let lo = self.newlines.partition_point(|&it| it < range.start()); + let hi = self.newlines.partition_point(|&it| it <= range.end()); + let all = std::iter::once(range.start()) + .chain(self.newlines[lo..hi].iter().copied()) + .chain(std::iter::once(range.end())); + + all.clone() + .zip(all.skip(1)) + .map(|(lo, hi)| TextRange::new(lo, hi)) + .filter(|it| !it.is_empty()) + } + + /// Returns the length of the original text. + pub fn len(&self) -> TextSize { + self.len + } +} diff --git a/src/tools/rust-analyzer/lib/line-index/src/tests.rs b/src/tools/rust-analyzer/lib/line-index/src/tests.rs new file mode 100644 index 0000000000000..31c01c20ee36f --- /dev/null +++ b/src/tools/rust-analyzer/lib/line-index/src/tests.rs @@ -0,0 +1,11 @@ +use super::LineIndex; + +#[test] +fn test_empty_index() { + let col_index = LineIndex::new( + " +const C: char = 'x'; +", + ); + assert_eq!(col_index.line_wide_chars.len(), 0); +} diff --git a/src/tools/rust-analyzer/lib/line-index/tests/it.rs b/src/tools/rust-analyzer/lib/line-index/tests/it.rs new file mode 100644 index 0000000000000..ce1c0bc6f143d --- /dev/null +++ b/src/tools/rust-analyzer/lib/line-index/tests/it.rs @@ -0,0 +1,62 @@ +use line_index::{LineCol, LineIndex, TextRange}; + +#[test] +fn test_line_index() { + let text = "hello\nworld"; + let table = [ + (00, 0, 0), + (01, 0, 1), + (05, 0, 5), + (06, 1, 0), + (07, 1, 1), + (08, 1, 2), + (10, 1, 4), + (11, 1, 5), + ]; + + let index = LineIndex::new(text); + for (offset, line, col) in table { + assert_eq!(index.line_col(offset.into()), LineCol { line, col }); + } + + let text = "\nhello\nworld"; + let table = [(0, 0, 0), (1, 1, 0), (2, 1, 1), (6, 1, 5), (7, 2, 0)]; + let index = LineIndex::new(text); + for (offset, line, col) in table { + assert_eq!(index.line_col(offset.into()), LineCol { line, col }); + } +} + +#[test] +fn test_char_len() { + assert_eq!('メ'.len_utf8(), 3); + assert_eq!('メ'.len_utf16(), 1); +} + +#[test] +fn test_splitlines() { + fn r(lo: u32, hi: u32) -> TextRange { + TextRange::new(lo.into(), hi.into()) + } + + let text = "a\nbb\nccc\n"; + let line_index = LineIndex::new(text); + + let actual = line_index.lines(r(0, 9)).collect::>(); + let expected = vec![r(0, 2), r(2, 5), r(5, 9)]; + assert_eq!(actual, expected); + + let text = ""; + let line_index = LineIndex::new(text); + + let actual = line_index.lines(r(0, 0)).collect::>(); + let expected = vec![]; + assert_eq!(actual, expected); + + let text = "\n"; + let line_index = LineIndex::new(text); + + let actual = line_index.lines(r(0, 1)).collect::>(); + let expected = vec![r(0, 1)]; + assert_eq!(actual, expected) +} diff --git a/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml b/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml index 6e32e3960526f..e78a9d2eb1674 100644 --- a/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml +++ b/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml @@ -8,8 +8,8 @@ edition = "2021" [dependencies] log = "0.4.17" -serde_json = "1.0.86" -serde = { version = "1.0.144", features = ["derive"] } +serde_json.workspace = true +serde.workspace = true crossbeam-channel = "0.5.6" [dev-dependencies] diff --git a/src/tools/rust-analyzer/lib/lsp-server/src/error.rs b/src/tools/rust-analyzer/lib/lsp-server/src/error.rs index 4c934d9ecca5d..755b3fd959624 100644 --- a/src/tools/rust-analyzer/lib/lsp-server/src/error.rs +++ b/src/tools/rust-analyzer/lib/lsp-server/src/error.rs @@ -2,7 +2,7 @@ use std::fmt; use crate::{Notification, Request}; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct ProtocolError(pub(crate) String); impl std::error::Error for ProtocolError {} diff --git a/src/tools/rust-analyzer/lib/lsp-server/src/lib.rs b/src/tools/rust-analyzer/lib/lsp-server/src/lib.rs index beccde40a8978..affab60a22783 100644 --- a/src/tools/rust-analyzer/lib/lsp-server/src/lib.rs +++ b/src/tools/rust-analyzer/lib/lsp-server/src/lib.rs @@ -126,6 +126,9 @@ impl Connection { self.sender.send(resp.into()).unwrap(); continue; } + Ok(Message::Notification(n)) if !n.is_exit() => { + continue; + } Ok(msg) => Err(ProtocolError(format!("expected initialize request, got {msg:?}"))), Err(e) => { Err(ProtocolError(format!("expected initialize request, got error: {e}"))) @@ -212,3 +215,70 @@ impl Connection { Ok(true) } } + +#[cfg(test)] +mod tests { + use crossbeam_channel::unbounded; + use lsp_types::notification::{Exit, Initialized, Notification}; + use lsp_types::request::{Initialize, Request}; + use lsp_types::{InitializeParams, InitializedParams}; + use serde_json::to_value; + + use crate::{Connection, Message, ProtocolError, RequestId}; + + struct TestCase { + test_messages: Vec, + expected_resp: Result<(RequestId, serde_json::Value), ProtocolError>, + } + + fn initialize_start_test(test_case: TestCase) { + let (reader_sender, reader_receiver) = unbounded::(); + let (writer_sender, writer_receiver) = unbounded::(); + let conn = Connection { sender: writer_sender, receiver: reader_receiver }; + + for msg in test_case.test_messages { + assert!(reader_sender.send(msg).is_ok()); + } + + let resp = conn.initialize_start(); + assert_eq!(test_case.expected_resp, resp); + + assert!(writer_receiver.recv_timeout(std::time::Duration::from_secs(1)).is_err()); + } + + #[test] + fn not_exit_notification() { + let notification = crate::Notification { + method: Initialized::METHOD.to_string(), + params: to_value(InitializedParams {}).unwrap(), + }; + + let params_as_value = to_value(InitializeParams::default()).unwrap(); + let req_id = RequestId::from(234); + let request = crate::Request { + id: req_id.clone(), + method: Initialize::METHOD.to_string(), + params: params_as_value.clone(), + }; + + initialize_start_test(TestCase { + test_messages: vec![notification.into(), request.into()], + expected_resp: Ok((req_id, params_as_value)), + }); + } + + #[test] + fn exit_notification() { + let notification = + crate::Notification { method: Exit::METHOD.to_string(), params: to_value(()).unwrap() }; + let notification_msg = Message::from(notification); + + initialize_start_test(TestCase { + test_messages: vec![notification_msg.clone()], + expected_resp: Err(ProtocolError(format!( + "expected initialize request, got {:?}", + notification_msg + ))), + }); + } +} diff --git a/src/tools/rust-installer/Cargo.toml b/src/tools/rust-installer/Cargo.toml index 97734f048ab33..85e979f07bff9 100644 --- a/src/tools/rust-installer/Cargo.toml +++ b/src/tools/rust-installer/Cargo.toml @@ -17,7 +17,6 @@ tar = "0.4.38" walkdir = "2" xz2 = "0.1.4" num_cpus = "1" -remove_dir_all = "0.5" [dependencies.clap] features = ["derive"] diff --git a/src/tools/rust-installer/src/util.rs b/src/tools/rust-installer/src/util.rs index 674617c657c59..6cac314b68d06 100644 --- a/src/tools/rust-installer/src/util.rs +++ b/src/tools/rust-installer/src/util.rs @@ -82,7 +82,7 @@ pub fn open_file>(path: P) -> Result { /// Wraps `remove_dir_all` with a nicer error message. pub fn remove_dir_all>(path: P) -> Result<()> { - remove_dir_all::remove_dir_all(path.as_ref()) + fs::remove_dir_all(path.as_ref()) .with_context(|| format!("failed to remove dir '{}'", path.as_ref().display()))?; Ok(()) } diff --git a/src/tools/rustdoc-js/tester.js b/src/tools/rustdoc-js/tester.js index 270704ebffde6..416517d15f5db 100644 --- a/src/tools/rustdoc-js/tester.js +++ b/src/tools/rustdoc-js/tester.js @@ -22,6 +22,10 @@ function contentToDiffLine(key, value) { return `"${key}": "${value}",`; } +function shouldIgnoreField(fieldName) { + return fieldName === "query" || fieldName === "correction"; +} + // This function is only called when no matching result was found and therefore will only display // the diff between the two items. function betterLookingDiff(entry, data) { @@ -135,6 +139,9 @@ function valueCheck(fullPath, expected, result, error_text, queryName) { } else if (expected !== null && typeof expected !== "undefined" && expected.constructor == Object) { // eslint-disable-line eqeqeq for (const key in expected) { + if (shouldIgnoreField(key)) { + continue; + } if (!Object.prototype.hasOwnProperty.call(expected, key)) { continue; } @@ -184,6 +191,9 @@ function runSearch(query, expected, doSearch, loadedFile, queryName) { const error_text = []; for (const key in expected) { + if (shouldIgnoreField(key)) { + continue; + } if (!Object.prototype.hasOwnProperty.call(expected, key)) { continue; } @@ -260,41 +270,49 @@ function checkResult(error_text, loadedFile, displaySuccess) { return 1; } -function runCheck(loadedFile, key, callback) { - const expected = loadedFile[key]; - const query = loadedFile.QUERY; - - if (Array.isArray(query)) { - if (!Array.isArray(expected)) { - console.log("FAILED"); - console.log(`==> If QUERY variable is an array, ${key} should be an array too`); - return 1; - } else if (query.length !== expected.length) { - console.log("FAILED"); - console.log(`==> QUERY variable should have the same length as ${key}`); - return 1; +function runCheckInner(callback, loadedFile, entry, getCorrections, extra) { + if (typeof entry.query !== "string") { + console.log("FAILED"); + console.log("==> Missing `query` field"); + return false; + } + let error_text = callback(entry.query, entry, extra ? "[ query `" + entry.query + "`]" : ""); + if (checkResult(error_text, loadedFile, false) !== 0) { + return false; + } + if (entry.correction !== undefined) { + error_text = runCorrections(entry.query, entry.correction, getCorrections, loadedFile); + if (checkResult(error_text, loadedFile, false) !== 0) { + return false; } - for (let i = 0; i < query.length; ++i) { - const error_text = callback(query[i], expected[i], "[ query `" + query[i] + "`]"); - if (checkResult(error_text, loadedFile, false) !== 0) { + } + return true; +} + +function runCheck(loadedFile, key, getCorrections, callback) { + const expected = loadedFile[key]; + + if (Array.isArray(expected)) { + for (const entry of expected) { + if (!runCheckInner(callback, loadedFile, entry, getCorrections, true)) { return 1; } } - console.log("OK"); - } else { - const error_text = callback(query, expected, ""); - if (checkResult(error_text, loadedFile, true) !== 0) { - return 1; - } + } else if (!runCheckInner(callback, loadedFile, expected, getCorrections, false)) { + return 1; } + console.log("OK"); return 0; } +function hasCheck(content, checkName) { + return content.startsWith(`const ${checkName}`) || content.includes(`\nconst ${checkName}`); +} + function runChecks(testFile, doSearch, parseQuery, getCorrections) { let checkExpected = false; let checkParsed = false; - let checkCorrections = false; - let testFileContent = readFile(testFile) + "exports.QUERY = QUERY;"; + let testFileContent = readFile(testFile); if (testFileContent.indexOf("FILTER_CRATE") !== -1) { testFileContent += "exports.FILTER_CRATE = FILTER_CRATE;"; @@ -302,21 +320,17 @@ function runChecks(testFile, doSearch, parseQuery, getCorrections) { testFileContent += "exports.FILTER_CRATE = null;"; } - if (testFileContent.indexOf("\nconst EXPECTED") !== -1) { + if (hasCheck(testFileContent, "EXPECTED")) { testFileContent += "exports.EXPECTED = EXPECTED;"; checkExpected = true; } - if (testFileContent.indexOf("\nconst PARSED") !== -1) { + if (hasCheck(testFileContent, "PARSED")) { testFileContent += "exports.PARSED = PARSED;"; checkParsed = true; } - if (testFileContent.indexOf("\nconst CORRECTIONS") !== -1) { - testFileContent += "exports.CORRECTIONS = CORRECTIONS;"; - checkCorrections = true; - } - if (!checkParsed && !checkExpected && !checkCorrections) { + if (!checkParsed && !checkExpected) { console.log("FAILED"); - console.log("==> At least `PARSED`, `EXPECTED`, or `CORRECTIONS` is needed!"); + console.log("==> At least `PARSED` or `EXPECTED` is needed!"); return 1; } @@ -324,20 +338,15 @@ function runChecks(testFile, doSearch, parseQuery, getCorrections) { let res = 0; if (checkExpected) { - res += runCheck(loadedFile, "EXPECTED", (query, expected, text) => { + res += runCheck(loadedFile, "EXPECTED", getCorrections, (query, expected, text) => { return runSearch(query, expected, doSearch, loadedFile, text); }); } if (checkParsed) { - res += runCheck(loadedFile, "PARSED", (query, expected, text) => { + res += runCheck(loadedFile, "PARSED", getCorrections, (query, expected, text) => { return runParser(query, expected, parseQuery, text); }); } - if (checkCorrections) { - res += runCheck(loadedFile, "CORRECTIONS", (query, expected) => { - return runCorrections(query, expected, getCorrections, loadedFile); - }); - } return res; } @@ -367,8 +376,7 @@ function loadSearchJS(doc_folder, resource_suffix) { }, getCorrections: function(queryStr, filterCrate, currentCrate) { const parsedQuery = searchModule.parseQuery(queryStr); - searchModule.execQuery(parsedQuery, searchWords, - filterCrate, currentCrate); + searchModule.execQuery(parsedQuery, searchWords, filterCrate, currentCrate); return parsedQuery.correction; }, parseQuery: searchModule.parseQuery, diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index db2b7910b711c..9979dfbd56a12 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -132,7 +132,6 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "crossbeam-epoch", "crossbeam-utils", "crypto-common", - "cstr", "datafrog", "derive_more", "digest", @@ -168,12 +167,14 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "instant", "intl-memoizer", "intl_pluralrules", + "io-lifetimes", "itertools", "itoa", "jobserver", "lazy_static", "libc", "libloading", + "linux-raw-sys", "litemap", "lock_api", "log", @@ -211,12 +212,12 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "regex", "regex-automata", "regex-syntax", - "remove_dir_all", "rustc-demangle", "rustc-hash", "rustc-rayon", "rustc-rayon-core", "rustc_version", + "rustix", "ruzstd", // via object in thorin-dwp "ryu", "scoped-tls", @@ -280,6 +281,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "winapi-util", "winapi-x86_64-pc-windows-gnu", "windows", + "windows-sys", "windows-targets", "windows_aarch64_gnullvm", "windows_aarch64_msvc", diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs index 01ef4abd3dca3..5c6a94877884d 100644 --- a/src/tools/tidy/src/ui_tests.rs +++ b/src/tools/tidy/src/ui_tests.rs @@ -11,7 +11,7 @@ use std::path::{Path, PathBuf}; const ENTRY_LIMIT: usize = 900; // FIXME: The following limits should be reduced eventually. const ISSUES_ENTRY_LIMIT: usize = 1898; -const ROOT_ENTRY_LIMIT: usize = 891; +const ROOT_ENTRY_LIMIT: usize = 870; const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[ "rs", // test source files diff --git a/src/tools/x/Cargo.lock b/src/tools/x/Cargo.lock index 723d6cb25ed6a..09e5c7507490d 100644 --- a/src/tools/x/Cargo.lock +++ b/src/tools/x/Cargo.lock @@ -1,5 +1,7 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "x" -version = "0.1.0" +version = "0.1.1" diff --git a/tests/codegen/array-codegen.rs b/tests/codegen/array-codegen.rs new file mode 100644 index 0000000000000..98488eb92ee02 --- /dev/null +++ b/tests/codegen/array-codegen.rs @@ -0,0 +1,35 @@ +// compile-flags: -O -C no-prepopulate-passes +// min-llvm-version: 15.0 (for opaque pointers) + +#![crate_type = "lib"] + +// CHECK-LABEL: @array_load +#[no_mangle] +pub fn array_load(a: &[u8; 4]) -> [u8; 4] { + // CHECK: %0 = alloca [4 x i8], align 1 + // CHECK: %[[TEMP1:.+]] = load <4 x i8>, ptr %a, align 1 + // CHECK: store <4 x i8> %[[TEMP1]], ptr %0, align 1 + // CHECK: %[[TEMP2:.+]] = load i32, ptr %0, align 1 + // CHECK: ret i32 %[[TEMP2]] + *a +} + +// CHECK-LABEL: @array_store +#[no_mangle] +pub fn array_store(a: [u8; 4], p: &mut [u8; 4]) { + // CHECK: %a = alloca [4 x i8] + // CHECK: %[[TEMP:.+]] = load <4 x i8>, ptr %a, align 1 + // CHECK-NEXT: store <4 x i8> %[[TEMP]], ptr %p, align 1 + *p = a; +} + +// CHECK-LABEL: @array_copy +#[no_mangle] +pub fn array_copy(a: &[u8; 4], p: &mut [u8; 4]) { + // CHECK: %[[LOCAL:.+]] = alloca [4 x i8], align 1 + // CHECK: %[[TEMP1:.+]] = load <4 x i8>, ptr %a, align 1 + // CHECK: store <4 x i8> %[[TEMP1]], ptr %[[LOCAL]], align 1 + // CHECK: %[[TEMP2:.+]] = load <4 x i8>, ptr %[[LOCAL]], align 1 + // CHECK: store <4 x i8> %[[TEMP2]], ptr %p, align 1 + *p = *a; +} diff --git a/tests/codegen/box-maybe-uninit-llvm14.rs b/tests/codegen/box-maybe-uninit-llvm14.rs index b0c88f76c436d..c9f88fb3fe452 100644 --- a/tests/codegen/box-maybe-uninit-llvm14.rs +++ b/tests/codegen/box-maybe-uninit-llvm14.rs @@ -31,4 +31,4 @@ pub fn box_uninitialized2() -> Box> { // Hide the LLVM 15+ `allocalign` attribute in the declaration of __rust_alloc // from the CHECK-NOT above. We don't check the attributes here because we can't rely // on all of them being set until LLVM 15. -// CHECK: declare noalias{{.*}} @__rust_alloc(i{{[0-9]+}} noundef, i{{[0-9]+.*}} noundef) +// CHECK: declare {{(dso_local )?}}noalias{{.*}} @__rust_alloc(i{{[0-9]+}} noundef, i{{[0-9]+.*}} noundef) diff --git a/tests/codegen/box-maybe-uninit.rs b/tests/codegen/box-maybe-uninit.rs index 2f88966996ab2..5c08b5832ad80 100644 --- a/tests/codegen/box-maybe-uninit.rs +++ b/tests/codegen/box-maybe-uninit.rs @@ -28,6 +28,6 @@ pub fn box_uninitialized2() -> Box> { // Hide the `allocalign` attribute in the declaration of __rust_alloc // from the CHECK-NOT above, and also verify the attributes got set reasonably. -// CHECK: declare noalias noundef ptr @__rust_alloc(i{{[0-9]+}} noundef, i{{[0-9]+}} allocalign noundef) unnamed_addr [[RUST_ALLOC_ATTRS:#[0-9]+]] +// CHECK: declare {{(dso_local )?}}noalias noundef ptr @__rust_alloc(i{{[0-9]+}} noundef, i{{[0-9]+}} allocalign noundef) unnamed_addr [[RUST_ALLOC_ATTRS:#[0-9]+]] -// CHECK-DAG: attributes [[RUST_ALLOC_ATTRS]] = { {{.*}} allockind("alloc,uninitialized,aligned") allocsize(0) uwtable "alloc-family"="__rust_alloc" {{.*}} } +// CHECK-DAG: attributes [[RUST_ALLOC_ATTRS]] = { {{.*}} allockind("alloc,uninitialized,aligned") allocsize(0) {{(uwtable )?}}"alloc-family"="__rust_alloc" {{.*}} } diff --git a/tests/codegen/call-metadata.rs b/tests/codegen/call-metadata.rs index 1c30c08d3b2e2..07cc0c963717e 100644 --- a/tests/codegen/call-metadata.rs +++ b/tests/codegen/call-metadata.rs @@ -6,7 +6,7 @@ #![crate_type = "lib"] pub fn test() { - // CHECK: call noundef i8 @some_true(), !range [[R0:![0-9]+]] + // CHECK: call noundef i8 @some_true(){{( #[0-9]+)?}}, !range [[R0:![0-9]+]] // CHECK: [[R0]] = !{i8 0, i8 3} some_true(); } diff --git a/tests/codegen/debug-column.rs b/tests/codegen/debug-column.rs index e61642b8e1be2..f3b19a2eb2f42 100644 --- a/tests/codegen/debug-column.rs +++ b/tests/codegen/debug-column.rs @@ -6,11 +6,11 @@ fn main() { unsafe { // Column numbers are 1-based. Regression test for #65437. - // CHECK: call void @giraffe(), !dbg [[A:!.*]] + // CHECK: call void @giraffe(){{( #[0-9]+)?}}, !dbg [[A:!.*]] giraffe(); // Column numbers use byte offests. Regression test for #67360 - // CHECK: call void @turtle(), !dbg [[B:!.*]] + // CHECK: call void @turtle(){{( #[0-9]+)?}}, !dbg [[B:!.*]] /* ż */ turtle(); // CHECK: [[A]] = !DILocation(line: 10, column: 9, diff --git a/tests/codegen/drop.rs b/tests/codegen/drop.rs index 994028271583f..3615ef47b531b 100644 --- a/tests/codegen/drop.rs +++ b/tests/codegen/drop.rs @@ -1,4 +1,5 @@ // ignore-wasm32-bare compiled with panic=abort by default +// needs-unwind - this test verifies the amount of drop calls when unwinding is used // compile-flags: -C no-prepopulate-passes #![crate_type = "lib"] diff --git a/tests/codegen/external-no-mangle-statics.rs b/tests/codegen/external-no-mangle-statics.rs index c6ecb7aa96abc..48023a2a901cd 100644 --- a/tests/codegen/external-no-mangle-statics.rs +++ b/tests/codegen/external-no-mangle-statics.rs @@ -6,72 +6,72 @@ // `#[no_mangle]`d static variables always have external linkage, i.e., no `internal` in their // definitions -// CHECK: @A = local_unnamed_addr constant +// CHECK: @A = {{(dso_local )?}}local_unnamed_addr constant #[no_mangle] static A: u8 = 0; -// CHECK: @B = local_unnamed_addr global +// CHECK: @B = {{(dso_local )?}}local_unnamed_addr global #[no_mangle] static mut B: u8 = 0; -// CHECK: @C = local_unnamed_addr constant +// CHECK: @C = {{(dso_local )?}}local_unnamed_addr constant #[no_mangle] pub static C: u8 = 0; -// CHECK: @D = local_unnamed_addr global +// CHECK: @D = {{(dso_local )?}}local_unnamed_addr global #[no_mangle] pub static mut D: u8 = 0; mod private { - // CHECK: @E = local_unnamed_addr constant + // CHECK: @E = {{(dso_local )?}}local_unnamed_addr constant #[no_mangle] static E: u8 = 0; - // CHECK: @F = local_unnamed_addr global + // CHECK: @F = {{(dso_local )?}}local_unnamed_addr global #[no_mangle] static mut F: u8 = 0; - // CHECK: @G = local_unnamed_addr constant + // CHECK: @G = {{(dso_local )?}}local_unnamed_addr constant #[no_mangle] pub static G: u8 = 0; - // CHECK: @H = local_unnamed_addr global + // CHECK: @H = {{(dso_local )?}}local_unnamed_addr global #[no_mangle] pub static mut H: u8 = 0; } const HIDDEN: () = { - // CHECK: @I = local_unnamed_addr constant + // CHECK: @I = {{(dso_local )?}}local_unnamed_addr constant #[no_mangle] static I: u8 = 0; - // CHECK: @J = local_unnamed_addr global + // CHECK: @J = {{(dso_local )?}}local_unnamed_addr global #[no_mangle] static mut J: u8 = 0; - // CHECK: @K = local_unnamed_addr constant + // CHECK: @K = {{(dso_local )?}}local_unnamed_addr constant #[no_mangle] pub static K: u8 = 0; - // CHECK: @L = local_unnamed_addr global + // CHECK: @L = {{(dso_local )?}}local_unnamed_addr global #[no_mangle] pub static mut L: u8 = 0; }; fn x() { - // CHECK: @M = local_unnamed_addr constant + // CHECK: @M = {{(dso_local )?}}local_unnamed_addr constant #[no_mangle] static M: fn() = x; - // CHECK: @N = local_unnamed_addr global + // CHECK: @N = {{(dso_local )?}}local_unnamed_addr global #[no_mangle] static mut N: u8 = 0; - // CHECK: @O = local_unnamed_addr constant + // CHECK: @O = {{(dso_local )?}}local_unnamed_addr constant #[no_mangle] pub static O: u8 = 0; - // CHECK: @P = local_unnamed_addr global + // CHECK: @P = {{(dso_local )?}}local_unnamed_addr global #[no_mangle] pub static mut P: u8 = 0; } diff --git a/tests/codegen/issues/issue-86106.rs b/tests/codegen/issues/issue-86106.rs index c0be7fab2f3e0..be5034dcfbd7e 100644 --- a/tests/codegen/issues/issue-86106.rs +++ b/tests/codegen/issues/issue-86106.rs @@ -7,7 +7,7 @@ #![crate_type = "lib"] -// CHECK-LABEL: define void @string_new +// CHECK-LABEL: define {{(dso_local )?}}void @string_new #[no_mangle] pub fn string_new() -> String { // CHECK: store ptr inttoptr @@ -17,7 +17,7 @@ pub fn string_new() -> String { String::new() } -// CHECK-LABEL: define void @empty_to_string +// CHECK-LABEL: define {{(dso_local )?}}void @empty_to_string #[no_mangle] pub fn empty_to_string() -> String { // CHECK: store ptr inttoptr diff --git a/tests/codegen/link_section.rs b/tests/codegen/link_section.rs index 88b8692b0ac54..2b26b604ad323 100644 --- a/tests/codegen/link_section.rs +++ b/tests/codegen/link_section.rs @@ -3,7 +3,7 @@ #![crate_type = "lib"] -// CHECK: @VAR1 = constant <{ [4 x i8] }> <{ [4 x i8] c"\01\00\00\00" }>, section ".test_one" +// CHECK: @VAR1 = {{(dso_local )?}}constant <{ [4 x i8] }> <{ [4 x i8] c"\01\00\00\00" }>, section ".test_one" #[no_mangle] #[link_section = ".test_one"] #[cfg(target_endian = "little")] @@ -19,17 +19,17 @@ pub enum E { B(f32) } -// CHECK: @VAR2 = constant {{.*}}, section ".test_two" +// CHECK: @VAR2 = {{(dso_local )?}}constant {{.*}}, section ".test_two" #[no_mangle] #[link_section = ".test_two"] pub static VAR2: E = E::A(666); -// CHECK: @VAR3 = constant {{.*}}, section ".test_three" +// CHECK: @VAR3 = {{(dso_local )?}}constant {{.*}}, section ".test_three" #[no_mangle] #[link_section = ".test_three"] pub static VAR3: E = E::B(1.); -// CHECK: define void @fn1() {{.*}} section ".test_four" { +// CHECK: define {{(dso_local )?}}void @fn1() {{.*}} section ".test_four" { #[no_mangle] #[link_section = ".test_four"] pub fn fn1() {} diff --git a/tests/codegen/mem-replace-simple-type.rs b/tests/codegen/mem-replace-simple-type.rs index 4253ef1366604..6151177de15b2 100644 --- a/tests/codegen/mem-replace-simple-type.rs +++ b/tests/codegen/mem-replace-simple-type.rs @@ -32,3 +32,14 @@ pub fn replace_ref_str<'a>(r: &mut &'a str, v: &'a str) -> &'a str { // CHECK: ret { ptr, i64 } %[[P2]] std::mem::replace(r, v) } + +#[no_mangle] +// CHECK-LABEL: @replace_short_array( +pub fn replace_short_array(r: &mut [u32; 3], v: [u32; 3]) -> [u32; 3] { + // CHECK-NOT: alloca + // CHECK: %[[R:.+]] = load <3 x i32>, ptr %r, align 4 + // CHECK: store <3 x i32> %[[R]], ptr %0 + // CHECK: %[[V:.+]] = load <3 x i32>, ptr %v, align 4 + // CHECK: store <3 x i32> %[[V]], ptr %r + std::mem::replace(r, v) +} diff --git a/tests/codegen/mir-inlined-line-numbers.rs b/tests/codegen/mir-inlined-line-numbers.rs index 19d83f0eee7c4..d13527b952130 100644 --- a/tests/codegen/mir-inlined-line-numbers.rs +++ b/tests/codegen/mir-inlined-line-numbers.rs @@ -19,7 +19,7 @@ pub fn example() { } // CHECK-LABEL: @example -// CHECK: tail call void @bar(), !dbg [[DBG_ID:![0-9]+]] +// CHECK: tail call void @bar(){{( #[0-9]+)?}}, !dbg [[DBG_ID:![0-9]+]] // CHECK: [[DBG_ID]] = !DILocation(line: 7, // CHECK-SAME: inlinedAt: [[INLINE_ID:![0-9]+]]) // CHECK: [[INLINE_ID]] = !DILocation(line: 18, diff --git a/tests/codegen/naked-noinline.rs b/tests/codegen/naked-noinline.rs index c0ac69f4ed778..5cfb500c0ef88 100644 --- a/tests/codegen/naked-noinline.rs +++ b/tests/codegen/naked-noinline.rs @@ -12,7 +12,7 @@ use std::arch::asm; pub unsafe extern "C" fn f() { // Check that f has naked and noinline attributes. // - // CHECK: define void @f() unnamed_addr [[ATTR:#[0-9]+]] + // CHECK: define {{(dso_local )?}}void @f() unnamed_addr [[ATTR:#[0-9]+]] // CHECK-NEXT: start: // CHECK-NEXT: call void asm asm!("", options(noreturn)); @@ -22,7 +22,7 @@ pub unsafe extern "C" fn f() { pub unsafe fn g() { // Check that call to f is not inlined. // - // CHECK-LABEL: define void @g() + // CHECK-LABEL: define {{(dso_local )?}}void @g() // CHECK-NEXT: start: // CHECK-NEXT: call void @f() f(); diff --git a/tests/codegen/personality_lifetimes.rs b/tests/codegen/personality_lifetimes.rs index 2104022f57874..9ff7a9b3e8879 100644 --- a/tests/codegen/personality_lifetimes.rs +++ b/tests/codegen/personality_lifetimes.rs @@ -1,5 +1,6 @@ // ignore-msvc // ignore-wasm32-bare compiled with panic=abort by default +// needs-unwind // compile-flags: -O -C no-prepopulate-passes diff --git a/tests/codegen/ptr-read-metadata.rs b/tests/codegen/ptr-read-metadata.rs index e1e3272662c4b..73d1db6df277b 100644 --- a/tests/codegen/ptr-read-metadata.rs +++ b/tests/codegen/ptr-read-metadata.rs @@ -9,7 +9,7 @@ use std::mem::MaybeUninit; -// CHECK-LABEL: define noundef i8 @copy_byte( +// CHECK-LABEL: define {{(dso_local )?}}noundef i8 @copy_byte( #[no_mangle] pub unsafe fn copy_byte(p: *const u8) -> u8 { // CHECK-NOT: load @@ -19,7 +19,7 @@ pub unsafe fn copy_byte(p: *const u8) -> u8 { *p } -// CHECK-LABEL: define noundef i8 @read_byte( +// CHECK-LABEL: define {{(dso_local )?}}noundef i8 @read_byte( #[no_mangle] pub unsafe fn read_byte(p: *const u8) -> u8 { // CHECK-NOT: load @@ -29,7 +29,7 @@ pub unsafe fn read_byte(p: *const u8) -> u8 { p.read() } -// CHECK-LABEL: define i8 @read_byte_maybe_uninit( +// CHECK-LABEL: define {{(dso_local )?}}i8 @read_byte_maybe_uninit( #[no_mangle] pub unsafe fn read_byte_maybe_uninit(p: *const MaybeUninit) -> MaybeUninit { // CHECK-NOT: load @@ -39,7 +39,7 @@ pub unsafe fn read_byte_maybe_uninit(p: *const MaybeUninit) -> MaybeUninit) -> u8 { // CHECK-NOT: load @@ -49,7 +49,7 @@ pub unsafe fn read_byte_assume_init(p: &MaybeUninit) -> u8 { p.assume_init_read() } -// CHECK-LABEL: define noundef i32 @copy_char( +// CHECK-LABEL: define {{(dso_local )?}}noundef i32 @copy_char( #[no_mangle] pub unsafe fn copy_char(p: *const char) -> char { // CHECK-NOT: load @@ -60,7 +60,7 @@ pub unsafe fn copy_char(p: *const char) -> char { *p } -// CHECK-LABEL: define noundef i32 @read_char( +// CHECK-LABEL: define {{(dso_local )?}}noundef i32 @read_char( #[no_mangle] pub unsafe fn read_char(p: *const char) -> char { // CHECK-NOT: load @@ -71,7 +71,7 @@ pub unsafe fn read_char(p: *const char) -> char { p.read() } -// CHECK-LABEL: define i32 @read_char_maybe_uninit( +// CHECK-LABEL: define {{(dso_local )?}}i32 @read_char_maybe_uninit( #[no_mangle] pub unsafe fn read_char_maybe_uninit(p: *const MaybeUninit) -> MaybeUninit { // CHECK-NOT: load @@ -82,7 +82,7 @@ pub unsafe fn read_char_maybe_uninit(p: *const MaybeUninit) -> MaybeUninit p.read() } -// CHECK-LABEL: define noundef i32 @read_char_assume_init( +// CHECK-LABEL: define {{(dso_local )?}}noundef i32 @read_char_assume_init( #[no_mangle] pub unsafe fn read_char_assume_init(p: &MaybeUninit) -> char { // CHECK-NOT: load diff --git a/tests/codegen/sanitizer-cfi-emit-type-metadata-id-itanium-cxx-abi.rs b/tests/codegen/sanitizer-cfi-emit-type-metadata-id-itanium-cxx-abi.rs index ebb26cd35c09c..472d921ace046 100644 --- a/tests/codegen/sanitizer-cfi-emit-type-metadata-id-itanium-cxx-abi.rs +++ b/tests/codegen/sanitizer-cfi-emit-type-metadata-id-itanium-cxx-abi.rs @@ -587,6 +587,6 @@ pub fn foo149(_: Type14, _: Type14, _: Type14) { } // CHECK: ![[TYPE144]] = !{i64 0, !"_ZTSFvu3refIvEE"} // CHECK: ![[TYPE145]] = !{i64 0, !"_ZTSFvu3refIvES_E"} // CHECK: ![[TYPE146]] = !{i64 0, !"_ZTSFvu3refIvES_S_E"} -// CHECK: ![[TYPE147]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3BarE -// CHECK: ![[TYPE148]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3BarS_E -// CHECK: ![[TYPE149]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3BarS_S_E +// CHECK: ![[TYPE147]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3BarE"} +// CHECK: ![[TYPE148]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3BarS_E"} +// CHECK: ![[TYPE149]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3BarS_S_E"} diff --git a/tests/codegen/swap-simd-types.rs b/tests/codegen/swap-simd-types.rs index c90b277eb4487..3472a42b0e65e 100644 --- a/tests/codegen/swap-simd-types.rs +++ b/tests/codegen/swap-simd-types.rs @@ -30,3 +30,12 @@ pub fn swap_m256_slice(x: &mut [__m256], y: &mut [__m256]) { x.swap_with_slice(y); } } + +// CHECK-LABEL: @swap_bytes32 +#[no_mangle] +pub fn swap_bytes32(x: &mut [u8; 32], y: &mut [u8; 32]) { +// CHECK-NOT: alloca +// CHECK: load <32 x i8>{{.+}}align 1 +// CHECK: store <32 x i8>{{.+}}align 1 + swap(x, y) +} diff --git a/tests/codegen/swap-small-types.rs b/tests/codegen/swap-small-types.rs index 03e2a2327fc4c..419645a3fc6bc 100644 --- a/tests/codegen/swap-small-types.rs +++ b/tests/codegen/swap-small-types.rs @@ -1,4 +1,4 @@ -// compile-flags: -O +// compile-flags: -O -Z merge-functions=disabled // only-x86_64 // ignore-debug: the debug assertions get in the way @@ -8,13 +8,28 @@ use std::mem::swap; type RGB48 = [u16; 3]; +// CHECK-LABEL: @swap_rgb48_manually( +#[no_mangle] +pub fn swap_rgb48_manually(x: &mut RGB48, y: &mut RGB48) { + // CHECK-NOT: alloca + // CHECK: %[[TEMP0:.+]] = load <3 x i16>, ptr %x, align 2 + // CHECK: %[[TEMP1:.+]] = load <3 x i16>, ptr %y, align 2 + // CHECK: store <3 x i16> %[[TEMP1]], ptr %x, align 2 + // CHECK: store <3 x i16> %[[TEMP0]], ptr %y, align 2 + + let temp = *x; + *x = *y; + *y = temp; +} + // CHECK-LABEL: @swap_rgb48 #[no_mangle] pub fn swap_rgb48(x: &mut RGB48, y: &mut RGB48) { - // FIXME MIR inlining messes up LLVM optimizations. -// WOULD-CHECK-NOT: alloca -// WOULD-CHECK: load i48 -// WOULD-CHECK: store i48 + // CHECK-NOT: alloca + // CHECK: load <3 x i16> + // CHECK: load <3 x i16> + // CHECK: store <3 x i16> + // CHECK: store <3 x i16> swap(x, y) } diff --git a/tests/codegen/tuple-layout-opt.rs b/tests/codegen/tuple-layout-opt.rs index 35f760851451e..309fe1d5ec902 100644 --- a/tests/codegen/tuple-layout-opt.rs +++ b/tests/codegen/tuple-layout-opt.rs @@ -6,31 +6,31 @@ #![crate_type="lib"] type ScalarZstLast = (u128, ()); -// CHECK: define i128 @test_ScalarZstLast(i128 %_1) +// CHECK: define {{(dso_local )?}}i128 @test_ScalarZstLast(i128 %_1) #[no_mangle] pub fn test_ScalarZstLast(_: ScalarZstLast) -> ScalarZstLast { loop {} } type ScalarZstFirst = ((), u128); -// CHECK: define i128 @test_ScalarZstFirst(i128 %_1) +// CHECK: define {{(dso_local )?}}i128 @test_ScalarZstFirst(i128 %_1) #[no_mangle] pub fn test_ScalarZstFirst(_: ScalarZstFirst) -> ScalarZstFirst { loop {} } type ScalarPairZstLast = (u8, u128, ()); -// CHECK: define { i128, i8 } @test_ScalarPairZstLast(i128 %_1.0, i8 %_1.1) +// CHECK: define {{(dso_local )?}}{ i128, i8 } @test_ScalarPairZstLast(i128 %_1.0, i8 %_1.1) #[no_mangle] pub fn test_ScalarPairZstLast(_: ScalarPairZstLast) -> ScalarPairZstLast { loop {} } type ScalarPairZstFirst = ((), u8, u128); -// CHECK: define { i8, i128 } @test_ScalarPairZstFirst(i8 %_1.0, i128 %_1.1) +// CHECK: define {{(dso_local )?}}{ i8, i128 } @test_ScalarPairZstFirst(i8 %_1.0, i128 %_1.1) #[no_mangle] pub fn test_ScalarPairZstFirst(_: ScalarPairZstFirst) -> ScalarPairZstFirst { loop {} } type ScalarPairLotsOfZsts = ((), u8, (), u128, ()); -// CHECK: define { i128, i8 } @test_ScalarPairLotsOfZsts(i128 %_1.0, i8 %_1.1) +// CHECK: define {{(dso_local )?}}{ i128, i8 } @test_ScalarPairLotsOfZsts(i128 %_1.0, i8 %_1.1) #[no_mangle] pub fn test_ScalarPairLotsOfZsts(_: ScalarPairLotsOfZsts) -> ScalarPairLotsOfZsts { loop {} } type ScalarPairLottaNesting = (((), ((), u8, (), u128, ())), ()); -// CHECK: define { i128, i8 } @test_ScalarPairLottaNesting(i128 %_1.0, i8 %_1.1) +// CHECK: define {{(dso_local )?}}{ i128, i8 } @test_ScalarPairLottaNesting(i128 %_1.0, i8 %_1.1) #[no_mangle] pub fn test_ScalarPairLottaNesting(_: ScalarPairLottaNesting) -> ScalarPairLottaNesting { loop {} } diff --git a/tests/codegen/union-abi.rs b/tests/codegen/union-abi.rs index c18f2a49fc369..8481ca8ccfa19 100644 --- a/tests/codegen/union-abi.rs +++ b/tests/codegen/union-abi.rs @@ -17,60 +17,60 @@ pub struct i64x4(i64, i64, i64, i64); #[derive(Copy, Clone)] pub union UnionI64x4{ a:(), b: i64x4 } -// CHECK: define void @test_UnionI64x4({{<4 x i64>\*|ptr}} {{.*}} %_1) +// CHECK: define {{(dso_local )?}}void @test_UnionI64x4({{<4 x i64>\*|ptr}} {{.*}} %_1) #[no_mangle] pub fn test_UnionI64x4(_: UnionI64x4) { loop {} } pub union UnionI64x4_{ a: i64x4, b: (), c:i64x4, d: Unhab, e: ((),()), f: UnionI64x4 } -// CHECK: define void @test_UnionI64x4_({{<4 x i64>\*|ptr}} {{.*}} %_1) +// CHECK: define {{(dso_local )?}}void @test_UnionI64x4_({{<4 x i64>\*|ptr}} {{.*}} %_1) #[no_mangle] pub fn test_UnionI64x4_(_: UnionI64x4_) { loop {} } pub union UnionI64x4I64{ a: i64x4, b: i64 } -// CHECK: define void @test_UnionI64x4I64({{%UnionI64x4I64\*|ptr}} {{.*}} %_1) +// CHECK: define {{(dso_local )?}}void @test_UnionI64x4I64({{%UnionI64x4I64\*|ptr}} {{.*}} %_1) #[no_mangle] pub fn test_UnionI64x4I64(_: UnionI64x4I64) { loop {} } pub union UnionI64x4Tuple{ a: i64x4, b: (i64, i64, i64, i64) } -// CHECK: define void @test_UnionI64x4Tuple({{%UnionI64x4Tuple\*|ptr}} {{.*}} %_1) +// CHECK: define {{(dso_local )?}}void @test_UnionI64x4Tuple({{%UnionI64x4Tuple\*|ptr}} {{.*}} %_1) #[no_mangle] pub fn test_UnionI64x4Tuple(_: UnionI64x4Tuple) { loop {} } pub union UnionF32{a:f32} -// CHECK: define float @test_UnionF32(float %_1) +// CHECK: define {{(dso_local )?}}float @test_UnionF32(float %_1) #[no_mangle] pub fn test_UnionF32(_: UnionF32) -> UnionF32 { loop {} } pub union UnionF32F32{a:f32, b:f32} -// CHECK: define float @test_UnionF32F32(float %_1) +// CHECK: define {{(dso_local )?}}float @test_UnionF32F32(float %_1) #[no_mangle] pub fn test_UnionF32F32(_: UnionF32F32) -> UnionF32F32 { loop {} } pub union UnionF32U32{a:f32, b:u32} -// CHECK: define i32 @test_UnionF32U32(i32{{( %0)?}}) +// CHECK: define {{(dso_local )?}}i32 @test_UnionF32U32(i32{{( %0)?}}) #[no_mangle] pub fn test_UnionF32U32(_: UnionF32U32) -> UnionF32U32 { loop {} } pub union UnionU128{a:u128} -// CHECK: define i128 @test_UnionU128(i128 %_1) +// CHECK: define {{(dso_local )?}}i128 @test_UnionU128(i128 %_1) #[no_mangle] pub fn test_UnionU128(_: UnionU128) -> UnionU128 { loop {} } #[repr(C)] pub union CUnionU128{a:u128} -// CHECK: define void @test_CUnionU128({{%CUnionU128\*|ptr}} {{.*}} %_1) +// CHECK: define {{(dso_local )?}}void @test_CUnionU128({{%CUnionU128\*|ptr}} {{.*}} %_1) #[no_mangle] pub fn test_CUnionU128(_: CUnionU128) { loop {} } pub union UnionBool { b:bool } -// CHECK: define noundef zeroext i1 @test_UnionBool(i8 %b) +// CHECK: define {{(dso_local )?}}noundef zeroext i1 @test_UnionBool(i8 %b) #[no_mangle] pub fn test_UnionBool(b: UnionBool) -> bool { unsafe { b.b } } // CHECK: %0 = trunc i8 %b to i1 diff --git a/tests/codegen/unwind-abis/c-unwind-abi.rs b/tests/codegen/unwind-abis/c-unwind-abi.rs index e258dbcacd229..fa5b6bad75cb3 100644 --- a/tests/codegen/unwind-abis/c-unwind-abi.rs +++ b/tests/codegen/unwind-abis/c-unwind-abi.rs @@ -1,4 +1,5 @@ // compile-flags: -C opt-level=0 +// needs-unwind // Test that `nounwind` attributes are correctly applied to exported `C` and `C-unwind` extern // functions. `C-unwind` functions MUST NOT have this attribute. We disable optimizations above diff --git a/tests/codegen/unwind-abis/cdecl-unwind-abi.rs b/tests/codegen/unwind-abis/cdecl-unwind-abi.rs index 19a7228839abf..64746d32175be 100644 --- a/tests/codegen/unwind-abis/cdecl-unwind-abi.rs +++ b/tests/codegen/unwind-abis/cdecl-unwind-abi.rs @@ -1,4 +1,5 @@ // compile-flags: -C opt-level=0 +// needs-unwind // Test that `nounwind` attributes are correctly applied to exported `cdecl` and // `cdecl-unwind` extern functions. `cdecl-unwind` functions MUST NOT have this attribute. We diff --git a/tests/codegen/unwind-abis/nounwind-on-stable-panic-unwind.rs b/tests/codegen/unwind-abis/nounwind-on-stable-panic-unwind.rs index c1c5bbdda3456..dc3911cd4ebac 100644 --- a/tests/codegen/unwind-abis/nounwind-on-stable-panic-unwind.rs +++ b/tests/codegen/unwind-abis/nounwind-on-stable-panic-unwind.rs @@ -1,5 +1,6 @@ // compile-flags: -C opt-level=0 // ignore-wasm32-bare compiled with panic=abort by default +// needs-unwind #![crate_type = "lib"] diff --git a/tests/codegen/unwind-abis/system-unwind-abi.rs b/tests/codegen/unwind-abis/system-unwind-abi.rs index 2591c1d481430..f274a33b09948 100644 --- a/tests/codegen/unwind-abis/system-unwind-abi.rs +++ b/tests/codegen/unwind-abis/system-unwind-abi.rs @@ -1,4 +1,5 @@ // compile-flags: -C opt-level=0 +// needs-unwind // Test that `nounwind` attributes are correctly applied to exported `system` and `system-unwind` // extern functions. `system-unwind` functions MUST NOT have this attribute. We disable diff --git a/tests/codegen/unwind-extern-exports.rs b/tests/codegen/unwind-extern-exports.rs index 6ac3c079f81ba..4e1e719d5cd18 100644 --- a/tests/codegen/unwind-extern-exports.rs +++ b/tests/codegen/unwind-extern-exports.rs @@ -1,5 +1,6 @@ // compile-flags: -C opt-level=0 // ignore-wasm32-bare compiled with panic=abort by default +// needs-unwind #![crate_type = "lib"] #![feature(c_unwind)] diff --git a/tests/codegen/unwind-extern-imports.rs b/tests/codegen/unwind-extern-imports.rs index e33e3e80521c1..260dcc628cc0e 100644 --- a/tests/codegen/unwind-extern-imports.rs +++ b/tests/codegen/unwind-extern-imports.rs @@ -1,5 +1,6 @@ // compile-flags: -C no-prepopulate-passes // ignore-wasm32-bare compiled with panic=abort by default +// needs-unwind #![crate_type = "lib"] #![feature(c_unwind)] diff --git a/tests/codegen/vec-shrink-panik.rs b/tests/codegen/vec-shrink-panik.rs index 88b7edff26093..606d68ff3ab38 100644 --- a/tests/codegen/vec-shrink-panik.rs +++ b/tests/codegen/vec-shrink-panik.rs @@ -5,6 +5,7 @@ // [new]min-llvm-version: 17 // compile-flags: -O // ignore-debug: the debug assertions get in the way +// needs-unwind #![crate_type = "lib"] #![feature(shrink_to)] diff --git a/tests/incremental/const-generics/hash-tyvid-regression-1.rs b/tests/incremental/const-generics/hash-tyvid-regression-1.rs index 5ff7b19d8945e..06d674234510a 100644 --- a/tests/incremental/const-generics/hash-tyvid-regression-1.rs +++ b/tests/incremental/const-generics/hash-tyvid-regression-1.rs @@ -1,8 +1,20 @@ // revisions: cfail #![feature(generic_const_exprs, adt_const_params)] #![allow(incomplete_features)] + +use std::marker::ConstParamTy; + +#[derive(PartialEq, Eq, ConstParamTy)] +struct NonZeroUsize(usize); + +impl NonZeroUsize { + const fn get(self) -> usize { + self.0 + } +} + // regression test for #77650 -fn c() +fn c() where [T; N.get()]: Sized, { diff --git a/tests/incremental/const-generics/hash-tyvid-regression-2.rs b/tests/incremental/const-generics/hash-tyvid-regression-2.rs index 5cdd43cd782c4..33f226ff611e8 100644 --- a/tests/incremental/const-generics/hash-tyvid-regression-2.rs +++ b/tests/incremental/const-generics/hash-tyvid-regression-2.rs @@ -1,11 +1,23 @@ // revisions: cfail #![feature(generic_const_exprs, adt_const_params)] #![allow(incomplete_features)] + +use std::marker::ConstParamTy; + +#[derive(PartialEq, Eq, ConstParamTy)] +struct NonZeroUsize(usize); + +impl NonZeroUsize { + const fn get(self) -> usize { + self.0 + } +} + // regression test for #77650 -struct C([T; N.get()]) +struct C([T; N.get()]) where [T; N.get()]: Sized; -impl<'a, const N: core::num::NonZeroUsize, A, B: PartialEq
> PartialEq<&'a [A]> for C +impl<'a, const N: NonZeroUsize, A, B: PartialEq> PartialEq<&'a [A]> for C where [B; N.get()]: Sized, { diff --git a/tests/incremental/const-generics/try_unify_abstract_const_regression_tests/issue-77708-3.rs b/tests/incremental/const-generics/try_unify_abstract_const_regression_tests/issue-77708-3.rs index fc114f224a2fa..f1c108fed11ba 100644 --- a/tests/incremental/const-generics/try_unify_abstract_const_regression_tests/issue-77708-3.rs +++ b/tests/incremental/const-generics/try_unify_abstract_const_regression_tests/issue-77708-3.rs @@ -2,7 +2,18 @@ #![feature(generic_const_exprs, adt_const_params)] #![allow(incomplete_features)] -use std::{convert::TryFrom, num::NonZeroUsize}; +use std::{convert::TryFrom}; + +use std::marker::ConstParamTy; + +#[derive(PartialEq, Eq, ConstParamTy)] +struct NonZeroUsize(usize); + +impl NonZeroUsize { + const fn get(self) -> usize { + self.0 + } +} struct A([u8; N.get()]) where diff --git a/tests/mir-opt/inline/cycle.main.Inline.diff b/tests/mir-opt/inline/cycle.main.Inline.diff index fdf6337a9bee6..7a39aae1baf5d 100644 --- a/tests/mir-opt/inline/cycle.main.Inline.diff +++ b/tests/mir-opt/inline/cycle.main.Inline.diff @@ -11,6 +11,14 @@ + let mut _3: &fn() {g}; // in scope 1 at $DIR/cycle.rs:6:5: 6:6 + let _4: (); // in scope 1 at $DIR/cycle.rs:6:5: 6:8 + scope 2 (inlined >::call - shim(fn() {g})) { // at $DIR/cycle.rs:6:5: 6:8 ++ scope 3 (inlined g) { // at $SRC_DIR/core/src/ops/function.rs:LL:COL ++ scope 4 (inlined f::) { // at $DIR/cycle.rs:12:5: 12:12 ++ debug g => main; // in scope 4 at $DIR/cycle.rs:5:6: 5:7 ++ let _6: (); // in scope 4 at $DIR/cycle.rs:6:5: 6:8 ++ scope 5 (inlined >::call - shim(fn() {main})) { // at $DIR/cycle.rs:6:5: 6:8 ++ } ++ } ++ } + } + } @@ -30,7 +38,11 @@ + _3 = &_2; // scope 1 at $DIR/cycle.rs:6:5: 6:6 + StorageLive(_5); // scope 1 at $DIR/cycle.rs:6:5: 6:8 + _5 = const (); // scope 1 at $DIR/cycle.rs:6:5: 6:8 -+ _4 = move (*_3)() -> [return: bb4, unwind: bb2]; // scope 2 at $SRC_DIR/core/src/ops/function.rs:LL:COL ++ StorageLive(_6); // scope 2 at $SRC_DIR/core/src/ops/function.rs:LL:COL ++ _6 = main() -> [return: bb4, unwind: bb2]; // scope 5 at $SRC_DIR/core/src/ops/function.rs:LL:COL ++ // mir::Constant ++ // + span: no-location ++ // + literal: Const { ty: fn() {main}, val: Value() } } bb1: { @@ -50,6 +62,7 @@ + } + + bb4: { ++ StorageDead(_6); // scope 2 at $SRC_DIR/core/src/ops/function.rs:LL:COL + StorageDead(_5); // scope 1 at $DIR/cycle.rs:6:5: 6:8 + StorageDead(_3); // scope 1 at $DIR/cycle.rs:6:7: 6:8 + drop(_2) -> bb1; // scope 1 at $DIR/cycle.rs:7:1: 7:2 diff --git a/tests/mir-opt/inline/exponential_runtime.main.Inline.diff b/tests/mir-opt/inline/exponential_runtime.main.Inline.diff index 30af8661dec73..7fad6aba82ec1 100644 --- a/tests/mir-opt/inline/exponential_runtime.main.Inline.diff +++ b/tests/mir-opt/inline/exponential_runtime.main.Inline.diff @@ -12,6 +12,26 @@ + let _5: (); // in scope 2 at $DIR/exponential_runtime.rs:62:9: 62:25 + let _6: (); // in scope 2 at $DIR/exponential_runtime.rs:63:9: 63:25 + let _7: (); // in scope 2 at $DIR/exponential_runtime.rs:64:9: 64:25 ++ scope 3 (inlined <() as E>::call) { // at $DIR/exponential_runtime.rs:62:9: 62:25 ++ let _8: (); // in scope 3 at $DIR/exponential_runtime.rs:50:9: 50:25 ++ let _9: (); // in scope 3 at $DIR/exponential_runtime.rs:51:9: 51:25 ++ let _10: (); // in scope 3 at $DIR/exponential_runtime.rs:52:9: 52:25 ++ scope 4 (inlined <() as D>::call) { // at $DIR/exponential_runtime.rs:50:9: 50:25 ++ let _11: (); // in scope 4 at $DIR/exponential_runtime.rs:38:9: 38:25 ++ let _12: (); // in scope 4 at $DIR/exponential_runtime.rs:39:9: 39:25 ++ let _13: (); // in scope 4 at $DIR/exponential_runtime.rs:40:9: 40:25 ++ scope 5 (inlined <() as C>::call) { // at $DIR/exponential_runtime.rs:38:9: 38:25 ++ let _14: (); // in scope 5 at $DIR/exponential_runtime.rs:26:9: 26:25 ++ let _15: (); // in scope 5 at $DIR/exponential_runtime.rs:27:9: 27:25 ++ let _16: (); // in scope 5 at $DIR/exponential_runtime.rs:28:9: 28:25 ++ scope 6 (inlined <() as B>::call) { // at $DIR/exponential_runtime.rs:26:9: 26:25 ++ let _17: (); // in scope 6 at $DIR/exponential_runtime.rs:14:9: 14:25 ++ let _18: (); // in scope 6 at $DIR/exponential_runtime.rs:15:9: 15:25 ++ let _19: (); // in scope 6 at $DIR/exponential_runtime.rs:16:9: 16:25 ++ } ++ } ++ } ++ } + } + } @@ -24,12 +44,24 @@ + StorageLive(_5); // scope 1 at $DIR/exponential_runtime.rs:74:9: 74:25 + StorageLive(_6); // scope 1 at $DIR/exponential_runtime.rs:74:9: 74:25 + StorageLive(_7); // scope 1 at $DIR/exponential_runtime.rs:74:9: 74:25 -+ _5 = <() as E>::call() -> bb4; // scope 2 at $DIR/exponential_runtime.rs:62:9: 62:25 ++ StorageLive(_8); // scope 2 at $DIR/exponential_runtime.rs:62:9: 62:25 ++ StorageLive(_9); // scope 2 at $DIR/exponential_runtime.rs:62:9: 62:25 ++ StorageLive(_10); // scope 2 at $DIR/exponential_runtime.rs:62:9: 62:25 ++ StorageLive(_11); // scope 3 at $DIR/exponential_runtime.rs:50:9: 50:25 ++ StorageLive(_12); // scope 3 at $DIR/exponential_runtime.rs:50:9: 50:25 ++ StorageLive(_13); // scope 3 at $DIR/exponential_runtime.rs:50:9: 50:25 ++ StorageLive(_14); // scope 4 at $DIR/exponential_runtime.rs:38:9: 38:25 ++ StorageLive(_15); // scope 4 at $DIR/exponential_runtime.rs:38:9: 38:25 ++ StorageLive(_16); // scope 4 at $DIR/exponential_runtime.rs:38:9: 38:25 ++ StorageLive(_17); // scope 5 at $DIR/exponential_runtime.rs:26:9: 26:25 ++ StorageLive(_18); // scope 5 at $DIR/exponential_runtime.rs:26:9: 26:25 ++ StorageLive(_19); // scope 5 at $DIR/exponential_runtime.rs:26:9: 26:25 ++ _17 = <() as A>::call() -> bb12; // scope 6 at $DIR/exponential_runtime.rs:14:9: 14:25 // mir::Constant - // + span: $DIR/exponential_runtime.rs:87:5: 87:20 - // + literal: Const { ty: fn() {<() as G>::call}, val: Value() } -+ // + span: $DIR/exponential_runtime.rs:62:9: 62:23 -+ // + literal: Const { ty: fn() {<() as E>::call}, val: Value() } ++ // + span: $DIR/exponential_runtime.rs:14:9: 14:23 ++ // + literal: Const { ty: fn() {<() as A>::call}, val: Value() } } bb1: { @@ -59,6 +91,9 @@ + } + + bb4: { ++ StorageDead(_10); // scope 2 at $DIR/exponential_runtime.rs:62:9: 62:25 ++ StorageDead(_9); // scope 2 at $DIR/exponential_runtime.rs:62:9: 62:25 ++ StorageDead(_8); // scope 2 at $DIR/exponential_runtime.rs:62:9: 62:25 + _6 = <() as E>::call() -> bb5; // scope 2 at $DIR/exponential_runtime.rs:63:9: 63:25 + // mir::Constant + // + span: $DIR/exponential_runtime.rs:63:9: 63:23 @@ -70,6 +105,71 @@ + // mir::Constant + // + span: $DIR/exponential_runtime.rs:64:9: 64:23 + // + literal: Const { ty: fn() {<() as E>::call}, val: Value() } ++ } ++ ++ bb6: { ++ StorageDead(_13); // scope 3 at $DIR/exponential_runtime.rs:50:9: 50:25 ++ StorageDead(_12); // scope 3 at $DIR/exponential_runtime.rs:50:9: 50:25 ++ StorageDead(_11); // scope 3 at $DIR/exponential_runtime.rs:50:9: 50:25 ++ _9 = <() as D>::call() -> bb7; // scope 3 at $DIR/exponential_runtime.rs:51:9: 51:25 ++ // mir::Constant ++ // + span: $DIR/exponential_runtime.rs:51:9: 51:23 ++ // + literal: Const { ty: fn() {<() as D>::call}, val: Value() } ++ } ++ ++ bb7: { ++ _10 = <() as D>::call() -> bb4; // scope 3 at $DIR/exponential_runtime.rs:52:9: 52:25 ++ // mir::Constant ++ // + span: $DIR/exponential_runtime.rs:52:9: 52:23 ++ // + literal: Const { ty: fn() {<() as D>::call}, val: Value() } ++ } ++ ++ bb8: { ++ StorageDead(_16); // scope 4 at $DIR/exponential_runtime.rs:38:9: 38:25 ++ StorageDead(_15); // scope 4 at $DIR/exponential_runtime.rs:38:9: 38:25 ++ StorageDead(_14); // scope 4 at $DIR/exponential_runtime.rs:38:9: 38:25 ++ _12 = <() as C>::call() -> bb9; // scope 4 at $DIR/exponential_runtime.rs:39:9: 39:25 ++ // mir::Constant ++ // + span: $DIR/exponential_runtime.rs:39:9: 39:23 ++ // + literal: Const { ty: fn() {<() as C>::call}, val: Value() } ++ } ++ ++ bb9: { ++ _13 = <() as C>::call() -> bb6; // scope 4 at $DIR/exponential_runtime.rs:40:9: 40:25 ++ // mir::Constant ++ // + span: $DIR/exponential_runtime.rs:40:9: 40:23 ++ // + literal: Const { ty: fn() {<() as C>::call}, val: Value() } ++ } ++ ++ bb10: { ++ StorageDead(_19); // scope 5 at $DIR/exponential_runtime.rs:26:9: 26:25 ++ StorageDead(_18); // scope 5 at $DIR/exponential_runtime.rs:26:9: 26:25 ++ StorageDead(_17); // scope 5 at $DIR/exponential_runtime.rs:26:9: 26:25 ++ _15 = <() as B>::call() -> bb11; // scope 5 at $DIR/exponential_runtime.rs:27:9: 27:25 ++ // mir::Constant ++ // + span: $DIR/exponential_runtime.rs:27:9: 27:23 ++ // + literal: Const { ty: fn() {<() as B>::call}, val: Value() } ++ } ++ ++ bb11: { ++ _16 = <() as B>::call() -> bb8; // scope 5 at $DIR/exponential_runtime.rs:28:9: 28:25 ++ // mir::Constant ++ // + span: $DIR/exponential_runtime.rs:28:9: 28:23 ++ // + literal: Const { ty: fn() {<() as B>::call}, val: Value() } ++ } ++ ++ bb12: { ++ _18 = <() as A>::call() -> bb13; // scope 6 at $DIR/exponential_runtime.rs:15:9: 15:25 ++ // mir::Constant ++ // + span: $DIR/exponential_runtime.rs:15:9: 15:23 ++ // + literal: Const { ty: fn() {<() as A>::call}, val: Value() } ++ } ++ ++ bb13: { ++ _19 = <() as A>::call() -> bb10; // scope 6 at $DIR/exponential_runtime.rs:16:9: 16:25 ++ // mir::Constant ++ // + span: $DIR/exponential_runtime.rs:16:9: 16:23 ++ // + literal: Const { ty: fn() {<() as A>::call}, val: Value() } } } diff --git a/tests/mir-opt/inline/inline_cycle.two.Inline.diff b/tests/mir-opt/inline/inline_cycle.two.Inline.diff index c8f58111da7da..48f0bd1030183 100644 --- a/tests/mir-opt/inline/inline_cycle.two.Inline.diff +++ b/tests/mir-opt/inline/inline_cycle.two.Inline.diff @@ -10,6 +10,14 @@ + debug f => _2; // in scope 1 at $DIR/inline_cycle.rs:54:22: 54:23 + let _3: (); // in scope 1 at $DIR/inline_cycle.rs:55:5: 55:8 + scope 2 (inlined >::call_once - shim(fn() {f})) { // at $DIR/inline_cycle.rs:55:5: 55:8 ++ scope 3 (inlined f) { // at $SRC_DIR/core/src/ops/function.rs:LL:COL ++ scope 4 (inlined call::) { // at $DIR/inline_cycle.rs:60:5: 60:12 ++ debug f => f; // in scope 4 at $DIR/inline_cycle.rs:54:22: 54:23 ++ let _5: (); // in scope 4 at $DIR/inline_cycle.rs:55:5: 55:8 ++ scope 5 (inlined >::call_once - shim(fn() {f})) { // at $DIR/inline_cycle.rs:55:5: 55:8 ++ } ++ } ++ } + } + } @@ -27,10 +35,15 @@ + StorageLive(_3); // scope 0 at $DIR/inline_cycle.rs:+1:5: +1:12 + StorageLive(_4); // scope 1 at $DIR/inline_cycle.rs:55:5: 55:8 + _4 = const (); // scope 1 at $DIR/inline_cycle.rs:55:5: 55:8 -+ _3 = move _2() -> bb1; // scope 2 at $SRC_DIR/core/src/ops/function.rs:LL:COL ++ StorageLive(_5); // scope 2 at $SRC_DIR/core/src/ops/function.rs:LL:COL ++ _5 = f() -> bb1; // scope 5 at $SRC_DIR/core/src/ops/function.rs:LL:COL ++ // mir::Constant ++ // + span: no-location ++ // + literal: Const { ty: fn() {f}, val: Value() } } bb1: { ++ StorageDead(_5); // scope 2 at $SRC_DIR/core/src/ops/function.rs:LL:COL + StorageDead(_4); // scope 1 at $DIR/inline_cycle.rs:55:5: 55:8 + StorageDead(_3); // scope 0 at $DIR/inline_cycle.rs:+1:5: +1:12 + StorageDead(_2); // scope 0 at $DIR/inline_cycle.rs:+1:5: +1:12 diff --git a/tests/mir-opt/inline/inline_cycle_generic.main.Inline.diff b/tests/mir-opt/inline/inline_cycle_generic.main.Inline.diff index 9429ca593646e..8696e624b2237 100644 --- a/tests/mir-opt/inline/inline_cycle_generic.main.Inline.diff +++ b/tests/mir-opt/inline/inline_cycle_generic.main.Inline.diff @@ -6,18 +6,20 @@ let _1: (); // in scope 0 at $DIR/inline_cycle_generic.rs:+1:5: +1:24 + scope 1 (inlined ::call) { // at $DIR/inline_cycle_generic.rs:10:5: 10:24 + scope 2 (inlined as Call>::call) { // at $DIR/inline_cycle_generic.rs:39:9: 39:31 ++ scope 3 (inlined ::call) { // at $DIR/inline_cycle_generic.rs:32:9: 32:28 ++ } + } + } bb0: { StorageLive(_1); // scope 0 at $DIR/inline_cycle_generic.rs:+1:5: +1:24 - _1 = ::call() -> bb1; // scope 0 at $DIR/inline_cycle_generic.rs:+1:5: +1:24 -+ _1 = ::call() -> bb1; // scope 2 at $DIR/inline_cycle_generic.rs:32:9: 32:28 ++ _1 = as Call>::call() -> bb1; // scope 3 at $DIR/inline_cycle_generic.rs:24:9: 24:31 // mir::Constant - // + span: $DIR/inline_cycle_generic.rs:10:5: 10:22 - // + literal: Const { ty: fn() {::call}, val: Value() } -+ // + span: $DIR/inline_cycle_generic.rs:32:9: 32:26 -+ // + literal: Const { ty: fn() {::call}, val: Value() } ++ // + span: $DIR/inline_cycle_generic.rs:24:9: 24:29 ++ // + literal: Const { ty: fn() { as Call>::call}, val: Value() } } bb1: { diff --git a/tests/mir-opt/inline/inline_diverging.h.Inline.diff b/tests/mir-opt/inline/inline_diverging.h.Inline.diff index 255451e867030..78cd47c5f4bf4 100644 --- a/tests/mir-opt/inline/inline_diverging.h.Inline.diff +++ b/tests/mir-opt/inline/inline_diverging.h.Inline.diff @@ -20,6 +20,8 @@ + } + } + scope 4 (inlined ! {sleep} as Fn<()>>::call - shim(fn() -> ! {sleep})) { // at $DIR/inline_diverging.rs:27:13: 27:16 ++ scope 5 (inlined sleep) { // at $SRC_DIR/core/src/ops/function.rs:LL:COL ++ } + } + } @@ -40,44 +42,11 @@ + _3 = &_2; // scope 1 at $DIR/inline_diverging.rs:27:13: 27:14 + StorageLive(_8); // scope 1 at $DIR/inline_diverging.rs:27:13: 27:16 + _8 = const (); // scope 1 at $DIR/inline_diverging.rs:27:13: 27:16 -+ _4 = move (*_3)() -> [return: bb6, unwind: bb4]; // scope 4 at $SRC_DIR/core/src/ops/function.rs:LL:COL ++ goto -> bb1; // scope 5 at $DIR/inline_diverging.rs:39:5: 39:12 + } + + bb1: { -+ StorageDead(_5); // scope 2 at $DIR/inline_diverging.rs:28:15: 28:16 -+ StorageLive(_7); // scope 3 at $DIR/inline_diverging.rs:29:6: 29:7 -+ _7 = move _4; // scope 3 at $DIR/inline_diverging.rs:29:6: 29:7 -+ _1 = (move _7, move _6); // scope 3 at $DIR/inline_diverging.rs:29:5: 29:11 -+ StorageDead(_7); // scope 3 at $DIR/inline_diverging.rs:29:10: 29:11 -+ StorageDead(_4); // scope 1 at $DIR/inline_diverging.rs:30:1: 30:2 -+ drop(_2) -> bb2; // scope 1 at $DIR/inline_diverging.rs:30:1: 30:2 -+ } -+ -+ bb2: { -+ unreachable; // scope 0 at $DIR/inline_diverging.rs:30:2: 30:2 -+ } -+ -+ bb3 (cleanup): { -+ drop(_4) -> [return: bb4, unwind terminate]; // scope 1 at $DIR/inline_diverging.rs:30:1: 30:2 -+ } -+ -+ bb4 (cleanup): { -+ drop(_2) -> [return: bb5, unwind terminate]; // scope 1 at $DIR/inline_diverging.rs:30:1: 30:2 -+ } -+ -+ bb5 (cleanup): { -+ resume; // scope 1 at $DIR/inline_diverging.rs:26:1: 30:2 -+ } -+ -+ bb6: { -+ StorageDead(_8); // scope 1 at $DIR/inline_diverging.rs:27:13: 27:16 -+ StorageDead(_3); // scope 1 at $DIR/inline_diverging.rs:27:15: 27:16 -+ StorageLive(_5); // scope 2 at $DIR/inline_diverging.rs:28:13: 28:14 -+ _5 = &_2; // scope 2 at $DIR/inline_diverging.rs:28:13: 28:14 -+ _6 = ! {sleep} as Fn<()>>::call(move _5, const ()) -> [return: bb1, unwind: bb3]; // scope 2 at $DIR/inline_diverging.rs:28:13: 28:16 -+ // mir::Constant -+ // + span: $DIR/inline_diverging.rs:28:13: 28:14 -+ // + literal: Const { ty: for<'a> extern "rust-call" fn(&'a fn() -> ! {sleep}, ()) -> ! {sleep} as FnOnce<()>>::Output { ! {sleep} as Fn<()>>::call}, val: Value() } ++ goto -> bb1; // scope 5 at $DIR/inline_diverging.rs:39:5: 39:12 } } diff --git a/tests/mir-opt/lower_intrinsics.align_of.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.align_of.LowerIntrinsics.diff index dd742d87a2955..3530f4a807f2c 100644 --- a/tests/mir-opt/lower_intrinsics.align_of.LowerIntrinsics.diff +++ b/tests/mir-opt/lower_intrinsics.align_of.LowerIntrinsics.diff @@ -7,7 +7,7 @@ bb0: { - _0 = std::intrinsics::min_align_of::() -> [return: bb1, unwind unreachable]; // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:42 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:21:5: 21:40 +- // + span: $DIR/lower_intrinsics.rs:27:5: 27:40 - // + literal: Const { ty: extern "rust-intrinsic" fn() -> usize {std::intrinsics::min_align_of::}, val: Value() } + _0 = AlignOf(T); // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:42 + goto -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:42 diff --git a/tests/mir-opt/lower_intrinsics.assume.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.assume.LowerIntrinsics.diff index e95c3f9bcc707..158ce62e209c2 100644 --- a/tests/mir-opt/lower_intrinsics.assume.LowerIntrinsics.diff +++ b/tests/mir-opt/lower_intrinsics.assume.LowerIntrinsics.diff @@ -11,7 +11,7 @@ StorageLive(_1); // scope 1 at $DIR/lower_intrinsics.rs:+2:9: +2:38 - _1 = std::intrinsics::assume(const true) -> [return: bb1, unwind unreachable]; // scope 1 at $DIR/lower_intrinsics.rs:+2:9: +2:38 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:106:9: 106:32 +- // + span: $DIR/lower_intrinsics.rs:112:9: 112:32 - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(bool) {std::intrinsics::assume}, val: Value() } + assume(const true); // scope 1 at $DIR/lower_intrinsics.rs:+2:9: +2:38 + goto -> bb1; // scope 1 at $DIR/lower_intrinsics.rs:+2:9: +2:38 diff --git a/tests/mir-opt/lower_intrinsics.discriminant.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.discriminant.LowerIntrinsics.diff index 1f03b7b0baf4b..6fc9616d85540 100644 --- a/tests/mir-opt/lower_intrinsics.discriminant.LowerIntrinsics.diff +++ b/tests/mir-opt/lower_intrinsics.discriminant.LowerIntrinsics.diff @@ -31,7 +31,7 @@ _3 = &(*_4); // scope 0 at $DIR/lower_intrinsics.rs:+1:42: +1:44 - _2 = discriminant_value::(move _3) -> [return: bb1, unwind unreachable]; // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:45 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:82:5: 82:41 +- // + span: $DIR/lower_intrinsics.rs:88:5: 88:41 - // + literal: Const { ty: for<'a> extern "rust-intrinsic" fn(&'a T) -> ::Discriminant {discriminant_value::}, val: Value() } + _2 = discriminant((*_3)); // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:45 + goto -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:45 @@ -46,13 +46,13 @@ StorageLive(_7); // scope 0 at $DIR/lower_intrinsics.rs:+2:42: +2:44 _19 = const _; // scope 0 at $DIR/lower_intrinsics.rs:+2:42: +2:44 // mir::Constant - // + span: $DIR/lower_intrinsics.rs:83:42: 83:44 + // + span: $DIR/lower_intrinsics.rs:89:42: 89:44 // + literal: Const { ty: &i32, val: Unevaluated(discriminant, [T], Some(promoted[2])) } _7 = &(*_19); // scope 0 at $DIR/lower_intrinsics.rs:+2:42: +2:44 _6 = &(*_7); // scope 0 at $DIR/lower_intrinsics.rs:+2:42: +2:44 - _5 = discriminant_value::(move _6) -> [return: bb2, unwind unreachable]; // scope 0 at $DIR/lower_intrinsics.rs:+2:5: +2:45 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:83:5: 83:41 +- // + span: $DIR/lower_intrinsics.rs:89:5: 89:41 - // + literal: Const { ty: for<'a> extern "rust-intrinsic" fn(&'a i32) -> ::Discriminant {discriminant_value::}, val: Value() } + _5 = discriminant((*_6)); // scope 0 at $DIR/lower_intrinsics.rs:+2:5: +2:45 + goto -> bb2; // scope 0 at $DIR/lower_intrinsics.rs:+2:5: +2:45 @@ -67,13 +67,13 @@ StorageLive(_11); // scope 0 at $DIR/lower_intrinsics.rs:+3:42: +3:45 _18 = const _; // scope 0 at $DIR/lower_intrinsics.rs:+3:42: +3:45 // mir::Constant - // + span: $DIR/lower_intrinsics.rs:84:42: 84:45 + // + span: $DIR/lower_intrinsics.rs:90:42: 90:45 // + literal: Const { ty: &(), val: Unevaluated(discriminant, [T], Some(promoted[1])) } _11 = &(*_18); // scope 0 at $DIR/lower_intrinsics.rs:+3:42: +3:45 _10 = &(*_11); // scope 0 at $DIR/lower_intrinsics.rs:+3:42: +3:45 - _9 = discriminant_value::<()>(move _10) -> [return: bb3, unwind unreachable]; // scope 0 at $DIR/lower_intrinsics.rs:+3:5: +3:46 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:84:5: 84:41 +- // + span: $DIR/lower_intrinsics.rs:90:5: 90:41 - // + literal: Const { ty: for<'a> extern "rust-intrinsic" fn(&'a ()) -> <() as DiscriminantKind>::Discriminant {discriminant_value::<()>}, val: Value() } + _9 = discriminant((*_10)); // scope 0 at $DIR/lower_intrinsics.rs:+3:5: +3:46 + goto -> bb3; // scope 0 at $DIR/lower_intrinsics.rs:+3:5: +3:46 @@ -88,13 +88,13 @@ StorageLive(_15); // scope 0 at $DIR/lower_intrinsics.rs:+4:42: +4:47 _17 = const _; // scope 0 at $DIR/lower_intrinsics.rs:+4:42: +4:47 // mir::Constant - // + span: $DIR/lower_intrinsics.rs:85:42: 85:47 + // + span: $DIR/lower_intrinsics.rs:91:42: 91:47 // + literal: Const { ty: &E, val: Unevaluated(discriminant, [T], Some(promoted[0])) } _15 = &(*_17); // scope 0 at $DIR/lower_intrinsics.rs:+4:42: +4:47 _14 = &(*_15); // scope 0 at $DIR/lower_intrinsics.rs:+4:42: +4:47 - _13 = discriminant_value::(move _14) -> [return: bb4, unwind unreachable]; // scope 0 at $DIR/lower_intrinsics.rs:+4:5: +4:48 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:85:5: 85:41 +- // + span: $DIR/lower_intrinsics.rs:91:5: 91:41 - // + literal: Const { ty: for<'a> extern "rust-intrinsic" fn(&'a E) -> ::Discriminant {discriminant_value::}, val: Value() } + _13 = discriminant((*_14)); // scope 0 at $DIR/lower_intrinsics.rs:+4:5: +4:48 + goto -> bb4; // scope 0 at $DIR/lower_intrinsics.rs:+4:5: +4:48 diff --git a/tests/mir-opt/lower_intrinsics.f_copy_nonoverlapping.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.f_copy_nonoverlapping.LowerIntrinsics.diff index a54e9a9320ee1..5b870ccf5ee28 100644 --- a/tests/mir-opt/lower_intrinsics.f_copy_nonoverlapping.LowerIntrinsics.diff +++ b/tests/mir-opt/lower_intrinsics.f_copy_nonoverlapping.LowerIntrinsics.diff @@ -49,7 +49,7 @@ StorageDead(_9); // scope 3 at $DIR/lower_intrinsics.rs:+4:90: +4:91 - _3 = copy_nonoverlapping::(move _4, move _8, const 0_usize) -> [return: bb1, unwind unreachable]; // scope 3 at $DIR/lower_intrinsics.rs:+4:9: +4:95 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:99:9: 99:28 +- // + span: $DIR/lower_intrinsics.rs:105:9: 105:28 - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(*const i32, *mut i32, usize) {copy_nonoverlapping::}, val: Value() } + copy_nonoverlapping(dst = move _8, src = move _4, count = const 0_usize); // scope 3 at $DIR/lower_intrinsics.rs:+4:9: +4:95 + goto -> bb1; // scope 3 at $DIR/lower_intrinsics.rs:+4:9: +4:95 diff --git a/tests/mir-opt/lower_intrinsics.forget.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.forget.LowerIntrinsics.diff index bfb000ccdb5c6..582a79f48d8c8 100644 --- a/tests/mir-opt/lower_intrinsics.forget.LowerIntrinsics.diff +++ b/tests/mir-opt/lower_intrinsics.forget.LowerIntrinsics.diff @@ -11,7 +11,7 @@ _2 = move _1; // scope 0 at $DIR/lower_intrinsics.rs:+1:30: +1:31 - _0 = std::intrinsics::forget::(move _2) -> [return: bb1, unwind unreachable]; // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:32 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:26:5: 26:29 +- // + span: $DIR/lower_intrinsics.rs:32:5: 32:29 - // + literal: Const { ty: extern "rust-intrinsic" fn(T) {std::intrinsics::forget::}, val: Value() } + _0 = const (); // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:32 + goto -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:32 diff --git a/tests/mir-opt/lower_intrinsics.non_const.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.non_const.LowerIntrinsics.diff index 64d82907c7e36..81ad97077b428 100644 --- a/tests/mir-opt/lower_intrinsics.non_const.LowerIntrinsics.diff +++ b/tests/mir-opt/lower_intrinsics.non_const.LowerIntrinsics.diff @@ -13,7 +13,7 @@ StorageLive(_1); // scope 0 at $DIR/lower_intrinsics.rs:+2:9: +2:18 _1 = std::intrinsics::size_of::; // scope 0 at $DIR/lower_intrinsics.rs:+2:21: +2:51 // mir::Constant - // + span: $DIR/lower_intrinsics.rs:37:21: 37:51 + // + span: $DIR/lower_intrinsics.rs:43:21: 43:51 // + literal: Const { ty: extern "rust-intrinsic" fn() -> usize {std::intrinsics::size_of::}, val: Value() } StorageLive(_2); // scope 1 at $DIR/lower_intrinsics.rs:+3:5: +3:14 _2 = _1; // scope 1 at $DIR/lower_intrinsics.rs:+3:5: +3:14 diff --git a/tests/mir-opt/lower_intrinsics.option_payload.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.option_payload.LowerIntrinsics.diff index b022e2ba42bb8..edc66e2c75ce4 100644 --- a/tests/mir-opt/lower_intrinsics.option_payload.LowerIntrinsics.diff +++ b/tests/mir-opt/lower_intrinsics.option_payload.LowerIntrinsics.diff @@ -24,7 +24,7 @@ _4 = &raw const (*_1); // scope 1 at $DIR/lower_intrinsics.rs:+2:55: +2:56 - _3 = option_payload_ptr::(move _4) -> [return: bb1, unwind unreachable]; // scope 1 at $DIR/lower_intrinsics.rs:+2:18: +2:57 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:137:18: 137:54 +- // + span: $DIR/lower_intrinsics.rs:143:18: 143:54 - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(*const Option) -> *const usize {option_payload_ptr::}, val: Value() } + _3 = &raw const (((*_4) as Some).0: usize); // scope 1 at $DIR/lower_intrinsics.rs:+2:18: +2:57 + goto -> bb1; // scope 1 at $DIR/lower_intrinsics.rs:+2:18: +2:57 @@ -37,7 +37,7 @@ _6 = &raw const (*_2); // scope 2 at $DIR/lower_intrinsics.rs:+3:55: +3:56 - _5 = option_payload_ptr::(move _6) -> [return: bb2, unwind unreachable]; // scope 2 at $DIR/lower_intrinsics.rs:+3:18: +3:57 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:138:18: 138:54 +- // + span: $DIR/lower_intrinsics.rs:144:18: 144:54 - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(*const Option) -> *const String {option_payload_ptr::}, val: Value() } + _5 = &raw const (((*_6) as Some).0: std::string::String); // scope 2 at $DIR/lower_intrinsics.rs:+3:18: +3:57 + goto -> bb2; // scope 2 at $DIR/lower_intrinsics.rs:+3:18: +3:57 diff --git a/tests/mir-opt/lower_intrinsics.ptr_offset.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.ptr_offset.LowerIntrinsics.diff index 60a1dd0ba7d09..1760efe77d98f 100644 --- a/tests/mir-opt/lower_intrinsics.ptr_offset.LowerIntrinsics.diff +++ b/tests/mir-opt/lower_intrinsics.ptr_offset.LowerIntrinsics.diff @@ -15,7 +15,7 @@ _4 = _2; // scope 0 at $DIR/lower_intrinsics.rs:+1:33: +1:34 - _0 = offset::<*const i32, isize>(move _3, move _4) -> [return: bb1, unwind unreachable]; // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:35 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:144:5: 144:29 +- // + span: $DIR/lower_intrinsics.rs:150:5: 150:29 - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(*const i32, isize) -> *const i32 {offset::<*const i32, isize>}, val: Value() } + _0 = Offset(move _3, move _4); // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:35 + goto -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:35 diff --git a/tests/mir-opt/lower_intrinsics.read_via_copy_primitive.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.read_via_copy_primitive.LowerIntrinsics.diff index 5805df48f544f..8583766348a6e 100644 --- a/tests/mir-opt/lower_intrinsics.read_via_copy_primitive.LowerIntrinsics.diff +++ b/tests/mir-opt/lower_intrinsics.read_via_copy_primitive.LowerIntrinsics.diff @@ -13,7 +13,7 @@ _2 = &raw const (*_1); // scope 1 at $DIR/lower_intrinsics.rs:+1:46: +1:47 - _0 = read_via_copy::(move _2) -> [return: bb1, unwind unreachable]; // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:48 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:119:14: 119:45 +- // + span: $DIR/lower_intrinsics.rs:125:14: 125:45 - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(*const i32) -> i32 {read_via_copy::}, val: Value() } + _0 = (*_2); // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:48 + goto -> bb1; // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:48 diff --git a/tests/mir-opt/lower_intrinsics.read_via_copy_uninhabited.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.read_via_copy_uninhabited.LowerIntrinsics.diff index 95b2ec49d8076..f64bc9dcf620c 100644 --- a/tests/mir-opt/lower_intrinsics.read_via_copy_uninhabited.LowerIntrinsics.diff +++ b/tests/mir-opt/lower_intrinsics.read_via_copy_uninhabited.LowerIntrinsics.diff @@ -13,7 +13,7 @@ _2 = &raw const (*_1); // scope 1 at $DIR/lower_intrinsics.rs:+1:46: +1:47 - _0 = read_via_copy::(move _2) -> unwind unreachable; // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:48 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:124:14: 124:45 +- // + span: $DIR/lower_intrinsics.rs:130:14: 130:45 - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(*const Never) -> Never {read_via_copy::}, val: Value() } + unreachable; // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:48 } diff --git a/tests/mir-opt/lower_intrinsics.rs b/tests/mir-opt/lower_intrinsics.rs index 0ca88a42e3fd0..30b5c78e647c8 100644 --- a/tests/mir-opt/lower_intrinsics.rs +++ b/tests/mir-opt/lower_intrinsics.rs @@ -11,6 +11,12 @@ pub fn wrapping(a: i32, b: i32) { let _z = core::intrinsics::wrapping_mul(a, b); } +// EMIT_MIR lower_intrinsics.unchecked.LowerIntrinsics.diff +pub unsafe fn unchecked(a: i32, b: i32) { + let _x = core::intrinsics::unchecked_div(a, b); + let _y = core::intrinsics::unchecked_rem(a, b); +} + // EMIT_MIR lower_intrinsics.size_of.LowerIntrinsics.diff pub fn size_of() -> usize { core::intrinsics::size_of::() diff --git a/tests/mir-opt/lower_intrinsics.size_of.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.size_of.LowerIntrinsics.diff index 27e8accea8be5..a880df6a5c236 100644 --- a/tests/mir-opt/lower_intrinsics.size_of.LowerIntrinsics.diff +++ b/tests/mir-opt/lower_intrinsics.size_of.LowerIntrinsics.diff @@ -7,7 +7,7 @@ bb0: { - _0 = std::intrinsics::size_of::() -> [return: bb1, unwind unreachable]; // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:37 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:16:5: 16:35 +- // + span: $DIR/lower_intrinsics.rs:22:5: 22:35 - // + literal: Const { ty: extern "rust-intrinsic" fn() -> usize {std::intrinsics::size_of::}, val: Value() } + _0 = SizeOf(T); // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:37 + goto -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:5: +1:37 diff --git a/tests/mir-opt/lower_intrinsics.transmute_inhabited.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.transmute_inhabited.LowerIntrinsics.diff index 1b3b7685185bc..cde7c64c57a56 100644 --- a/tests/mir-opt/lower_intrinsics.transmute_inhabited.LowerIntrinsics.diff +++ b/tests/mir-opt/lower_intrinsics.transmute_inhabited.LowerIntrinsics.diff @@ -13,7 +13,7 @@ _2 = _1; // scope 1 at $DIR/lower_intrinsics.rs:+1:34: +1:35 - _0 = transmute::(move _2) -> [return: bb1, unwind unreachable]; // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:36 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:43:14: 43:33 +- // + span: $DIR/lower_intrinsics.rs:49:14: 49:33 - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(std::cmp::Ordering) -> i8 {transmute::}, val: Value() } + _0 = move _2 as i8 (Transmute); // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:36 + goto -> bb1; // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:36 diff --git a/tests/mir-opt/lower_intrinsics.transmute_ref_dst.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.transmute_ref_dst.LowerIntrinsics.diff index c6a7d2287e75d..6fc0f3d3e3fee 100644 --- a/tests/mir-opt/lower_intrinsics.transmute_ref_dst.LowerIntrinsics.diff +++ b/tests/mir-opt/lower_intrinsics.transmute_ref_dst.LowerIntrinsics.diff @@ -13,7 +13,7 @@ _2 = _1; // scope 1 at $DIR/lower_intrinsics.rs:+1:34: +1:35 - _0 = transmute::<&T, *const T>(move _2) -> [return: bb1, unwind unreachable]; // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:36 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:53:14: 53:33 +- // + span: $DIR/lower_intrinsics.rs:59:14: 59:33 - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(&T) -> *const T {transmute::<&T, *const T>}, val: Value() } + _0 = move _2 as *const T (Transmute); // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:36 + goto -> bb1; // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:36 diff --git a/tests/mir-opt/lower_intrinsics.transmute_to_box_uninhabited.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.transmute_to_box_uninhabited.LowerIntrinsics.diff index aa5d9619d10a9..e6887a382a2d6 100644 --- a/tests/mir-opt/lower_intrinsics.transmute_to_box_uninhabited.LowerIntrinsics.diff +++ b/tests/mir-opt/lower_intrinsics.transmute_to_box_uninhabited.LowerIntrinsics.diff @@ -12,7 +12,7 @@ StorageLive(_1); // scope 0 at $DIR/lower_intrinsics.rs:+1:9: +1:10 - _1 = transmute::>(const 1_usize) -> [return: bb1, unwind unreachable]; // scope 0 at $DIR/lower_intrinsics.rs:+1:25: +1:52 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:70:25: 70:44 +- // + span: $DIR/lower_intrinsics.rs:76:25: 76:44 - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(usize) -> Box {transmute::>}, val: Value() } + _1 = const 1_usize as std::boxed::Box (Transmute); // scope 0 at $DIR/lower_intrinsics.rs:+1:25: +1:52 + goto -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:25: +1:52 diff --git a/tests/mir-opt/lower_intrinsics.transmute_to_mut_uninhabited.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.transmute_to_mut_uninhabited.LowerIntrinsics.diff index 5fafd45fe8526..b2a44b7c56114 100644 --- a/tests/mir-opt/lower_intrinsics.transmute_to_mut_uninhabited.LowerIntrinsics.diff +++ b/tests/mir-opt/lower_intrinsics.transmute_to_mut_uninhabited.LowerIntrinsics.diff @@ -12,7 +12,7 @@ StorageLive(_1); // scope 0 at $DIR/lower_intrinsics.rs:+1:9: +1:10 - _1 = transmute::(const 1_usize) -> [return: bb1, unwind unreachable]; // scope 0 at $DIR/lower_intrinsics.rs:+1:25: +1:52 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:64:25: 64:44 +- // + span: $DIR/lower_intrinsics.rs:70:25: 70:44 - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(usize) -> &mut Never {transmute::}, val: Value() } + _1 = const 1_usize as &mut Never (Transmute); // scope 0 at $DIR/lower_intrinsics.rs:+1:25: +1:52 + goto -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:25: +1:52 diff --git a/tests/mir-opt/lower_intrinsics.transmute_to_ref_uninhabited.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.transmute_to_ref_uninhabited.LowerIntrinsics.diff index 08dead1321194..c49d3aeff70b6 100644 --- a/tests/mir-opt/lower_intrinsics.transmute_to_ref_uninhabited.LowerIntrinsics.diff +++ b/tests/mir-opt/lower_intrinsics.transmute_to_ref_uninhabited.LowerIntrinsics.diff @@ -12,7 +12,7 @@ StorageLive(_1); // scope 0 at $DIR/lower_intrinsics.rs:+1:9: +1:10 - _1 = transmute::(const 1_usize) -> [return: bb1, unwind unreachable]; // scope 0 at $DIR/lower_intrinsics.rs:+1:21: +1:48 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:58:21: 58:40 +- // + span: $DIR/lower_intrinsics.rs:64:21: 64:40 - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(usize) -> &Never {transmute::}, val: Value() } + _1 = const 1_usize as &Never (Transmute); // scope 0 at $DIR/lower_intrinsics.rs:+1:21: +1:48 + goto -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:21: +1:48 diff --git a/tests/mir-opt/lower_intrinsics.transmute_uninhabited.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.transmute_uninhabited.LowerIntrinsics.diff index f0b76127dd5e6..06759d74a3205 100644 --- a/tests/mir-opt/lower_intrinsics.transmute_uninhabited.LowerIntrinsics.diff +++ b/tests/mir-opt/lower_intrinsics.transmute_uninhabited.LowerIntrinsics.diff @@ -13,7 +13,7 @@ _2 = _1; // scope 1 at $DIR/lower_intrinsics.rs:+1:47: +1:48 - _0 = transmute::<(), Never>(move _2) -> unwind unreachable; // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:49 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:48:14: 48:46 +- // + span: $DIR/lower_intrinsics.rs:54:14: 54:46 - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(()) -> Never {transmute::<(), Never>}, val: Value() } + _0 = move _2 as Never (Transmute); // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:49 + unreachable; // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:49 diff --git a/tests/mir-opt/lower_intrinsics.unchecked.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.unchecked.LowerIntrinsics.diff new file mode 100644 index 0000000000000..9bb43d850ebf3 --- /dev/null +++ b/tests/mir-opt/lower_intrinsics.unchecked.LowerIntrinsics.diff @@ -0,0 +1,60 @@ +- // MIR for `unchecked` before LowerIntrinsics ++ // MIR for `unchecked` after LowerIntrinsics + + fn unchecked(_1: i32, _2: i32) -> () { + debug a => _1; // in scope 0 at $DIR/lower_intrinsics.rs:+0:25: +0:26 + debug b => _2; // in scope 0 at $DIR/lower_intrinsics.rs:+0:33: +0:34 + let mut _0: (); // return place in scope 0 at $DIR/lower_intrinsics.rs:+0:41: +0:41 + let _3: i32; // in scope 0 at $DIR/lower_intrinsics.rs:+1:9: +1:11 + let mut _4: i32; // in scope 0 at $DIR/lower_intrinsics.rs:+1:46: +1:47 + let mut _5: i32; // in scope 0 at $DIR/lower_intrinsics.rs:+1:49: +1:50 + let mut _7: i32; // in scope 0 at $DIR/lower_intrinsics.rs:+2:46: +2:47 + let mut _8: i32; // in scope 0 at $DIR/lower_intrinsics.rs:+2:49: +2:50 + scope 1 { + debug _x => _3; // in scope 1 at $DIR/lower_intrinsics.rs:+1:9: +1:11 + let _6: i32; // in scope 1 at $DIR/lower_intrinsics.rs:+2:9: +2:11 + scope 2 { + debug _y => _6; // in scope 2 at $DIR/lower_intrinsics.rs:+2:9: +2:11 + } + } + + bb0: { + StorageLive(_3); // scope 0 at $DIR/lower_intrinsics.rs:+1:9: +1:11 + StorageLive(_4); // scope 0 at $DIR/lower_intrinsics.rs:+1:46: +1:47 + _4 = _1; // scope 0 at $DIR/lower_intrinsics.rs:+1:46: +1:47 + StorageLive(_5); // scope 0 at $DIR/lower_intrinsics.rs:+1:49: +1:50 + _5 = _2; // scope 0 at $DIR/lower_intrinsics.rs:+1:49: +1:50 +- _3 = unchecked_div::(move _4, move _5) -> [return: bb1, unwind unreachable]; // scope 0 at $DIR/lower_intrinsics.rs:+1:14: +1:51 +- // mir::Constant +- // + span: $DIR/lower_intrinsics.rs:16:14: 16:45 +- // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(i32, i32) -> i32 {unchecked_div::}, val: Value() } ++ _3 = Div(move _4, move _5); // scope 0 at $DIR/lower_intrinsics.rs:+1:14: +1:51 ++ goto -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:14: +1:51 + } + + bb1: { + StorageDead(_5); // scope 0 at $DIR/lower_intrinsics.rs:+1:50: +1:51 + StorageDead(_4); // scope 0 at $DIR/lower_intrinsics.rs:+1:50: +1:51 + StorageLive(_6); // scope 1 at $DIR/lower_intrinsics.rs:+2:9: +2:11 + StorageLive(_7); // scope 1 at $DIR/lower_intrinsics.rs:+2:46: +2:47 + _7 = _1; // scope 1 at $DIR/lower_intrinsics.rs:+2:46: +2:47 + StorageLive(_8); // scope 1 at $DIR/lower_intrinsics.rs:+2:49: +2:50 + _8 = _2; // scope 1 at $DIR/lower_intrinsics.rs:+2:49: +2:50 +- _6 = unchecked_rem::(move _7, move _8) -> [return: bb2, unwind unreachable]; // scope 1 at $DIR/lower_intrinsics.rs:+2:14: +2:51 +- // mir::Constant +- // + span: $DIR/lower_intrinsics.rs:17:14: 17:45 +- // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(i32, i32) -> i32 {unchecked_rem::}, val: Value() } ++ _6 = Rem(move _7, move _8); // scope 1 at $DIR/lower_intrinsics.rs:+2:14: +2:51 ++ goto -> bb2; // scope 1 at $DIR/lower_intrinsics.rs:+2:14: +2:51 + } + + bb2: { + StorageDead(_8); // scope 1 at $DIR/lower_intrinsics.rs:+2:50: +2:51 + StorageDead(_7); // scope 1 at $DIR/lower_intrinsics.rs:+2:50: +2:51 + _0 = const (); // scope 0 at $DIR/lower_intrinsics.rs:+0:41: +3:2 + StorageDead(_6); // scope 1 at $DIR/lower_intrinsics.rs:+3:1: +3:2 + StorageDead(_3); // scope 0 at $DIR/lower_intrinsics.rs:+3:1: +3:2 + return; // scope 0 at $DIR/lower_intrinsics.rs:+3:2: +3:2 + } + } + diff --git a/tests/mir-opt/lower_intrinsics.unreachable.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.unreachable.LowerIntrinsics.diff index 28e45909c3372..83c9c508bc0b0 100644 --- a/tests/mir-opt/lower_intrinsics.unreachable.LowerIntrinsics.diff +++ b/tests/mir-opt/lower_intrinsics.unreachable.LowerIntrinsics.diff @@ -13,7 +13,7 @@ StorageLive(_2); // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:45 - _2 = std::intrinsics::unreachable() -> unwind unreachable; // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:45 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:31:14: 31:43 +- // + span: $DIR/lower_intrinsics.rs:37:14: 37:43 - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn() -> ! {std::intrinsics::unreachable}, val: Value() } + unreachable; // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:45 } diff --git a/tests/mir-opt/lower_intrinsics.with_overflow.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.with_overflow.LowerIntrinsics.diff index 9cf4fbb88f389..4ae4466a60038 100644 --- a/tests/mir-opt/lower_intrinsics.with_overflow.LowerIntrinsics.diff +++ b/tests/mir-opt/lower_intrinsics.with_overflow.LowerIntrinsics.diff @@ -32,7 +32,7 @@ _5 = _2; // scope 0 at $DIR/lower_intrinsics.rs:+1:53: +1:54 - _3 = add_with_overflow::(move _4, move _5) -> [return: bb1, unwind unreachable]; // scope 0 at $DIR/lower_intrinsics.rs:+1:14: +1:55 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:112:14: 112:49 +- // + span: $DIR/lower_intrinsics.rs:118:14: 118:49 - // + literal: Const { ty: extern "rust-intrinsic" fn(i32, i32) -> (i32, bool) {add_with_overflow::}, val: Value() } + _3 = CheckedAdd(move _4, move _5); // scope 0 at $DIR/lower_intrinsics.rs:+1:14: +1:55 + goto -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:+1:14: +1:55 @@ -48,7 +48,7 @@ _8 = _2; // scope 1 at $DIR/lower_intrinsics.rs:+2:53: +2:54 - _6 = sub_with_overflow::(move _7, move _8) -> [return: bb2, unwind unreachable]; // scope 1 at $DIR/lower_intrinsics.rs:+2:14: +2:55 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:113:14: 113:49 +- // + span: $DIR/lower_intrinsics.rs:119:14: 119:49 - // + literal: Const { ty: extern "rust-intrinsic" fn(i32, i32) -> (i32, bool) {sub_with_overflow::}, val: Value() } + _6 = CheckedSub(move _7, move _8); // scope 1 at $DIR/lower_intrinsics.rs:+2:14: +2:55 + goto -> bb2; // scope 1 at $DIR/lower_intrinsics.rs:+2:14: +2:55 @@ -64,7 +64,7 @@ _11 = _2; // scope 2 at $DIR/lower_intrinsics.rs:+3:53: +3:54 - _9 = mul_with_overflow::(move _10, move _11) -> [return: bb3, unwind unreachable]; // scope 2 at $DIR/lower_intrinsics.rs:+3:14: +3:55 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:114:14: 114:49 +- // + span: $DIR/lower_intrinsics.rs:120:14: 120:49 - // + literal: Const { ty: extern "rust-intrinsic" fn(i32, i32) -> (i32, bool) {mul_with_overflow::}, val: Value() } + _9 = CheckedMul(move _10, move _11); // scope 2 at $DIR/lower_intrinsics.rs:+3:14: +3:55 + goto -> bb3; // scope 2 at $DIR/lower_intrinsics.rs:+3:14: +3:55 diff --git a/tests/mir-opt/lower_intrinsics.write_via_move_string.LowerIntrinsics.diff b/tests/mir-opt/lower_intrinsics.write_via_move_string.LowerIntrinsics.diff index 38d99f661dc64..2eabd7f626b69 100644 --- a/tests/mir-opt/lower_intrinsics.write_via_move_string.LowerIntrinsics.diff +++ b/tests/mir-opt/lower_intrinsics.write_via_move_string.LowerIntrinsics.diff @@ -17,7 +17,7 @@ _4 = move _2; // scope 1 at $DIR/lower_intrinsics.rs:+1:50: +1:51 - _0 = write_via_move::(move _3, move _4) -> [return: bb1, unwind unreachable]; // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:52 - // mir::Constant -- // + span: $DIR/lower_intrinsics.rs:129:14: 129:46 +- // + span: $DIR/lower_intrinsics.rs:135:14: 135:46 - // + literal: Const { ty: unsafe extern "rust-intrinsic" fn(*mut String, String) {write_via_move::}, val: Value() } + (*_3) = move _4; // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:52 + goto -> bb1; // scope 1 at $DIR/lower_intrinsics.rs:+1:14: +1:52 diff --git a/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.mir index 55945bbc8fcc0..dff3cbbe76d22 100644 --- a/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.mir @@ -38,37 +38,37 @@ fn checked_shl(_1: u32, _2: u32) -> Option { debug self => _4; // in scope 10 at $SRC_DIR/core/src/convert/mod.rs:LL:COL scope 11 (inlined >::try_from) { // at $SRC_DIR/core/src/convert/mod.rs:LL:COL debug value => _4; // in scope 11 at $SRC_DIR/core/src/convert/mod.rs:LL:COL - scope 21 (inlined >::into) { // at $SRC_DIR/core/src/convert/mod.rs:LL:COL - debug self => _4; // in scope 21 at $SRC_DIR/core/src/convert/mod.rs:LL:COL - scope 22 (inlined >::from) { // at $SRC_DIR/core/src/convert/mod.rs:LL:COL - debug t => _4; // in scope 22 at $SRC_DIR/core/src/convert/mod.rs:LL:COL + scope 12 (inlined >::into) { // at $SRC_DIR/core/src/convert/mod.rs:LL:COL + debug self => _4; // in scope 12 at $SRC_DIR/core/src/convert/mod.rs:LL:COL + scope 13 (inlined >::from) { // at $SRC_DIR/core/src/convert/mod.rs:LL:COL + debug t => _4; // in scope 13 at $SRC_DIR/core/src/convert/mod.rs:LL:COL } } } } - scope 12 (inlined Result::::ok) { // at $SRC_DIR/core/src/num/mod.rs:LL:COL - debug self => _5; // in scope 12 at $SRC_DIR/core/src/result.rs:LL:COL - let _6: u32; // in scope 12 at $SRC_DIR/core/src/result.rs:LL:COL - scope 13 { - debug x => _6; // in scope 13 at $SRC_DIR/core/src/result.rs:LL:COL + scope 14 (inlined Result::::ok) { // at $SRC_DIR/core/src/num/mod.rs:LL:COL + debug self => _5; // in scope 14 at $SRC_DIR/core/src/result.rs:LL:COL + let _6: u32; // in scope 14 at $SRC_DIR/core/src/result.rs:LL:COL + scope 15 { + debug x => _6; // in scope 15 at $SRC_DIR/core/src/result.rs:LL:COL } } - scope 14 (inlined #[track_caller] Option::::unwrap_unchecked) { // at $SRC_DIR/core/src/num/mod.rs:LL:COL - debug self => _7; // in scope 14 at $SRC_DIR/core/src/option.rs:LL:COL - let mut _13: &std::option::Option; // in scope 14 at $SRC_DIR/core/src/option.rs:LL:COL - scope 15 { - debug val => _8; // in scope 15 at $SRC_DIR/core/src/option.rs:LL:COL + scope 16 (inlined #[track_caller] Option::::unwrap_unchecked) { // at $SRC_DIR/core/src/num/mod.rs:LL:COL + debug self => _7; // in scope 16 at $SRC_DIR/core/src/option.rs:LL:COL + let mut _13: &std::option::Option; // in scope 16 at $SRC_DIR/core/src/option.rs:LL:COL + scope 17 { + debug val => _8; // in scope 17 at $SRC_DIR/core/src/option.rs:LL:COL } - scope 16 { - scope 18 (inlined unreachable_unchecked) { // at $SRC_DIR/core/src/option.rs:LL:COL - scope 19 { - scope 20 (inlined unreachable_unchecked::runtime) { // at $SRC_DIR/core/src/intrinsics.rs:LL:COL + scope 18 { + scope 20 (inlined unreachable_unchecked) { // at $SRC_DIR/core/src/option.rs:LL:COL + scope 21 { + scope 22 (inlined unreachable_unchecked::runtime) { // at $SRC_DIR/core/src/intrinsics.rs:LL:COL } } } } - scope 17 (inlined Option::::is_some) { // at $SRC_DIR/core/src/option.rs:LL:COL - debug self => _13; // in scope 17 at $SRC_DIR/core/src/option.rs:LL:COL + scope 19 (inlined Option::::is_some) { // at $SRC_DIR/core/src/option.rs:LL:COL + debug self => _13; // in scope 19 at $SRC_DIR/core/src/option.rs:LL:COL } } } @@ -84,7 +84,7 @@ fn checked_shl(_1: u32, _2: u32) -> Option { StorageLive(_10); // scope 0 at $DIR/checked_ops.rs:+1:7: +1:23 StorageLive(_11); // scope 0 at $DIR/checked_ops.rs:+1:7: +1:23 StorageLive(_9); // scope 3 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL - StorageLive(_4); // scope 3 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL + StorageLive(_4); // scope 5 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL StorageLive(_3); // scope 5 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL _3 = const 31_u32; // scope 5 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL _4 = BitAnd(_2, move _3); // scope 5 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL @@ -94,12 +94,12 @@ fn checked_shl(_1: u32, _2: u32) -> Option { StorageLive(_5); // scope 9 at $SRC_DIR/core/src/num/mod.rs:LL:COL _5 = Result::::Ok(_4); // scope 11 at $SRC_DIR/core/src/convert/mod.rs:LL:COL StorageLive(_6); // scope 9 at $SRC_DIR/core/src/num/mod.rs:LL:COL - _6 = move ((_5 as Ok).0: u32); // scope 12 at $SRC_DIR/core/src/result.rs:LL:COL - _7 = Option::::Some(move _6); // scope 13 at $SRC_DIR/core/src/result.rs:LL:COL + _6 = move ((_5 as Ok).0: u32); // scope 14 at $SRC_DIR/core/src/result.rs:LL:COL + _7 = Option::::Some(move _6); // scope 15 at $SRC_DIR/core/src/result.rs:LL:COL StorageDead(_6); // scope 9 at $SRC_DIR/core/src/num/mod.rs:LL:COL StorageDead(_5); // scope 9 at $SRC_DIR/core/src/num/mod.rs:LL:COL StorageLive(_13); // scope 9 at $SRC_DIR/core/src/num/mod.rs:LL:COL - _8 = move ((_7 as Some).0: u32); // scope 14 at $SRC_DIR/core/src/option.rs:LL:COL + _8 = move ((_7 as Some).0: u32); // scope 16 at $SRC_DIR/core/src/option.rs:LL:COL StorageDead(_13); // scope 9 at $SRC_DIR/core/src/num/mod.rs:LL:COL StorageDead(_7); // scope 9 at $SRC_DIR/core/src/num/mod.rs:LL:COL _9 = unchecked_shl::(_1, move _8) -> [return: bb1, unwind unreachable]; // scope 7 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL @@ -110,7 +110,7 @@ fn checked_shl(_1: u32, _2: u32) -> Option { bb1: { StorageDead(_8); // scope 7 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL - StorageDead(_4); // scope 3 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL + StorageDead(_4); // scope 5 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL _10 = Ge(_2, const _); // scope 3 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL _11 = move _9; // scope 3 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL StorageDead(_9); // scope 3 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL diff --git a/tests/mir-opt/pre-codegen/loops.int_range.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/loops.int_range.PreCodegen.after.mir index a5002cd6afa13..86e0a62b6f968 100644 --- a/tests/mir-opt/pre-codegen/loops.int_range.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/loops.int_range.PreCodegen.after.mir @@ -7,14 +7,14 @@ fn int_range(_1: usize, _2: usize) -> () { let mut _3: std::ops::Range; // in scope 0 at $DIR/loops.rs:+1:14: +1:24 let mut _4: std::ops::Range; // in scope 0 at $DIR/loops.rs:+1:14: +1:24 let mut _5: &mut std::ops::Range; // in scope 0 at $DIR/loops.rs:+1:14: +1:24 - let mut _9: std::option::Option; // in scope 0 at $DIR/loops.rs:+1:14: +1:24 - let mut _12: isize; // in scope 0 at $DIR/loops.rs:+1:5: +3:6 - let _14: (); // in scope 0 at $DIR/loops.rs:+1:14: +1:24 + let mut _11: std::option::Option; // in scope 0 at $DIR/loops.rs:+1:14: +1:24 + let mut _14: isize; // in scope 0 at $DIR/loops.rs:+1:5: +3:6 + let _16: (); // in scope 0 at $DIR/loops.rs:+1:14: +1:24 scope 1 { debug iter => _4; // in scope 1 at $DIR/loops.rs:+1:14: +1:24 - let _13: usize; // in scope 1 at $DIR/loops.rs:+1:9: +1:10 + let _15: usize; // in scope 1 at $DIR/loops.rs:+1:9: +1:10 scope 2 { - debug i => _13; // in scope 2 at $DIR/loops.rs:+1:9: +1:10 + debug i => _15; // in scope 2 at $DIR/loops.rs:+1:9: +1:10 } scope 4 (inlined iter::range::>::next) { // at $DIR/loops.rs:8:14: 8:24 debug self => _5; // in scope 4 at $SRC_DIR/core/src/iter/range.rs:LL:COL @@ -22,14 +22,20 @@ fn int_range(_1: usize, _2: usize) -> () { debug self => _5; // in scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL let mut _6: &usize; // in scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL let mut _7: &usize; // in scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL - let mut _8: bool; // in scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL - let _10: usize; // in scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL - let mut _11: usize; // in scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL + let mut _10: bool; // in scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL + let _12: usize; // in scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL + let mut _13: usize; // in scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL scope 6 { - debug old => _10; // in scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL + debug old => _12; // in scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL scope 7 { } } + scope 8 (inlined cmp::impls::::lt) { // at $SRC_DIR/core/src/iter/range.rs:LL:COL + debug self => _6; // in scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL + debug other => _7; // in scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL + let mut _8: usize; // in scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL + let mut _9: usize; // in scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL + } } } } @@ -45,74 +51,74 @@ fn int_range(_1: usize, _2: usize) -> () { } bb1: { - StorageLive(_9); // scope 1 at $DIR/loops.rs:+1:14: +1:24 + StorageLive(_11); // scope 1 at $DIR/loops.rs:+1:14: +1:24 _5 = &mut _4; // scope 1 at $DIR/loops.rs:+1:14: +1:24 - StorageLive(_10); // scope 4 at $SRC_DIR/core/src/iter/range.rs:LL:COL - StorageLive(_8); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL + StorageLive(_12); // scope 4 at $SRC_DIR/core/src/iter/range.rs:LL:COL + StorageLive(_10); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL StorageLive(_6); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL _6 = &((*_5).0: usize); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL StorageLive(_7); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL _7 = &((*_5).1: usize); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL - _8 = ::lt(move _6, move _7) -> bb2; // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL - // mir::Constant - // + span: $SRC_DIR/core/src/iter/range.rs:LL:COL - // + literal: Const { ty: for<'a, 'b> fn(&'a usize, &'b usize) -> bool {::lt}, val: Value() } - } - - bb2: { + StorageLive(_8); // scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL + _8 = (*_6); // scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL + StorageLive(_9); // scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL + _9 = (*_7); // scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL + _10 = Lt(move _8, move _9); // scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL + StorageDead(_9); // scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL + StorageDead(_8); // scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL StorageDead(_7); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL StorageDead(_6); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL - switchInt(move _8) -> [0: bb3, otherwise: bb4]; // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL + switchInt(move _10) -> [0: bb2, otherwise: bb3]; // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL } - bb3: { - _9 = Option::::None; // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL - goto -> bb6; // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL + bb2: { + _11 = Option::::None; // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL + goto -> bb5; // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL } - bb4: { - _10 = ((*_5).0: usize); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL - StorageLive(_11); // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL - _11 = ::forward_unchecked(_10, const 1_usize) -> bb5; // scope 7 at $SRC_DIR/core/src/iter/range.rs:LL:COL + bb3: { + _12 = ((*_5).0: usize); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL + StorageLive(_13); // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL + _13 = ::forward_unchecked(_12, const 1_usize) -> bb4; // scope 7 at $SRC_DIR/core/src/iter/range.rs:LL:COL // mir::Constant // + span: $SRC_DIR/core/src/iter/range.rs:LL:COL // + literal: Const { ty: unsafe fn(usize, usize) -> usize {::forward_unchecked}, val: Value() } } - bb5: { - ((*_5).0: usize) = move _11; // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL - StorageDead(_11); // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL - _9 = Option::::Some(_10); // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL - goto -> bb6; // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL + bb4: { + ((*_5).0: usize) = move _13; // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL + StorageDead(_13); // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL + _11 = Option::::Some(_12); // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL + goto -> bb5; // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL } - bb6: { - StorageDead(_8); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL - StorageDead(_10); // scope 4 at $SRC_DIR/core/src/iter/range.rs:LL:COL - _12 = discriminant(_9); // scope 1 at $DIR/loops.rs:+1:14: +1:24 - switchInt(move _12) -> [0: bb7, 1: bb8, otherwise: bb10]; // scope 1 at $DIR/loops.rs:+1:14: +1:24 + bb5: { + StorageDead(_10); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL + StorageDead(_12); // scope 4 at $SRC_DIR/core/src/iter/range.rs:LL:COL + _14 = discriminant(_11); // scope 1 at $DIR/loops.rs:+1:14: +1:24 + switchInt(move _14) -> [0: bb6, 1: bb7, otherwise: bb9]; // scope 1 at $DIR/loops.rs:+1:14: +1:24 } - bb7: { - StorageDead(_9); // scope 1 at $DIR/loops.rs:+3:5: +3:6 + bb6: { + StorageDead(_11); // scope 1 at $DIR/loops.rs:+3:5: +3:6 StorageDead(_4); // scope 0 at $DIR/loops.rs:+3:5: +3:6 return; // scope 0 at $DIR/loops.rs:+4:2: +4:2 } - bb8: { - _13 = ((_9 as Some).0: usize); // scope 1 at $DIR/loops.rs:+1:9: +1:10 - _14 = opaque::(_13) -> bb9; // scope 2 at $DIR/loops.rs:+2:9: +2:18 + bb7: { + _15 = ((_11 as Some).0: usize); // scope 1 at $DIR/loops.rs:+1:9: +1:10 + _16 = opaque::(_15) -> bb8; // scope 2 at $DIR/loops.rs:+2:9: +2:18 // mir::Constant // + span: $DIR/loops.rs:9:9: 9:15 // + literal: Const { ty: fn(usize) {opaque::}, val: Value() } } - bb9: { - StorageDead(_9); // scope 1 at $DIR/loops.rs:+3:5: +3:6 + bb8: { + StorageDead(_11); // scope 1 at $DIR/loops.rs:+3:5: +3:6 goto -> bb1; // scope 1 at $DIR/loops.rs:+1:5: +3:6 } - bb10: { + bb9: { unreachable; // scope 1 at $DIR/loops.rs:+1:14: +1:24 } } diff --git a/tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.mir index 693939e75f4f4..06a4e35f1f900 100644 --- a/tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.mir @@ -8,16 +8,16 @@ fn forward_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () { let mut _4: std::ops::Range; // in scope 0 at $DIR/range_iter.rs:+1:14: +1:24 let mut _5: std::ops::Range; // in scope 0 at $DIR/range_iter.rs:+1:14: +1:24 let mut _6: &mut std::ops::Range; // in scope 0 at $DIR/range_iter.rs:+1:14: +1:24 - let mut _10: std::option::Option; // in scope 0 at $DIR/range_iter.rs:+1:14: +1:24 - let mut _13: isize; // in scope 0 at $DIR/range_iter.rs:+1:5: +3:6 - let mut _15: &impl Fn(u32); // in scope 0 at $DIR/range_iter.rs:+2:9: +2:10 - let mut _16: (u32,); // in scope 0 at $DIR/range_iter.rs:+2:9: +2:13 - let _17: (); // in scope 0 at $DIR/range_iter.rs:+1:14: +1:24 + let mut _12: std::option::Option; // in scope 0 at $DIR/range_iter.rs:+1:14: +1:24 + let mut _15: isize; // in scope 0 at $DIR/range_iter.rs:+1:5: +3:6 + let mut _17: &impl Fn(u32); // in scope 0 at $DIR/range_iter.rs:+2:9: +2:10 + let mut _18: (u32,); // in scope 0 at $DIR/range_iter.rs:+2:9: +2:13 + let _19: (); // in scope 0 at $DIR/range_iter.rs:+1:14: +1:24 scope 1 { debug iter => _5; // in scope 1 at $DIR/range_iter.rs:+1:14: +1:24 - let _14: u32; // in scope 1 at $DIR/range_iter.rs:+1:9: +1:10 + let _16: u32; // in scope 1 at $DIR/range_iter.rs:+1:9: +1:10 scope 2 { - debug x => _14; // in scope 2 at $DIR/range_iter.rs:+1:9: +1:10 + debug x => _16; // in scope 2 at $DIR/range_iter.rs:+1:9: +1:10 } scope 4 (inlined iter::range::>::next) { // at $DIR/range_iter.rs:21:14: 21:24 debug self => _6; // in scope 4 at $SRC_DIR/core/src/iter/range.rs:LL:COL @@ -25,14 +25,20 @@ fn forward_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () { debug self => _6; // in scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL let mut _7: &u32; // in scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL let mut _8: &u32; // in scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL - let mut _9: bool; // in scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL - let _11: u32; // in scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL - let mut _12: u32; // in scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL + let mut _11: bool; // in scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL + let _13: u32; // in scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL + let mut _14: u32; // in scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL scope 6 { - debug old => _11; // in scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL + debug old => _13; // in scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL scope 7 { } } + scope 8 (inlined cmp::impls::::lt) { // at $SRC_DIR/core/src/iter/range.rs:LL:COL + debug self => _7; // in scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL + debug other => _8; // in scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL + let mut _9: u32; // in scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL + let mut _10: u32; // in scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL + } } } } @@ -48,92 +54,92 @@ fn forward_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () { } bb1: { - StorageLive(_10); // scope 1 at $DIR/range_iter.rs:+1:14: +1:24 + StorageLive(_12); // scope 1 at $DIR/range_iter.rs:+1:14: +1:24 _6 = &mut _5; // scope 1 at $DIR/range_iter.rs:+1:14: +1:24 - StorageLive(_11); // scope 4 at $SRC_DIR/core/src/iter/range.rs:LL:COL - StorageLive(_9); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL + StorageLive(_13); // scope 4 at $SRC_DIR/core/src/iter/range.rs:LL:COL + StorageLive(_11); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL StorageLive(_7); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL _7 = &((*_6).0: u32); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL StorageLive(_8); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL _8 = &((*_6).1: u32); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL - _9 = ::lt(move _7, move _8) -> [return: bb2, unwind: bb12]; // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL - // mir::Constant - // + span: $SRC_DIR/core/src/iter/range.rs:LL:COL - // + literal: Const { ty: for<'a, 'b> fn(&'a u32, &'b u32) -> bool {::lt}, val: Value() } - } - - bb2: { + StorageLive(_9); // scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL + _9 = (*_7); // scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL + StorageLive(_10); // scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL + _10 = (*_8); // scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL + _11 = Lt(move _9, move _10); // scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL + StorageDead(_10); // scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL + StorageDead(_9); // scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL StorageDead(_8); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL StorageDead(_7); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL - switchInt(move _9) -> [0: bb3, otherwise: bb4]; // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL + switchInt(move _11) -> [0: bb2, otherwise: bb3]; // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL } - bb3: { - _10 = Option::::None; // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL - goto -> bb6; // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL + bb2: { + _12 = Option::::None; // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL + goto -> bb5; // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL } - bb4: { - _11 = ((*_6).0: u32); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL - StorageLive(_12); // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL - _12 = ::forward_unchecked(_11, const 1_usize) -> [return: bb5, unwind: bb12]; // scope 7 at $SRC_DIR/core/src/iter/range.rs:LL:COL + bb3: { + _13 = ((*_6).0: u32); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL + StorageLive(_14); // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL + _14 = ::forward_unchecked(_13, const 1_usize) -> [return: bb4, unwind: bb11]; // scope 7 at $SRC_DIR/core/src/iter/range.rs:LL:COL // mir::Constant // + span: $SRC_DIR/core/src/iter/range.rs:LL:COL // + literal: Const { ty: unsafe fn(u32, usize) -> u32 {::forward_unchecked}, val: Value() } } - bb5: { - ((*_6).0: u32) = move _12; // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL - StorageDead(_12); // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL - _10 = Option::::Some(_11); // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL - goto -> bb6; // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL + bb4: { + ((*_6).0: u32) = move _14; // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL + StorageDead(_14); // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL + _12 = Option::::Some(_13); // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL + goto -> bb5; // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL } - bb6: { - StorageDead(_9); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL - StorageDead(_11); // scope 4 at $SRC_DIR/core/src/iter/range.rs:LL:COL - _13 = discriminant(_10); // scope 1 at $DIR/range_iter.rs:+1:14: +1:24 - switchInt(move _13) -> [0: bb7, 1: bb9, otherwise: bb11]; // scope 1 at $DIR/range_iter.rs:+1:14: +1:24 + bb5: { + StorageDead(_11); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL + StorageDead(_13); // scope 4 at $SRC_DIR/core/src/iter/range.rs:LL:COL + _15 = discriminant(_12); // scope 1 at $DIR/range_iter.rs:+1:14: +1:24 + switchInt(move _15) -> [0: bb6, 1: bb8, otherwise: bb10]; // scope 1 at $DIR/range_iter.rs:+1:14: +1:24 } - bb7: { - StorageDead(_10); // scope 1 at $DIR/range_iter.rs:+3:5: +3:6 + bb6: { + StorageDead(_12); // scope 1 at $DIR/range_iter.rs:+3:5: +3:6 StorageDead(_5); // scope 0 at $DIR/range_iter.rs:+3:5: +3:6 - drop(_3) -> bb8; // scope 0 at $DIR/range_iter.rs:+4:1: +4:2 + drop(_3) -> bb7; // scope 0 at $DIR/range_iter.rs:+4:1: +4:2 } - bb8: { + bb7: { return; // scope 0 at $DIR/range_iter.rs:+4:2: +4:2 } - bb9: { - _14 = ((_10 as Some).0: u32); // scope 1 at $DIR/range_iter.rs:+1:9: +1:10 - StorageLive(_15); // scope 2 at $DIR/range_iter.rs:+2:9: +2:10 - _15 = &_3; // scope 2 at $DIR/range_iter.rs:+2:9: +2:10 - StorageLive(_16); // scope 2 at $DIR/range_iter.rs:+2:9: +2:13 - _16 = (_14,); // scope 2 at $DIR/range_iter.rs:+2:9: +2:13 - _17 = >::call(move _15, move _16) -> [return: bb10, unwind: bb12]; // scope 2 at $DIR/range_iter.rs:+2:9: +2:13 + bb8: { + _16 = ((_12 as Some).0: u32); // scope 1 at $DIR/range_iter.rs:+1:9: +1:10 + StorageLive(_17); // scope 2 at $DIR/range_iter.rs:+2:9: +2:10 + _17 = &_3; // scope 2 at $DIR/range_iter.rs:+2:9: +2:10 + StorageLive(_18); // scope 2 at $DIR/range_iter.rs:+2:9: +2:13 + _18 = (_16,); // scope 2 at $DIR/range_iter.rs:+2:9: +2:13 + _19 = >::call(move _17, move _18) -> [return: bb9, unwind: bb11]; // scope 2 at $DIR/range_iter.rs:+2:9: +2:13 // mir::Constant // + span: $DIR/range_iter.rs:22:9: 22:10 // + literal: Const { ty: for<'a> extern "rust-call" fn(&'a impl Fn(u32), (u32,)) -> >::Output {>::call}, val: Value() } } - bb10: { - StorageDead(_16); // scope 2 at $DIR/range_iter.rs:+2:12: +2:13 - StorageDead(_15); // scope 2 at $DIR/range_iter.rs:+2:12: +2:13 - StorageDead(_10); // scope 1 at $DIR/range_iter.rs:+3:5: +3:6 + bb9: { + StorageDead(_18); // scope 2 at $DIR/range_iter.rs:+2:12: +2:13 + StorageDead(_17); // scope 2 at $DIR/range_iter.rs:+2:12: +2:13 + StorageDead(_12); // scope 1 at $DIR/range_iter.rs:+3:5: +3:6 goto -> bb1; // scope 1 at $DIR/range_iter.rs:+1:5: +3:6 } - bb11: { + bb10: { unreachable; // scope 1 at $DIR/range_iter.rs:+1:14: +1:24 } - bb12 (cleanup): { - drop(_3) -> [return: bb13, unwind terminate]; // scope 0 at $DIR/range_iter.rs:+4:1: +4:2 + bb11 (cleanup): { + drop(_3) -> [return: bb12, unwind terminate]; // scope 0 at $DIR/range_iter.rs:+4:1: +4:2 } - bb13 (cleanup): { + bb12 (cleanup): { resume; // scope 0 at $DIR/range_iter.rs:+0:1: +4:2 } } diff --git a/tests/mir-opt/pre-codegen/range_iter.range_iter_next.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/range_iter.range_iter_next.PreCodegen.after.mir index 668ec31c6c134..f15722deee013 100644 --- a/tests/mir-opt/pre-codegen/range_iter.range_iter_next.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/range_iter.range_iter_next.PreCodegen.after.mir @@ -9,60 +9,66 @@ fn range_iter_next(_1: &mut std::ops::Range) -> Option { debug self => _1; // in scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL let mut _2: &u32; // in scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL let mut _3: &u32; // in scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL - let mut _4: bool; // in scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL - let _5: u32; // in scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL - let mut _6: u32; // in scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL + let mut _6: bool; // in scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL + let _7: u32; // in scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL + let mut _8: u32; // in scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL scope 3 { - debug old => _5; // in scope 3 at $SRC_DIR/core/src/iter/range.rs:LL:COL + debug old => _7; // in scope 3 at $SRC_DIR/core/src/iter/range.rs:LL:COL scope 4 { } } + scope 5 (inlined cmp::impls::::lt) { // at $SRC_DIR/core/src/iter/range.rs:LL:COL + debug self => _2; // in scope 5 at $SRC_DIR/core/src/cmp.rs:LL:COL + debug other => _3; // in scope 5 at $SRC_DIR/core/src/cmp.rs:LL:COL + let mut _4: u32; // in scope 5 at $SRC_DIR/core/src/cmp.rs:LL:COL + let mut _5: u32; // in scope 5 at $SRC_DIR/core/src/cmp.rs:LL:COL + } } } bb0: { - StorageLive(_5); // scope 1 at $SRC_DIR/core/src/iter/range.rs:LL:COL - StorageLive(_4); // scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL + StorageLive(_7); // scope 1 at $SRC_DIR/core/src/iter/range.rs:LL:COL + StorageLive(_6); // scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL StorageLive(_2); // scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL _2 = &((*_1).0: u32); // scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL StorageLive(_3); // scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL _3 = &((*_1).1: u32); // scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL - _4 = ::lt(move _2, move _3) -> bb1; // scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL - // mir::Constant - // + span: $SRC_DIR/core/src/iter/range.rs:LL:COL - // + literal: Const { ty: for<'a, 'b> fn(&'a u32, &'b u32) -> bool {::lt}, val: Value() } - } - - bb1: { + StorageLive(_4); // scope 5 at $SRC_DIR/core/src/cmp.rs:LL:COL + _4 = (*_2); // scope 5 at $SRC_DIR/core/src/cmp.rs:LL:COL + StorageLive(_5); // scope 5 at $SRC_DIR/core/src/cmp.rs:LL:COL + _5 = (*_3); // scope 5 at $SRC_DIR/core/src/cmp.rs:LL:COL + _6 = Lt(move _4, move _5); // scope 5 at $SRC_DIR/core/src/cmp.rs:LL:COL + StorageDead(_5); // scope 5 at $SRC_DIR/core/src/cmp.rs:LL:COL + StorageDead(_4); // scope 5 at $SRC_DIR/core/src/cmp.rs:LL:COL StorageDead(_3); // scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL StorageDead(_2); // scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL - switchInt(move _4) -> [0: bb2, otherwise: bb3]; // scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL + switchInt(move _6) -> [0: bb1, otherwise: bb2]; // scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL } - bb2: { + bb1: { _0 = Option::::None; // scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL - goto -> bb5; // scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL + goto -> bb4; // scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL } - bb3: { - _5 = ((*_1).0: u32); // scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL - StorageLive(_6); // scope 3 at $SRC_DIR/core/src/iter/range.rs:LL:COL - _6 = ::forward_unchecked(_5, const 1_usize) -> bb4; // scope 4 at $SRC_DIR/core/src/iter/range.rs:LL:COL + bb2: { + _7 = ((*_1).0: u32); // scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL + StorageLive(_8); // scope 3 at $SRC_DIR/core/src/iter/range.rs:LL:COL + _8 = ::forward_unchecked(_7, const 1_usize) -> bb3; // scope 4 at $SRC_DIR/core/src/iter/range.rs:LL:COL // mir::Constant // + span: $SRC_DIR/core/src/iter/range.rs:LL:COL // + literal: Const { ty: unsafe fn(u32, usize) -> u32 {::forward_unchecked}, val: Value() } } - bb4: { - ((*_1).0: u32) = move _6; // scope 3 at $SRC_DIR/core/src/iter/range.rs:LL:COL - StorageDead(_6); // scope 3 at $SRC_DIR/core/src/iter/range.rs:LL:COL - _0 = Option::::Some(_5); // scope 3 at $SRC_DIR/core/src/iter/range.rs:LL:COL - goto -> bb5; // scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL + bb3: { + ((*_1).0: u32) = move _8; // scope 3 at $SRC_DIR/core/src/iter/range.rs:LL:COL + StorageDead(_8); // scope 3 at $SRC_DIR/core/src/iter/range.rs:LL:COL + _0 = Option::::Some(_7); // scope 3 at $SRC_DIR/core/src/iter/range.rs:LL:COL + goto -> bb4; // scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL } - bb5: { - StorageDead(_4); // scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL - StorageDead(_5); // scope 1 at $SRC_DIR/core/src/iter/range.rs:LL:COL + bb4: { + StorageDead(_6); // scope 2 at $SRC_DIR/core/src/iter/range.rs:LL:COL + StorageDead(_7); // scope 1 at $SRC_DIR/core/src/iter/range.rs:LL:COL return; // scope 0 at $DIR/range_iter.rs:+2:2: +2:2 } } diff --git a/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.mir index ca7a4a64f4574..870496f14ea90 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.mir @@ -8,21 +8,21 @@ fn range_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { let mut _4: std::ops::Range; // in scope 0 at $DIR/slice_iter.rs:+1:14: +1:28 let mut _5: std::ops::Range; // in scope 0 at $DIR/slice_iter.rs:+1:14: +1:28 let mut _6: &mut std::ops::Range; // in scope 0 at $DIR/slice_iter.rs:+1:14: +1:28 - let mut _10: std::option::Option; // in scope 0 at $DIR/slice_iter.rs:+1:14: +1:28 - let mut _13: isize; // in scope 0 at $DIR/slice_iter.rs:+1:5: +4:6 - let mut _15: usize; // in scope 0 at $DIR/slice_iter.rs:+2:18: +2:26 - let mut _16: bool; // in scope 0 at $DIR/slice_iter.rs:+2:18: +2:26 - let mut _18: &impl Fn(usize, &T); // in scope 0 at $DIR/slice_iter.rs:+3:9: +3:10 - let mut _19: (usize, &T); // in scope 0 at $DIR/slice_iter.rs:+3:9: +3:16 - let _20: (); // in scope 0 at $DIR/slice_iter.rs:+1:14: +1:28 + let mut _12: std::option::Option; // in scope 0 at $DIR/slice_iter.rs:+1:14: +1:28 + let mut _15: isize; // in scope 0 at $DIR/slice_iter.rs:+1:5: +4:6 + let mut _17: usize; // in scope 0 at $DIR/slice_iter.rs:+2:18: +2:26 + let mut _18: bool; // in scope 0 at $DIR/slice_iter.rs:+2:18: +2:26 + let mut _20: &impl Fn(usize, &T); // in scope 0 at $DIR/slice_iter.rs:+3:9: +3:10 + let mut _21: (usize, &T); // in scope 0 at $DIR/slice_iter.rs:+3:9: +3:16 + let _22: (); // in scope 0 at $DIR/slice_iter.rs:+1:14: +1:28 scope 1 { debug iter => _5; // in scope 1 at $DIR/slice_iter.rs:+1:14: +1:28 - let _14: usize; // in scope 1 at $DIR/slice_iter.rs:+1:9: +1:10 + let _16: usize; // in scope 1 at $DIR/slice_iter.rs:+1:9: +1:10 scope 2 { - debug i => _14; // in scope 2 at $DIR/slice_iter.rs:+1:9: +1:10 - let _17: &T; // in scope 2 at $DIR/slice_iter.rs:+2:13: +2:14 + debug i => _16; // in scope 2 at $DIR/slice_iter.rs:+1:9: +1:10 + let _19: &T; // in scope 2 at $DIR/slice_iter.rs:+2:13: +2:14 scope 3 { - debug x => _17; // in scope 3 at $DIR/slice_iter.rs:+2:13: +2:14 + debug x => _19; // in scope 3 at $DIR/slice_iter.rs:+2:13: +2:14 } } scope 5 (inlined iter::range::>::next) { // at $DIR/slice_iter.rs:49:14: 49:28 @@ -31,14 +31,20 @@ fn range_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { debug self => _6; // in scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL let mut _7: &usize; // in scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL let mut _8: &usize; // in scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL - let mut _9: bool; // in scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL - let _11: usize; // in scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL - let mut _12: usize; // in scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL + let mut _11: bool; // in scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL + let _13: usize; // in scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL + let mut _14: usize; // in scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL scope 7 { - debug old => _11; // in scope 7 at $SRC_DIR/core/src/iter/range.rs:LL:COL + debug old => _13; // in scope 7 at $SRC_DIR/core/src/iter/range.rs:LL:COL scope 8 { } } + scope 9 (inlined cmp::impls::::lt) { // at $SRC_DIR/core/src/iter/range.rs:LL:COL + debug self => _7; // in scope 9 at $SRC_DIR/core/src/cmp.rs:LL:COL + debug other => _8; // in scope 9 at $SRC_DIR/core/src/cmp.rs:LL:COL + let mut _9: usize; // in scope 9 at $SRC_DIR/core/src/cmp.rs:LL:COL + let mut _10: usize; // in scope 9 at $SRC_DIR/core/src/cmp.rs:LL:COL + } } } } @@ -57,99 +63,99 @@ fn range_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { } bb1: { - StorageLive(_10); // scope 1 at $DIR/slice_iter.rs:+1:14: +1:28 + StorageLive(_12); // scope 1 at $DIR/slice_iter.rs:+1:14: +1:28 _6 = &mut _5; // scope 1 at $DIR/slice_iter.rs:+1:14: +1:28 - StorageLive(_11); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL - StorageLive(_9); // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL + StorageLive(_13); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL + StorageLive(_11); // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL StorageLive(_7); // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL _7 = &((*_6).0: usize); // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL StorageLive(_8); // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL _8 = &((*_6).1: usize); // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL - _9 = ::lt(move _7, move _8) -> [return: bb2, unwind: bb13]; // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL - // mir::Constant - // + span: $SRC_DIR/core/src/iter/range.rs:LL:COL - // + literal: Const { ty: for<'a, 'b> fn(&'a usize, &'b usize) -> bool {::lt}, val: Value() } - } - - bb2: { + StorageLive(_9); // scope 9 at $SRC_DIR/core/src/cmp.rs:LL:COL + _9 = (*_7); // scope 9 at $SRC_DIR/core/src/cmp.rs:LL:COL + StorageLive(_10); // scope 9 at $SRC_DIR/core/src/cmp.rs:LL:COL + _10 = (*_8); // scope 9 at $SRC_DIR/core/src/cmp.rs:LL:COL + _11 = Lt(move _9, move _10); // scope 9 at $SRC_DIR/core/src/cmp.rs:LL:COL + StorageDead(_10); // scope 9 at $SRC_DIR/core/src/cmp.rs:LL:COL + StorageDead(_9); // scope 9 at $SRC_DIR/core/src/cmp.rs:LL:COL StorageDead(_8); // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL StorageDead(_7); // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL - switchInt(move _9) -> [0: bb3, otherwise: bb4]; // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL + switchInt(move _11) -> [0: bb2, otherwise: bb3]; // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL } - bb3: { - _10 = Option::::None; // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL - goto -> bb6; // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL + bb2: { + _12 = Option::::None; // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL + goto -> bb5; // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL } - bb4: { - _11 = ((*_6).0: usize); // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL - StorageLive(_12); // scope 7 at $SRC_DIR/core/src/iter/range.rs:LL:COL - _12 = ::forward_unchecked(_11, const 1_usize) -> [return: bb5, unwind: bb13]; // scope 8 at $SRC_DIR/core/src/iter/range.rs:LL:COL + bb3: { + _13 = ((*_6).0: usize); // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL + StorageLive(_14); // scope 7 at $SRC_DIR/core/src/iter/range.rs:LL:COL + _14 = ::forward_unchecked(_13, const 1_usize) -> [return: bb4, unwind: bb12]; // scope 8 at $SRC_DIR/core/src/iter/range.rs:LL:COL // mir::Constant // + span: $SRC_DIR/core/src/iter/range.rs:LL:COL // + literal: Const { ty: unsafe fn(usize, usize) -> usize {::forward_unchecked}, val: Value() } } + bb4: { + ((*_6).0: usize) = move _14; // scope 7 at $SRC_DIR/core/src/iter/range.rs:LL:COL + StorageDead(_14); // scope 7 at $SRC_DIR/core/src/iter/range.rs:LL:COL + _12 = Option::::Some(_13); // scope 7 at $SRC_DIR/core/src/iter/range.rs:LL:COL + goto -> bb5; // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL + } + bb5: { - ((*_6).0: usize) = move _12; // scope 7 at $SRC_DIR/core/src/iter/range.rs:LL:COL - StorageDead(_12); // scope 7 at $SRC_DIR/core/src/iter/range.rs:LL:COL - _10 = Option::::Some(_11); // scope 7 at $SRC_DIR/core/src/iter/range.rs:LL:COL - goto -> bb6; // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL + StorageDead(_11); // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL + StorageDead(_13); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL + _15 = discriminant(_12); // scope 1 at $DIR/slice_iter.rs:+1:14: +1:28 + switchInt(move _15) -> [0: bb6, 1: bb8, otherwise: bb11]; // scope 1 at $DIR/slice_iter.rs:+1:14: +1:28 } bb6: { - StorageDead(_9); // scope 6 at $SRC_DIR/core/src/iter/range.rs:LL:COL - StorageDead(_11); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL - _13 = discriminant(_10); // scope 1 at $DIR/slice_iter.rs:+1:14: +1:28 - switchInt(move _13) -> [0: bb7, 1: bb9, otherwise: bb12]; // scope 1 at $DIR/slice_iter.rs:+1:14: +1:28 + StorageDead(_12); // scope 1 at $DIR/slice_iter.rs:+4:5: +4:6 + StorageDead(_5); // scope 0 at $DIR/slice_iter.rs:+4:5: +4:6 + drop(_2) -> bb7; // scope 0 at $DIR/slice_iter.rs:+5:1: +5:2 } bb7: { - StorageDead(_10); // scope 1 at $DIR/slice_iter.rs:+4:5: +4:6 - StorageDead(_5); // scope 0 at $DIR/slice_iter.rs:+4:5: +4:6 - drop(_2) -> bb8; // scope 0 at $DIR/slice_iter.rs:+5:1: +5:2 + return; // scope 0 at $DIR/slice_iter.rs:+5:2: +5:2 } bb8: { - return; // scope 0 at $DIR/slice_iter.rs:+5:2: +5:2 + _16 = ((_12 as Some).0: usize); // scope 1 at $DIR/slice_iter.rs:+1:9: +1:10 + _17 = Len((*_1)); // scope 2 at $DIR/slice_iter.rs:+2:18: +2:26 + _18 = Lt(_16, _17); // scope 2 at $DIR/slice_iter.rs:+2:18: +2:26 + assert(move _18, "index out of bounds: the length is {} but the index is {}", move _17, _16) -> [success: bb9, unwind: bb12]; // scope 2 at $DIR/slice_iter.rs:+2:18: +2:26 } bb9: { - _14 = ((_10 as Some).0: usize); // scope 1 at $DIR/slice_iter.rs:+1:9: +1:10 - _15 = Len((*_1)); // scope 2 at $DIR/slice_iter.rs:+2:18: +2:26 - _16 = Lt(_14, _15); // scope 2 at $DIR/slice_iter.rs:+2:18: +2:26 - assert(move _16, "index out of bounds: the length is {} but the index is {}", move _15, _14) -> [success: bb10, unwind: bb13]; // scope 2 at $DIR/slice_iter.rs:+2:18: +2:26 - } - - bb10: { - _17 = &(*_1)[_14]; // scope 2 at $DIR/slice_iter.rs:+2:17: +2:26 - StorageLive(_18); // scope 3 at $DIR/slice_iter.rs:+3:9: +3:10 - _18 = &_2; // scope 3 at $DIR/slice_iter.rs:+3:9: +3:10 - StorageLive(_19); // scope 3 at $DIR/slice_iter.rs:+3:9: +3:16 - _19 = (_14, _17); // scope 3 at $DIR/slice_iter.rs:+3:9: +3:16 - _20 = >::call(move _18, move _19) -> [return: bb11, unwind: bb13]; // scope 3 at $DIR/slice_iter.rs:+3:9: +3:16 + _19 = &(*_1)[_16]; // scope 2 at $DIR/slice_iter.rs:+2:17: +2:26 + StorageLive(_20); // scope 3 at $DIR/slice_iter.rs:+3:9: +3:10 + _20 = &_2; // scope 3 at $DIR/slice_iter.rs:+3:9: +3:10 + StorageLive(_21); // scope 3 at $DIR/slice_iter.rs:+3:9: +3:16 + _21 = (_16, _19); // scope 3 at $DIR/slice_iter.rs:+3:9: +3:16 + _22 = >::call(move _20, move _21) -> [return: bb10, unwind: bb12]; // scope 3 at $DIR/slice_iter.rs:+3:9: +3:16 // mir::Constant // + span: $DIR/slice_iter.rs:51:9: 51:10 // + literal: Const { ty: for<'a> extern "rust-call" fn(&'a impl Fn(usize, &T), (usize, &T)) -> >::Output {>::call}, val: Value() } } - bb11: { - StorageDead(_19); // scope 3 at $DIR/slice_iter.rs:+3:15: +3:16 - StorageDead(_18); // scope 3 at $DIR/slice_iter.rs:+3:15: +3:16 - StorageDead(_10); // scope 1 at $DIR/slice_iter.rs:+4:5: +4:6 + bb10: { + StorageDead(_21); // scope 3 at $DIR/slice_iter.rs:+3:15: +3:16 + StorageDead(_20); // scope 3 at $DIR/slice_iter.rs:+3:15: +3:16 + StorageDead(_12); // scope 1 at $DIR/slice_iter.rs:+4:5: +4:6 goto -> bb1; // scope 1 at $DIR/slice_iter.rs:+1:5: +4:6 } - bb12: { + bb11: { unreachable; // scope 1 at $DIR/slice_iter.rs:+1:14: +1:28 } - bb13 (cleanup): { - drop(_2) -> [return: bb14, unwind terminate]; // scope 0 at $DIR/slice_iter.rs:+5:1: +5:2 + bb12 (cleanup): { + drop(_2) -> [return: bb13, unwind terminate]; // scope 0 at $DIR/slice_iter.rs:+5:1: +5:2 } - bb14 (cleanup): { + bb13 (cleanup): { resume; // scope 0 at $DIR/slice_iter.rs:+0:1: +5:2 } } diff --git a/tests/run-make-fulldeps/hotplug_codegen_backend/the_backend.rs b/tests/run-make-fulldeps/hotplug_codegen_backend/the_backend.rs index 7db100a08a106..086ca0bdf37d3 100644 --- a/tests/run-make-fulldeps/hotplug_codegen_backend/the_backend.rs +++ b/tests/run-make-fulldeps/hotplug_codegen_backend/the_backend.rs @@ -15,7 +15,7 @@ extern crate rustc_target; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_codegen_ssa::{CodegenResults, CrateInfo}; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::FxIndexMap; use rustc_errors::ErrorGuaranteed; use rustc_metadata::EncodedMetadata; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; @@ -49,11 +49,11 @@ impl CodegenBackend for TheBackend { ongoing_codegen: Box, _sess: &Session, _outputs: &OutputFilenames, - ) -> Result<(CodegenResults, FxHashMap), ErrorGuaranteed> { + ) -> Result<(CodegenResults, FxIndexMap), ErrorGuaranteed> { let codegen_results = ongoing_codegen .downcast::() .expect("in join_codegen: ongoing_codegen is not a CodegenResults"); - Ok((*codegen_results, FxHashMap::default())) + Ok((*codegen_results, FxIndexMap::default())) } fn link( @@ -62,7 +62,7 @@ impl CodegenBackend for TheBackend { codegen_results: CodegenResults, outputs: &OutputFilenames, ) -> Result<(), ErrorGuaranteed> { - use rustc_session::{config::CrateType, output::out_filename}; + use rustc_session::{config::{CrateType, OutFileName}, output::out_filename}; use std::io::Write; let crate_name = codegen_results.crate_info.local_crate_name; for &crate_type in sess.opts.crate_types.iter() { @@ -70,8 +70,16 @@ impl CodegenBackend for TheBackend { sess.fatal(format!("Crate type is {:?}", crate_type)); } let output_name = out_filename(sess, crate_type, &outputs, crate_name); - let mut out_file = ::std::fs::File::create(output_name).unwrap(); - write!(out_file, "This has been \"compiled\" successfully.").unwrap(); + match output_name { + OutFileName::Real(ref path) => { + let mut out_file = ::std::fs::File::create(path).unwrap(); + write!(out_file, "This has been \"compiled\" successfully.").unwrap(); + } + OutFileName::Stdout => { + let mut stdout = std::io::stdout(); + write!(stdout, "This has been \"compiled\" successfully.").unwrap(); + } + } } Ok(()) } diff --git a/tests/run-make-fulldeps/issue-19371/foo.rs b/tests/run-make-fulldeps/issue-19371/foo.rs index 53ec79e477bd4..6d08cfd07f89e 100644 --- a/tests/run-make-fulldeps/issue-19371/foo.rs +++ b/tests/run-make-fulldeps/issue-19371/foo.rs @@ -6,7 +6,7 @@ extern crate rustc_session; extern crate rustc_span; use rustc_interface::interface; -use rustc_session::config::{Input, Options, OutputType, OutputTypes}; +use rustc_session::config::{Input, Options, OutFileName, OutputType, OutputTypes}; use rustc_span::source_map::FileName; use std::path::PathBuf; @@ -50,7 +50,7 @@ fn compile(code: String, output: PathBuf, sysroot: PathBuf) { crate_cfg: Default::default(), crate_check_cfg: Default::default(), input, - output_file: Some(output), + output_file: Some(OutFileName::Real(output)), output_dir: None, file_loader: None, locale_resources: &[], diff --git a/tests/run-make/emit-to-stdout/Makefile b/tests/run-make/emit-to-stdout/Makefile new file mode 100644 index 0000000000000..80e9b6a4ded67 --- /dev/null +++ b/tests/run-make/emit-to-stdout/Makefile @@ -0,0 +1,51 @@ +include ../tools.mk + +SRC=test.rs +OUT=$(TMPDIR)/out + +all: asm llvm-ir dep-info mir llvm-bc obj metadata link multiple-types multiple-types-option-o + +asm: $(OUT) + $(RUSTC) --emit asm=$(OUT)/$@ $(SRC) + $(RUSTC) --emit asm=- $(SRC) | diff - $(OUT)/$@ +llvm-ir: $(OUT) + $(RUSTC) --emit llvm-ir=$(OUT)/$@ $(SRC) + $(RUSTC) --emit llvm-ir=- $(SRC) | diff - $(OUT)/$@ +dep-info: $(OUT) + $(RUSTC) -Z dep-info-omit-d-target=yes --emit dep-info=$(OUT)/$@ $(SRC) + $(RUSTC) --emit dep-info=- $(SRC) | diff - $(OUT)/$@ +mir: $(OUT) + $(RUSTC) --emit mir=$(OUT)/$@ $(SRC) + $(RUSTC) --emit mir=- $(SRC) | diff - $(OUT)/$@ + +llvm-bc: $(OUT) + $(RUSTC) --emit llvm-bc=- $(SRC) 1>/dev/ptmx 2>$(OUT)/$@ || true + diff $(OUT)/$@ emit-llvm-bc.stderr +obj: $(OUT) + $(RUSTC) --emit obj=- $(SRC) 1>/dev/ptmx 2>$(OUT)/$@ || true + diff $(OUT)/$@ emit-obj.stderr + +# For metadata output, a temporary directory will be created to hold the temporary +# metadata file. But when output is stdout, the temporary directory will be located +# in the same place as $(SRC), which is mounted as read-only in the tests. Thus as +# a workaround, $(SRC) is copied to the test output directory $(OUT) and we compile +# it there. +metadata: $(OUT) + cp $(SRC) $(OUT) + (cd $(OUT); $(RUSTC) --emit metadata=- $(SRC) 1>/dev/ptmx 2>$(OUT)/$@ || true) + diff $(OUT)/$@ emit-metadata.stderr + +link: $(OUT) + $(RUSTC) --emit link=- $(SRC) 1>/dev/ptmx 2>$(OUT)/$@ || true + diff $(OUT)/$@ emit-link.stderr + +multiple-types: $(OUT) + $(RUSTC) --emit asm=- --emit llvm-ir=- --emit dep-info=- --emit mir=- $(SRC) 2>$(OUT)/$@ || true + diff $(OUT)/$@ emit-multiple-types.stderr + +multiple-types-option-o: $(OUT) + $(RUSTC) -o - --emit asm,llvm-ir,dep-info,mir $(SRC) 2>$(OUT)/$@ || true + diff $(OUT)/$@ emit-multiple-types.stderr + +$(OUT): + mkdir -p $(OUT) diff --git a/tests/run-make/emit-to-stdout/emit-link.stderr b/tests/run-make/emit-to-stdout/emit-link.stderr new file mode 100644 index 0000000000000..a9d856503e629 --- /dev/null +++ b/tests/run-make/emit-to-stdout/emit-link.stderr @@ -0,0 +1,4 @@ +error: option `-o` or `--emit` is used to write binary output type `link` to stdout, but stdout is a tty + +error: aborting due to previous error + diff --git a/tests/run-make/emit-to-stdout/emit-llvm-bc.stderr b/tests/run-make/emit-to-stdout/emit-llvm-bc.stderr new file mode 100644 index 0000000000000..7b53bd16e7a54 --- /dev/null +++ b/tests/run-make/emit-to-stdout/emit-llvm-bc.stderr @@ -0,0 +1,4 @@ +error: option `-o` or `--emit` is used to write binary output type `llvm-bc` to stdout, but stdout is a tty + +error: aborting due to previous error + diff --git a/tests/run-make/emit-to-stdout/emit-metadata.stderr b/tests/run-make/emit-to-stdout/emit-metadata.stderr new file mode 100644 index 0000000000000..ee1e52937b987 --- /dev/null +++ b/tests/run-make/emit-to-stdout/emit-metadata.stderr @@ -0,0 +1,4 @@ +error: option `-o` or `--emit` is used to write binary output type `metadata` to stdout, but stdout is a tty + +error: aborting due to previous error + diff --git a/tests/run-make/emit-to-stdout/emit-multiple-types.stderr b/tests/run-make/emit-to-stdout/emit-multiple-types.stderr new file mode 100644 index 0000000000000..b8a683cd9ebe6 --- /dev/null +++ b/tests/run-make/emit-to-stdout/emit-multiple-types.stderr @@ -0,0 +1,4 @@ +error: can't use option `-o` or `--emit` to write multiple output types to stdout + +error: aborting due to previous error + diff --git a/tests/run-make/emit-to-stdout/emit-obj.stderr b/tests/run-make/emit-to-stdout/emit-obj.stderr new file mode 100644 index 0000000000000..b130353084415 --- /dev/null +++ b/tests/run-make/emit-to-stdout/emit-obj.stderr @@ -0,0 +1,4 @@ +error: option `-o` or `--emit` is used to write binary output type `obj` to stdout, but stdout is a tty + +error: aborting due to previous error + diff --git a/tests/run-make/emit-to-stdout/test.rs b/tests/run-make/emit-to-stdout/test.rs new file mode 100644 index 0000000000000..c1bfaa6cab5d9 --- /dev/null +++ b/tests/run-make/emit-to-stdout/test.rs @@ -0,0 +1 @@ +#![crate_type = "rlib"] diff --git a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/print.checks b/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/print.checks index 8a5493650a720..e02fe09488916 100644 --- a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/print.checks +++ b/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/print.checks @@ -1,6 +1,6 @@ CHECK: print CHECK: lfence -CHECK: lfence -CHECK: lfence -CHECK: callq 0x{{[[:xdigit:]]*}} <_Unwind_Resume> -CHECK-NEXT: ud2 +CHECK: popq +CHECK-NEXT: popq [[REGISTER:%[a-z]+]] +CHECK-NEXT: lfence +CHECK-NEXT: jmpq *[[REGISTER]] diff --git a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/script.sh b/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/script.sh index 235bb603b842b..04a34724518e8 100644 --- a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/script.sh +++ b/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/script.sh @@ -33,6 +33,15 @@ function check { ${objdump} --disassemble-symbols="${func}" --demangle \ ${enclave} > ${asm} ${filecheck} --input-file ${asm} ${checks} + + if [ "${func_re}" != "rust_plus_one_global_asm" && + "${func_re}" != "cmake_plus_one_c_global_asm" ]; then + # The assembler cannot avoid explicit `ret` instructions. Sequences + # of `shlq $0x0, (%rsp); lfence; retq` are used instead. + # https://www.intel.com/content/www/us/en/developer/articles/technical/ + # software-security-guidance/technical-documentation/load-value-injection.html + ${filecheck} --implicit-check-not ret --input-file ${asm} ${checks} + fi } build diff --git a/tests/rustdoc-gui/sidebar-mobile.goml b/tests/rustdoc-gui/sidebar-mobile.goml index 3b022c7e9d040..4b8337ace3a2f 100644 --- a/tests/rustdoc-gui/sidebar-mobile.goml +++ b/tests/rustdoc-gui/sidebar-mobile.goml @@ -73,16 +73,16 @@ define-function: ( call-function: ("check-colors", { "theme": "ayu", - "color": "rgb(197, 197, 197)", - "background": "rgb(20, 25, 31)", + "color": "#c5c5c5", + "background": "#14191f", }) call-function: ("check-colors", { "theme": "dark", - "color": "rgb(221, 221, 221)", - "background": "rgb(80, 80, 80)", + "color": "#ddd", + "background": "#505050", }) call-function: ("check-colors", { "theme": "light", - "color": "rgb(0, 0, 0)", - "background": "rgb(245, 245, 245)", + "color": "black", + "background": "#F5F5F5", }) diff --git a/tests/rustdoc-gui/theme-change.goml b/tests/rustdoc-gui/theme-change.goml index ae6947213897e..e4b031b735eb0 100644 --- a/tests/rustdoc-gui/theme-change.goml +++ b/tests/rustdoc-gui/theme-change.goml @@ -3,9 +3,9 @@ go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" set-local-storage: {"rustdoc-use-system-theme": "false", "rustdoc-theme": "dark"} reload: -store-value: (background_light, "rgb(255, 255, 255)") -store-value: (background_dark, "rgb(53, 53, 53)") -store-value: (background_ayu, "rgb(15, 20, 25)") +store-value: (background_light, "white") +store-value: (background_dark, "#353535") +store-value: (background_ayu, "#0f1419") click: "#settings-menu" wait-for: "#theme-ayu" diff --git a/tests/rustdoc-js-std/alias-1.js b/tests/rustdoc-js-std/alias-1.js index 7c6327fcdd7ca..b27b3da217966 100644 --- a/tests/rustdoc-js-std/alias-1.js +++ b/tests/rustdoc-js-std/alias-1.js @@ -1,6 +1,5 @@ -const QUERY = '&'; - const EXPECTED = { + 'query': '&', 'others': [ { 'path': 'std', 'name': 'reference' }, ], diff --git a/tests/rustdoc-js-std/alias-2.js b/tests/rustdoc-js-std/alias-2.js index 798fa29efbd2d..5735b573bcbda 100644 --- a/tests/rustdoc-js-std/alias-2.js +++ b/tests/rustdoc-js-std/alias-2.js @@ -1,6 +1,5 @@ -const QUERY = '+'; - const EXPECTED = { + 'query': '+', 'others': [ { 'path': 'std::ops', 'name': 'AddAssign' }, { 'path': 'std::ops', 'name': 'Add' }, diff --git a/tests/rustdoc-js-std/alias-3.js b/tests/rustdoc-js-std/alias-3.js index 392b1e8183786..ed3776b3c2ae0 100644 --- a/tests/rustdoc-js-std/alias-3.js +++ b/tests/rustdoc-js-std/alias-3.js @@ -1,6 +1,5 @@ -const QUERY = '!'; - const EXPECTED = { + 'query': '!', 'others': [ { 'path': 'std', 'name': 'never' }, ], diff --git a/tests/rustdoc-js-std/alias-4.js b/tests/rustdoc-js-std/alias-4.js index bf2bb4d2981fc..35840a472c10b 100644 --- a/tests/rustdoc-js-std/alias-4.js +++ b/tests/rustdoc-js-std/alias-4.js @@ -1,6 +1,5 @@ -const QUERY = '<'; - const EXPECTED = { + 'query': '<', 'others': [ { 'name': 'Ord' }, ], diff --git a/tests/rustdoc-js-std/alias.js b/tests/rustdoc-js-std/alias.js index 2b709c99119ae..bf707fa03dc2a 100644 --- a/tests/rustdoc-js-std/alias.js +++ b/tests/rustdoc-js-std/alias.js @@ -1,8 +1,7 @@ // ignore-order -const QUERY = '['; - const EXPECTED = { + 'query': '[', 'others': [ { 'path': 'std', 'name': 'slice' }, { 'path': 'std::ops', 'name': 'IndexMut' }, diff --git a/tests/rustdoc-js-std/asrawfd.js b/tests/rustdoc-js-std/asrawfd.js index 369a34f9c6eb7..5b3cfeabbcdd2 100644 --- a/tests/rustdoc-js-std/asrawfd.js +++ b/tests/rustdoc-js-std/asrawfd.js @@ -1,8 +1,7 @@ // ignore-order -const QUERY = 'RawFd::as_raw_fd'; - const EXPECTED = { + 'query': 'RawFd::as_raw_fd', 'others': [ // Reproduction test for https://github.com/rust-lang/rust/issues/78724 // Validate that type alias methods get the correct path. diff --git a/tests/rustdoc-js-std/basic.js b/tests/rustdoc-js-std/basic.js index 824cac7108332..baff24b0af699 100644 --- a/tests/rustdoc-js-std/basic.js +++ b/tests/rustdoc-js-std/basic.js @@ -1,6 +1,5 @@ -const QUERY = 'String'; - const EXPECTED = { + 'query': 'String', 'others': [ { 'path': 'std::string', 'name': 'String' }, { 'path': 'std::ffi', 'name': 'CString' }, diff --git a/tests/rustdoc-js-std/deduplication.js b/tests/rustdoc-js-std/deduplication.js index f02f6cf55ed21..51279dd5ed467 100644 --- a/tests/rustdoc-js-std/deduplication.js +++ b/tests/rustdoc-js-std/deduplication.js @@ -1,8 +1,7 @@ // ignore-order -const QUERY = 'is_nan'; - const EXPECTED = { + 'query': 'is_nan', 'others': [ { 'path': 'std::f32', 'name': 'is_nan' }, { 'path': 'std::f64', 'name': 'is_nan' }, diff --git a/tests/rustdoc-js-std/enum-option.js b/tests/rustdoc-js-std/enum-option.js index 902e09069108d..216dafe3b129e 100644 --- a/tests/rustdoc-js-std/enum-option.js +++ b/tests/rustdoc-js-std/enum-option.js @@ -1,6 +1,5 @@ -const QUERY = 'enum:Option'; - const EXPECTED = { + 'query': 'enum:Option', 'others': [ { 'path': 'std::option', 'name': 'Option' }, ], diff --git a/tests/rustdoc-js-std/filter-crate.js b/tests/rustdoc-js-std/filter-crate.js index b47a1fefa41d0..95f2969d29924 100644 --- a/tests/rustdoc-js-std/filter-crate.js +++ b/tests/rustdoc-js-std/filter-crate.js @@ -1,9 +1,9 @@ // exact-check -const QUERY = '"hashmap"'; const FILTER_CRATE = 'core'; const EXPECTED = { + 'query': 'hashmap', 'others': [ ], }; diff --git a/tests/rustdoc-js-std/fn-forget.js b/tests/rustdoc-js-std/fn-forget.js index 66a5fcaa7813d..addecf4e44fe4 100644 --- a/tests/rustdoc-js-std/fn-forget.js +++ b/tests/rustdoc-js-std/fn-forget.js @@ -1,6 +1,5 @@ -const QUERY = 'fn:forget'; - const EXPECTED = { + 'query': 'fn:forget', 'others': [ { 'path': 'std::mem', 'name': 'forget' }, { 'path': 'std::fmt', 'name': 'format' }, diff --git a/tests/rustdoc-js-std/from_u.js b/tests/rustdoc-js-std/from_u.js index e3f3cd436aa6a..7c9375ba529a8 100644 --- a/tests/rustdoc-js-std/from_u.js +++ b/tests/rustdoc-js-std/from_u.js @@ -1,6 +1,5 @@ -const QUERY = 'from_u'; - const EXPECTED = { + 'query': 'from_u', 'others': [ { 'path': 'std::char', 'name': 'from_u32' }, { 'path': 'std::str', 'name': 'from_utf8' }, diff --git a/tests/rustdoc-js-std/keyword.js b/tests/rustdoc-js-std/keyword.js index 868ddd7b6dceb..b85ba34138bae 100644 --- a/tests/rustdoc-js-std/keyword.js +++ b/tests/rustdoc-js-std/keyword.js @@ -1,8 +1,7 @@ // ignore-order -const QUERY = 'fn'; - const EXPECTED = { + 'query': 'fn', 'others': [ { 'path': 'std', 'name': 'fn', ty: 15 }, // 15 is for primitive types { 'path': 'std', 'name': 'fn', ty: 21 }, // 21 is for keywords diff --git a/tests/rustdoc-js-std/macro-check.js b/tests/rustdoc-js-std/macro-check.js index 242e0cbf5f4de..c22b1753fd71b 100644 --- a/tests/rustdoc-js-std/macro-check.js +++ b/tests/rustdoc-js-std/macro-check.js @@ -1,8 +1,7 @@ // ignore-order -const QUERY = 'panic'; - const EXPECTED = { + 'query': 'panic', 'others': [ { 'path': 'std', 'name': 'panic', ty: 14 }, // 15 is for macros { 'path': 'std', 'name': 'panic', ty: 0 }, // 0 is for modules diff --git a/tests/rustdoc-js-std/macro-print.js b/tests/rustdoc-js-std/macro-print.js index 1b4c7b4057020..2ef1c89e49b0f 100644 --- a/tests/rustdoc-js-std/macro-print.js +++ b/tests/rustdoc-js-std/macro-print.js @@ -1,6 +1,5 @@ -const QUERY = 'macro:print'; - const EXPECTED = { + 'query': 'macro:print', 'others': [ { 'path': 'std', 'name': 'print' }, { 'path': 'std', 'name': 'println' }, diff --git a/tests/rustdoc-js-std/never.js b/tests/rustdoc-js-std/never.js index 392b1e8183786..ed3776b3c2ae0 100644 --- a/tests/rustdoc-js-std/never.js +++ b/tests/rustdoc-js-std/never.js @@ -1,6 +1,5 @@ -const QUERY = '!'; - const EXPECTED = { + 'query': '!', 'others': [ { 'path': 'std', 'name': 'never' }, ], diff --git a/tests/rustdoc-js-std/option-type-signatures.js b/tests/rustdoc-js-std/option-type-signatures.js index 6bf421a213560..8f6b0450dd319 100644 --- a/tests/rustdoc-js-std/option-type-signatures.js +++ b/tests/rustdoc-js-std/option-type-signatures.js @@ -1,15 +1,12 @@ -const QUERY = [ - 'option, fnonce -> option', - 'option -> default', -]; - const EXPECTED = [ { + 'query': 'option, fnonce -> option', 'others': [ { 'path': 'std::option::Option', 'name': 'map' }, ], }, { + 'query': 'option -> default', 'others': [ { 'path': 'std::option::Option', 'name': 'unwrap_or_default' }, { 'path': 'std::option::Option', 'name': 'get_or_insert_default' }, diff --git a/tests/rustdoc-js-std/parser-errors.js b/tests/rustdoc-js-std/parser-errors.js index d1aa840ab08a2..aa8ee86d67247 100644 --- a/tests/rustdoc-js-std/parser-errors.js +++ b/tests/rustdoc-js-std/parser-errors.js @@ -1,50 +1,6 @@ -const QUERY = [ - '

', - '->

', - 'a<"P">', - '"P" "P"', - 'P "P"', - '"p" p', - '"const": p', - "a<:a>", - "a<::a>", - "((a))", - "(p -> p", - "::a::b", - "a::::b", - "a::b::", - ":a", - "a b:", - "a (b:", - "_:", - "_:a", - "a-bb", - "a>bb", - "ab'", - "a->", - '"p" ', - '"p" a', - "a,<", - "aaaaa<>b", - "fn:aaaaa<>b", - "->a<>b", - "a<->", - "a:: a", - "a ::a", - "a:", - "a<>:", - "a,:", - " a<> :", - "mod : :", - "a!a", - "a!!", - "mod:a!", - "a!::a", - "a<", -]; - const PARSED = [ { + query: '

', elems: [], foundElems: 0, original: "

", @@ -53,6 +9,7 @@ const PARSED = [ error: "Found generics without a path", }, { + query: '->

', elems: [], foundElems: 0, original: "->

", @@ -61,6 +18,7 @@ const PARSED = [ error: "Found generics without a path", }, { + query: 'a<"P">', elems: [], foundElems: 0, original: "a<\"P\">", @@ -69,6 +27,7 @@ const PARSED = [ error: "Unexpected `\"` in generics", }, { + query: '"P" "P"', elems: [], foundElems: 0, original: "\"P\" \"P\"", @@ -77,6 +36,7 @@ const PARSED = [ error: "Cannot have more than one literal search element", }, { + query: 'P "P"', elems: [], foundElems: 0, original: "P \"P\"", @@ -85,6 +45,7 @@ const PARSED = [ error: "Cannot use literal search when there is more than one element", }, { + query: '"p" p', elems: [], foundElems: 0, original: "\"p\" p", @@ -93,6 +54,7 @@ const PARSED = [ error: "You cannot have more than one element if you use quotes", }, { + query: '"const": p', elems: [], foundElems: 0, original: "\"const\": p", @@ -101,6 +63,7 @@ const PARSED = [ error: "You cannot use quotes on type filter", }, { + query: "a<:a>", elems: [], foundElems: 0, original: "a<:a>", @@ -109,6 +72,7 @@ const PARSED = [ error: "Expected type filter before `:`", }, { + query: "a<::a>", elems: [], foundElems: 0, original: "a<::a>", @@ -117,6 +81,7 @@ const PARSED = [ error: "Unexpected `::`: paths cannot start with `::`", }, { + query: "((a))", elems: [], foundElems: 0, original: "((a))", @@ -125,6 +90,7 @@ const PARSED = [ error: "Unexpected `(`", }, { + query: "(p -> p", elems: [], foundElems: 0, original: "(p -> p", @@ -133,6 +99,7 @@ const PARSED = [ error: "Unexpected `(`", }, { + query: "::a::b", elems: [], foundElems: 0, original: "::a::b", @@ -141,6 +108,7 @@ const PARSED = [ error: "Paths cannot start with `::`", }, { + query: "a::::b", elems: [], foundElems: 0, original: "a::::b", @@ -149,6 +117,7 @@ const PARSED = [ error: "Unexpected `::::`", }, { + query: "a::b::", elems: [], foundElems: 0, original: "a::b::", @@ -157,6 +126,7 @@ const PARSED = [ error: "Paths cannot end with `::`", }, { + query: ":a", elems: [], foundElems: 0, original: ":a", @@ -165,6 +135,7 @@ const PARSED = [ error: "Expected type filter before `:`", }, { + query: "a b:", elems: [], foundElems: 0, original: "a b:", @@ -173,6 +144,7 @@ const PARSED = [ error: "Unexpected `:` (expected path after type filter)", }, { + query: "a (b:", elems: [], foundElems: 0, original: "a (b:", @@ -181,6 +153,7 @@ const PARSED = [ error: "Unexpected `(`", }, { + query: "_:", elems: [], foundElems: 0, original: "_:", @@ -189,6 +162,7 @@ const PARSED = [ error: "Unexpected `:` (expected path after type filter)", }, { + query: "_:a", elems: [], foundElems: 0, original: "_:a", @@ -197,6 +171,7 @@ const PARSED = [ error: "Unknown type filter `_`", }, { + query: "a-bb", elems: [], foundElems: 0, original: "a-bb", @@ -205,6 +180,7 @@ const PARSED = [ error: "Unexpected `-` (did you mean `->`?)", }, { + query: "a>bb", elems: [], foundElems: 0, original: "a>bb", @@ -213,6 +189,7 @@ const PARSED = [ error: "Unexpected `>` (did you mean `->`?)", }, { + query: "ab'", elems: [], foundElems: 0, original: "ab'", @@ -221,6 +198,7 @@ const PARSED = [ error: "Unexpected `'`", }, { + query: "a->", elems: [], foundElems: 0, original: "a->", @@ -229,6 +207,7 @@ const PARSED = [ error: "Expected at least one item after `->`", }, { + query: '"p" ', elems: [], foundElems: 0, original: '"p" ', @@ -237,6 +216,7 @@ const PARSED = [ error: "Found generics without a path", }, { + query: '"p" a', elems: [], foundElems: 0, original: '"p" a', @@ -245,6 +225,7 @@ const PARSED = [ error: "You cannot have more than one element if you use quotes", }, { + query: "a,<", elems: [], foundElems: 0, original: 'a,<', @@ -253,6 +234,7 @@ const PARSED = [ error: 'Found generics without a path', }, { + query: "aaaaa<>b", elems: [], foundElems: 0, original: 'aaaaa<>b', @@ -261,6 +243,7 @@ const PARSED = [ error: 'Expected `,`, ` `, `:` or `->`, found `b`', }, { + query: "fn:aaaaa<>b", elems: [], foundElems: 0, original: 'fn:aaaaa<>b', @@ -269,6 +252,7 @@ const PARSED = [ error: 'Expected `,`, ` `, `:` or `->`, found `b`', }, { + query: "->a<>b", elems: [], foundElems: 0, original: '->a<>b', @@ -277,6 +261,7 @@ const PARSED = [ error: 'Expected `,` or ` `, found `b`', }, { + query: "a<->", elems: [], foundElems: 0, original: 'a<->', @@ -285,6 +270,7 @@ const PARSED = [ error: 'Unexpected `-` after `<`', }, { + query: "a:: a", elems: [], foundElems: 0, original: 'a:: a', @@ -293,6 +279,7 @@ const PARSED = [ error: 'Paths cannot end with `::`', }, { + query: "a ::a", elems: [], foundElems: 0, original: 'a ::a', @@ -301,6 +288,7 @@ const PARSED = [ error: 'Paths cannot start with `::`', }, { + query: "a:", elems: [], foundElems: 0, original: "a:", @@ -309,6 +297,7 @@ const PARSED = [ error: 'Unexpected `<` in type filter', }, { + query: "a<>:", elems: [], foundElems: 0, original: "a<>:", @@ -317,6 +306,7 @@ const PARSED = [ error: 'Unexpected `<` in type filter', }, { + query: "a,:", elems: [], foundElems: 0, original: "a,:", @@ -325,6 +315,7 @@ const PARSED = [ error: 'Unexpected `,` in type filter', }, { + query: " a<> :", elems: [], foundElems: 0, original: "a<> :", @@ -333,6 +324,7 @@ const PARSED = [ error: 'Unexpected `<` in type filter', }, { + query: "mod : :", elems: [], foundElems: 0, original: "mod : :", @@ -341,6 +333,7 @@ const PARSED = [ error: 'Unexpected `:`', }, { + query: "a!a", elems: [], foundElems: 0, original: "a!a", @@ -349,6 +342,7 @@ const PARSED = [ error: 'Unexpected `!`: it can only be at the end of an ident', }, { + query: "a!!", elems: [], foundElems: 0, original: "a!!", @@ -357,6 +351,7 @@ const PARSED = [ error: 'Cannot have more than one `!` in an ident', }, { + query: "mod:a!", elems: [], foundElems: 0, original: "mod:a!", @@ -365,6 +360,7 @@ const PARSED = [ error: 'Invalid search type: macro `!` and `mod` both specified', }, { + query: "a!::a", elems: [], foundElems: 0, original: "a!::a", @@ -373,6 +369,7 @@ const PARSED = [ error: 'Cannot have associated items in macros', }, { + query: "a<", elems: [], foundElems: 0, original: "a<", diff --git a/tests/rustdoc-js-std/parser-filter.js b/tests/rustdoc-js-std/parser-filter.js index e23447ab75dc4..6f5d66e57ba0c 100644 --- a/tests/rustdoc-js-std/parser-filter.js +++ b/tests/rustdoc-js-std/parser-filter.js @@ -1,17 +1,6 @@ -const QUERY = [ - 'fn:foo', - 'enum : foo', - 'macro:foo', - 'macro!', - 'macro:mac!', - 'a::mac!', - '-> fn:foo', - '-> fn:foo', - '-> fn:foo', -]; - const PARSED = [ { + query: 'fn:foo', elems: [{ name: "foo", fullPath: ["foo"], @@ -27,6 +16,7 @@ const PARSED = [ error: null, }, { + query: 'enum : foo', elems: [{ name: "foo", fullPath: ["foo"], @@ -42,6 +32,7 @@ const PARSED = [ error: null, }, { + query: 'macro:foo', elems: [], foundElems: 0, original: "macro:foo", @@ -50,6 +41,7 @@ const PARSED = [ error: "Unexpected `<` in type filter", }, { + query: 'macro!', elems: [{ name: "macro", fullPath: ["macro"], @@ -65,6 +57,7 @@ const PARSED = [ error: null, }, { + query: 'macro:mac!', elems: [{ name: "mac", fullPath: ["mac"], @@ -80,6 +73,7 @@ const PARSED = [ error: null, }, { + query: 'a::mac!', elems: [{ name: "a::mac", fullPath: ["a", "mac"], @@ -95,6 +89,7 @@ const PARSED = [ error: null, }, { + query: '-> fn:foo', elems: [], foundElems: 1, original: "-> fn:foo", @@ -110,6 +105,7 @@ const PARSED = [ error: null, }, { + query: '-> fn:foo', elems: [], foundElems: 1, original: "-> fn:foo", @@ -134,6 +130,7 @@ const PARSED = [ error: null, }, { + query: '-> fn:foo', elems: [], foundElems: 1, original: "-> fn:foo", diff --git a/tests/rustdoc-js-std/parser-generics.js b/tests/rustdoc-js-std/parser-generics.js index 5a2266dbe3697..eb3cf45515576 100644 --- a/tests/rustdoc-js-std/parser-generics.js +++ b/tests/rustdoc-js-std/parser-generics.js @@ -1,14 +1,6 @@ -const QUERY = [ - 'A, E>', - 'p<> u8', - '"p"', - 'p>', - 'p, r>', - 'p>', -]; - const PARSED = [ { + query: 'A, E>', elems: [], foundElems: 0, original: 'A, E>', @@ -17,6 +9,7 @@ const PARSED = [ error: 'Unclosed `<`', }, { + query: 'p<> u8', elems: [ { name: "p", @@ -42,6 +35,7 @@ const PARSED = [ error: null, }, { + query: '"p"', elems: [ { name: "p", @@ -67,6 +61,7 @@ const PARSED = [ error: null, }, { + query: 'p>', elems: [ { name: "p", @@ -100,6 +95,7 @@ const PARSED = [ error: null, }, { + query: 'p, r>', elems: [ { name: "p", @@ -140,6 +136,7 @@ const PARSED = [ error: null, }, { + query: 'p>', elems: [ { name: "p", diff --git a/tests/rustdoc-js-std/parser-ident.js b/tests/rustdoc-js-std/parser-ident.js index be42b7aa46307..d9ee5fb564b68 100644 --- a/tests/rustdoc-js-std/parser-ident.js +++ b/tests/rustdoc-js-std/parser-ident.js @@ -1,14 +1,6 @@ -const QUERY = [ - "R", - "!", - "a!", - "a!::b", - "!::b", - "a!::b!", -]; - const PARSED = [ { + query: "R", elems: [{ name: "r", fullPath: ["r"], @@ -32,6 +24,7 @@ const PARSED = [ error: null, }, { + query: "!", elems: [{ name: "!", fullPath: ["!"], @@ -47,6 +40,7 @@ const PARSED = [ error: null, }, { + query: "a!", elems: [{ name: "a", fullPath: ["a"], @@ -62,6 +56,7 @@ const PARSED = [ error: null, }, { + query: "a!::b", elems: [], foundElems: 0, original: "a!::b", @@ -70,6 +65,7 @@ const PARSED = [ error: "Cannot have associated items in macros", }, { + query: "!::b", elems: [{ name: "!::b", fullPath: ["!", "b"], @@ -85,6 +81,7 @@ const PARSED = [ error: null, }, { + query: "a!::b!", elems: [], foundElems: 0, original: "a!::b!", diff --git a/tests/rustdoc-js-std/parser-literal.js b/tests/rustdoc-js-std/parser-literal.js index 3a31d1bddfff5..87c06224dbf2d 100644 --- a/tests/rustdoc-js-std/parser-literal.js +++ b/tests/rustdoc-js-std/parser-literal.js @@ -1,7 +1,6 @@ -const QUERY = ['R

']; - const PARSED = [ { + query: 'R

', elems: [{ name: "r", fullPath: ["r"], diff --git a/tests/rustdoc-js-std/parser-paths.js b/tests/rustdoc-js-std/parser-paths.js index f3e421f5ffa50..8d4dedf3f46c8 100644 --- a/tests/rustdoc-js-std/parser-paths.js +++ b/tests/rustdoc-js-std/parser-paths.js @@ -1,7 +1,6 @@ -const QUERY = ['A::B', 'A::B,C', 'A::B,C', 'mod::a']; - const PARSED = [ { + query: 'A::B', elems: [{ name: "a::b", fullPath: ["a", "b"], @@ -17,6 +16,7 @@ const PARSED = [ error: null, }, { + query: 'A::B,C', elems: [ { name: "a::b", @@ -42,6 +42,7 @@ const PARSED = [ error: null, }, { + query: 'A::B,C', elems: [ { name: "a::b", @@ -75,6 +76,7 @@ const PARSED = [ error: null, }, { + query: 'mod::a', elems: [{ name: "mod::a", fullPath: ["mod", "a"], diff --git a/tests/rustdoc-js-std/parser-quote.js b/tests/rustdoc-js-std/parser-quote.js index d5d67cac892f5..9d2a3620ed7a3 100644 --- a/tests/rustdoc-js-std/parser-quote.js +++ b/tests/rustdoc-js-std/parser-quote.js @@ -1,15 +1,6 @@ -const QUERY = [ - '-> "p"', - '"p",', - '"p" -> a', - '"a" -> "p"', - '->"-"', - '"a', - '""', -]; - const PARSED = [ { + query: '-> "p"', elems: [], foundElems: 1, original: '-> "p"', @@ -25,6 +16,7 @@ const PARSED = [ error: null, }, { + query: '"p",', elems: [{ name: "p", fullPath: ["p"], @@ -40,6 +32,7 @@ const PARSED = [ error: null, }, { + query: '"p" -> a', elems: [], foundElems: 0, original: '"p" -> a', @@ -48,6 +41,7 @@ const PARSED = [ error: "You cannot have more than one element if you use quotes", }, { + query: '"a" -> "p"', elems: [], foundElems: 0, original: '"a" -> "p"', @@ -56,6 +50,7 @@ const PARSED = [ error: "Cannot have more than one literal search element", }, { + query: '->"-"', elems: [], foundElems: 0, original: '->"-"', @@ -64,6 +59,7 @@ const PARSED = [ error: 'Unexpected `-` in a string element', }, { + query: '"a', elems: [], foundElems: 0, original: '"a', @@ -72,6 +68,7 @@ const PARSED = [ error: 'Unclosed `"`', }, { + query: '""', elems: [], foundElems: 0, original: '""', diff --git a/tests/rustdoc-js-std/parser-returned.js b/tests/rustdoc-js-std/parser-returned.js index c2981319055db..665e2a9b2e3d7 100644 --- a/tests/rustdoc-js-std/parser-returned.js +++ b/tests/rustdoc-js-std/parser-returned.js @@ -1,13 +1,6 @@ -const QUERY = [ - "-> F

", - "-> P", - "->,a", - "aaaaa->a", - "-> !", -]; - const PARSED = [ { + query: "-> F

", elems: [], foundElems: 1, original: "-> F

", @@ -31,6 +24,7 @@ const PARSED = [ error: null, }, { + query: "-> P", elems: [], foundElems: 1, original: "-> P", @@ -46,6 +40,7 @@ const PARSED = [ error: null, }, { + query: "->,a", elems: [], foundElems: 1, original: "->,a", @@ -61,6 +56,7 @@ const PARSED = [ error: null, }, { + query: "aaaaa->a", elems: [{ name: "aaaaa", fullPath: ["aaaaa"], @@ -83,6 +79,7 @@ const PARSED = [ error: null, }, { + query: "-> !", elems: [], foundElems: 1, original: "-> !", diff --git a/tests/rustdoc-js-std/parser-separators.js b/tests/rustdoc-js-std/parser-separators.js index fc8c5114c4e96..69f9ac29ad3ce 100644 --- a/tests/rustdoc-js-std/parser-separators.js +++ b/tests/rustdoc-js-std/parser-separators.js @@ -1,17 +1,8 @@ // ignore-tidy-tab -const QUERY = [ - 'aaaaaa b', - 'a b', - 'a,b', - 'a\tb', - 'a', - 'a', - 'a', -]; - const PARSED = [ { + query: 'aaaaaa b', elems: [ { name: 'aaaaaa', @@ -37,6 +28,7 @@ const PARSED = [ error: null, }, { + query: 'a b', elems: [ { name: 'a', @@ -62,6 +54,7 @@ const PARSED = [ error: null, }, { + query: 'a,b', elems: [ { name: 'a', @@ -87,6 +80,7 @@ const PARSED = [ error: null, }, { + query: 'a\tb', elems: [ { name: 'a', @@ -112,6 +106,7 @@ const PARSED = [ error: null, }, { + query: 'a', elems: [ { name: 'a', @@ -144,6 +139,7 @@ const PARSED = [ error: null, }, { + query: 'a', elems: [ { name: 'a', @@ -176,6 +172,7 @@ const PARSED = [ error: null, }, { + query: 'a', elems: [ { name: 'a', diff --git a/tests/rustdoc-js-std/parser-weird-queries.js b/tests/rustdoc-js-std/parser-weird-queries.js index dc1049a70bc38..0e08eaf73c876 100644 --- a/tests/rustdoc-js-std/parser-weird-queries.js +++ b/tests/rustdoc-js-std/parser-weird-queries.js @@ -1,18 +1,10 @@ // This test is mostly to check that the parser still kinda outputs something // (and doesn't enter an infinite loop!) even though the query is completely // invalid. -const QUERY = [ - 'a b', - 'a b', - 'a,b(c)', - 'aaa,a', - ',,,,', - 'mod :', - 'mod\t:', -]; const PARSED = [ { + query: 'a b', elems: [ { name: "a", @@ -38,6 +30,7 @@ const PARSED = [ error: null, }, { + query: 'a b', elems: [ { name: "a", @@ -63,6 +56,7 @@ const PARSED = [ error: null, }, { + query: 'a,b(c)', elems: [], foundElems: 0, original: "a,b(c)", @@ -71,6 +65,7 @@ const PARSED = [ error: "Unexpected `(`", }, { + query: 'aaa,a', elems: [ { name: "aaa", @@ -96,6 +91,7 @@ const PARSED = [ error: null, }, { + query: ',,,,', elems: [], foundElems: 0, original: ",,,,", @@ -104,6 +100,7 @@ const PARSED = [ error: null, }, { + query: 'mod :', elems: [], foundElems: 0, original: 'mod :', @@ -112,6 +109,7 @@ const PARSED = [ error: "Unexpected `:` (expected path after type filter)", }, { + query: 'mod\t:', elems: [], foundElems: 0, original: 'mod\t:', diff --git a/tests/rustdoc-js-std/path-ordering.js b/tests/rustdoc-js-std/path-ordering.js index 7dcdd40231248..c3d61d238cc35 100644 --- a/tests/rustdoc-js-std/path-ordering.js +++ b/tests/rustdoc-js-std/path-ordering.js @@ -1,7 +1,6 @@ -const QUERY = 'hashset::insert'; - const EXPECTED = { - 'others': [ + query: 'hashset::insert', + others: [ // ensure hashset::insert comes first { 'path': 'std::collections::hash_set::HashSet', 'name': 'insert' }, { 'path': 'std::collections::hash_set::HashSet', 'name': 'get_or_insert' }, diff --git a/tests/rustdoc-js-std/primitive.js b/tests/rustdoc-js-std/primitive.js index e5690383e4f0b..737e429bf5514 100644 --- a/tests/rustdoc-js-std/primitive.js +++ b/tests/rustdoc-js-std/primitive.js @@ -1,15 +1,6 @@ -const QUERY = [ - 'i8', - 'u32', - 'str', - 'char', - 'unit', - 'tuple', - 'fn', -]; - const EXPECTED = [ { + 'query': 'i8', 'others': [ { 'path': 'std', @@ -19,6 +10,7 @@ const EXPECTED = [ ] }, { + 'query': 'u32', 'others': [ { 'path': 'std', @@ -28,6 +20,7 @@ const EXPECTED = [ ] }, { + 'query': 'str', 'others': [ { 'path': 'std', @@ -37,6 +30,7 @@ const EXPECTED = [ ] }, { + 'query': 'char', 'others': [ { 'path': 'std', @@ -46,6 +40,7 @@ const EXPECTED = [ ] }, { + 'query': 'unit', 'others': [ { 'path': 'std', @@ -55,6 +50,7 @@ const EXPECTED = [ ] }, { + 'query': 'tuple', 'others': [ { 'path': 'std', @@ -64,6 +60,7 @@ const EXPECTED = [ ] }, { + 'query': 'fn', 'others': [ { 'path': 'std', diff --git a/tests/rustdoc-js-std/println-typo.js b/tests/rustdoc-js-std/println-typo.js index 7ca3ab8e56333..a4dd90a44d5ba 100644 --- a/tests/rustdoc-js-std/println-typo.js +++ b/tests/rustdoc-js-std/println-typo.js @@ -1,9 +1,9 @@ // exact-check -const QUERY = 'prinltn'; const FILTER_CRATE = 'std'; const EXPECTED = { + 'query': 'prinltn', 'others': [ { 'path': 'std', 'name': 'println' }, { 'path': 'std', 'name': 'print' }, diff --git a/tests/rustdoc-js-std/quoted.js b/tests/rustdoc-js-std/quoted.js index aec8484a41f6d..8a9275019255c 100644 --- a/tests/rustdoc-js-std/quoted.js +++ b/tests/rustdoc-js-std/quoted.js @@ -1,9 +1,9 @@ // ignore-order -const QUERY = '"error"'; const FILTER_CRATE = 'std'; const EXPECTED = { + 'query': '"error"', 'others': [ { 'path': 'std', 'name': 'error' }, { 'path': 'std::fmt', 'name': 'Error' }, diff --git a/tests/rustdoc-js-std/reference-shrink.js b/tests/rustdoc-js-std/reference-shrink.js index f90be6d1bfd35..b602bbdca188d 100644 --- a/tests/rustdoc-js-std/reference-shrink.js +++ b/tests/rustdoc-js-std/reference-shrink.js @@ -1,8 +1,7 @@ // exact-check -const QUERY = 'reference::shrink'; - const EXPECTED = { + 'query': 'reference::shrink', // avoid including the method that's not going to be in the HTML 'others': [], }; diff --git a/tests/rustdoc-js-std/regex.js b/tests/rustdoc-js-std/regex.js index a6843c595f7ad..7dc38939a17d4 100644 --- a/tests/rustdoc-js-std/regex.js +++ b/tests/rustdoc-js-std/regex.js @@ -1,9 +1,8 @@ // exact-check // https://github.com/rust-lang/rust/issues/103357 -const QUERY = 'regex'; - const EXPECTED = { + 'query': 'regex', 'others': [], 'in_args': [], 'returned': [], diff --git a/tests/rustdoc-js-std/return-specific-literal.js b/tests/rustdoc-js-std/return-specific-literal.js index c7c347240b751..86ed3aceb4e84 100644 --- a/tests/rustdoc-js-std/return-specific-literal.js +++ b/tests/rustdoc-js-std/return-specific-literal.js @@ -1,6 +1,5 @@ -const QUERY = 'struct:"string"'; - const EXPECTED = { + 'query': 'struct:"string"', 'in_args': [ { 'path': 'std::string::String', 'name': 'ne' }, ], diff --git a/tests/rustdoc-js-std/return-specific.js b/tests/rustdoc-js-std/return-specific.js index d9a910553b8de..be54a1c977254 100644 --- a/tests/rustdoc-js-std/return-specific.js +++ b/tests/rustdoc-js-std/return-specific.js @@ -1,6 +1,5 @@ -const QUERY = 'struct:string'; - const EXPECTED = { + 'query': 'struct:string', 'in_args': [ { 'path': 'std::string::String', 'name': 'ne' }, ], diff --git a/tests/rustdoc-js-std/should-fail.js b/tests/rustdoc-js-std/should-fail.js index b85a47dc08a88..94f82efd9b497 100644 --- a/tests/rustdoc-js-std/should-fail.js +++ b/tests/rustdoc-js-std/should-fail.js @@ -1,8 +1,7 @@ // should-fail -const QUERY = 'fn'; - const EXPECTED = { + 'query': 'fn', 'others': [ { 'path': 'std', 'name': 'fn', ty: 14 }, ], diff --git a/tests/rustdoc-js-std/string-from_ut.js b/tests/rustdoc-js-std/string-from_ut.js index f9edf4408db85..1fff6ee28bb42 100644 --- a/tests/rustdoc-js-std/string-from_ut.js +++ b/tests/rustdoc-js-std/string-from_ut.js @@ -1,6 +1,5 @@ -const QUERY = 'String::from_ut'; - const EXPECTED = { + 'query': 'String::from_ut', 'others': [ { 'path': 'std::string::String', 'name': 'from_utf8' }, { 'path': 'std::string::String', 'name': 'from_utf8' }, diff --git a/tests/rustdoc-js-std/struct-vec.js b/tests/rustdoc-js-std/struct-vec.js index 29609904b1957..dd72aaa1ab86c 100644 --- a/tests/rustdoc-js-std/struct-vec.js +++ b/tests/rustdoc-js-std/struct-vec.js @@ -1,6 +1,5 @@ -const QUERY = 'struct:VecD'; - const EXPECTED = { + 'query': 'struct:VecD', 'others': [ { 'path': 'std::collections', 'name': 'VecDeque' }, { 'path': 'std::vec', 'name': 'Vec' }, diff --git a/tests/rustdoc-js-std/typed-query.js b/tests/rustdoc-js-std/typed-query.js index eeb3e18886959..8e84645889adf 100644 --- a/tests/rustdoc-js-std/typed-query.js +++ b/tests/rustdoc-js-std/typed-query.js @@ -1,9 +1,9 @@ // exact-check -const QUERY = 'macro:print'; const FILTER_CRATE = 'std'; const EXPECTED = { + 'query': 'macro:print', 'others': [ { 'path': 'std', 'name': 'print' }, { 'path': 'std', 'name': 'println' }, diff --git a/tests/rustdoc-js-std/vec-new.js b/tests/rustdoc-js-std/vec-new.js index fc44a566af21f..309f3543faffe 100644 --- a/tests/rustdoc-js-std/vec-new.js +++ b/tests/rustdoc-js-std/vec-new.js @@ -1,6 +1,5 @@ -const QUERY = 'Vec::new'; - const EXPECTED = { + 'query': 'Vec::new', 'others': [ { 'path': 'std::vec::Vec', 'name': 'new' }, { 'path': 'alloc::vec::Vec', 'name': 'new' }, diff --git a/tests/rustdoc-js/basic.js b/tests/rustdoc-js/basic.js index d99b23468b60c..e186d510887cb 100644 --- a/tests/rustdoc-js/basic.js +++ b/tests/rustdoc-js/basic.js @@ -1,6 +1,5 @@ -const QUERY = 'Fo'; - const EXPECTED = { + 'query': 'Fo', 'others': [ { 'path': 'basic', 'name': 'Foo' }, ], diff --git a/tests/rustdoc-js/doc-alias-filter-out.js b/tests/rustdoc-js/doc-alias-filter-out.js index 46a089d06ebef..fd25370dff3c1 100644 --- a/tests/rustdoc-js/doc-alias-filter-out.js +++ b/tests/rustdoc-js/doc-alias-filter-out.js @@ -1,9 +1,8 @@ // exact-check -const QUERY = 'true'; - const FILTER_CRATE = 'some_other_crate'; const EXPECTED = { + 'query': 'true', 'others': [], }; diff --git a/tests/rustdoc-js/doc-alias-filter.js b/tests/rustdoc-js/doc-alias-filter.js index e06047ba7606e..1d2dd8b9a8cde 100644 --- a/tests/rustdoc-js/doc-alias-filter.js +++ b/tests/rustdoc-js/doc-alias-filter.js @@ -1,10 +1,9 @@ // exact-check -const QUERY = '"true"'; - const FILTER_CRATE = 'doc_alias_filter'; const EXPECTED = { + 'query': '"true"', 'others': [ { 'path': 'doc_alias_filter', diff --git a/tests/rustdoc-js/doc-alias-whitespace.js b/tests/rustdoc-js/doc-alias-whitespace.js index c9fc0c4311f19..64784b5698be9 100644 --- a/tests/rustdoc-js/doc-alias-whitespace.js +++ b/tests/rustdoc-js/doc-alias-whitespace.js @@ -1,11 +1,8 @@ // exact-check -const QUERY = [ - 'Demon Lord', -]; - const EXPECTED = [ { + 'query': 'Demon Lord', 'others': [ { 'path': 'doc_alias_whitespace', diff --git a/tests/rustdoc-js/doc-alias.js b/tests/rustdoc-js/doc-alias.js index 62c8e7a74b940..7e4e8a776d899 100644 --- a/tests/rustdoc-js/doc-alias.js +++ b/tests/rustdoc-js/doc-alias.js @@ -1,31 +1,6 @@ -const QUERY = [ - 'StructItem', - 'StructFieldItem', - 'StructMethodItem', - 'ImplTraitItem', - 'StructImplConstItem', - 'ImplTraitFunction', - 'EnumItem', - 'VariantItem', - 'EnumMethodItem', - 'TypedefItem', - 'TraitItem', - 'TraitTypeItem', - 'AssociatedConstItem', - 'TraitFunctionItem', - 'FunctionItem', - 'ModuleItem', - 'ConstItem', - 'StaticItem', - 'UnionItem', - 'UnionFieldItem', - 'UnionMethodItem', - 'MacroItem', -]; - const EXPECTED = [ { - // StructItem + 'query': 'StructItem', 'others': [ { 'path': 'doc_alias', @@ -37,7 +12,7 @@ const EXPECTED = [ ], }, { - // StructFieldItem + 'query': 'StructFieldItem', 'others': [ { 'path': 'doc_alias::Struct', @@ -49,7 +24,7 @@ const EXPECTED = [ ], }, { - // StructMethodItem + 'query': 'StructMethodItem', 'others': [ { 'path': 'doc_alias::Struct', @@ -61,11 +36,11 @@ const EXPECTED = [ ], }, { - // ImplTraitItem + 'query': 'ImplTraitItem', 'others': [], }, { - // StructImplConstItem + 'query': 'StructImplConstItem', 'others': [ { 'path': 'doc_alias::Struct', @@ -77,7 +52,7 @@ const EXPECTED = [ ], }, { - // ImplTraitFunction + 'query': 'ImplTraitFunction', 'others': [ { 'path': 'doc_alias::Struct', @@ -89,7 +64,7 @@ const EXPECTED = [ ], }, { - // EnumItem + 'query': 'EnumItem', 'others': [ { 'path': 'doc_alias', @@ -101,7 +76,7 @@ const EXPECTED = [ ], }, { - // VariantItem + 'query': 'VariantItem', 'others': [ { 'path': 'doc_alias::Enum', @@ -113,7 +88,7 @@ const EXPECTED = [ ], }, { - // EnumMethodItem + 'query': 'EnumMethodItem', 'others': [ { 'path': 'doc_alias::Enum', @@ -125,7 +100,7 @@ const EXPECTED = [ ], }, { - // TypedefItem + 'query': 'TypedefItem', 'others': [ { 'path': 'doc_alias', @@ -137,7 +112,7 @@ const EXPECTED = [ ], }, { - // TraitItem + 'query': 'TraitItem', 'others': [ { 'path': 'doc_alias', @@ -149,7 +124,7 @@ const EXPECTED = [ ], }, { - // TraitTypeItem + 'query': 'TraitTypeItem', 'others': [ { 'path': 'doc_alias::Trait', @@ -161,7 +136,7 @@ const EXPECTED = [ ], }, { - // AssociatedConstItem + 'query': 'AssociatedConstItem', 'others': [ { 'path': 'doc_alias::Trait', @@ -173,7 +148,7 @@ const EXPECTED = [ ], }, { - // TraitFunctionItem + 'query': 'TraitFunctionItem', 'others': [ { 'path': 'doc_alias::Trait', @@ -185,7 +160,7 @@ const EXPECTED = [ ], }, { - // FunctionItem + 'query': 'FunctionItem', 'others': [ { 'path': 'doc_alias', @@ -197,7 +172,7 @@ const EXPECTED = [ ], }, { - // ModuleItem + 'query': 'ModuleItem', 'others': [ { 'path': 'doc_alias', @@ -209,7 +184,7 @@ const EXPECTED = [ ], }, { - // ConstItem + 'query': 'ConstItem', 'others': [ { 'path': 'doc_alias', @@ -225,7 +200,7 @@ const EXPECTED = [ ], }, { - // StaticItem + 'query': 'StaticItem', 'others': [ { 'path': 'doc_alias', @@ -237,7 +212,7 @@ const EXPECTED = [ ], }, { - // UnionItem + 'query': 'UnionItem', 'others': [ { 'path': 'doc_alias', @@ -255,7 +230,7 @@ const EXPECTED = [ ], }, { - // UnionFieldItem + 'query': 'UnionFieldItem', 'others': [ { 'path': 'doc_alias::Union', @@ -267,7 +242,7 @@ const EXPECTED = [ ], }, { - // UnionMethodItem + 'query': 'UnionMethodItem', 'others': [ { 'path': 'doc_alias::Union', @@ -279,7 +254,7 @@ const EXPECTED = [ ], }, { - // MacroItem + 'query': 'MacroItem', 'others': [ { 'path': 'doc_alias', diff --git a/tests/rustdoc-js/exact-match.js b/tests/rustdoc-js/exact-match.js index b0a411bee5829..ce3a76f9b7dd3 100644 --- a/tests/rustdoc-js/exact-match.js +++ b/tests/rustdoc-js/exact-match.js @@ -1,6 +1,5 @@ -const QUERY = 'si::pc'; - const EXPECTED = { + 'query': 'si::pc', 'others': [ { 'path': 'exact_match::Si', 'name': 'pc' }, { 'path': 'exact_match::Psi', 'name': 'pc' }, diff --git a/tests/rustdoc-js/foreign-type-path.js b/tests/rustdoc-js/foreign-type-path.js index 334761badcab1..b11123d3ed9b1 100644 --- a/tests/rustdoc-js/foreign-type-path.js +++ b/tests/rustdoc-js/foreign-type-path.js @@ -1,6 +1,5 @@ -const QUERY = 'MyForeignType::my_method'; - const EXPECTED = { + 'query': 'MyForeignType::my_method', 'others': [ // Test case for https://github.com/rust-lang/rust/pull/96887#pullrequestreview-967154358 // Validates that the parent path for a foreign type method is correct. diff --git a/tests/rustdoc-js/generics-impl.js b/tests/rustdoc-js/generics-impl.js index 5051743bda2d1..5e33e224876fe 100644 --- a/tests/rustdoc-js/generics-impl.js +++ b/tests/rustdoc-js/generics-impl.js @@ -1,68 +1,56 @@ // exact-check -const QUERY = [ - 'Aaaaaaa -> u32', - 'Aaaaaaa -> bool', - 'Aaaaaaa -> usize', - 'Read -> u64', - 'trait:Read -> u64', - 'struct:Read -> u64', - 'bool -> u64', - 'Ddddddd -> u64', - '-> Ddddddd' -]; - const EXPECTED = [ { - // Aaaaaaa -> u32 + 'query': 'Aaaaaaa -> u32', 'others': [ { 'path': 'generics_impl::Aaaaaaa', 'name': 'bbbbbbb' }, ], }, { - // Aaaaaaa -> bool + 'query': 'Aaaaaaa -> bool', 'others': [ { 'path': 'generics_impl::Aaaaaaa', 'name': 'ccccccc' }, ], }, { - // Aaaaaaa -> usize + 'query': 'Aaaaaaa -> usize', 'others': [ { 'path': 'generics_impl::Aaaaaaa', 'name': 'read' }, ], }, { - // Read -> u64 + 'query': 'Read -> u64', 'others': [ { 'path': 'generics_impl::Ddddddd', 'name': 'eeeeeee' }, { 'path': 'generics_impl::Ddddddd', 'name': 'ggggggg' }, ], }, { - // trait:Read -> u64 + 'query': 'trait:Read -> u64', 'others': [ { 'path': 'generics_impl::Ddddddd', 'name': 'eeeeeee' }, { 'path': 'generics_impl::Ddddddd', 'name': 'ggggggg' }, ], }, { - // struct:Read -> u64 + 'query': 'struct:Read -> u64', 'others': [], }, { - // bool -> u64 + 'query': 'bool -> u64', 'others': [ { 'path': 'generics_impl::Ddddddd', 'name': 'fffffff' }, ], }, { - // Ddddddd -> u64 + 'query': 'Ddddddd -> u64', 'others': [ { 'path': 'generics_impl::Ddddddd', 'name': 'ggggggg' }, ], }, { - // -> Ddddddd + 'query': '-> Ddddddd', 'others': [ { 'path': 'generics_impl::Ddddddd', 'name': 'hhhhhhh' }, ], diff --git a/tests/rustdoc-js/generics-multi-trait.js b/tests/rustdoc-js/generics-multi-trait.js index e7fcea876c85c..7097cabe7a21f 100644 --- a/tests/rustdoc-js/generics-multi-trait.js +++ b/tests/rustdoc-js/generics-multi-trait.js @@ -1,14 +1,9 @@ // exact-check -const QUERY = [ - 'Result', - 'Zzzzzzzzzzzzzzzzzz', - 'Nonononononononono', -]; - const EXPECTED = [ // check one of the generic items { + 'query': 'Result', 'in_args': [ { 'path': 'generics_multi_trait', 'name': 'beta' }, ], @@ -17,6 +12,7 @@ const EXPECTED = [ ], }, { + 'query': 'Zzzzzzzzzzzzzzzzzz', 'in_args': [ { 'path': 'generics_multi_trait', 'name': 'beta' }, ], @@ -26,6 +22,7 @@ const EXPECTED = [ }, // ignore the name of the generic itself { + 'query': 'Nonononononononono', 'in_args': [], 'returned': [], }, diff --git a/tests/rustdoc-js/generics-nested.js b/tests/rustdoc-js/generics-nested.js index 8701f2d49861a..294c194907489 100644 --- a/tests/rustdoc-js/generics-nested.js +++ b/tests/rustdoc-js/generics-nested.js @@ -1,31 +1,24 @@ // exact-check -const QUERY = [ - '-> Out>', - '-> Out>', - '-> Out', - '-> Out', -]; - const EXPECTED = [ { - // -> Out> + 'query': '-> Out>', 'others': [ { 'path': 'generics_nested', 'name': 'alef' }, ], }, { - // -> Out> + 'query': '-> Out>', 'others': [], }, { - // -> Out + 'query': '-> Out', 'others': [ { 'path': 'generics_nested', 'name': 'bet' }, ], }, { - // -> Out + 'query': '-> Out', 'others': [ { 'path': 'generics_nested', 'name': 'bet' }, ], diff --git a/tests/rustdoc-js/generics-trait.js b/tests/rustdoc-js/generics-trait.js index 0e84751603ed6..4ccfb8f4e4d02 100644 --- a/tests/rustdoc-js/generics-trait.js +++ b/tests/rustdoc-js/generics-trait.js @@ -1,22 +1,9 @@ // exact-check -const QUERY = [ - 'Result', - 'Result', - 'OtherThingxxxxxxxx', - 'OtherThingxxxxxxxy', -]; - -const CORRECTIONS = [ - null, - null, - null, - 'OtherThingxxxxxxxx', -]; - const EXPECTED = [ - // Result { + 'query': 'Result', + 'correction': null, 'in_args': [ { 'path': 'generics_trait', 'name': 'beta' }, ], @@ -24,13 +11,15 @@ const EXPECTED = [ { 'path': 'generics_trait', 'name': 'bet' }, ], }, - // Result { + 'query': 'Result', + 'correction': null, 'in_args': [], 'returned': [], }, - // OtherThingxxxxxxxx { + 'query': 'OtherThingxxxxxxxx', + 'correction': null, 'in_args': [ { 'path': 'generics_trait', 'name': 'alpha' }, ], @@ -38,8 +27,9 @@ const EXPECTED = [ { 'path': 'generics_trait', 'name': 'alef' }, ], }, - // OtherThingxxxxxxxy { + 'query': 'OtherThingxxxxxxxy', + 'correction': 'OtherThingxxxxxxxx', 'in_args': [ { 'path': 'generics_trait', 'name': 'alpha' }, ], diff --git a/tests/rustdoc-js/generics.js b/tests/rustdoc-js/generics.js index f79c709ad6cf0..ebc92ccfc0575 100644 --- a/tests/rustdoc-js/generics.js +++ b/tests/rustdoc-js/generics.js @@ -1,20 +1,8 @@ // exact-check -const QUERY = [ - 'R

', - 'R', - 'R', - '"P"', - 'P', - 'ExtraCreditStructMulti', - 'TraitCat', - 'TraitDog', - 'Result', -]; - const EXPECTED = [ { - // R

+ 'query': 'R

', 'returned': [ { 'path': 'generics', 'name': 'alef' }, ], @@ -23,7 +11,7 @@ const EXPECTED = [ ], }, { - // R + 'query': 'R', 'returned': [ { 'path': 'generics', 'name': 'alef' }, ], @@ -32,12 +20,12 @@ const EXPECTED = [ ], }, { - // R + 'query': 'R', 'returned': [], 'in_args': [], }, { - // "P" + 'query': '"P"', 'others': [ { 'path': 'generics', 'name': 'P' }, ], @@ -49,7 +37,7 @@ const EXPECTED = [ ], }, { - // P + 'query': 'P', 'returned': [ { 'path': 'generics', 'name': 'alef' }, ], @@ -58,26 +46,26 @@ const EXPECTED = [ ], }, { - // "ExtraCreditStructMulti" + 'query': '"ExtraCreditStructMulti"', 'in_args': [ { 'path': 'generics', 'name': 'extracreditlabhomework' }, ], 'returned': [], }, { - // TraitCat + 'query': 'TraitCat', 'in_args': [ { 'path': 'generics', 'name': 'gamma' }, ], }, { - // TraitDog + 'query': 'TraitDog', 'in_args': [ { 'path': 'generics', 'name': 'gamma' }, ], }, { - // Result + 'query': 'Result', 'others': [], 'returned': [ { 'path': 'generics', 'name': 'super_soup' }, diff --git a/tests/rustdoc-js/impl-trait.js b/tests/rustdoc-js/impl-trait.js index 8d594bf8aea75..710e594b54774 100644 --- a/tests/rustdoc-js/impl-trait.js +++ b/tests/rustdoc-js/impl-trait.js @@ -1,32 +1,24 @@ // ignore-order -const QUERY = [ - 'Aaaaaaa -> i32', - 'Aaaaaaa -> Aaaaaaa', - 'Aaaaaaa -> usize', - '-> Aaaaaaa', - 'Aaaaaaa', -]; - const EXPECTED = [ { - // Aaaaaaa -> i32 + 'query': 'Aaaaaaa -> i32', 'others': [ { 'path': 'impl_trait::Ccccccc', 'name': 'eeeeeee' }, ], }, { - // Aaaaaaa -> Aaaaaaa + 'query': 'Aaaaaaa -> Aaaaaaa', 'others': [ { 'path': 'impl_trait::Ccccccc', 'name': 'fffffff' }, ], }, { - // Aaaaaaa -> usize + 'query': 'Aaaaaaa -> usize', 'others': [], }, { - // -> Aaaaaaa + 'query': '-> Aaaaaaa', 'others': [ { 'path': 'impl_trait::Ccccccc', 'name': 'fffffff' }, { 'path': 'impl_trait::Ccccccc', 'name': 'ddddddd' }, @@ -34,7 +26,7 @@ const EXPECTED = [ ], }, { - // Aaaaaaa + 'query': 'Aaaaaaa', 'others': [ { 'path': 'impl_trait', 'name': 'Aaaaaaa' }, ], diff --git a/tests/rustdoc-js/macro-search.js b/tests/rustdoc-js/macro-search.js index 2b179ce146bf0..241f7f1728859 100644 --- a/tests/rustdoc-js/macro-search.js +++ b/tests/rustdoc-js/macro-search.js @@ -1,8 +1,7 @@ // exact-check -const QUERY = 'abracadabra!'; - const EXPECTED = { + 'query': 'abracadabra!', 'others': [ { 'path': 'macro_search', 'name': 'abracadabra' }, { 'path': 'macro_search', 'name': 'abracadabra_b' }, diff --git a/tests/rustdoc-js/module-substring.js b/tests/rustdoc-js/module-substring.js index f17a97f13dc7e..7a10397ebc620 100644 --- a/tests/rustdoc-js/module-substring.js +++ b/tests/rustdoc-js/module-substring.js @@ -1,6 +1,5 @@ -const QUERY = 'ig::pc'; - const EXPECTED = { + 'query': 'ig::pc', 'others': [ { 'path': 'module_substring::Sig', 'name': 'pc' }, { 'path': 'module_substring::Si', 'name': 'pc' }, diff --git a/tests/rustdoc-js/path-ordering.js b/tests/rustdoc-js/path-ordering.js index 4aee569b0f481..f2e6fe2fa61c4 100644 --- a/tests/rustdoc-js/path-ordering.js +++ b/tests/rustdoc-js/path-ordering.js @@ -1,8 +1,7 @@ // exact-check -const QUERY = 'b::ccccccc'; - const EXPECTED = { + 'query': 'b::ccccccc', 'others': [ // `ccccccc` is an exact match for all three of these. // However `b` is a closer match for `bb` than for any diff --git a/tests/rustdoc-js/primitive.js b/tests/rustdoc-js/primitive.js index 4aec98c340379..ad8f6663aad12 100644 --- a/tests/rustdoc-js/primitive.js +++ b/tests/rustdoc-js/primitive.js @@ -1,33 +1,30 @@ // exact-check -const QUERY = [ - "i32", - "str", - "primitive:str", - "struct:str", - "TotoIsSomewhere", -]; - const EXPECTED = [ { + 'query': 'i32', 'in_args': [ { 'path': 'primitive', 'name': 'foo' }, ], }, { + 'query': 'str', 'returned': [ { 'path': 'primitive', 'name': 'foo' }, ], }, { + 'query': 'primitive:str', 'returned': [ { 'path': 'primitive', 'name': 'foo' }, ], }, { + 'query': 'struct:str', 'returned': [], }, { + 'query': 'TotoIsSomewhere', 'others': [], 'in_args': [], 'returned': [], diff --git a/tests/rustdoc-js/prototype.js b/tests/rustdoc-js/prototype.js index 2f1d841c3be19..da72fdce3db93 100644 --- a/tests/rustdoc-js/prototype.js +++ b/tests/rustdoc-js/prototype.js @@ -1,14 +1,14 @@ // exact-check -const QUERY = ['constructor', '__proto__']; - const EXPECTED = [ { + 'query': 'constructor', 'others': [], 'returned': [], 'in_args': [], }, { + 'query': '__proto__', 'others': [], 'returned': [], 'in_args': [], diff --git a/tests/rustdoc-js/raw-pointer.js b/tests/rustdoc-js/raw-pointer.js index 140b955ea713a..f2b1294ee3c86 100644 --- a/tests/rustdoc-js/raw-pointer.js +++ b/tests/rustdoc-js/raw-pointer.js @@ -1,33 +1,25 @@ // ignore-order -const QUERY = [ - 'Aaaaaaa -> i32', - 'Aaaaaaa -> Aaaaaaa', - 'Aaaaaaa -> usize', - '-> Aaaaaaa', - 'Aaaaaaa', -]; - const EXPECTED = [ { - // Aaaaaaa -> i32 + 'query': 'Aaaaaaa -> i32', 'others': [ { 'path': 'raw_pointer::Ccccccc', 'name': 'eeeeeee' }, ], }, { - // Aaaaaaa -> Aaaaaaa + 'query': 'Aaaaaaa -> Aaaaaaa', 'others': [ { 'path': 'raw_pointer::Ccccccc', 'name': 'fffffff' }, { 'path': 'raw_pointer::Ccccccc', 'name': 'ggggggg' }, ], }, { - // Aaaaaaa -> usize + 'query': 'Aaaaaaa -> usize', 'others': [], }, { - // -> Aaaaaaa + 'query': '-> Aaaaaaa', 'others': [ { 'path': 'raw_pointer::Ccccccc', 'name': 'fffffff' }, { 'path': 'raw_pointer::Ccccccc', 'name': 'ggggggg' }, @@ -36,7 +28,7 @@ const EXPECTED = [ ], }, { - // Aaaaaaa + 'query': 'Aaaaaaa', 'others': [ { 'path': 'raw_pointer', 'name': 'Aaaaaaa' }, ], diff --git a/tests/rustdoc-js/reexport.js b/tests/rustdoc-js/reexport.js index 871e75d9b2b31..9021cc2e90fe0 100644 --- a/tests/rustdoc-js/reexport.js +++ b/tests/rustdoc-js/reexport.js @@ -1,15 +1,15 @@ // exact-check -const QUERY = ['Subscriber', 'AnotherOne']; - const EXPECTED = [ { + 'query': 'Subscriber', 'others': [ { 'path': 'reexport::fmt', 'name': 'Subscriber' }, { 'path': 'reexport', 'name': 'FmtSubscriber' }, ], }, { + 'query': 'AnotherOne', 'others': [ { 'path': 'reexport', 'name': 'AnotherOne' }, ], diff --git a/tests/rustdoc-js/search-bag-semantics.js b/tests/rustdoc-js/search-bag-semantics.js index c56a3df5f904c..4b598cd80cc07 100644 --- a/tests/rustdoc-js/search-bag-semantics.js +++ b/tests/rustdoc-js/search-bag-semantics.js @@ -1,18 +1,15 @@ // exact-check -const QUERY = [ - 'P', - 'P, P', -]; - const EXPECTED = [ { + 'query': 'P', 'in_args': [ { 'path': 'search_bag_semantics', 'name': 'alacazam' }, { 'path': 'search_bag_semantics', 'name': 'abracadabra' }, ], }, { + 'query': 'P, P', 'others': [ { 'path': 'search_bag_semantics', 'name': 'abracadabra' }, ], diff --git a/tests/rustdoc-js/search-short-types.js b/tests/rustdoc-js/search-short-types.js index 3b2f15a40bf87..5048e0443c1c2 100644 --- a/tests/rustdoc-js/search-short-types.js +++ b/tests/rustdoc-js/search-short-types.js @@ -1,6 +1,5 @@ -const QUERY = 'P'; - const EXPECTED = { + 'query': 'P', 'others': [ { 'path': 'search_short_types', 'name': 'P' }, { 'path': 'search_short_types::VeryLongTypeName', 'name': 'p' }, diff --git a/tests/rustdoc-js/slice-array.js b/tests/rustdoc-js/slice-array.js index 8c21e06dc4e4f..1c06566920c2b 100644 --- a/tests/rustdoc-js/slice-array.js +++ b/tests/rustdoc-js/slice-array.js @@ -1,63 +1,52 @@ // exact-check -const QUERY = [ - 'R>', - 'primitive:slice>', - 'R>', - 'primitive:slice>', - 'R>', - 'primitive:array>', - 'primitive:array', - 'primitive:array', -]; - const EXPECTED = [ { - // R> + 'query': 'R>', 'returned': [], 'in_args': [ { 'path': 'slice_array', 'name': 'alpha' }, ], }, { - // primitive:slice> + 'query': 'primitive:slice>', 'returned': [ { 'path': 'slice_array', 'name': 'alef' }, ], 'in_args': [], }, { - // R> + 'query': 'R>', 'returned': [], 'in_args': [], }, { - // primitive:slice> + 'query': 'primitive:slice>', 'returned': [], 'in_args': [], }, { - // R> + 'query': 'R>', 'returned': [ { 'path': 'slice_array', 'name': 'bet' }, ], 'in_args': [], }, { - // primitive:array> + 'query': 'primitive:array>', 'returned': [], 'in_args': [ { 'path': 'slice_array', 'name': 'beta' }, ], }, { - // primitive::array + 'query': 'primitive:array', 'in_args': [ { 'path': 'slice_array', 'name': 'gamma' }, ], }, { - // primitive::array + 'query': 'primitive:array', 'in_args': [ { 'path': 'slice_array', 'name': 'gamma' }, ], diff --git a/tests/rustdoc-js/struct-like-variant.js b/tests/rustdoc-js/struct-like-variant.js index f6deea51e7d4d..7b9bec7aea832 100644 --- a/tests/rustdoc-js/struct-like-variant.js +++ b/tests/rustdoc-js/struct-like-variant.js @@ -1,6 +1,5 @@ -const QUERY = 'name'; - const EXPECTED = { + 'query': 'name', 'others': [ { 'path': 'struct_like_variant::Enum::Bar', 'name': 'name', 'desc': 'This is a name.' }, ], diff --git a/tests/rustdoc-js/substring.js b/tests/rustdoc-js/substring.js index af05cd1ad34d1..96efa992bb685 100644 --- a/tests/rustdoc-js/substring.js +++ b/tests/rustdoc-js/substring.js @@ -1,6 +1,5 @@ -const QUERY = 'waker_from'; - const EXPECTED = { + 'query': 'waker_from', 'others': [ { 'path': 'substring::SuperWaker', 'name': 'local_waker_from_nonlocal' }, { 'path': 'substring::SuperWakerTask', 'name': 'local_waker_from_nonlocal' }, diff --git a/tests/rustdoc-js/summaries.js b/tests/rustdoc-js/summaries.js index dfb11e80414c1..ae3aefb0c48a0 100644 --- a/tests/rustdoc-js/summaries.js +++ b/tests/rustdoc-js/summaries.js @@ -1,19 +1,20 @@ // ignore-tidy-linelength -const QUERY = ['summaries', 'summaries::Sidebar', 'summaries::Sidebar2']; - const EXPECTED = [ { + 'query': 'summaries', 'others': [ { 'path': '', 'name': 'summaries', 'desc': 'This summary has a link, [code], and Sidebar2 intra-doc.' }, ], }, { + 'query': 'summaries::Sidebar', 'others': [ { 'path': 'summaries', 'name': 'Sidebar', 'desc': 'This code will be rendered in a code tag.' }, ], }, { + 'query': 'summaries::Sidebar2', 'others': [ { 'path': 'summaries', 'name': 'Sidebar2', 'desc': '' }, ], diff --git a/tests/rustdoc-js/where-clause.js b/tests/rustdoc-js/where-clause.js index 86254a80e20f3..8dccf197be060 100644 --- a/tests/rustdoc-js/where-clause.js +++ b/tests/rustdoc-js/where-clause.js @@ -1,28 +1,31 @@ -const QUERY = ['trait', '-> trait', 't1, t2', '-> shazam', 'drizzel -> shazam']; - const EXPECTED = [ { + 'query': 'trait', 'in_args': [ { 'path': 'where_clause', 'name': 'abracadabra' }, ], }, { + 'query': '-> trait', 'others': [ { 'path': 'where_clause', 'name': 'alacazam' }, ], }, { + 'query': 't1, t2', 'others': [ { 'path': 'where_clause', 'name': 'presto' }, ], }, { + 'query': '-> shazam', 'others': [ { 'path': 'where_clause', 'name': 'bippety' }, { 'path': 'where_clause::Drizzel', 'name': 'boppety' }, ], }, { + 'query': 'drizzel -> shazam', 'others': [ { 'path': 'where_clause::Drizzel', 'name': 'boppety' }, ], diff --git a/tests/rustdoc/anchors.no_type_anchor2.html b/tests/rustdoc/anchors.no_type_anchor2.html index f8b59160f15ee..71e93990e29e9 100644 --- a/tests/rustdoc/anchors.no_type_anchor2.html +++ b/tests/rustdoc/anchors.no_type_anchor2.html @@ -1 +1 @@ -

type Y = u32

\ No newline at end of file +

pub type Y = u32

\ No newline at end of file diff --git a/tests/rustdoc/assoc-consts.rs b/tests/rustdoc/assoc-consts.rs index 68a11c57b5292..08dfa879d4363 100644 --- a/tests/rustdoc/assoc-consts.rs +++ b/tests/rustdoc/assoc-consts.rs @@ -46,7 +46,6 @@ pub fn f(_: &(ToString + 'static)) {} impl Bar { // @has assoc_consts/struct.Bar.html '//*[@id="associatedconstant.F"]' \ // "const F: fn(_: &(dyn ToString + 'static))" - // FIXME(fmease): Hide default lifetime, render "const F: fn(_: &dyn ToString)" pub const F: fn(_: &(ToString + 'static)) = f; } diff --git a/tests/rustdoc/double-hyphen-to-dash.rs b/tests/rustdoc/double-hyphen-to-dash.rs new file mode 100644 index 0000000000000..66905f90cfc93 --- /dev/null +++ b/tests/rustdoc/double-hyphen-to-dash.rs @@ -0,0 +1,9 @@ +// This test ensures that `--` (double-hyphen) is correctly converted into `–` (dash). + +#![crate_name = "foo"] + +// @has 'foo/index.html' '//*[@class="desc docblock-short"]' '–' +// @has 'foo/struct.Bar.html' '//*[@class="docblock"]' '–' + +/// -- +pub struct Bar; diff --git a/tests/rustdoc/inline-private-with-intermediate-doc-hidden.rs b/tests/rustdoc/inline-private-with-intermediate-doc-hidden.rs new file mode 100644 index 0000000000000..e382940a47eb8 --- /dev/null +++ b/tests/rustdoc/inline-private-with-intermediate-doc-hidden.rs @@ -0,0 +1,23 @@ +// This test ensures that if a private item is re-exported with an intermediate +// `#[doc(hidden)]` re-export, it'll still be inlined (and not include any attribute +// from the doc hidden re-export. + +#![crate_name = "foo"] + +// @has 'foo/index.html' +// There should only be one struct displayed. +// @count - '//*[@id="main-content"]/*[@class="small-section-header"]' 1 +// @has - '//*[@id="main-content"]/*[@class="small-section-header"]' 'Structs' +// @has - '//*[@id="main-content"]//a[@href="struct.Reexport.html"]' 'Reexport' +// @has - '//*[@id="main-content"]//*[@class="desc docblock-short"]' 'Visible. Original.' + +mod private { + /// Original. + pub struct Bar3; +} + +/// Hidden. +#[doc(hidden)] +pub use crate::private::Bar3; +/// Visible. +pub use self::Bar3 as Reexport; diff --git a/tests/rustdoc/inline_cross/auxiliary/dyn_trait.rs b/tests/rustdoc/inline_cross/auxiliary/dyn_trait.rs index 9ac2e3d96debd..644d0699e9d07 100644 --- a/tests/rustdoc/inline_cross/auxiliary/dyn_trait.rs +++ b/tests/rustdoc/inline_cross/auxiliary/dyn_trait.rs @@ -1,3 +1,5 @@ +// ignore-tidy-linelength + pub type Ty0 = dyn for<'any> FnOnce(&'any str) -> bool; pub type Ty1<'obj> = dyn std::fmt::Display + 'obj; @@ -6,12 +8,60 @@ pub type Ty2 = dyn for<'a, 'r> Container<'r, Item<'a, 'static> = ()>; pub type Ty3<'s> = &'s dyn ToString; -pub fn func0(_: &(dyn Fn() + '_)) {} - -pub fn func1<'func>(_: &(dyn Fn() + 'func)) {} - pub trait Container<'r> { type Item<'a, 'ctx>; } -pub trait Shape<'a> {} +// Trait-object types inside of a container type that has lifetime bounds ("wrapped"). + +pub fn late_bound_wrapped_elided(_: &(dyn Fn() + '_)) {} +pub fn late_bound_wrapped_late0<'f>(_: &mut (dyn Fn() + 'f)) {} +pub fn late_bound_wrapped_defaulted0<'f>(_: &'f mut dyn Fn()) {} +pub type EarlyBoundWrappedDefaulted0<'x> = std::cell::Ref<'x, dyn Trait>; +pub type EarlyBoundWrappedDefaulted1<'x> = &'x dyn Trait; +pub type EarlyBoundWrappedEarly<'x, 'y> = std::cell::Ref<'x, dyn Trait + 'y>; +pub type EarlyBoundWrappedStatic<'x> = std::cell::Ref<'x, dyn Trait + 'static>; +pub fn late_bound_wrapped_defaulted1<'l>(_: std::cell::Ref<'l, dyn Trait>) {} +pub fn late_bound_wrapped_late1<'l, 'm>(_: std::cell::Ref<'l, dyn Trait + 'm>) {} +pub fn late_bound_wrapped_early<'e, 'l>(_: std::cell::Ref<'l, dyn Trait + 'e>) where 'e: {} // `'e` is early-bound +pub fn elided_bound_wrapped_defaulted(_: std::cell::Ref<'_, dyn Trait>) {} +pub type StaticBoundWrappedDefaulted0 = std::cell::Ref<'static, dyn Trait>; +pub type StaticBoundWrappedDefaulted1 = &'static dyn Trait; +pub type AmbiguousBoundWrappedEarly0<'r, 's> = AmbiguousBoundWrapper<'s, 'r, dyn Trait + 's>; +pub type AmbiguousBoundWrappedEarly1<'r, 's> = AmbiguousBoundWrapper<'s, 'r, dyn Trait + 'r>; +pub type AmbiguousBoundWrappedStatic<'q> = AmbiguousBoundWrapper<'q, 'q, dyn Trait + 'static>; + +// Trait-object types inside of a container type that doesn't have lifetime bounds ("wrapped"). + +pub type NoBoundsWrappedDefaulted = Box; +pub type NoBoundsWrappedEarly<'e> = Box; +pub fn no_bounds_wrapped_late<'l>(_: Box) {} +pub fn no_bounds_wrapped_elided(_: Box) {} + +// Trait-object types outside of a container (“bare”). + +pub type BareNoBoundsDefaulted = dyn Trait; +pub type BareNoBoundsEarly<'p> = dyn Trait + 'p; +pub type BareEarlyBoundDefaulted0<'u> = dyn EarlyBoundTrait0<'u>; +pub type BareEarlyBoundDefaulted1 = dyn for<'any> EarlyBoundTrait0<'any>; +pub type BareEarlyBoundDefaulted2<'w> = dyn EarlyBoundTrait1<'static, 'w>; +pub type BareEarlyBoundEarly<'i, 'j> = dyn EarlyBoundTrait0<'i> + 'j; +pub type BareEarlyBoundStatic<'i> = dyn EarlyBoundTrait0<'i> + 'static; +pub type BareStaticBoundDefaulted = dyn StaticBoundTrait; +pub type BareHigherRankedBoundDefaulted0 = dyn HigherRankedBoundTrait0; +pub type BareHigherRankedBoundDefaulted1<'r> = dyn HigherRankedBoundTrait1<'r>; +pub type BareAmbiguousBoundEarly0<'m, 'n> = dyn AmbiguousBoundTrait<'m, 'n> + 'm; +pub type BareAmbiguousBoundEarly1<'m, 'n> = dyn AmbiguousBoundTrait<'m, 'n> + 'n; +pub type BareAmbiguousBoundStatic<'o> = dyn AmbiguousBoundTrait<'o, 'o> + 'static; + +// Trait and container definitions. + +pub trait Trait {} // no bounds +pub trait EarlyBoundTrait0<'b>: 'b {} +pub trait EarlyBoundTrait1<'unused, 'c>: 'c {} +pub trait StaticBoundTrait: 'static {} +pub trait HigherRankedBoundTrait0 where for<'a> Self: 'a {} +pub trait HigherRankedBoundTrait1<'e> where for<'l> Self: 'e + 'l {} +pub trait AmbiguousBoundTrait<'a, 'b>: 'a + 'b {} + +pub struct AmbiguousBoundWrapper<'a, 'b, T: ?Sized + 'a + 'b>(&'a T, &'b T); diff --git a/tests/rustdoc/inline_cross/dyn_trait.rs b/tests/rustdoc/inline_cross/dyn_trait.rs index 649d98f71396a..1de01af83d13e 100644 --- a/tests/rustdoc/inline_cross/dyn_trait.rs +++ b/tests/rustdoc/inline_cross/dyn_trait.rs @@ -1,31 +1,130 @@ #![crate_name = "user"] +// In each test case, we include the trailing semicolon to ensure that nothing extra comes +// after the type like an unwanted outlives-bound. + // aux-crate:dyn_trait=dyn_trait.rs // edition:2021 // @has user/type.Ty0.html -// @has - '//*[@class="rust item-decl"]//code' "dyn for<'any> FnOnce(&'any str) -> bool + 'static" -// FIXME(fmease): Hide default lifetime bound `'static` +// @has - '//*[@class="rust item-decl"]//code' "dyn for<'any> FnOnce(&'any str) -> bool;" pub use dyn_trait::Ty0; // @has user/type.Ty1.html -// @has - '//*[@class="rust item-decl"]//code' "dyn Display + 'obj" +// @has - '//*[@class="rust item-decl"]//code' "dyn Display + 'obj;" pub use dyn_trait::Ty1; // @has user/type.Ty2.html -// @has - '//*[@class="rust item-decl"]//code' "dyn for<'a, 'r> Container<'r, Item<'a, 'static> = ()>" +// @has - '//*[@class="rust item-decl"]//code' "dyn for<'a, 'r> Container<'r, Item<'a, 'static> = ()>;" pub use dyn_trait::Ty2; // @has user/type.Ty3.html -// @has - '//*[@class="rust item-decl"]//code' "&'s (dyn ToString + 's)" -// FIXME(fmease): Hide default lifetime bound, render "&'s dyn ToString" +// @has - '//*[@class="rust item-decl"]//code' "&'s dyn ToString;" pub use dyn_trait::Ty3; -// @has user/fn.func0.html -// @has - '//pre[@class="rust item-decl"]' "func0(_: &dyn Fn())" -// FIXME(fmease): Show placeholder-lifetime bound, render "func0(_: &(dyn Fn() + '_))" -pub use dyn_trait::func0; +// Below we check if we correctly elide trait-object lifetime bounds if they coincide with their +// default (known as "object lifetime default" or "default trait object lifetime"). + +// @has user/fn.lbwel.html +// @has - '//pre[@class="rust item-decl"]' "lbwel(_: &dyn Fn())" +pub use dyn_trait::late_bound_wrapped_elided as lbwel; +// @has user/fn.lbwl0.html +// has - '//pre[@class="rust item-decl"]' "lbwl0<'f>(_: &mut (dyn Fn() + 'f))" +pub use dyn_trait::late_bound_wrapped_late0 as lbwl0; +// @has user/fn.lbwd0.html +// has - '//pre[@class="rust item-decl"]' "lbwd0<'f>(_: &'f mut dyn Fn())" +pub use dyn_trait::late_bound_wrapped_defaulted0 as lbwd0; +// @has user/type.EarlyBoundWrappedDefaulted0.html +// @has - '//*[@class="rust item-decl"]//code' "Ref<'x, dyn Trait>;" +pub use dyn_trait::EarlyBoundWrappedDefaulted0; +// @has user/type.EarlyBoundWrappedDefaulted1.html +// @has - '//*[@class="rust item-decl"]//code' "&'x dyn Trait;" +pub use dyn_trait::EarlyBoundWrappedDefaulted1; +// @has user/type.EarlyBoundWrappedEarly.html +// @has - '//*[@class="rust item-decl"]//code' "Ref<'x, dyn Trait + 'y>" +pub use dyn_trait::EarlyBoundWrappedEarly; +// @has user/type.EarlyBoundWrappedStatic.html +// @has - '//*[@class="rust item-decl"]//code' "Ref<'x, dyn Trait + 'static>" +pub use dyn_trait::EarlyBoundWrappedStatic; +// @has user/fn.lbwd1.html +// @has - '//pre[@class="rust item-decl"]' "lbwd1<'l>(_: Ref<'l, dyn Trait>)" +pub use dyn_trait::late_bound_wrapped_defaulted1 as lbwd1; +// @has user/fn.lbwl1.html +// @has - '//pre[@class="rust item-decl"]' "lbwl1<'l, 'm>(_: Ref<'l, dyn Trait + 'm>)" +pub use dyn_trait::late_bound_wrapped_late1 as lbwl1; +// @has user/fn.lbwe.html +// @has - '//pre[@class="rust item-decl"]' "lbwe<'e, 'l>(_: Ref<'l, dyn Trait + 'e>)" +pub use dyn_trait::late_bound_wrapped_early as lbwe; +// @has user/fn.ebwd.html +// @has - '//pre[@class="rust item-decl"]' "ebwd(_: Ref<'_, dyn Trait>)" +pub use dyn_trait::elided_bound_wrapped_defaulted as ebwd; +// @has user/type.StaticBoundWrappedDefaulted0.html +// @has - '//*[@class="rust item-decl"]//code' "Ref<'static, dyn Trait>;" +pub use dyn_trait::StaticBoundWrappedDefaulted0; +// @has user/type.StaticBoundWrappedDefaulted1.html +// @has - '//*[@class="rust item-decl"]//code' "&'static dyn Trait;" +pub use dyn_trait::StaticBoundWrappedDefaulted1; +// @has user/type.AmbiguousBoundWrappedEarly0.html +// @has - '//*[@class="rust item-decl"]//code' "AmbiguousBoundWrapper<'s, 'r, dyn Trait + 's>;" +pub use dyn_trait::AmbiguousBoundWrappedEarly0; +// @has user/type.AmbiguousBoundWrappedEarly1.html +// @has - '//*[@class="rust item-decl"]//code' "AmbiguousBoundWrapper<'s, 'r, dyn Trait + 'r>;" +pub use dyn_trait::AmbiguousBoundWrappedEarly1; +// @has user/type.AmbiguousBoundWrappedStatic.html +// @has - '//*[@class="rust item-decl"]//code' "AmbiguousBoundWrapper<'q, 'q, dyn Trait + 'static>;" +pub use dyn_trait::AmbiguousBoundWrappedStatic; + +// @has user/type.NoBoundsWrappedDefaulted.html +// @has - '//*[@class="rust item-decl"]//code' "Box;" +pub use dyn_trait::NoBoundsWrappedDefaulted; +// @has user/type.NoBoundsWrappedEarly.html +// @has - '//*[@class="rust item-decl"]//code' "Box;" +pub use dyn_trait::NoBoundsWrappedEarly; +// @has user/fn.nbwl.html +// @has - '//pre[@class="rust item-decl"]' "nbwl<'l>(_: Box)" +pub use dyn_trait::no_bounds_wrapped_late as nbwl; +// @has user/fn.nbwel.html +// @has - '//pre[@class="rust item-decl"]' "nbwel(_: Box)" +// NB: It might seem counterintuitive to display the explicitly elided lifetime `'_` here instead of +// eliding it but this behavior is correct: The default is `'static` here which != `'_`. +pub use dyn_trait::no_bounds_wrapped_elided as nbwel; -// @has user/fn.func1.html -// @has - '//pre[@class="rust item-decl"]' "func1<'func>(_: &(dyn Fn() + 'func))" -pub use dyn_trait::func1; +// @has user/type.BareNoBoundsDefaulted.html +// @has - '//*[@class="rust item-decl"]//code' "dyn Trait;" +pub use dyn_trait::BareNoBoundsDefaulted; +// @has user/type.BareNoBoundsEarly.html +// @has - '//*[@class="rust item-decl"]//code' "dyn Trait + 'p;" +pub use dyn_trait::BareNoBoundsEarly; +// @has user/type.BareEarlyBoundDefaulted0.html +// @has - '//*[@class="rust item-decl"]//code' "dyn EarlyBoundTrait0<'u>;" +pub use dyn_trait::BareEarlyBoundDefaulted0; +// @has user/type.BareEarlyBoundDefaulted1.html +// @has - '//*[@class="rust item-decl"]//code' "dyn for<'any> EarlyBoundTrait0<'any>;" +pub use dyn_trait::BareEarlyBoundDefaulted1; +// @has user/type.BareEarlyBoundDefaulted2.html +// @has - '//*[@class="rust item-decl"]//code' "dyn EarlyBoundTrait1<'static, 'w>;" +pub use dyn_trait::BareEarlyBoundDefaulted2; +// @has user/type.BareEarlyBoundEarly.html +// @has - '//*[@class="rust item-decl"]//code' "dyn EarlyBoundTrait0<'i> + 'j;" +pub use dyn_trait::BareEarlyBoundEarly; +// @has user/type.BareEarlyBoundStatic.html +// @has - '//*[@class="rust item-decl"]//code' "dyn EarlyBoundTrait0<'i> + 'static;" +pub use dyn_trait::BareEarlyBoundStatic; +// @has user/type.BareStaticBoundDefaulted.html +// @has - '//*[@class="rust item-decl"]//code' "dyn StaticBoundTrait;" +pub use dyn_trait::BareStaticBoundDefaulted; +// @has user/type.BareHigherRankedBoundDefaulted0.html +// @has - '//*[@class="rust item-decl"]//code' "dyn HigherRankedBoundTrait0;" +pub use dyn_trait::BareHigherRankedBoundDefaulted0; +// @has user/type.BareHigherRankedBoundDefaulted1.html +// @has - '//*[@class="rust item-decl"]//code' "dyn HigherRankedBoundTrait1<'r>;" +pub use dyn_trait::BareHigherRankedBoundDefaulted1; +// @has user/type.BareAmbiguousBoundEarly0.html +// @has - '//*[@class="rust item-decl"]//code' "dyn AmbiguousBoundTrait<'m, 'n> + 'm;" +pub use dyn_trait::BareAmbiguousBoundEarly0; +// @has user/type.BareAmbiguousBoundEarly1.html +// @has - '//*[@class="rust item-decl"]//code' "dyn AmbiguousBoundTrait<'m, 'n> + 'n;" +pub use dyn_trait::BareAmbiguousBoundEarly1; +// @has user/type.BareAmbiguousBoundStatic.html +// @has - '//*[@class="rust item-decl"]//code' "dyn AmbiguousBoundTrait<'o, 'o> + 'static;" +pub use dyn_trait::BareAmbiguousBoundStatic; diff --git a/tests/rustdoc/issue-32077-type-alias-impls.rs b/tests/rustdoc/issue-32077-type-alias-impls.rs new file mode 100644 index 0000000000000..555d0579bee79 --- /dev/null +++ b/tests/rustdoc/issue-32077-type-alias-impls.rs @@ -0,0 +1,59 @@ +// Regression test for . + +#![crate_name = "foo"] + +pub struct GenericStruct(T); + +impl GenericStruct { + pub fn on_gen(arg: T) {} +} + +impl GenericStruct { + pub fn on_u32(arg: u32) {} +} + +pub trait Foo {} +pub trait Bar {} + +impl Foo for GenericStruct {} +impl Bar for GenericStruct {} + +// @has 'foo/type.TypedefStruct.html' +// We check that we have the implementation of the type alias itself. +// @has - '//*[@id="impl-TypedefStruct"]/h3' 'impl TypedefStruct' +// @has - '//*[@id="method.on_alias"]/h4' 'pub fn on_alias()' +// @has - '//*[@id="impl-GenericStruct%3CT%3E"]/h3' 'impl GenericStruct' +// @has - '//*[@id="method.on_gen"]/h4' 'pub fn on_gen(arg: T)' +// @has - '//*[@id="impl-Foo-for-GenericStruct%3CT%3E"]/h3' 'impl Foo for GenericStruct' +// This trait implementation doesn't match the type alias parameters so shouldn't appear in docs. +// @!has - '//h3' 'impl Bar for GenericStruct {}' +// Same goes for the `Deref` impl. +// @!has - '//h2' 'Methods from Deref' +pub type TypedefStruct = GenericStruct; + +impl TypedefStruct { + pub fn on_alias() {} +} + +impl std::ops::Deref for GenericStruct { + type Target = u32; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +pub struct Wrap(GenericStruct); + +// @has 'foo/type.Alias.html' +// @has - '//h2' 'Methods from Deref' +// @has - '//*[@id="impl-Deref-for-Wrap%3CT%3E"]/h3' 'impl Deref for Wrap' +pub type Alias = Wrap; + +impl std::ops::Deref for Wrap { + type Target = GenericStruct; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} diff --git a/tests/rustdoc/reexport-attr-merge.rs b/tests/rustdoc/reexport-attr-merge.rs index f6c23a1365f46..6cc054e7a8b2c 100644 --- a/tests/rustdoc/reexport-attr-merge.rs +++ b/tests/rustdoc/reexport-attr-merge.rs @@ -19,9 +19,9 @@ pub use Foo1 as Foo2; // First we ensure that only the reexport `Bar2` and the inlined struct `Bar` // are inlined. // @count - '//a[@class="struct"]' 2 -// Then we check that both `cfg` are displayed. +// Then we check that `cfg` is displayed for base item, but not for intermediate re-exports. // @has - '//*[@class="stab portability"]' 'foo' -// @has - '//*[@class="stab portability"]' 'bar' +// @!has - '//*[@class="stab portability"]' 'bar' // And finally we check that the only element displayed is `Bar`. // @has - '//a[@class="struct"]' 'Bar' #[doc(inline)] diff --git a/tests/rustdoc/visibility.rs b/tests/rustdoc/visibility.rs index 4099b54f08152..4f9b06fd7a207 100644 --- a/tests/rustdoc/visibility.rs +++ b/tests/rustdoc/visibility.rs @@ -1,6 +1,8 @@ // compile-flags: --document-private-items #![crate_name = "foo"] +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] // @!has 'foo/index.html' '//a[@href="struct.FooPublic.html"]/..' 'FooPublic 🔒' // @has 'foo/struct.FooPublic.html' '//pre' 'pub struct FooPublic' @@ -103,3 +105,26 @@ impl PubTrait for FooPublic { const CONST: usize = 0; fn function() {} } + +pub struct Assoc; + +// @has foo/struct.Assoc.html +impl Assoc { + // @has - '//*[@id="associatedtype.TypePub"]' 'pub type TypePub' + pub type TypePub = usize; + + // @has - '//*[@id="associatedtype.TypePriv"]' 'pub(crate) type TypePriv' + type TypePriv = usize; + + // @has - '//*[@id="associatedconstant.CONST_PUB"]' 'pub const CONST_PUB' + pub const CONST_PUB: usize = 0; + + // @has - '//*[@id="associatedconstant.CONST_PRIV"]' 'pub(crate) const CONST_PRIV' + const CONST_PRIV: usize = 0; + + // @has - '//*[@id="method.function_pub"]' 'pub fn function_pub()' + pub fn function_pub() {} + + // @has - '//*[@id="method.function_priv"]' 'pub(crate) fn function_priv()' + fn function_priv() {} +} diff --git a/tests/ui/associated-types/dont-suggest-cyclic-constraint.rs b/tests/ui/associated-types/dont-suggest-cyclic-constraint.rs new file mode 100644 index 0000000000000..6894f6b6cc4a0 --- /dev/null +++ b/tests/ui/associated-types/dont-suggest-cyclic-constraint.rs @@ -0,0 +1,11 @@ +use std::fmt::Debug; + +fn foo(mut iter: I, value: &I::Item) +where + I::Item: Eq + Debug, +{ + debug_assert_eq!(iter.next(), Some(value)); + //~^ ERROR mismatched types +} + +fn main() {} diff --git a/tests/ui/associated-types/dont-suggest-cyclic-constraint.stderr b/tests/ui/associated-types/dont-suggest-cyclic-constraint.stderr new file mode 100644 index 0000000000000..3ecac9c83e571 --- /dev/null +++ b/tests/ui/associated-types/dont-suggest-cyclic-constraint.stderr @@ -0,0 +1,12 @@ +error[E0308]: mismatched types + --> $DIR/dont-suggest-cyclic-constraint.rs:7:35 + | +LL | debug_assert_eq!(iter.next(), Some(value)); + | ^^^^^^^^^^^ expected `Option<::Item>`, found `Option<&::Item>` + | + = note: expected enum `Option<::Item>` + found enum `Option<&::Item>` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/async-await/drop-track-bad-field-in-fru.stderr b/tests/ui/async-await/drop-track-bad-field-in-fru.stderr index 07ab8b3c90353..b49b15db64cee 100644 --- a/tests/ui/async-await/drop-track-bad-field-in-fru.stderr +++ b/tests/ui/async-await/drop-track-bad-field-in-fru.stderr @@ -3,6 +3,8 @@ error[E0559]: variant `Option<_>::None` has no field named `value` | LL | None { value: (), ..Default::default() }.await; | ^^^^^ `Option<_>::None` does not have this field + | + = note: all struct fields are already assigned error[E0277]: `Option<_>` is not a future --> $DIR/drop-track-bad-field-in-fru.rs:7:46 diff --git a/tests/ui/async-await/issues/issue-112225-1.rs b/tests/ui/async-await/issues/issue-112225-1.rs new file mode 100644 index 0000000000000..e28cbee214e1a --- /dev/null +++ b/tests/ui/async-await/issues/issue-112225-1.rs @@ -0,0 +1,18 @@ +// check-pass +// edition:2021 + +use core::future::Future; + +fn main() { + do_async(async { (0,) }, { + // closure must be inside block + |info| println!("{:?}", info.0) + }); +} + +fn do_async(_tokio_fut: Fut, _glib_closure: F) +where + Fut: Future, + F: FnOnce(R), +{ +} diff --git a/tests/ui/async-await/issues/issue-112225-2.rs b/tests/ui/async-await/issues/issue-112225-2.rs new file mode 100644 index 0000000000000..50fa1a79b6beb --- /dev/null +++ b/tests/ui/async-await/issues/issue-112225-2.rs @@ -0,0 +1,20 @@ +// edition:2021 + +// With the current compiler logic, we cannot have both the `112225-1` case, +// and this `112225-2` case working, as the type inference depends on the evaluation +// order, and there is some explicit ordering going on. +// See the `check_closures` part in `FnCtxt::check_argument_types`. +// The `112225-1` case was a regression in real world code, whereas the `112225-2` +// case never used to work prior to 1.70. + +use core::future::Future; + +fn main() { + let x = Default::default(); + //~^ ERROR: type annotations needed + do_async( + async { x.0; }, + { || { let _: &(i32,) = &x; } }, + ); +} +fn do_async(_fut: Fut, _val: T, ) {} diff --git a/tests/ui/async-await/issues/issue-112225-2.stderr b/tests/ui/async-await/issues/issue-112225-2.stderr new file mode 100644 index 0000000000000..5926a4f3995ad --- /dev/null +++ b/tests/ui/async-await/issues/issue-112225-2.stderr @@ -0,0 +1,17 @@ +error[E0282]: type annotations needed + --> $DIR/issue-112225-2.rs:13:9 + | +LL | let x = Default::default(); + | ^ +... +LL | async { x.0; }, + | - type must be known at this point + | +help: consider giving `x` an explicit type + | +LL | let x: /* Type */ = Default::default(); + | ++++++++++++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/async-await/return-type-notation/ty-or-ct-params.rs b/tests/ui/async-await/return-type-notation/ty-or-ct-params.rs new file mode 100644 index 0000000000000..7871a2fed03b8 --- /dev/null +++ b/tests/ui/async-await/return-type-notation/ty-or-ct-params.rs @@ -0,0 +1,20 @@ +// edition: 2021 + +#![feature(async_fn_in_trait, return_type_notation)] +//~^ WARN the feature `return_type_notation` is incomplete + +trait Foo { + async fn bar() {} + + async fn baz() {} +} + +fn test() +where + T: Foo, + //~^ ERROR return type notation is not allowed for functions that have const parameters + //~| ERROR return type notation is not allowed for functions that have type parameters +{ +} + +fn main() {} diff --git a/tests/ui/async-await/return-type-notation/ty-or-ct-params.stderr b/tests/ui/async-await/return-type-notation/ty-or-ct-params.stderr new file mode 100644 index 0000000000000..76928c5d7a3e2 --- /dev/null +++ b/tests/ui/async-await/return-type-notation/ty-or-ct-params.stderr @@ -0,0 +1,29 @@ +warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/ty-or-ct-params.rs:3:31 + | +LL | #![feature(async_fn_in_trait, return_type_notation)] + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #109417 for more information + = note: `#[warn(incomplete_features)]` on by default + +error: return type notation is not allowed for functions that have type parameters + --> $DIR/ty-or-ct-params.rs:14:12 + | +LL | async fn bar() {} + | - type parameter declared here +... +LL | T: Foo, + | ^^^^^^^^^^^ + +error: return type notation is not allowed for functions that have const parameters + --> $DIR/ty-or-ct-params.rs:14:25 + | +LL | async fn baz() {} + | -------------- const parameter declared here +... +LL | T: Foo, + | ^^^^^^^^^^^ + +error: aborting due to 2 previous errors; 1 warning emitted + diff --git a/tests/ui/attributes/doc-test-literal.rs b/tests/ui/attributes/doc-test-literal.rs new file mode 100644 index 0000000000000..a06a1afcb3f2f --- /dev/null +++ b/tests/ui/attributes/doc-test-literal.rs @@ -0,0 +1,7 @@ +#![deny(warnings)] + +#![doc(test(""))] +//~^ ERROR `#![doc(test(...)]` does not take a literal +//~^^ WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +fn main() {} diff --git a/tests/ui/attributes/doc-test-literal.stderr b/tests/ui/attributes/doc-test-literal.stderr new file mode 100644 index 0000000000000..ebee09994ba9f --- /dev/null +++ b/tests/ui/attributes/doc-test-literal.stderr @@ -0,0 +1,17 @@ +error: `#![doc(test(...)]` does not take a literal + --> $DIR/doc-test-literal.rs:3:13 + | +LL | #![doc(test(""))] + | ^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #82730 +note: the lint level is defined here + --> $DIR/doc-test-literal.rs:1:9 + | +LL | #![deny(warnings)] + | ^^^^^^^^ + = note: `#[deny(invalid_doc_attributes)]` implied by `#[deny(warnings)]` + +error: aborting due to previous error + diff --git a/tests/ui/auto-traits/issue-23080-2.stderr b/tests/ui/auto-traits/issue-23080-2.current.stderr similarity index 91% rename from tests/ui/auto-traits/issue-23080-2.stderr rename to tests/ui/auto-traits/issue-23080-2.current.stderr index fed485612daca..a57c6d9b0cbf9 100644 --- a/tests/ui/auto-traits/issue-23080-2.stderr +++ b/tests/ui/auto-traits/issue-23080-2.current.stderr @@ -1,5 +1,5 @@ error[E0380]: auto traits cannot have associated items - --> $DIR/issue-23080-2.rs:5:10 + --> $DIR/issue-23080-2.rs:8:10 | LL | unsafe auto trait Trait { | ----- auto traits cannot have associated items diff --git a/tests/ui/auto-traits/issue-23080-2.next.stderr b/tests/ui/auto-traits/issue-23080-2.next.stderr new file mode 100644 index 0000000000000..a57c6d9b0cbf9 --- /dev/null +++ b/tests/ui/auto-traits/issue-23080-2.next.stderr @@ -0,0 +1,11 @@ +error[E0380]: auto traits cannot have associated items + --> $DIR/issue-23080-2.rs:8:10 + | +LL | unsafe auto trait Trait { + | ----- auto traits cannot have associated items +LL | type Output; + | -----^^^^^^- help: remove these associated items + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0380`. diff --git a/tests/ui/auto-traits/issue-23080-2.rs b/tests/ui/auto-traits/issue-23080-2.rs index cb4cf6de1efe9..882b8f3938428 100644 --- a/tests/ui/auto-traits/issue-23080-2.rs +++ b/tests/ui/auto-traits/issue-23080-2.rs @@ -1,3 +1,6 @@ +// revisions: current next +//[next] compile-flags: -Ztrait-solver=next + #![feature(auto_traits)] #![feature(negative_impls)] diff --git a/tests/ui/borrowck/issue-111554.rs b/tests/ui/borrowck/issue-111554.rs new file mode 100644 index 0000000000000..0dad55be3acff --- /dev/null +++ b/tests/ui/borrowck/issue-111554.rs @@ -0,0 +1,28 @@ +struct Foo {} + +impl Foo { + pub fn foo(&mut self) { + || bar(&mut self); + //~^ ERROR cannot borrow `self` as mutable, as it is not declared as mutable + } + + pub fn baz(&self) { + || bar(&mut self); + //~^ ERROR cannot borrow `self` as mutable, as it is not declared as mutable + //~| ERROR cannot borrow data in a `&` reference as mutable + } + + pub fn qux(mut self) { + || bar(&mut self); + // OK + } + + pub fn quux(self) { + || bar(&mut self); + //~^ ERROR cannot borrow `self` as mutable, as it is not declared as mutable + } +} + +fn bar(_: &mut Foo) {} + +fn main() {} diff --git a/tests/ui/borrowck/issue-111554.stderr b/tests/ui/borrowck/issue-111554.stderr new file mode 100644 index 0000000000000..6b7a42e495999 --- /dev/null +++ b/tests/ui/borrowck/issue-111554.stderr @@ -0,0 +1,29 @@ +error[E0596]: cannot borrow `self` as mutable, as it is not declared as mutable + --> $DIR/issue-111554.rs:5:16 + | +LL | || bar(&mut self); + | ^^^^^^^^^ cannot borrow as mutable + +error[E0596]: cannot borrow `self` as mutable, as it is not declared as mutable + --> $DIR/issue-111554.rs:10:16 + | +LL | || bar(&mut self); + | ^^^^^^^^^ cannot borrow as mutable + +error[E0596]: cannot borrow data in a `&` reference as mutable + --> $DIR/issue-111554.rs:10:16 + | +LL | || bar(&mut self); + | ^^^^^^^^^ cannot borrow as mutable + +error[E0596]: cannot borrow `self` as mutable, as it is not declared as mutable + --> $DIR/issue-111554.rs:21:16 + | +LL | pub fn quux(self) { + | ---- help: consider changing this to be mutable: `mut self` +LL | || bar(&mut self); + | ^^^^^^^^^ cannot borrow as mutable + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0596`. diff --git a/tests/ui/cfg/auxiliary/cfg_false_lib.rs b/tests/ui/cfg/auxiliary/cfg_false_lib.rs index 3c011d72b02c5..6c2dbb44d2a40 100644 --- a/tests/ui/cfg/auxiliary/cfg_false_lib.rs +++ b/tests/ui/cfg/auxiliary/cfg_false_lib.rs @@ -1,6 +1,4 @@ -// It is unclear whether a fully unconfigured crate should link to standard library, -// or what its `no_std`/`no_core`/`compiler_builtins` status, more precisely. -// Currently the usual standard library prelude is added to such crates, -// and therefore they link to libstd. +// `#![no_std]` on a fully unconfigured crate is respected if it's placed before `cfg(FALSE)`. +// This crate has no such attribute, therefore this crate does link to libstd. #![cfg(FALSE)] diff --git a/tests/ui/cfg/auxiliary/cfg_false_lib_no_std_after.rs b/tests/ui/cfg/auxiliary/cfg_false_lib_no_std_after.rs new file mode 100644 index 0000000000000..3cfa6c510d020 --- /dev/null +++ b/tests/ui/cfg/auxiliary/cfg_false_lib_no_std_after.rs @@ -0,0 +1,5 @@ +// `#![no_std]` on a fully unconfigured crate is respected if it's placed before `cfg(FALSE)`. +// Therefore this crate does link to libstd. + +#![cfg(FALSE)] +#![no_std] diff --git a/tests/ui/cfg/auxiliary/cfg_false_lib_no_std_before.rs b/tests/ui/cfg/auxiliary/cfg_false_lib_no_std_before.rs new file mode 100644 index 0000000000000..8e89545b8f40d --- /dev/null +++ b/tests/ui/cfg/auxiliary/cfg_false_lib_no_std_before.rs @@ -0,0 +1,8 @@ +// `#![no_std]` on a fully unconfigured crate is respected if it's placed before `cfg(FALSE)`. +// Therefore this crate doesn't link to libstd. + +// no-prefer-dynamic + +#![no_std] +#![crate_type = "lib"] +#![cfg(FALSE)] diff --git a/tests/ui/cfg/auxiliary/cfged_out.rs b/tests/ui/cfg/auxiliary/cfged_out.rs new file mode 100644 index 0000000000000..f6a9089cf29de --- /dev/null +++ b/tests/ui/cfg/auxiliary/cfged_out.rs @@ -0,0 +1,22 @@ +pub mod inner { + #[cfg(FALSE)] + pub fn uwu() {} + + #[cfg(FALSE)] + pub mod doesnt_exist { + pub fn hello() {} + } + + pub mod wrong { + #[cfg(feature = "suggesting me fails the test!!")] + pub fn meow() {} + } + + pub mod right { + #[cfg(feature = "what-a-cool-feature")] + pub fn meow() {} + } +} + +#[cfg(i_dont_exist_and_you_can_do_nothing_about_it)] +pub fn vanished() {} diff --git a/tests/ui/cfg/cfg-false-feature.rs b/tests/ui/cfg/cfg-false-feature.rs index 21ea3ec79b4d6..84c231562f1e1 100644 --- a/tests/ui/cfg/cfg-false-feature.rs +++ b/tests/ui/cfg/cfg-false-feature.rs @@ -1,5 +1,4 @@ -// It is unclear which features should be in effect in a fully unconfigured crate (issue #104633). -// Currently none on the features are in effect, so we get the feature gates reported. +// Features above `cfg(FALSE)` are in effect in a fully unconfigured crate (issue #104633). // check-pass // compile-flags: --crate-type lib @@ -8,8 +7,7 @@ #![cfg(FALSE)] #![feature(box_syntax)] -macro mac() {} //~ WARN `macro` is experimental - //~| WARN unstable syntax can change at any point in the future +macro mac() {} // OK trait A = Clone; //~ WARN trait aliases are experimental //~| WARN unstable syntax can change at any point in the future diff --git a/tests/ui/cfg/cfg-false-feature.stderr b/tests/ui/cfg/cfg-false-feature.stderr index 14673fbdb1444..34093036205fe 100644 --- a/tests/ui/cfg/cfg-false-feature.stderr +++ b/tests/ui/cfg/cfg-false-feature.stderr @@ -1,5 +1,5 @@ warning: trait aliases are experimental - --> $DIR/cfg-false-feature.rs:14:1 + --> $DIR/cfg-false-feature.rs:12:1 | LL | trait A = Clone; | ^^^^^^^^^^^^^^^^ @@ -9,19 +9,8 @@ LL | trait A = Clone; = warning: unstable syntax can change at any point in the future, causing a hard error! = note: for more information, see issue #65860 -warning: `macro` is experimental - --> $DIR/cfg-false-feature.rs:11:1 - | -LL | macro mac() {} - | ^^^^^^^^^^^^^^ - | - = note: see issue #39412 for more information - = help: add `#![feature(decl_macro)]` to the crate attributes to enable - = warning: unstable syntax can change at any point in the future, causing a hard error! - = note: for more information, see issue #65860 - warning: box pattern syntax is experimental - --> $DIR/cfg-false-feature.rs:18:9 + --> $DIR/cfg-false-feature.rs:16:9 | LL | let box _ = Box::new(0); | ^^^^^ @@ -31,5 +20,5 @@ LL | let box _ = Box::new(0); = warning: unstable syntax can change at any point in the future, causing a hard error! = note: for more information, see issue #65860 -warning: 3 warnings emitted +warning: 2 warnings emitted diff --git a/tests/ui/cfg/cfg_false_no_std-1.rs b/tests/ui/cfg/cfg_false_no_std-1.rs new file mode 100644 index 0000000000000..bcb49e5135364 --- /dev/null +++ b/tests/ui/cfg/cfg_false_no_std-1.rs @@ -0,0 +1,10 @@ +// No error, panic handler is supplied by libstd linked though the empty library. + +// check-pass +// aux-build: cfg_false_lib_no_std_after.rs + +#![no_std] + +extern crate cfg_false_lib_no_std_after as _; + +fn main() {} diff --git a/tests/ui/cfg/cfg_false_no_std-2.rs b/tests/ui/cfg/cfg_false_no_std-2.rs new file mode 100644 index 0000000000000..0a2bfd5f68b12 --- /dev/null +++ b/tests/ui/cfg/cfg_false_no_std-2.rs @@ -0,0 +1,11 @@ +// Error, the linked empty library is `no_std` and doesn't provide a panic handler. + +// dont-check-compiler-stderr +// error-pattern: `#[panic_handler]` function required, but not found +// aux-build: cfg_false_lib_no_std_before.rs + +#![no_std] + +extern crate cfg_false_lib_no_std_before as _; + +fn main() {} diff --git a/tests/ui/cfg/cfg_false_no_std.rs b/tests/ui/cfg/cfg_false_no_std.rs index 319ea078187c2..4fa831715ede1 100644 --- a/tests/ui/cfg/cfg_false_no_std.rs +++ b/tests/ui/cfg/cfg_false_no_std.rs @@ -1,5 +1,4 @@ -// Currently no error because the panic handler is supplied by libstd linked though the empty -// library, but the desirable behavior is unclear (see comments in cfg_false_lib.rs). +// No error, panic handler is supplied by libstd linked though the empty library. // check-pass // aux-build: cfg_false_lib.rs diff --git a/tests/ui/cfg/diagnostics-cross-crate.rs b/tests/ui/cfg/diagnostics-cross-crate.rs new file mode 100644 index 0000000000000..d2725c94b083b --- /dev/null +++ b/tests/ui/cfg/diagnostics-cross-crate.rs @@ -0,0 +1,31 @@ +// aux-build:cfged_out.rs + +extern crate cfged_out; + +fn main() { + // There is no uwu at this path - no diagnostic. + cfged_out::uwu(); //~ ERROR cannot find function + //~^ NOTE not found in `cfged_out` + + // It does exist here - diagnostic. + cfged_out::inner::uwu(); //~ ERROR cannot find function + //~^ NOTE found an item that was configured out + //~| NOTE not found in `cfged_out::inner` + + // The module isn't found - we would like to get a diagnostic, but currently don't due to + // the awkward way the resolver diagnostics are currently implemented. + // FIXME(Nilstrieb): Also add a note to the cfg diagnostic here + cfged_out::inner::doesnt_exist::hello(); //~ ERROR failed to resolve + //~^ NOTE could not find `doesnt_exist` in `inner` + + // It should find the one in the right module, not the wrong one. + cfged_out::inner::right::meow(); //~ ERROR cannot find function + //~^ NOTE found an item that was configured out + //~| NOTE not found in `cfged_out::inner::right + //~| NOTE the item is gated behind the `what-a-cool-feature` feature + + // Exists in the crate root - diagnostic. + cfged_out::vanished(); //~ ERROR cannot find function + //~^ NOTE found an item that was configured out + //~| NOTE not found in `cfged_out` +} diff --git a/tests/ui/cfg/diagnostics-cross-crate.stderr b/tests/ui/cfg/diagnostics-cross-crate.stderr new file mode 100644 index 0000000000000..046929bc26023 --- /dev/null +++ b/tests/ui/cfg/diagnostics-cross-crate.stderr @@ -0,0 +1,53 @@ +error[E0433]: failed to resolve: could not find `doesnt_exist` in `inner` + --> $DIR/diagnostics-cross-crate.rs:18:23 + | +LL | cfged_out::inner::doesnt_exist::hello(); + | ^^^^^^^^^^^^ could not find `doesnt_exist` in `inner` + +error[E0425]: cannot find function `uwu` in crate `cfged_out` + --> $DIR/diagnostics-cross-crate.rs:7:16 + | +LL | cfged_out::uwu(); + | ^^^ not found in `cfged_out` + +error[E0425]: cannot find function `uwu` in module `cfged_out::inner` + --> $DIR/diagnostics-cross-crate.rs:11:23 + | +LL | cfged_out::inner::uwu(); + | ^^^ not found in `cfged_out::inner` + | +note: found an item that was configured out + --> $DIR/auxiliary/cfged_out.rs:3:12 + | +LL | pub fn uwu() {} + | ^^^ + +error[E0425]: cannot find function `meow` in module `cfged_out::inner::right` + --> $DIR/diagnostics-cross-crate.rs:22:30 + | +LL | cfged_out::inner::right::meow(); + | ^^^^ not found in `cfged_out::inner::right` + | +note: found an item that was configured out + --> $DIR/auxiliary/cfged_out.rs:17:16 + | +LL | pub fn meow() {} + | ^^^^ + = note: the item is gated behind the `what-a-cool-feature` feature + +error[E0425]: cannot find function `vanished` in crate `cfged_out` + --> $DIR/diagnostics-cross-crate.rs:28:16 + | +LL | cfged_out::vanished(); + | ^^^^^^^^ not found in `cfged_out` + | +note: found an item that was configured out + --> $DIR/auxiliary/cfged_out.rs:22:8 + | +LL | pub fn vanished() {} + | ^^^^^^^^ + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0425, E0433. +For more information about an error, try `rustc --explain E0425`. diff --git a/tests/ui/cfg/diagnostics-not-a-def.rs b/tests/ui/cfg/diagnostics-not-a-def.rs new file mode 100644 index 0000000000000..7293947122675 --- /dev/null +++ b/tests/ui/cfg/diagnostics-not-a-def.rs @@ -0,0 +1,12 @@ +pub mod inner { + pub fn i_am_here() { + #[cfg(feature = "another one that doesn't exist")] + loop {} + } +} + +fn main() { + inner::i_am_here(); + // ensure that nothing bad happens when we are checking for cfgs + inner::i_am_not(); //~ ERROR cannot find function +} diff --git a/tests/ui/cfg/diagnostics-not-a-def.stderr b/tests/ui/cfg/diagnostics-not-a-def.stderr new file mode 100644 index 0000000000000..af0e1a1727579 --- /dev/null +++ b/tests/ui/cfg/diagnostics-not-a-def.stderr @@ -0,0 +1,9 @@ +error[E0425]: cannot find function `i_am_not` in module `inner` + --> $DIR/diagnostics-not-a-def.rs:11:12 + | +LL | inner::i_am_not(); + | ^^^^^^^^ not found in `inner` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/cfg/diagnostics-reexport.rs b/tests/ui/cfg/diagnostics-reexport.rs new file mode 100644 index 0000000000000..1d43d6ba02f56 --- /dev/null +++ b/tests/ui/cfg/diagnostics-reexport.rs @@ -0,0 +1,16 @@ +pub mod inner { + #[cfg(FALSE)] + mod gone { + pub fn uwu() {} + } + + #[cfg(FALSE)] + pub use super::uwu; + //~^ NOTE found an item that was configured out +} + +fn main() { + // There is no uwu at this path - no diagnostic. + inner::uwu(); //~ ERROR cannot find function + //~^ NOTE not found in `inner` +} diff --git a/tests/ui/cfg/diagnostics-reexport.stderr b/tests/ui/cfg/diagnostics-reexport.stderr new file mode 100644 index 0000000000000..6c977cbfa4172 --- /dev/null +++ b/tests/ui/cfg/diagnostics-reexport.stderr @@ -0,0 +1,15 @@ +error[E0425]: cannot find function `uwu` in module `inner` + --> $DIR/diagnostics-reexport.rs:14:12 + | +LL | inner::uwu(); + | ^^^ not found in `inner` + | +note: found an item that was configured out + --> $DIR/diagnostics-reexport.rs:8:20 + | +LL | pub use super::uwu; + | ^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/cfg/diagnostics-same-crate.rs b/tests/ui/cfg/diagnostics-same-crate.rs new file mode 100644 index 0000000000000..f76ace06a762d --- /dev/null +++ b/tests/ui/cfg/diagnostics-same-crate.rs @@ -0,0 +1,51 @@ +pub mod inner { + #[cfg(FALSE)] + pub fn uwu() {} + //~^ NOTE found an item that was configured out + + #[cfg(FALSE)] + pub mod doesnt_exist { + pub fn hello() {} + } + + pub mod wrong { + #[cfg(feature = "suggesting me fails the test!!")] + pub fn meow() {} + } + + pub mod right { + #[cfg(feature = "what-a-cool-feature")] + pub fn meow() {} + //~^ NOTE found an item that was configured out + } +} + +#[cfg(i_dont_exist_and_you_can_do_nothing_about_it)] +pub fn vanished() {} + +fn main() { + // There is no uwu at this path - no diagnostic. + uwu(); //~ ERROR cannot find function + //~^ NOTE not found in this scope + + // It does exist here - diagnostic. + inner::uwu(); //~ ERROR cannot find function + //~| NOTE not found in `inner` + + // The module isn't found - we would like to get a diagnostic, but currently don't due to + // the awkward way the resolver diagnostics are currently implemented. + // FIXME(Nilstrieb): Also add a note to the cfg diagnostic here + inner::doesnt_exist::hello(); //~ ERROR failed to resolve + //~| NOTE could not find `doesnt_exist` in `inner` + + // It should find the one in the right module, not the wrong one. + inner::right::meow(); //~ ERROR cannot find function + //~| NOTE not found in `inner::right + //~| NOTE the item is gated behind the `what-a-cool-feature` feature + + // Exists in the crate root - we would generally want a diagnostic, + // but currently don't have one. + // Not that it matters much though, this is highly unlikely to confuse anyone. + vanished(); //~ ERROR cannot find function + //~^ NOTE not found in this scope +} diff --git a/tests/ui/cfg/diagnostics-same-crate.stderr b/tests/ui/cfg/diagnostics-same-crate.stderr new file mode 100644 index 0000000000000..30ee6479bd26c --- /dev/null +++ b/tests/ui/cfg/diagnostics-same-crate.stderr @@ -0,0 +1,47 @@ +error[E0433]: failed to resolve: could not find `doesnt_exist` in `inner` + --> $DIR/diagnostics-same-crate.rs:38:12 + | +LL | inner::doesnt_exist::hello(); + | ^^^^^^^^^^^^ could not find `doesnt_exist` in `inner` + +error[E0425]: cannot find function `uwu` in module `inner` + --> $DIR/diagnostics-same-crate.rs:32:12 + | +LL | inner::uwu(); + | ^^^ not found in `inner` + | +note: found an item that was configured out + --> $DIR/diagnostics-same-crate.rs:3:12 + | +LL | pub fn uwu() {} + | ^^^ + +error[E0425]: cannot find function `meow` in module `inner::right` + --> $DIR/diagnostics-same-crate.rs:42:19 + | +LL | inner::right::meow(); + | ^^^^ not found in `inner::right` + | +note: found an item that was configured out + --> $DIR/diagnostics-same-crate.rs:18:16 + | +LL | pub fn meow() {} + | ^^^^ + = note: the item is gated behind the `what-a-cool-feature` feature + +error[E0425]: cannot find function `uwu` in this scope + --> $DIR/diagnostics-same-crate.rs:28:5 + | +LL | uwu(); + | ^^^ not found in this scope + +error[E0425]: cannot find function `vanished` in this scope + --> $DIR/diagnostics-same-crate.rs:49:5 + | +LL | vanished(); + | ^^^^^^^^ not found in this scope + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0425, E0433. +For more information about an error, try `rustc --explain E0425`. diff --git a/tests/ui/closures/issue-111932.rs b/tests/ui/closures/issue-111932.rs new file mode 100644 index 0000000000000..eb3fe08cbc409 --- /dev/null +++ b/tests/ui/closures/issue-111932.rs @@ -0,0 +1,9 @@ +trait Foo: std::fmt::Debug {} + +fn print_foos(foos: impl Iterator) { + foos.for_each(|foo| { //~ ERROR [E0277] + println!("{:?}", foo); //~ ERROR [E0277] + }); +} + +fn main() {} diff --git a/tests/ui/closures/issue-111932.stderr b/tests/ui/closures/issue-111932.stderr new file mode 100644 index 0000000000000..937bdf3bea255 --- /dev/null +++ b/tests/ui/closures/issue-111932.stderr @@ -0,0 +1,26 @@ +error[E0277]: the size for values of type `(dyn Foo + 'static)` cannot be known at compilation time + --> $DIR/issue-111932.rs:4:20 + | +LL | foos.for_each(|foo| { + | ^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `(dyn Foo + 'static)` + = note: all function arguments must have a statically known size + = help: unsized fn params are gated as an unstable feature + +error[E0277]: the size for values of type `dyn Foo` cannot be known at compilation time + --> $DIR/issue-111932.rs:5:26 + | +LL | println!("{:?}", foo); + | ---- ^^^ doesn't have a size known at compile-time + | | + | required by a bound introduced by this call + | + = help: the trait `Sized` is not implemented for `dyn Foo` +note: required by a bound in `core::fmt::rt::Argument::<'a>::new_debug` + --> $SRC_DIR/core/src/fmt/rt.rs:LL:COL + = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/closures/issue-72408-nested-closures-exponential.rs b/tests/ui/closures/issue-72408-nested-closures-exponential.rs index 2d6ba936572d5..d064ebceffd5c 100644 --- a/tests/ui/closures/issue-72408-nested-closures-exponential.rs +++ b/tests/ui/closures/issue-72408-nested-closures-exponential.rs @@ -1,4 +1,5 @@ // build-pass +// ignore-compare-mode-next-solver (hangs) // Closures include captured types twice in a type tree. // diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_bad.stderr b/tests/ui/const-generics/adt_const_params/const_param_ty_bad.stderr index de5704ee429ed..48910b82a1032 100644 --- a/tests/ui/const-generics/adt_const_params/const_param_ty_bad.stderr +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_bad.stderr @@ -11,6 +11,10 @@ note: required by a bound in `check` | LL | fn check(_: impl std::marker::ConstParamTy) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check` +help: use parentheses to call this function + | +LL | check(main()); + | ++ error[E0277]: `[closure@$DIR/const_param_ty_bad.rs:8:11: 8:13]` can't be used as a const parameter type --> $DIR/const_param_ty_bad.rs:8:11 @@ -25,6 +29,10 @@ note: required by a bound in `check` | LL | fn check(_: impl std::marker::ConstParamTy) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check` +help: use parentheses to call this closure + | +LL | check(|| {}()); + | ++ error[E0277]: `fn()` can't be used as a const parameter type --> $DIR/const_param_ty_bad.rs:9:11 @@ -39,6 +47,10 @@ note: required by a bound in `check` | LL | fn check(_: impl std::marker::ConstParamTy) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check` +help: use parentheses to call this function pointer + | +LL | check(main as fn()()); + | ++ error[E0277]: `&mut ()` can't be used as a const parameter type --> $DIR/const_param_ty_bad.rs:10:11 @@ -48,11 +60,17 @@ LL | check(&mut ()); | | | required by a bound introduced by this call | + = note: `ConstParamTy` is implemented for `&()`, but not for `&mut ()` note: required by a bound in `check` --> $DIR/const_param_ty_bad.rs:4:18 | LL | fn check(_: impl std::marker::ConstParamTy) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check` +help: consider removing the leading `&`-reference + | +LL - check(&mut ()); +LL + check(()); + | error[E0277]: `*mut ()` can't be used as a const parameter type --> $DIR/const_param_ty_bad.rs:11:11 @@ -62,6 +80,7 @@ LL | check(&mut () as *mut ()); | | | required by a bound introduced by this call | + = help: the trait `ConstParamTy` is implemented for `()` note: required by a bound in `check` --> $DIR/const_param_ty_bad.rs:4:18 | @@ -76,6 +95,7 @@ LL | check(&() as *const ()); | | | required by a bound introduced by this call | + = help: the trait `ConstParamTy` is implemented for `()` note: required by a bound in `check` --> $DIR/const_param_ty_bad.rs:4:18 | diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_good.rs b/tests/ui/const-generics/adt_const_params/const_param_ty_good.rs index 87ae83dd96603..100ab332a40d8 100644 --- a/tests/ui/const-generics/adt_const_params/const_param_ty_good.rs +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_good.rs @@ -49,5 +49,7 @@ fn main() { check::>(); check::>(); - // FIXME: test tuples + check::<()>(); + check::<(i32,)>(); + check::<(D, D)>(); } diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_no_structural_eq.rs b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_no_structural_eq.rs index 37986de481f11..08f7c5cb5423e 100644 --- a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_no_structural_eq.rs +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_no_structural_eq.rs @@ -9,9 +9,11 @@ struct CantParam(ImplementsConstParamTy); impl std::marker::ConstParamTy for CantParam {} //~^ error: the type `CantParam` does not `#[derive(Eq)]` +//~| error: the type `CantParam` does not `#[derive(PartialEq)]` #[derive(std::marker::ConstParamTy)] //~^ error: the type `CantParamDerive` does not `#[derive(Eq)]` +//~| error: the type `CantParamDerive` does not `#[derive(PartialEq)]` struct CantParamDerive(ImplementsConstParamTy); fn check() {} diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_no_structural_eq.stderr b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_no_structural_eq.stderr index 52701d5591442..43c5b96dc7cb8 100644 --- a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_no_structural_eq.stderr +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_no_structural_eq.stderr @@ -1,3 +1,12 @@ +error[E0277]: the type `CantParam` does not `#[derive(PartialEq)]` + --> $DIR/const_param_ty_impl_no_structural_eq.rs:10:36 + | +LL | impl std::marker::ConstParamTy for CantParam {} + | ^^^^^^^^^ the trait `StructuralPartialEq` is not implemented for `CantParam` + | +note: required by a bound in `ConstParamTy` + --> $SRC_DIR/core/src/marker.rs:LL:COL + error[E0277]: the type `CantParam` does not `#[derive(Eq)]` --> $DIR/const_param_ty_impl_no_structural_eq.rs:10:36 | @@ -7,8 +16,18 @@ LL | impl std::marker::ConstParamTy for CantParam {} note: required by a bound in `ConstParamTy` --> $SRC_DIR/core/src/marker.rs:LL:COL +error[E0277]: the type `CantParamDerive` does not `#[derive(PartialEq)]` + --> $DIR/const_param_ty_impl_no_structural_eq.rs:14:10 + | +LL | #[derive(std::marker::ConstParamTy)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `StructuralPartialEq` is not implemented for `CantParamDerive` + | +note: required by a bound in `ConstParamTy` + --> $SRC_DIR/core/src/marker.rs:LL:COL + = note: this error originates in the derive macro `std::marker::ConstParamTy` (in Nightly builds, run with -Z macro-backtrace for more info) + error[E0277]: the type `CantParamDerive` does not `#[derive(Eq)]` - --> $DIR/const_param_ty_impl_no_structural_eq.rs:13:10 + --> $DIR/const_param_ty_impl_no_structural_eq.rs:14:10 | LL | #[derive(std::marker::ConstParamTy)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `StructuralEq` is not implemented for `CantParamDerive` @@ -17,6 +36,6 @@ note: required by a bound in `ConstParamTy` --> $SRC_DIR/core/src/marker.rs:LL:COL = note: this error originates in the derive macro `std::marker::ConstParamTy` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 2 previous errors +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_union.rs b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_union.rs index d70377a20c170..c04e96c569b41 100644 --- a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_union.rs +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_union.rs @@ -14,6 +14,7 @@ impl Eq for Union {} impl std::marker::StructuralEq for Union {} impl std::marker::ConstParamTy for Union {} +//~^ ERROR the type `Union` does not `#[derive(PartialEq)]` #[derive(std::marker::ConstParamTy)] //~^ ERROR this trait cannot be derived for unions diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_union.stderr b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_union.stderr index 2937030460503..985b933c40c79 100644 --- a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_union.stderr +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_union.stderr @@ -1,8 +1,18 @@ error: this trait cannot be derived for unions - --> $DIR/const_param_ty_impl_union.rs:18:10 + --> $DIR/const_param_ty_impl_union.rs:19:10 | LL | #[derive(std::marker::ConstParamTy)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to previous error +error[E0277]: the type `Union` does not `#[derive(PartialEq)]` + --> $DIR/const_param_ty_impl_union.rs:16:36 + | +LL | impl std::marker::ConstParamTy for Union {} + | ^^^^^ the trait `StructuralPartialEq` is not implemented for `Union` + | +note: required by a bound in `ConstParamTy` + --> $SRC_DIR/core/src/marker.rs:LL:COL + +error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/const-generics/const-param-type-depends-on-type-param.full.stderr b/tests/ui/const-generics/const-param-type-depends-on-type-param.full.stderr index 938fb08b7956e..e508890dd7c8b 100644 --- a/tests/ui/const-generics/const-param-type-depends-on-type-param.full.stderr +++ b/tests/ui/const-generics/const-param-type-depends-on-type-param.full.stderr @@ -6,16 +6,6 @@ LL | pub struct Dependent([(); X]); | = note: type parameters may not be used in the type of const parameters -error[E0392]: parameter `T` is never used - --> $DIR/const-param-type-depends-on-type-param.rs:11:22 - | -LL | pub struct Dependent([(); X]); - | ^ unused parameter - | - = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` - = help: if you intended `T` to be a const parameter, use `const T: usize` instead - -error: aborting due to 2 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0392, E0770. -For more information about an error, try `rustc --explain E0392`. +For more information about this error, try `rustc --explain E0770`. diff --git a/tests/ui/const-generics/const-param-type-depends-on-type-param.min.stderr b/tests/ui/const-generics/const-param-type-depends-on-type-param.min.stderr index 938fb08b7956e..e508890dd7c8b 100644 --- a/tests/ui/const-generics/const-param-type-depends-on-type-param.min.stderr +++ b/tests/ui/const-generics/const-param-type-depends-on-type-param.min.stderr @@ -6,16 +6,6 @@ LL | pub struct Dependent([(); X]); | = note: type parameters may not be used in the type of const parameters -error[E0392]: parameter `T` is never used - --> $DIR/const-param-type-depends-on-type-param.rs:11:22 - | -LL | pub struct Dependent([(); X]); - | ^ unused parameter - | - = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` - = help: if you intended `T` to be a const parameter, use `const T: usize` instead - -error: aborting due to 2 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0392, E0770. -For more information about an error, try `rustc --explain E0392`. +For more information about this error, try `rustc --explain E0770`. diff --git a/tests/ui/const-generics/const-param-type-depends-on-type-param.rs b/tests/ui/const-generics/const-param-type-depends-on-type-param.rs index 71d91fd7e7f45..fc3aa9cbc27c9 100644 --- a/tests/ui/const-generics/const-param-type-depends-on-type-param.rs +++ b/tests/ui/const-generics/const-param-type-depends-on-type-param.rs @@ -10,6 +10,5 @@ pub struct Dependent([(); X]); //~^ ERROR: the type of const parameters must not depend on other generic parameters -//~| ERROR: parameter `T` is never used fn main() {} diff --git a/tests/ui/const-generics/const-param-with-additional-obligations.rs b/tests/ui/const-generics/const-param-with-additional-obligations.rs new file mode 100644 index 0000000000000..f53cf85cdd39c --- /dev/null +++ b/tests/ui/const-generics/const-param-with-additional-obligations.rs @@ -0,0 +1,17 @@ +#![feature(adt_const_params)] +#![allow(incomplete_features)] + +use std::marker::ConstParamTy; + +#[derive(Eq, PartialEq)] +struct Foo(T); + +trait Other {} + +impl ConstParamTy for Foo where T: Other + ConstParamTy {} + +fn foo>() {} +//~^ ERROR `Foo` must implement `ConstParamTy` to be used as the type of a const generic parameter +//~| NOTE `u8` must implement `Other`, but it does not + +fn main() {} diff --git a/tests/ui/const-generics/const-param-with-additional-obligations.stderr b/tests/ui/const-generics/const-param-with-additional-obligations.stderr new file mode 100644 index 0000000000000..f7ec4d57401c1 --- /dev/null +++ b/tests/ui/const-generics/const-param-with-additional-obligations.stderr @@ -0,0 +1,11 @@ +error[E0741]: `Foo` must implement `ConstParamTy` to be used as the type of a const generic parameter + --> $DIR/const-param-with-additional-obligations.rs:13:17 + | +LL | fn foo>() {} + | ^^^^^^^ + | + = note: `u8` must implement `Other`, but it does not + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0741`. diff --git a/tests/ui/const-generics/float-generic.adt_const_params.stderr b/tests/ui/const-generics/float-generic.adt_const_params.stderr index fef5ef0d1facf..6fe5390471ddc 100644 --- a/tests/ui/const-generics/float-generic.adt_const_params.stderr +++ b/tests/ui/const-generics/float-generic.adt_const_params.stderr @@ -3,8 +3,6 @@ error[E0741]: `f32` is forbidden as the type of a const generic parameter | LL | fn foo() {} | ^^^ - | - = note: floats do not derive `Eq` or `Ord`, which are required for const parameters error: aborting due to previous error diff --git a/tests/ui/const-generics/forbid-non-structural_match-types.rs b/tests/ui/const-generics/forbid-non-structural_match-types.rs index 6ae9d5cfbb54f..4fec2a9f32f81 100644 --- a/tests/ui/const-generics/forbid-non-structural_match-types.rs +++ b/tests/ui/const-generics/forbid-non-structural_match-types.rs @@ -1,13 +1,15 @@ #![feature(adt_const_params)] #![allow(incomplete_features)] -#[derive(PartialEq, Eq)] +use std::marker::ConstParamTy; + +#[derive(PartialEq, Eq, ConstParamTy)] struct A; struct B; // ok struct C; -struct D; //~ ERROR `C` must be annotated with `#[derive(PartialEq, Eq)]` +struct D; //~ ERROR `C` must implement `ConstParamTy` to be used as the type of a const generic parameter fn main() {} diff --git a/tests/ui/const-generics/forbid-non-structural_match-types.stderr b/tests/ui/const-generics/forbid-non-structural_match-types.stderr index 81b9bdfbd6027..0efb9e9d3c287 100644 --- a/tests/ui/const-generics/forbid-non-structural_match-types.stderr +++ b/tests/ui/const-generics/forbid-non-structural_match-types.stderr @@ -1,8 +1,14 @@ -error[E0741]: `C` must be annotated with `#[derive(PartialEq, Eq)]` to be used as the type of a const parameter - --> $DIR/forbid-non-structural_match-types.rs:11:19 +error[E0741]: `C` must implement `ConstParamTy` to be used as the type of a const generic parameter + --> $DIR/forbid-non-structural_match-types.rs:13:19 | LL | struct D; - | ^ `C` doesn't derive both `PartialEq` and `Eq` + | ^ + | +help: add `#[derive(ConstParamTy, PartialEq, Eq)]` to the struct + | +LL + #[derive(ConstParamTy, PartialEq, Eq)] +LL | struct C; + | error: aborting due to previous error diff --git a/tests/ui/const-generics/generic_const_exprs/array-size-in-generic-struct-param.full.stderr b/tests/ui/const-generics/generic_const_exprs/array-size-in-generic-struct-param.full.stderr index 1d10dfdf10c6e..c478718b4cc79 100644 --- a/tests/ui/const-generics/generic_const_exprs/array-size-in-generic-struct-param.full.stderr +++ b/tests/ui/const-generics/generic_const_exprs/array-size-in-generic-struct-param.full.stderr @@ -7,7 +7,7 @@ LL | struct ArithArrayLen([u32; 0 + N]); = help: try adding a `where` bound using this expression: `where [(); 0 + N]:` error: overly complex generic constant - --> $DIR/array-size-in-generic-struct-param.rs:19:15 + --> $DIR/array-size-in-generic-struct-param.rs:23:15 | LL | arr: [u8; CFG.arr_size], | ^^^^^^^^^^^^ field access is not supported in generic constants diff --git a/tests/ui/const-generics/generic_const_exprs/array-size-in-generic-struct-param.min.stderr b/tests/ui/const-generics/generic_const_exprs/array-size-in-generic-struct-param.min.stderr index 18e9135d07235..956e9c9c988a4 100644 --- a/tests/ui/const-generics/generic_const_exprs/array-size-in-generic-struct-param.min.stderr +++ b/tests/ui/const-generics/generic_const_exprs/array-size-in-generic-struct-param.min.stderr @@ -8,7 +8,7 @@ LL | struct ArithArrayLen([u32; 0 + N]); = help: use `#![feature(generic_const_exprs)]` to allow generic const expressions error: generic parameters may not be used in const operations - --> $DIR/array-size-in-generic-struct-param.rs:19:15 + --> $DIR/array-size-in-generic-struct-param.rs:23:15 | LL | arr: [u8; CFG.arr_size], | ^^^ cannot perform const operation using `CFG` @@ -17,7 +17,7 @@ LL | arr: [u8; CFG.arr_size], = help: use `#![feature(generic_const_exprs)]` to allow generic const expressions error: `Config` is forbidden as the type of a const generic parameter - --> $DIR/array-size-in-generic-struct-param.rs:17:21 + --> $DIR/array-size-in-generic-struct-param.rs:21:21 | LL | struct B { | ^^^^^^ diff --git a/tests/ui/const-generics/generic_const_exprs/array-size-in-generic-struct-param.rs b/tests/ui/const-generics/generic_const_exprs/array-size-in-generic-struct-param.rs index 7d3fe413c1706..33ca6dcb3049c 100644 --- a/tests/ui/const-generics/generic_const_exprs/array-size-in-generic-struct-param.rs +++ b/tests/ui/const-generics/generic_const_exprs/array-size-in-generic-struct-param.rs @@ -9,7 +9,11 @@ struct ArithArrayLen([u32; 0 + N]); //[full]~^ ERROR unconstrained generic constant //[min]~^^ ERROR generic parameters may not be used in const operations +#[cfg(full)] +use std::marker::ConstParamTy; + #[derive(PartialEq, Eq)] +#[cfg_attr(full, derive(ConstParamTy))] struct Config { arr_size: usize, } diff --git a/tests/ui/const-generics/generic_const_exprs/inline-const-in-const-generic-defaults.rs b/tests/ui/const-generics/generic_const_exprs/inline-const-in-const-generic-defaults.rs new file mode 100644 index 0000000000000..d81cba6275405 --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/inline-const-in-const-generic-defaults.rs @@ -0,0 +1,9 @@ +// check-pass + +#![feature(generic_const_exprs)] +#![feature(inline_const)] +#![allow(incomplete_features)] + +pub struct ConstDefaultUnstable; + +pub fn main() {} diff --git a/tests/ui/const-generics/invalid-enum.rs b/tests/ui/const-generics/invalid-enum.rs index cb6d05349db97..fcfad300c44e5 100644 --- a/tests/ui/const-generics/invalid-enum.rs +++ b/tests/ui/const-generics/invalid-enum.rs @@ -1,7 +1,9 @@ #![feature(adt_const_params)] #![allow(incomplete_features)] -#[derive(PartialEq, Eq)] +use std::marker::ConstParamTy; + +#[derive(PartialEq, Eq, ConstParamTy)] enum CompileFlag { A, B, diff --git a/tests/ui/const-generics/invalid-enum.stderr b/tests/ui/const-generics/invalid-enum.stderr index 0d3643f6f899a..7e8a632b34f79 100644 --- a/tests/ui/const-generics/invalid-enum.stderr +++ b/tests/ui/const-generics/invalid-enum.stderr @@ -1,5 +1,5 @@ error[E0573]: expected type, found variant `CompileFlag::A` - --> $DIR/invalid-enum.rs:21:12 + --> $DIR/invalid-enum.rs:23:12 | LL | test_1::(); | ^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | test_1::(); | help: try using the variant's enum: `CompileFlag` error[E0573]: expected type, found variant `CompileFlag::A` - --> $DIR/invalid-enum.rs:25:15 + --> $DIR/invalid-enum.rs:27:15 | LL | test_2::<_, CompileFlag::A>(0); | ^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | test_2::<_, CompileFlag::A>(0); | help: try using the variant's enum: `CompileFlag` error[E0573]: expected type, found variant `CompileFlag::A` - --> $DIR/invalid-enum.rs:29:18 + --> $DIR/invalid-enum.rs:31:18 | LL | let _: Example = Example { x: 0 }; | ^^^^^^^^^^^^^^ @@ -26,7 +26,7 @@ LL | let _: Example = Example { x: 0 }; | help: try using the variant's enum: `CompileFlag` error[E0747]: unresolved item provided when a constant was expected - --> $DIR/invalid-enum.rs:29:18 + --> $DIR/invalid-enum.rs:31:18 | LL | let _: Example = Example { x: 0 }; | ^^^^^^^^^^^^^^ @@ -37,7 +37,7 @@ LL | let _: Example<{ CompileFlag::A }, _> = Example { x: 0 }; | + + error[E0747]: type provided when a constant was expected - --> $DIR/invalid-enum.rs:33:18 + --> $DIR/invalid-enum.rs:35:18 | LL | let _: Example = Example { x: 0 }; | ^^^^^^^^^^^^^^^^^^^ @@ -48,7 +48,7 @@ LL | let _: Example<{ Example::ASSOC_FLAG }, _> = Example { x: 0 }; | + + error[E0747]: unresolved item provided when a constant was expected - --> $DIR/invalid-enum.rs:21:12 + --> $DIR/invalid-enum.rs:23:12 | LL | test_1::(); | ^^^^^^^^^^^^^^ @@ -59,7 +59,7 @@ LL | test_1::<{ CompileFlag::A }>(); | + + error[E0747]: unresolved item provided when a constant was expected - --> $DIR/invalid-enum.rs:25:15 + --> $DIR/invalid-enum.rs:27:15 | LL | test_2::<_, CompileFlag::A>(0); | ^^^^^^^^^^^^^^ diff --git a/tests/ui/const-generics/issue-66451.rs b/tests/ui/const-generics/issue-66451.rs index 3335f7d598480..c8d5515e98739 100644 --- a/tests/ui/const-generics/issue-66451.rs +++ b/tests/ui/const-generics/issue-66451.rs @@ -1,13 +1,15 @@ #![feature(adt_const_params)] #![allow(incomplete_features)] -#[derive(Debug, PartialEq, Eq)] +use std::marker::ConstParamTy; + +#[derive(Debug, PartialEq, Eq, ConstParamTy)] struct Foo { value: i32, nested: &'static Bar, } -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, ConstParamTy)] struct Bar(T); struct Test; diff --git a/tests/ui/const-generics/issue-66451.stderr b/tests/ui/const-generics/issue-66451.stderr index e0cb0b661ff65..946d5148667ec 100644 --- a/tests/ui/const-generics/issue-66451.stderr +++ b/tests/ui/const-generics/issue-66451.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-66451.rs:27:10 + --> $DIR/issue-66451.rs:29:10 | LL | let y: Test<{ | ____________- diff --git a/tests/ui/const-generics/issue-80471.rs b/tests/ui/const-generics/issue-80471.rs index d0af8a5eaa89f..fa6f1fde435a0 100644 --- a/tests/ui/const-generics/issue-80471.rs +++ b/tests/ui/const-generics/issue-80471.rs @@ -8,6 +8,6 @@ enum Nat { } fn foo() {} -//~^ ERROR `Box` must be annotated with `#[derive(PartialEq, Eq)]` to be used as the type of a const parameter +//~^ ERROR `Nat` must implement `ConstParamTy` to be used as the type of a const generic parameter fn main() {} diff --git a/tests/ui/const-generics/issue-80471.stderr b/tests/ui/const-generics/issue-80471.stderr index b89706710bcf2..3b7143de543d7 100644 --- a/tests/ui/const-generics/issue-80471.stderr +++ b/tests/ui/const-generics/issue-80471.stderr @@ -7,11 +7,17 @@ LL | #![feature(adt_const_params)] = note: see issue #95174 for more information = note: `#[warn(incomplete_features)]` on by default -error[E0741]: `Box` must be annotated with `#[derive(PartialEq, Eq)]` to be used as the type of a const parameter +error[E0741]: `Nat` must implement `ConstParamTy` to be used as the type of a const generic parameter --> $DIR/issue-80471.rs:10:17 | LL | fn foo() {} | ^^^ + | +help: add `#[derive(ConstParamTy)]` to the struct + | +LL + #[derive(ConstParamTy)] +LL | enum Nat { + | error: aborting due to previous error; 1 warning emitted diff --git a/tests/ui/const-generics/issues/issue-63322-forbid-dyn.full.stderr b/tests/ui/const-generics/issues/issue-63322-forbid-dyn.full.stderr index 16fabd1e88fd0..e2d8c5ca0e148 100644 --- a/tests/ui/const-generics/issues/issue-63322-forbid-dyn.full.stderr +++ b/tests/ui/const-generics/issues/issue-63322-forbid-dyn.full.stderr @@ -1,8 +1,10 @@ -error[E0741]: `(dyn A + 'static)` must be annotated with `#[derive(PartialEq, Eq)]` to be used as the type of a const parameter +error[E0741]: `&'static (dyn A + 'static)` can't be used as a const parameter type --> $DIR/issue-63322-forbid-dyn.rs:9:18 | LL | fn test() { | ^^^^^^^^^^^^^^ + | + = note: `(dyn A + 'static)` must implement `ConstParamTy`, but it does not error: aborting due to previous error diff --git a/tests/ui/const-generics/issues/issue-63322-forbid-dyn.rs b/tests/ui/const-generics/issues/issue-63322-forbid-dyn.rs index 116c3fcfb2170..8bc35ab3d3795 100644 --- a/tests/ui/const-generics/issues/issue-63322-forbid-dyn.rs +++ b/tests/ui/const-generics/issues/issue-63322-forbid-dyn.rs @@ -7,7 +7,7 @@ struct B; impl A for B {} fn test() { - //[full]~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` to be used + //[full]~^ ERROR `&'static (dyn A + 'static)` can't be used as a const parameter type //[min]~^^ ERROR `&'static (dyn A + 'static)` is forbidden unimplemented!() } diff --git a/tests/ui/const-generics/issues/issue-71381.full.stderr b/tests/ui/const-generics/issues/issue-71381.full.stderr index 962eaf75b98f5..b6460e0017fa5 100644 --- a/tests/ui/const-generics/issues/issue-71381.full.stderr +++ b/tests/ui/const-generics/issues/issue-71381.full.stderr @@ -14,19 +14,6 @@ LL | const FN: unsafe extern "C" fn(Args), | = note: type parameters may not be used in the type of const parameters -error[E0741]: using function pointers as const generic parameters is forbidden - --> $DIR/issue-71381.rs:14:61 - | -LL | pub fn call_me(&self) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error[E0741]: using function pointers as const generic parameters is forbidden - --> $DIR/issue-71381.rs:23:19 - | -LL | const FN: unsafe extern "C" fn(Args), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 4 previous errors +error: aborting due to 2 previous errors -Some errors have detailed explanations: E0741, E0770. -For more information about an error, try `rustc --explain E0741`. +For more information about this error, try `rustc --explain E0770`. diff --git a/tests/ui/const-generics/issues/issue-71381.rs b/tests/ui/const-generics/issues/issue-71381.rs index 66f819dbe06e2..8a878efb42a0e 100644 --- a/tests/ui/const-generics/issues/issue-71381.rs +++ b/tests/ui/const-generics/issues/issue-71381.rs @@ -12,8 +12,8 @@ unsafe extern "C" fn pass(args: PassArg) { impl Test { pub fn call_me(&self) { - //~^ ERROR: using function pointers as const generic parameters is forbidden - //~| ERROR: the type of const parameters must not depend on other generic parameters + //[min]~^ ERROR: using function pointers as const generic parameters is forbidden + //~^^ ERROR: the type of const parameters must not depend on other generic parameters self.0 = Self::trampiline:: as _ } @@ -21,8 +21,8 @@ impl Test { Args: Sized, const IDX: usize, const FN: unsafe extern "C" fn(Args), - //~^ ERROR: using function pointers as const generic parameters is forbidden - //~| ERROR: the type of const parameters must not depend on other generic parameters + //[min]~^ ERROR: using function pointers as const generic parameters is forbidden + //~^^ ERROR: the type of const parameters must not depend on other generic parameters >( args: Args, ) { @@ -31,6 +31,6 @@ impl Test { } fn main() { - let x = Test(); + let x = Test(std::ptr::null()); x.call_me::() } diff --git a/tests/ui/const-generics/issues/issue-71611.full.stderr b/tests/ui/const-generics/issues/issue-71611.full.stderr index e109459f2be6f..b55f410a02370 100644 --- a/tests/ui/const-generics/issues/issue-71611.full.stderr +++ b/tests/ui/const-generics/issues/issue-71611.full.stderr @@ -6,13 +6,6 @@ LL | fn func(outer: A) { | = note: type parameters may not be used in the type of const parameters -error[E0741]: using function pointers as const generic parameters is forbidden - --> $DIR/issue-71611.rs:5:21 - | -LL | fn func(outer: A) { - | ^^^^^^^^^^^^ - -error: aborting due to 2 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0741, E0770. -For more information about an error, try `rustc --explain E0741`. +For more information about this error, try `rustc --explain E0770`. diff --git a/tests/ui/const-generics/issues/issue-71611.rs b/tests/ui/const-generics/issues/issue-71611.rs index fbb91ca18aa4f..c917f66818bad 100644 --- a/tests/ui/const-generics/issues/issue-71611.rs +++ b/tests/ui/const-generics/issues/issue-71611.rs @@ -3,8 +3,8 @@ #![cfg_attr(full, allow(incomplete_features))] fn func(outer: A) { - //~^ ERROR: using function pointers as const generic parameters is forbidden - //~| ERROR: the type of const parameters must not depend on other generic parameters + //[min]~^ ERROR: using function pointers as const generic parameters is forbidden + //~^^ ERROR: the type of const parameters must not depend on other generic parameters F(outer); } diff --git a/tests/ui/const-generics/issues/issue-74255.min.stderr b/tests/ui/const-generics/issues/issue-74255.min.stderr index b462d84487ee8..bbcf8682b7119 100644 --- a/tests/ui/const-generics/issues/issue-74255.min.stderr +++ b/tests/ui/const-generics/issues/issue-74255.min.stderr @@ -1,5 +1,5 @@ error: `IceEnum` is forbidden as the type of a const generic parameter - --> $DIR/issue-74255.rs:14:31 + --> $DIR/issue-74255.rs:18:31 | LL | fn ice_struct_fn() {} | ^^^^^^^ diff --git a/tests/ui/const-generics/issues/issue-74255.rs b/tests/ui/const-generics/issues/issue-74255.rs index 0e523926fb0b1..60b2fd37c44f6 100644 --- a/tests/ui/const-generics/issues/issue-74255.rs +++ b/tests/ui/const-generics/issues/issue-74255.rs @@ -3,7 +3,11 @@ #![cfg_attr(full, feature(adt_const_params))] #![cfg_attr(full, allow(incomplete_features))] +#[cfg(full)] +use std::marker::ConstParamTy; + #[derive(PartialEq, Eq)] +#[cfg_attr(full, derive(ConstParamTy))] enum IceEnum { Variant } diff --git a/tests/ui/const-generics/issues/issue-74950.min.stderr b/tests/ui/const-generics/issues/issue-74950.min.stderr index 729ecc2022c99..c37ee93d420fd 100644 --- a/tests/ui/const-generics/issues/issue-74950.min.stderr +++ b/tests/ui/const-generics/issues/issue-74950.min.stderr @@ -1,5 +1,5 @@ error: `Inner` is forbidden as the type of a const generic parameter - --> $DIR/issue-74950.rs:17:23 + --> $DIR/issue-74950.rs:20:23 | LL | struct Outer; | ^^^^^ @@ -8,7 +8,7 @@ LL | struct Outer; = help: more complex types are supported with `#![feature(adt_const_params)]` error: `Inner` is forbidden as the type of a const generic parameter - --> $DIR/issue-74950.rs:17:23 + --> $DIR/issue-74950.rs:20:23 | LL | struct Outer; | ^^^^^ @@ -17,7 +17,7 @@ LL | struct Outer; = help: more complex types are supported with `#![feature(adt_const_params)]` error: `Inner` is forbidden as the type of a const generic parameter - --> $DIR/issue-74950.rs:17:23 + --> $DIR/issue-74950.rs:20:23 | LL | struct Outer; | ^^^^^ @@ -26,7 +26,7 @@ LL | struct Outer; = help: more complex types are supported with `#![feature(adt_const_params)]` error: `Inner` is forbidden as the type of a const generic parameter - --> $DIR/issue-74950.rs:17:23 + --> $DIR/issue-74950.rs:20:23 | LL | struct Outer; | ^^^^^ @@ -35,7 +35,7 @@ LL | struct Outer; = help: more complex types are supported with `#![feature(adt_const_params)]` error: `Inner` is forbidden as the type of a const generic parameter - --> $DIR/issue-74950.rs:17:23 + --> $DIR/issue-74950.rs:20:23 | LL | struct Outer; | ^^^^^ diff --git a/tests/ui/const-generics/issues/issue-74950.rs b/tests/ui/const-generics/issues/issue-74950.rs index 3e1ca4735db61..43bb322656b3e 100644 --- a/tests/ui/const-generics/issues/issue-74950.rs +++ b/tests/ui/const-generics/issues/issue-74950.rs @@ -3,8 +3,11 @@ #![cfg_attr(full, feature(adt_const_params))] #![cfg_attr(full, allow(incomplete_features))] +#[cfg(full)] +use std::marker::ConstParamTy; #[derive(PartialEq, Eq)] +#[cfg_attr(full, derive(ConstParamTy))] struct Inner; // Note: We emit the error 5 times if we don't deduplicate: diff --git a/tests/ui/const-generics/issues/issue-87076.rs b/tests/ui/const-generics/issues/issue-87076.rs index 8a567678b824e..a32c1f965f8b5 100644 --- a/tests/ui/const-generics/issues/issue-87076.rs +++ b/tests/ui/const-generics/issues/issue-87076.rs @@ -3,7 +3,9 @@ #![feature(adt_const_params)] #![allow(incomplete_features)] -#[derive(PartialEq, Eq)] +use std::marker::ConstParamTy; + +#[derive(PartialEq, Eq, ConstParamTy)] pub struct UnitDims { pub time: u8, pub length: u8, diff --git a/tests/ui/const-generics/issues/issue-97278.rs b/tests/ui/const-generics/issues/issue-97278.rs index da0a9776fd40f..8e7a1fcd99597 100644 --- a/tests/ui/const-generics/issues/issue-97278.rs +++ b/tests/ui/const-generics/issues/issue-97278.rs @@ -9,6 +9,6 @@ enum Bar { } fn test() {} -//~^ ERROR `Arc` must be annotated with `#[derive(PartialEq, Eq)]` +//~^ ERROR `Bar` must implement `ConstParamTy` to be used as the type of a const generic parameter fn main() {} diff --git a/tests/ui/const-generics/issues/issue-97278.stderr b/tests/ui/const-generics/issues/issue-97278.stderr index ff13cb505ab9b..31e92f840e189 100644 --- a/tests/ui/const-generics/issues/issue-97278.stderr +++ b/tests/ui/const-generics/issues/issue-97278.stderr @@ -1,8 +1,14 @@ -error[E0741]: `Arc` must be annotated with `#[derive(PartialEq, Eq)]` to be used as the type of a const parameter +error[E0741]: `Bar` must implement `ConstParamTy` to be used as the type of a const generic parameter --> $DIR/issue-97278.rs:11:20 | LL | fn test() {} | ^^^ + | +help: add `#[derive(ConstParamTy)]` to the struct + | +LL + #[derive(ConstParamTy)] +LL | enum Bar { + | error: aborting due to previous error diff --git a/tests/ui/const-generics/issues/issue-99641.rs b/tests/ui/const-generics/issues/issue-99641.rs index fae6d3fc41fb8..dd075a6ad0524 100644 --- a/tests/ui/const-generics/issues/issue-99641.rs +++ b/tests/ui/const-generics/issues/issue-99641.rs @@ -3,10 +3,10 @@ fn main() { pub struct Color; - //~^ ERROR using function pointers + //~^ ERROR `(fn(),)` can't be used as a const parameter type impl Color { - //~^ ERROR using function pointers + //~^ ERROR `(fn(),)` can't be used as a const parameter type pub fn new() -> Self { Color:: } diff --git a/tests/ui/const-generics/issues/issue-99641.stderr b/tests/ui/const-generics/issues/issue-99641.stderr index 349ebba08d53f..800aec3ef2ce7 100644 --- a/tests/ui/const-generics/issues/issue-99641.stderr +++ b/tests/ui/const-generics/issues/issue-99641.stderr @@ -1,14 +1,18 @@ -error[E0741]: using function pointers as const generic parameters is forbidden +error[E0741]: `(fn(),)` can't be used as a const parameter type --> $DIR/issue-99641.rs:5:35 | LL | pub struct Color; | ^^^^^^^ + | + = note: `fn()` must implement `ConstParamTy`, but it does not -error[E0741]: using function pointers as const generic parameters is forbidden +error[E0741]: `(fn(),)` can't be used as a const parameter type --> $DIR/issue-99641.rs:8:23 | LL | impl Color { | ^^^^^^^ + | + = note: `fn()` must implement `ConstParamTy`, but it does not error: aborting due to 2 previous errors diff --git a/tests/ui/const-generics/overlapping_impls.rs b/tests/ui/const-generics/overlapping_impls.rs index e599eadd8cf48..2ce6c4a823c37 100644 --- a/tests/ui/const-generics/overlapping_impls.rs +++ b/tests/ui/const-generics/overlapping_impls.rs @@ -2,7 +2,8 @@ #![allow(incomplete_features)] #![feature(adt_const_params)] #![feature(generic_const_exprs)] -use std::marker::PhantomData; + +use std::marker::{ConstParamTy, PhantomData}; struct Foo {} @@ -22,7 +23,7 @@ pub struct Foo2 { _marker: PhantomData, } -#[derive(PartialEq, Eq)] +#[derive(PartialEq, Eq, ConstParamTy)] pub enum Protocol { Variant1, Variant2, diff --git a/tests/ui/const-generics/std/const-generics-range.full.stderr b/tests/ui/const-generics/std/const-generics-range.full.stderr new file mode 100644 index 0000000000000..5bf48ad738587 --- /dev/null +++ b/tests/ui/const-generics/std/const-generics-range.full.stderr @@ -0,0 +1,39 @@ +error[E0741]: `std::ops::Range` must implement `ConstParamTy` to be used as the type of a const generic parameter + --> $DIR/const-generics-range.rs:8:24 + | +LL | struct _Range>; + | ^^^^^^^^^^^^^^^^^^^^^^ + +error[E0741]: `RangeFrom` must implement `ConstParamTy` to be used as the type of a const generic parameter + --> $DIR/const-generics-range.rs:13:28 + | +LL | struct _RangeFrom>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0741]: `RangeFull` must implement `ConstParamTy` to be used as the type of a const generic parameter + --> $DIR/const-generics-range.rs:18:28 + | +LL | struct _RangeFull; + | ^^^^^^^^^^^^^^^^^^^ + +error[E0741]: `RangeInclusive` must implement `ConstParamTy` to be used as the type of a const generic parameter + --> $DIR/const-generics-range.rs:24:33 + | +LL | struct _RangeInclusive>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0741]: `RangeTo` must implement `ConstParamTy` to be used as the type of a const generic parameter + --> $DIR/const-generics-range.rs:29:26 + | +LL | struct _RangeTo>; + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0741]: `RangeToInclusive` must implement `ConstParamTy` to be used as the type of a const generic parameter + --> $DIR/const-generics-range.rs:34:35 + | +LL | struct _RangeToInclusive>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0741`. diff --git a/tests/ui/const-generics/std/const-generics-range.min.stderr b/tests/ui/const-generics/std/const-generics-range.min.stderr index d4b2ad6fd0c7b..53fca6e884a9a 100644 --- a/tests/ui/const-generics/std/const-generics-range.min.stderr +++ b/tests/ui/const-generics/std/const-generics-range.min.stderr @@ -1,5 +1,5 @@ error: `std::ops::Range` is forbidden as the type of a const generic parameter - --> $DIR/const-generics-range.rs:7:24 + --> $DIR/const-generics-range.rs:8:24 | LL | struct _Range>; | ^^^^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | struct _Range>; = help: more complex types are supported with `#![feature(adt_const_params)]` error: `RangeFrom` is forbidden as the type of a const generic parameter - --> $DIR/const-generics-range.rs:12:28 + --> $DIR/const-generics-range.rs:13:28 | LL | struct _RangeFrom>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | struct _RangeFrom>; = help: more complex types are supported with `#![feature(adt_const_params)]` error: `RangeFull` is forbidden as the type of a const generic parameter - --> $DIR/const-generics-range.rs:17:28 + --> $DIR/const-generics-range.rs:18:28 | LL | struct _RangeFull; | ^^^^^^^^^^^^^^^^^^^ @@ -26,7 +26,7 @@ LL | struct _RangeFull; = help: more complex types are supported with `#![feature(adt_const_params)]` error: `RangeInclusive` is forbidden as the type of a const generic parameter - --> $DIR/const-generics-range.rs:23:33 + --> $DIR/const-generics-range.rs:24:33 | LL | struct _RangeInclusive>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -35,7 +35,7 @@ LL | struct _RangeInclusive>; = help: more complex types are supported with `#![feature(adt_const_params)]` error: `RangeTo` is forbidden as the type of a const generic parameter - --> $DIR/const-generics-range.rs:28:26 + --> $DIR/const-generics-range.rs:29:26 | LL | struct _RangeTo>; | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -44,7 +44,7 @@ LL | struct _RangeTo>; = help: more complex types are supported with `#![feature(adt_const_params)]` error: `RangeToInclusive` is forbidden as the type of a const generic parameter - --> $DIR/const-generics-range.rs:33:35 + --> $DIR/const-generics-range.rs:34:35 | LL | struct _RangeToInclusive>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/const-generics/std/const-generics-range.rs b/tests/ui/const-generics/std/const-generics-range.rs index 46c06f312b98f..bda59f3ec4590 100644 --- a/tests/ui/const-generics/std/const-generics-range.rs +++ b/tests/ui/const-generics/std/const-generics-range.rs @@ -1,4 +1,5 @@ -// [full] check-pass +// [full] known-bug: unknown + // revisions: full min #![cfg_attr(full, feature(adt_const_params))] #![cfg_attr(full, allow(incomplete_features))] diff --git a/tests/ui/consts/refs_check_const_eq-issue-88384.rs b/tests/ui/consts/refs_check_const_eq-issue-88384.rs index 1496b28bd3ee6..fb0405b651cc5 100644 --- a/tests/ui/consts/refs_check_const_eq-issue-88384.rs +++ b/tests/ui/consts/refs_check_const_eq-issue-88384.rs @@ -8,10 +8,10 @@ struct CompileTimeSettings{ } struct Foo; -//~^ ERROR using function pointers as const generic parameters is forbidden +//~^ ERROR `CompileTimeSettings` must implement `ConstParamTy` to be used as the type of a const generic parameter impl Foo { - //~^ ERROR using function pointers as const generic parameters is forbidden + //~^ ERROR `CompileTimeSettings` must implement `ConstParamTy` to be used as the type of a const generic parameter fn call_hooks(){ } } diff --git a/tests/ui/consts/refs_check_const_eq-issue-88384.stderr b/tests/ui/consts/refs_check_const_eq-issue-88384.stderr index 3855b5f2a68e0..c490cd053e70e 100644 --- a/tests/ui/consts/refs_check_const_eq-issue-88384.stderr +++ b/tests/ui/consts/refs_check_const_eq-issue-88384.stderr @@ -7,17 +7,29 @@ LL | #![feature(adt_const_params)] = note: see issue #95174 for more information = note: `#[warn(incomplete_features)]` on by default -error[E0741]: using function pointers as const generic parameters is forbidden +error[E0741]: `CompileTimeSettings` must implement `ConstParamTy` to be used as the type of a const generic parameter --> $DIR/refs_check_const_eq-issue-88384.rs:10:21 | LL | struct Foo; | ^^^^^^^^^^^^^^^^^^^ + | +help: add `#[derive(ConstParamTy)]` to the struct + | +LL + #[derive(ConstParamTy)] +LL | struct CompileTimeSettings{ + | -error[E0741]: using function pointers as const generic parameters is forbidden +error[E0741]: `CompileTimeSettings` must implement `ConstParamTy` to be used as the type of a const generic parameter --> $DIR/refs_check_const_eq-issue-88384.rs:13:15 | LL | impl Foo { | ^^^^^^^^^^^^^^^^^^^ + | +help: add `#[derive(ConstParamTy)]` to the struct + | +LL + #[derive(ConstParamTy)] +LL | struct CompileTimeSettings{ + | error: aborting due to 2 previous errors; 1 warning emitted diff --git a/tests/ui/did_you_mean/issue-42599_available_fields_note.stderr b/tests/ui/did_you_mean/issue-42599_available_fields_note.stderr index dbd9dc1bc404f..c20bbce3f24a9 100644 --- a/tests/ui/did_you_mean/issue-42599_available_fields_note.stderr +++ b/tests/ui/did_you_mean/issue-42599_available_fields_note.stderr @@ -10,7 +10,7 @@ error[E0560]: struct `Demo` has no field named `egregiously_nonexistent_field` LL | Self { secret_integer: 3, egregiously_nonexistent_field: () } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Demo` does not have this field | - = note: available fields are: `favorite_integer`, `secret_integer`, `innocently_misspellable`, `another_field`, `yet_another_field` ... and 2 others + = note: available fields are: `favorite_integer`, `innocently_misspellable`, `another_field`, `yet_another_field`, `always_more_fields`, `and_ever` error[E0609]: no field `inocently_mispellable` on type `Demo` --> $DIR/issue-42599_available_fields_note.rs:32:41 diff --git a/tests/ui/empty/empty-macro-use.stderr b/tests/ui/empty/empty-macro-use.stderr index e0b3b8685d6eb..5d552e4c40846 100644 --- a/tests/ui/empty/empty-macro-use.stderr +++ b/tests/ui/empty/empty-macro-use.stderr @@ -4,8 +4,10 @@ error: cannot find macro `macro_two` in this scope LL | macro_two!(); | ^^^^^^^^^ | - = help: consider importing this macro: - two_macros::macro_two +help: consider importing this macro + | +LL + use two_macros::macro_two; + | error: aborting due to previous error diff --git a/tests/ui/discrim/discrim-ill-typed.rs b/tests/ui/enum-discriminant/discriminant-ill-typed.rs similarity index 100% rename from tests/ui/discrim/discrim-ill-typed.rs rename to tests/ui/enum-discriminant/discriminant-ill-typed.rs diff --git a/tests/ui/discrim/discrim-ill-typed.stderr b/tests/ui/enum-discriminant/discriminant-ill-typed.stderr similarity index 85% rename from tests/ui/discrim/discrim-ill-typed.stderr rename to tests/ui/enum-discriminant/discriminant-ill-typed.stderr index 27f516341ee31..2757145285c79 100644 --- a/tests/ui/discrim/discrim-ill-typed.stderr +++ b/tests/ui/enum-discriminant/discriminant-ill-typed.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/discrim-ill-typed.rs:15:16 + --> $DIR/discriminant-ill-typed.rs:15:16 | LL | OhNo = 0_u8, | ^^^^ expected `i8`, found `u8` @@ -10,7 +10,7 @@ LL | OhNo = 0_i8, | ~~ error[E0308]: mismatched types - --> $DIR/discrim-ill-typed.rs:28:16 + --> $DIR/discriminant-ill-typed.rs:28:16 | LL | OhNo = 0_i8, | ^^^^ expected `u8`, found `i8` @@ -21,7 +21,7 @@ LL | OhNo = 0_u8, | ~~ error[E0308]: mismatched types - --> $DIR/discrim-ill-typed.rs:41:16 + --> $DIR/discriminant-ill-typed.rs:41:16 | LL | OhNo = 0_u16, | ^^^^^ expected `i16`, found `u16` @@ -32,7 +32,7 @@ LL | OhNo = 0_i16, | ~~~ error[E0308]: mismatched types - --> $DIR/discrim-ill-typed.rs:54:16 + --> $DIR/discriminant-ill-typed.rs:54:16 | LL | OhNo = 0_i16, | ^^^^^ expected `u16`, found `i16` @@ -43,7 +43,7 @@ LL | OhNo = 0_u16, | ~~~ error[E0308]: mismatched types - --> $DIR/discrim-ill-typed.rs:67:16 + --> $DIR/discriminant-ill-typed.rs:67:16 | LL | OhNo = 0_u32, | ^^^^^ expected `i32`, found `u32` @@ -54,7 +54,7 @@ LL | OhNo = 0_i32, | ~~~ error[E0308]: mismatched types - --> $DIR/discrim-ill-typed.rs:80:16 + --> $DIR/discriminant-ill-typed.rs:80:16 | LL | OhNo = 0_i32, | ^^^^^ expected `u32`, found `i32` @@ -65,7 +65,7 @@ LL | OhNo = 0_u32, | ~~~ error[E0308]: mismatched types - --> $DIR/discrim-ill-typed.rs:93:16 + --> $DIR/discriminant-ill-typed.rs:93:16 | LL | OhNo = 0_u64, | ^^^^^ expected `i64`, found `u64` @@ -76,7 +76,7 @@ LL | OhNo = 0_i64, | ~~~ error[E0308]: mismatched types - --> $DIR/discrim-ill-typed.rs:106:16 + --> $DIR/discriminant-ill-typed.rs:106:16 | LL | OhNo = 0_i64, | ^^^^^ expected `u64`, found `i64` diff --git a/tests/ui/discrim/discrim-overflow-2.rs b/tests/ui/enum-discriminant/discriminant-overflow-2.rs similarity index 100% rename from tests/ui/discrim/discrim-overflow-2.rs rename to tests/ui/enum-discriminant/discriminant-overflow-2.rs diff --git a/tests/ui/discrim/discrim-overflow.stderr b/tests/ui/enum-discriminant/discriminant-overflow-2.stderr similarity index 82% rename from tests/ui/discrim/discrim-overflow.stderr rename to tests/ui/enum-discriminant/discriminant-overflow-2.stderr index 1b331bb1b8d98..5f7387c590833 100644 --- a/tests/ui/discrim/discrim-overflow.stderr +++ b/tests/ui/enum-discriminant/discriminant-overflow-2.stderr @@ -1,5 +1,5 @@ error[E0370]: enum discriminant overflowed - --> $DIR/discrim-overflow.rs:11:9 + --> $DIR/discriminant-overflow-2.rs:13:9 | LL | OhNo, | ^^^^ overflowed on value after 127 @@ -7,7 +7,7 @@ LL | OhNo, = note: explicitly set `OhNo = -128` if that is desired outcome error[E0370]: enum discriminant overflowed - --> $DIR/discrim-overflow.rs:22:9 + --> $DIR/discriminant-overflow-2.rs:22:9 | LL | OhNo, | ^^^^ overflowed on value after 255 @@ -15,7 +15,7 @@ LL | OhNo, = note: explicitly set `OhNo = 0` if that is desired outcome error[E0370]: enum discriminant overflowed - --> $DIR/discrim-overflow.rs:33:9 + --> $DIR/discriminant-overflow-2.rs:31:9 | LL | OhNo, | ^^^^ overflowed on value after 32767 @@ -23,7 +23,7 @@ LL | OhNo, = note: explicitly set `OhNo = -32768` if that is desired outcome error[E0370]: enum discriminant overflowed - --> $DIR/discrim-overflow.rs:44:9 + --> $DIR/discriminant-overflow-2.rs:40:9 | LL | OhNo, | ^^^^ overflowed on value after 65535 @@ -31,7 +31,7 @@ LL | OhNo, = note: explicitly set `OhNo = 0` if that is desired outcome error[E0370]: enum discriminant overflowed - --> $DIR/discrim-overflow.rs:56:9 + --> $DIR/discriminant-overflow-2.rs:49:9 | LL | OhNo, | ^^^^ overflowed on value after 2147483647 @@ -39,7 +39,7 @@ LL | OhNo, = note: explicitly set `OhNo = -2147483648` if that is desired outcome error[E0370]: enum discriminant overflowed - --> $DIR/discrim-overflow.rs:68:9 + --> $DIR/discriminant-overflow-2.rs:58:9 | LL | OhNo, | ^^^^ overflowed on value after 4294967295 @@ -47,7 +47,7 @@ LL | OhNo, = note: explicitly set `OhNo = 0` if that is desired outcome error[E0370]: enum discriminant overflowed - --> $DIR/discrim-overflow.rs:80:9 + --> $DIR/discriminant-overflow-2.rs:67:9 | LL | OhNo, | ^^^^ overflowed on value after 9223372036854775807 @@ -55,7 +55,7 @@ LL | OhNo, = note: explicitly set `OhNo = -9223372036854775808` if that is desired outcome error[E0370]: enum discriminant overflowed - --> $DIR/discrim-overflow.rs:92:9 + --> $DIR/discriminant-overflow-2.rs:76:9 | LL | OhNo, | ^^^^ overflowed on value after 18446744073709551615 diff --git a/tests/ui/discrim/discrim-overflow.rs b/tests/ui/enum-discriminant/discriminant-overflow.rs similarity index 100% rename from tests/ui/discrim/discrim-overflow.rs rename to tests/ui/enum-discriminant/discriminant-overflow.rs diff --git a/tests/ui/discrim/discrim-overflow-2.stderr b/tests/ui/enum-discriminant/discriminant-overflow.stderr similarity index 83% rename from tests/ui/discrim/discrim-overflow-2.stderr rename to tests/ui/enum-discriminant/discriminant-overflow.stderr index 3ca84c6675357..2ecc1839f57de 100644 --- a/tests/ui/discrim/discrim-overflow-2.stderr +++ b/tests/ui/enum-discriminant/discriminant-overflow.stderr @@ -1,5 +1,5 @@ error[E0370]: enum discriminant overflowed - --> $DIR/discrim-overflow-2.rs:13:9 + --> $DIR/discriminant-overflow.rs:11:9 | LL | OhNo, | ^^^^ overflowed on value after 127 @@ -7,7 +7,7 @@ LL | OhNo, = note: explicitly set `OhNo = -128` if that is desired outcome error[E0370]: enum discriminant overflowed - --> $DIR/discrim-overflow-2.rs:22:9 + --> $DIR/discriminant-overflow.rs:22:9 | LL | OhNo, | ^^^^ overflowed on value after 255 @@ -15,7 +15,7 @@ LL | OhNo, = note: explicitly set `OhNo = 0` if that is desired outcome error[E0370]: enum discriminant overflowed - --> $DIR/discrim-overflow-2.rs:31:9 + --> $DIR/discriminant-overflow.rs:33:9 | LL | OhNo, | ^^^^ overflowed on value after 32767 @@ -23,7 +23,7 @@ LL | OhNo, = note: explicitly set `OhNo = -32768` if that is desired outcome error[E0370]: enum discriminant overflowed - --> $DIR/discrim-overflow-2.rs:40:9 + --> $DIR/discriminant-overflow.rs:44:9 | LL | OhNo, | ^^^^ overflowed on value after 65535 @@ -31,7 +31,7 @@ LL | OhNo, = note: explicitly set `OhNo = 0` if that is desired outcome error[E0370]: enum discriminant overflowed - --> $DIR/discrim-overflow-2.rs:49:9 + --> $DIR/discriminant-overflow.rs:56:9 | LL | OhNo, | ^^^^ overflowed on value after 2147483647 @@ -39,7 +39,7 @@ LL | OhNo, = note: explicitly set `OhNo = -2147483648` if that is desired outcome error[E0370]: enum discriminant overflowed - --> $DIR/discrim-overflow-2.rs:58:9 + --> $DIR/discriminant-overflow.rs:68:9 | LL | OhNo, | ^^^^ overflowed on value after 4294967295 @@ -47,7 +47,7 @@ LL | OhNo, = note: explicitly set `OhNo = 0` if that is desired outcome error[E0370]: enum discriminant overflowed - --> $DIR/discrim-overflow-2.rs:67:9 + --> $DIR/discriminant-overflow.rs:80:9 | LL | OhNo, | ^^^^ overflowed on value after 9223372036854775807 @@ -55,7 +55,7 @@ LL | OhNo, = note: explicitly set `OhNo = -9223372036854775808` if that is desired outcome error[E0370]: enum discriminant overflowed - --> $DIR/discrim-overflow-2.rs:76:9 + --> $DIR/discriminant-overflow.rs:92:9 | LL | OhNo, | ^^^^ overflowed on value after 18446744073709551615 diff --git a/tests/ui/error-codes/E0560.stderr b/tests/ui/error-codes/E0560.stderr index 6b634f1855dc1..bb5ce478ae1a7 100644 --- a/tests/ui/error-codes/E0560.stderr +++ b/tests/ui/error-codes/E0560.stderr @@ -4,7 +4,7 @@ error[E0560]: struct `Simba` has no field named `father` LL | let s = Simba { mother: 1, father: 0 }; | ^^^^^^ `Simba` does not have this field | - = note: available fields are: `mother` + = note: all struct fields are already assigned error: aborting due to previous error diff --git a/tests/ui/hygiene/no_implicit_prelude-2018.stderr b/tests/ui/hygiene/no_implicit_prelude-2018.stderr index 3f31b041b6203..b22f3e75b6f4f 100644 --- a/tests/ui/hygiene/no_implicit_prelude-2018.stderr +++ b/tests/ui/hygiene/no_implicit_prelude-2018.stderr @@ -4,8 +4,10 @@ error: cannot find macro `print` in this scope LL | print!(); | ^^^^^ | - = help: consider importing this macro: - std::print +help: consider importing this macro + | +LL + use std::print; + | error: aborting due to previous error diff --git a/tests/ui/implied-bounds/references-err.rs b/tests/ui/implied-bounds/references-err.rs new file mode 100644 index 0000000000000..203d512e39812 --- /dev/null +++ b/tests/ui/implied-bounds/references-err.rs @@ -0,0 +1,22 @@ +trait Identity { + type Identity; +} +impl Identity for T { + type Identity = T; +} + +trait Trait { + type Assoc: Identity; + fn tokenize(&self) -> ::Identity; +} + +impl Trait for () { + type Assoc = DoesNotExist; + //~^ ERROR cannot find type `DoesNotExist` in this scope + + fn tokenize(&self) -> ::Identity { + unimplemented!() + } +} + +fn main() {} diff --git a/tests/ui/implied-bounds/references-err.stderr b/tests/ui/implied-bounds/references-err.stderr new file mode 100644 index 0000000000000..6076eea3c75fa --- /dev/null +++ b/tests/ui/implied-bounds/references-err.stderr @@ -0,0 +1,9 @@ +error[E0412]: cannot find type `DoesNotExist` in this scope + --> $DIR/references-err.rs:14:18 + | +LL | type Assoc = DoesNotExist; + | ^^^^^^^^^^^^ not found in this scope + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0412`. diff --git a/tests/ui/inference/deref-suggestion.stderr b/tests/ui/inference/deref-suggestion.stderr index c58aab4226988..096989db0b4e8 100644 --- a/tests/ui/inference/deref-suggestion.stderr +++ b/tests/ui/inference/deref-suggestion.stderr @@ -84,15 +84,16 @@ LL | fn foo3(_: u32) {} | ^^^^ ------ error[E0308]: mismatched types - --> $DIR/deref-suggestion.rs:37:5 + --> $DIR/deref-suggestion.rs:37:22 | LL | assert_eq!(3i32, &3i32); - | ^^^^^^^^^^^^^^^^^^^^^^^ - | | - | expected `i32`, found `&i32` - | expected because this is `i32` + | ^^^^^ expected `i32`, found `&i32` + | +help: consider removing the borrow + | +LL - assert_eq!(3i32, &3i32); +LL + assert_eq!(3i32, 3i32); | - = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0308]: mismatched types --> $DIR/deref-suggestion.rs:40:17 diff --git a/tests/ui/issues/issue-100605.stderr b/tests/ui/issues/issue-100605.stderr index be30eef2af48f..6f11f44755a9d 100644 --- a/tests/ui/issues/issue-100605.stderr +++ b/tests/ui/issues/issue-100605.stderr @@ -13,10 +13,6 @@ note: function defined here | LL | fn takes_option(_arg: Option<&String>) {} | ^^^^^^^^^^^^ --------------------- -help: you can convert from `&Option` to `Option<&T>` using `.as_ref()` - | -LL | takes_option(None.as_ref()); - | ~~~~~~~~~~~~~ help: consider removing the borrow | LL - takes_option(&None); @@ -27,10 +23,8 @@ error[E0308]: mismatched types --> $DIR/issue-100605.rs:8:18 | LL | takes_option(&res); - | ------------ ^^^^ - | | | - | | expected `Option<&String>`, found `&Option` - | | help: you can convert from `&Option` to `Option<&T>` using `.as_ref()`: `res.as_ref()` + | ------------ ^^^^ expected `Option<&String>`, found `&Option` + | | | arguments to this function are incorrect | = note: expected enum `Option<&String>` @@ -40,6 +34,11 @@ note: function defined here | LL | fn takes_option(_arg: Option<&String>) {} | ^^^^^^^^^^^^ --------------------- +help: try using `.as_ref()` to convert `&Option` to `Option<&String>` + | +LL - takes_option(&res); +LL + takes_option(res.as_ref()); + | error: aborting due to 2 previous errors diff --git a/tests/ui/issues/issue-13167.rs b/tests/ui/issues/issue-13167.rs index 8584c98decf4e..9a9f129ec3ab6 100644 --- a/tests/ui/issues/issue-13167.rs +++ b/tests/ui/issues/issue-13167.rs @@ -1,5 +1,7 @@ // check-pass // pretty-expanded FIXME #23616 +// revisions: current next +//[next] compile-flags: -Ztrait-solver=next use std::slice; diff --git a/tests/ui/issues/issue-15734.rs b/tests/ui/issues/issue-15734.rs index be582060601e7..27410d4c3b08e 100644 --- a/tests/ui/issues/issue-15734.rs +++ b/tests/ui/issues/issue-15734.rs @@ -1,6 +1,6 @@ // run-pass -// If `Index` used an associated type for its output, this test would -// work more smoothly. +// revisions: current next +//[next] compile-flags: -Ztrait-solver=next use std::ops::Index; diff --git a/tests/ui/issues/issue-37311-type-length-limit/issue-37311.rs b/tests/ui/issues/issue-37311-type-length-limit/issue-37311.rs index 50d1f166c9865..c109be005238f 100644 --- a/tests/ui/issues/issue-37311-type-length-limit/issue-37311.rs +++ b/tests/ui/issues/issue-37311-type-length-limit/issue-37311.rs @@ -1,5 +1,6 @@ // build-fail // normalize-stderr-test: ".nll/" -> "/" +// ignore-compare-mode-next-solver (hangs) trait Mirror { type Image; diff --git a/tests/ui/issues/issue-37311-type-length-limit/issue-37311.stderr b/tests/ui/issues/issue-37311-type-length-limit/issue-37311.stderr index 5b8299fe839d7..87832dd29b284 100644 --- a/tests/ui/issues/issue-37311-type-length-limit/issue-37311.stderr +++ b/tests/ui/issues/issue-37311-type-length-limit/issue-37311.stderr @@ -1,11 +1,11 @@ error: reached the recursion limit while instantiating `<(&(&(..., ...), ...), ...) as Foo>::recurse` - --> $DIR/issue-37311.rs:17:9 + --> $DIR/issue-37311.rs:18:9 | LL | (self, self).recurse(); | ^^^^^^^^^^^^^^^^^^^^^^ | note: `::recurse` defined here - --> $DIR/issue-37311.rs:16:5 + --> $DIR/issue-37311.rs:17:5 | LL | fn recurse(&self) { | ^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/issues/issue-50811.rs b/tests/ui/issues/issue-50811.rs index 683c856049fdc..2a20e50fa4543 100644 --- a/tests/ui/issues/issue-50811.rs +++ b/tests/ui/issues/issue-50811.rs @@ -1,5 +1,6 @@ // run-pass #![feature(test)] +#![allow(invalid_nan_comparisons)] extern crate test; diff --git a/tests/ui/issues/issue-5439.stderr b/tests/ui/issues/issue-5439.stderr index dc8f8b878d730..a91e4b31f4bdf 100644 --- a/tests/ui/issues/issue-5439.stderr +++ b/tests/ui/issues/issue-5439.stderr @@ -4,7 +4,7 @@ error[E0560]: struct `Foo` has no field named `nonexistent` LL | return Box::new(Foo { nonexistent: self, foo: i }); | ^^^^^^^^^^^ `Foo` does not have this field | - = note: available fields are: `foo` + = note: all struct fields are already assigned error: aborting due to previous error diff --git a/tests/ui/issues/issue-62375.stderr b/tests/ui/issues/issue-62375.stderr index cd632e64fe5fc..f6d7968c0c405 100644 --- a/tests/ui/issues/issue-62375.stderr +++ b/tests/ui/issues/issue-62375.stderr @@ -11,8 +11,11 @@ note: an implementation of `PartialEq A {A::Value}>` might be missing | LL | enum A { | ^^^^^^ must implement `PartialEq A {A::Value}>` -note: the trait `PartialEq` must be implemented - --> $SRC_DIR/core/src/cmp.rs:LL:COL +help: consider annotating `A` with `#[derive(PartialEq)]` + | +LL + #[derive(PartialEq)] +LL | enum A { + | help: use parentheses to construct this tuple variant | LL | a == A::Value(/* () */); diff --git a/tests/ui/iterators/issue-58952-filter-type-length.rs b/tests/ui/iterators/issue-58952-filter-type-length.rs index 6d12db8d13730..8e9cc84ec0339 100644 --- a/tests/ui/iterators/issue-58952-filter-type-length.rs +++ b/tests/ui/iterators/issue-58952-filter-type-length.rs @@ -1,4 +1,6 @@ // run-pass +// ignore-compare-mode-next-solver (hangs) + //! This snippet causes the type length to blowup exponentially, //! so check that we don't accidentally exceed the type length limit. // FIXME: Once the size of iterator adaptors is further reduced, diff --git a/tests/ui/let-else/let-else-ref-bindings.stderr b/tests/ui/let-else/let-else-ref-bindings.stderr index ada1805e7256f..0886d7f1770e9 100644 --- a/tests/ui/let-else/let-else-ref-bindings.stderr +++ b/tests/ui/let-else/let-else-ref-bindings.stderr @@ -6,6 +6,10 @@ LL | let Some(ref a): Option<&[u8]> = some else { return }; | = note: expected enum `Option<&[u8]>` found enum `Option>` +help: try using `.as_deref()` to convert `Option>` to `Option<&[u8]>` + | +LL | let Some(ref a): Option<&[u8]> = some.as_deref() else { return }; + | +++++++++++ error[E0308]: mismatched types --> $DIR/let-else-ref-bindings.rs:20:38 @@ -15,6 +19,11 @@ LL | let Some(ref a): Option<&[u8]> = &some else { return }; | = note: expected enum `Option<&[u8]>` found reference `&Option>` +help: try using `.as_deref()` to convert `&Option>` to `Option<&[u8]>` + | +LL - let Some(ref a): Option<&[u8]> = &some else { return }; +LL + let Some(ref a): Option<&[u8]> = some.as_deref() else { return }; + | error[E0308]: mismatched types --> $DIR/let-else-ref-bindings.rs:24:34 @@ -26,6 +35,10 @@ LL | let Some(a): Option<&[u8]> = some else { return }; | = note: expected enum `Option<&[u8]>` found enum `Option>` +help: try using `.as_deref()` to convert `Option>` to `Option<&[u8]>` + | +LL | let Some(a): Option<&[u8]> = some.as_deref() else { return }; + | +++++++++++ error[E0308]: mismatched types --> $DIR/let-else-ref-bindings.rs:27:34 @@ -37,6 +50,11 @@ LL | let Some(a): Option<&[u8]> = &some else { return }; | = note: expected enum `Option<&[u8]>` found reference `&Option>` +help: try using `.as_deref()` to convert `&Option>` to `Option<&[u8]>` + | +LL - let Some(a): Option<&[u8]> = &some else { return }; +LL + let Some(a): Option<&[u8]> = some.as_deref() else { return }; + | error[E0308]: mismatched types --> $DIR/let-else-ref-bindings.rs:44:46 diff --git a/tests/ui/lint/auxiliary/missing_docs.rs b/tests/ui/lint/auxiliary/missing_docs.rs new file mode 100644 index 0000000000000..4a835673a596b --- /dev/null +++ b/tests/ui/lint/auxiliary/missing_docs.rs @@ -0,0 +1 @@ +pub struct Foo; diff --git a/tests/ui/lint/invalid-nan-comparison-suggestion.fixed b/tests/ui/lint/invalid-nan-comparison-suggestion.fixed new file mode 100644 index 0000000000000..feafc6c1b8c13 --- /dev/null +++ b/tests/ui/lint/invalid-nan-comparison-suggestion.fixed @@ -0,0 +1,36 @@ +// check-pass +// run-rustfix + +fn main() { + let x = 5f32; + let _ = x.is_nan(); + //~^ WARN incorrect NaN comparison + let _ = !x.is_nan(); + //~^ WARN incorrect NaN comparison + + let x = 5f64; + let _ = x.is_nan(); + //~^ WARN incorrect NaN comparison + let _ = !x.is_nan(); + //~^ WARN incorrect NaN comparison + + let b = &2.3f32; + if !b.is_nan() {} + //~^ WARN incorrect NaN comparison + + let b = &2.3f32; + if !b.is_nan() {} + //~^ WARN incorrect NaN comparison + + let _ = + !b.is_nan(); + + #[allow(unused_macros)] + macro_rules! nan { () => { f32::NAN }; } + macro_rules! number { () => { 5f32 }; } + + let _ = number!().is_nan(); + //~^ WARN incorrect NaN comparison + let _ = !number!().is_nan(); + //~^ WARN incorrect NaN comparison +} diff --git a/tests/ui/lint/invalid-nan-comparison-suggestion.rs b/tests/ui/lint/invalid-nan-comparison-suggestion.rs new file mode 100644 index 0000000000000..ad5eb66e5f17d --- /dev/null +++ b/tests/ui/lint/invalid-nan-comparison-suggestion.rs @@ -0,0 +1,39 @@ +// check-pass +// run-rustfix + +fn main() { + let x = 5f32; + let _ = x == f32::NAN; + //~^ WARN incorrect NaN comparison + let _ = x != f32::NAN; + //~^ WARN incorrect NaN comparison + + let x = 5f64; + let _ = x == f64::NAN; + //~^ WARN incorrect NaN comparison + let _ = x != f64::NAN; + //~^ WARN incorrect NaN comparison + + let b = &2.3f32; + if b != &f32::NAN {} + //~^ WARN incorrect NaN comparison + + let b = &2.3f32; + if b != { &f32::NAN } {} + //~^ WARN incorrect NaN comparison + + let _ = + b != { + //~^ WARN incorrect NaN comparison + &f32::NAN + }; + + #[allow(unused_macros)] + macro_rules! nan { () => { f32::NAN }; } + macro_rules! number { () => { 5f32 }; } + + let _ = nan!() == number!(); + //~^ WARN incorrect NaN comparison + let _ = number!() != nan!(); + //~^ WARN incorrect NaN comparison +} diff --git a/tests/ui/lint/invalid-nan-comparison-suggestion.stderr b/tests/ui/lint/invalid-nan-comparison-suggestion.stderr new file mode 100644 index 0000000000000..c310341de07b7 --- /dev/null +++ b/tests/ui/lint/invalid-nan-comparison-suggestion.stderr @@ -0,0 +1,114 @@ +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/invalid-nan-comparison-suggestion.rs:6:13 + | +LL | let _ = x == f32::NAN; + | ^^^^^^^^^^^^^ + | + = note: `#[warn(invalid_nan_comparisons)]` on by default +help: use `f32::is_nan()` or `f64::is_nan()` instead + | +LL - let _ = x == f32::NAN; +LL + let _ = x.is_nan(); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/invalid-nan-comparison-suggestion.rs:8:13 + | +LL | let _ = x != f32::NAN; + | ^^^^^^^^^^^^^ + | +help: use `f32::is_nan()` or `f64::is_nan()` instead + | +LL - let _ = x != f32::NAN; +LL + let _ = !x.is_nan(); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/invalid-nan-comparison-suggestion.rs:12:13 + | +LL | let _ = x == f64::NAN; + | ^^^^^^^^^^^^^ + | +help: use `f32::is_nan()` or `f64::is_nan()` instead + | +LL - let _ = x == f64::NAN; +LL + let _ = x.is_nan(); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/invalid-nan-comparison-suggestion.rs:14:13 + | +LL | let _ = x != f64::NAN; + | ^^^^^^^^^^^^^ + | +help: use `f32::is_nan()` or `f64::is_nan()` instead + | +LL - let _ = x != f64::NAN; +LL + let _ = !x.is_nan(); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/invalid-nan-comparison-suggestion.rs:18:8 + | +LL | if b != &f32::NAN {} + | ^^^^^^^^^^^^^^ + | +help: use `f32::is_nan()` or `f64::is_nan()` instead + | +LL - if b != &f32::NAN {} +LL + if !b.is_nan() {} + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/invalid-nan-comparison-suggestion.rs:22:8 + | +LL | if b != { &f32::NAN } {} + | ^^^^^^^^^^^^^^^^^^ + | +help: use `f32::is_nan()` or `f64::is_nan()` instead + | +LL - if b != { &f32::NAN } {} +LL + if !b.is_nan() {} + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/invalid-nan-comparison-suggestion.rs:26:9 + | +LL | / b != { +LL | | +LL | | &f32::NAN +LL | | }; + | |_________^ + | +help: use `f32::is_nan()` or `f64::is_nan()` instead + | +LL - b != { +LL + !b.is_nan(); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/invalid-nan-comparison-suggestion.rs:35:13 + | +LL | let _ = nan!() == number!(); + | ^^^^^^^^^^^^^^^^^^^ + | +help: use `f32::is_nan()` or `f64::is_nan()` instead + | +LL - let _ = nan!() == number!(); +LL + let _ = number!().is_nan(); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/invalid-nan-comparison-suggestion.rs:37:13 + | +LL | let _ = number!() != nan!(); + | ^^^^^^^^^^^^^^^^^^^ + | +help: use `f32::is_nan()` or `f64::is_nan()` instead + | +LL - let _ = number!() != nan!(); +LL + let _ = !number!().is_nan(); + | + +warning: 9 warnings emitted + diff --git a/tests/ui/lint/invalid-nan-comparison.rs b/tests/ui/lint/invalid-nan-comparison.rs new file mode 100644 index 0000000000000..d7e793ca58301 --- /dev/null +++ b/tests/ui/lint/invalid-nan-comparison.rs @@ -0,0 +1,51 @@ +// check-pass + +fn main() { + f32(); + f64(); +} + +const TEST: bool = 5f32 == f32::NAN; +//~^ WARN incorrect NaN comparison + +fn f32() { + macro_rules! number { () => { 5f32 }; } + let x = number!(); + x == f32::NAN; + //~^ WARN incorrect NaN comparison + x != f32::NAN; + //~^ WARN incorrect NaN comparison + x < f32::NAN; + //~^ WARN incorrect NaN comparison + x > f32::NAN; + //~^ WARN incorrect NaN comparison + x <= f32::NAN; + //~^ WARN incorrect NaN comparison + x >= f32::NAN; + //~^ WARN incorrect NaN comparison + number!() == f32::NAN; + //~^ WARN incorrect NaN comparison + f32::NAN != number!(); + //~^ WARN incorrect NaN comparison +} + +fn f64() { + macro_rules! number { () => { 5f64 }; } + let x = number!(); + x == f64::NAN; + //~^ WARN incorrect NaN comparison + x != f64::NAN; + //~^ WARN incorrect NaN comparison + x < f64::NAN; + //~^ WARN incorrect NaN comparison + x > f64::NAN; + //~^ WARN incorrect NaN comparison + x <= f64::NAN; + //~^ WARN incorrect NaN comparison + x >= f64::NAN; + //~^ WARN incorrect NaN comparison + number!() == f64::NAN; + //~^ WARN incorrect NaN comparison + f64::NAN != number!(); + //~^ WARN incorrect NaN comparison +} diff --git a/tests/ui/lint/invalid-nan-comparison.stderr b/tests/ui/lint/invalid-nan-comparison.stderr new file mode 100644 index 0000000000000..054c06d38b30b --- /dev/null +++ b/tests/ui/lint/invalid-nan-comparison.stderr @@ -0,0 +1,159 @@ +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/invalid-nan-comparison.rs:8:20 + | +LL | const TEST: bool = 5f32 == f32::NAN; + | ^^^^^^^^^^^^^^^^ + | + = note: `#[warn(invalid_nan_comparisons)]` on by default +help: use `f32::is_nan()` or `f64::is_nan()` instead + | +LL - const TEST: bool = 5f32 == f32::NAN; +LL + const TEST: bool = 5f32.is_nan(); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/invalid-nan-comparison.rs:14:5 + | +LL | x == f32::NAN; + | ^^^^^^^^^^^^^ + | +help: use `f32::is_nan()` or `f64::is_nan()` instead + | +LL - x == f32::NAN; +LL + x.is_nan(); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/invalid-nan-comparison.rs:16:5 + | +LL | x != f32::NAN; + | ^^^^^^^^^^^^^ + | +help: use `f32::is_nan()` or `f64::is_nan()` instead + | +LL - x != f32::NAN; +LL + !x.is_nan(); + | + +warning: incorrect NaN comparison, NaN is not orderable + --> $DIR/invalid-nan-comparison.rs:18:5 + | +LL | x < f32::NAN; + | ^^^^^^^^^^^^ + +warning: incorrect NaN comparison, NaN is not orderable + --> $DIR/invalid-nan-comparison.rs:20:5 + | +LL | x > f32::NAN; + | ^^^^^^^^^^^^ + +warning: incorrect NaN comparison, NaN is not orderable + --> $DIR/invalid-nan-comparison.rs:22:5 + | +LL | x <= f32::NAN; + | ^^^^^^^^^^^^^ + +warning: incorrect NaN comparison, NaN is not orderable + --> $DIR/invalid-nan-comparison.rs:24:5 + | +LL | x >= f32::NAN; + | ^^^^^^^^^^^^^ + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/invalid-nan-comparison.rs:26:5 + | +LL | number!() == f32::NAN; + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: use `f32::is_nan()` or `f64::is_nan()` instead + | +LL - number!() == f32::NAN; +LL + number!().is_nan(); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/invalid-nan-comparison.rs:28:5 + | +LL | f32::NAN != number!(); + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: use `f32::is_nan()` or `f64::is_nan()` instead + | +LL - f32::NAN != number!(); +LL + !number!().is_nan(); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/invalid-nan-comparison.rs:35:5 + | +LL | x == f64::NAN; + | ^^^^^^^^^^^^^ + | +help: use `f32::is_nan()` or `f64::is_nan()` instead + | +LL - x == f64::NAN; +LL + x.is_nan(); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/invalid-nan-comparison.rs:37:5 + | +LL | x != f64::NAN; + | ^^^^^^^^^^^^^ + | +help: use `f32::is_nan()` or `f64::is_nan()` instead + | +LL - x != f64::NAN; +LL + !x.is_nan(); + | + +warning: incorrect NaN comparison, NaN is not orderable + --> $DIR/invalid-nan-comparison.rs:39:5 + | +LL | x < f64::NAN; + | ^^^^^^^^^^^^ + +warning: incorrect NaN comparison, NaN is not orderable + --> $DIR/invalid-nan-comparison.rs:41:5 + | +LL | x > f64::NAN; + | ^^^^^^^^^^^^ + +warning: incorrect NaN comparison, NaN is not orderable + --> $DIR/invalid-nan-comparison.rs:43:5 + | +LL | x <= f64::NAN; + | ^^^^^^^^^^^^^ + +warning: incorrect NaN comparison, NaN is not orderable + --> $DIR/invalid-nan-comparison.rs:45:5 + | +LL | x >= f64::NAN; + | ^^^^^^^^^^^^^ + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/invalid-nan-comparison.rs:47:5 + | +LL | number!() == f64::NAN; + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: use `f32::is_nan()` or `f64::is_nan()` instead + | +LL - number!() == f64::NAN; +LL + number!().is_nan(); + | + +warning: incorrect NaN comparison, NaN cannot be directly compared to itself + --> $DIR/invalid-nan-comparison.rs:49:5 + | +LL | f64::NAN != number!(); + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: use `f32::is_nan()` or `f64::is_nan()` instead + | +LL - f64::NAN != number!(); +LL + !number!().is_nan(); + | + +warning: 17 warnings emitted + diff --git a/tests/ui/lint/lint-missing-doc.rs b/tests/ui/lint/lint-missing-doc.rs index e4c9f8f559b18..b59f2212f51b3 100644 --- a/tests/ui/lint/lint-missing-doc.rs +++ b/tests/ui/lint/lint-missing-doc.rs @@ -1,5 +1,6 @@ // When denying at the crate level, be sure to not get random warnings from the // injected intrinsics by the compiler. +// aux-build:missing_docs.rs #![deny(missing_docs)] #![allow(dead_code)] #![feature(associated_type_defaults, extern_types)] @@ -8,6 +9,9 @@ //! Some garbage docs for the crate here #![doc="More garbage"] +// There should be not "missing_docs" warning on "pub extern crate". +pub extern crate missing_docs; + type Typedef = String; pub type PubTypedef = String; //~ ERROR: missing documentation for a type alias diff --git a/tests/ui/lint/lint-missing-doc.stderr b/tests/ui/lint/lint-missing-doc.stderr index c94bd3b8dfb3f..adcc21c44b262 100644 --- a/tests/ui/lint/lint-missing-doc.stderr +++ b/tests/ui/lint/lint-missing-doc.stderr @@ -1,155 +1,155 @@ error: missing documentation for a type alias - --> $DIR/lint-missing-doc.rs:12:1 + --> $DIR/lint-missing-doc.rs:16:1 | LL | pub type PubTypedef = String; | ^^^^^^^^^^^^^^^^^^^ | note: the lint level is defined here - --> $DIR/lint-missing-doc.rs:3:9 + --> $DIR/lint-missing-doc.rs:4:9 | LL | #![deny(missing_docs)] | ^^^^^^^^^^^^ error: missing documentation for a struct - --> $DIR/lint-missing-doc.rs:19:1 + --> $DIR/lint-missing-doc.rs:23:1 | LL | pub struct PubFoo { | ^^^^^^^^^^^^^^^^^ error: missing documentation for a struct field - --> $DIR/lint-missing-doc.rs:20:5 + --> $DIR/lint-missing-doc.rs:24:5 | LL | pub a: isize, | ^^^^^^^^^^^^ error: missing documentation for a module - --> $DIR/lint-missing-doc.rs:31:1 + --> $DIR/lint-missing-doc.rs:35:1 | LL | pub mod pub_module_no_dox {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a function - --> $DIR/lint-missing-doc.rs:35:1 + --> $DIR/lint-missing-doc.rs:39:1 | LL | pub fn foo2() {} | ^^^^^^^^^^^^^ error: missing documentation for a trait - --> $DIR/lint-missing-doc.rs:53:1 + --> $DIR/lint-missing-doc.rs:57:1 | LL | pub trait C { | ^^^^^^^^^^^ error: missing documentation for a method - --> $DIR/lint-missing-doc.rs:54:5 + --> $DIR/lint-missing-doc.rs:58:5 | LL | fn foo(&self); | ^^^^^^^^^^^^^^ error: missing documentation for a method - --> $DIR/lint-missing-doc.rs:55:5 + --> $DIR/lint-missing-doc.rs:59:5 | LL | fn foo_with_impl(&self) {} | ^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for an associated function - --> $DIR/lint-missing-doc.rs:56:5 + --> $DIR/lint-missing-doc.rs:60:5 | LL | fn foo_no_self(); | ^^^^^^^^^^^^^^^^^ error: missing documentation for an associated function - --> $DIR/lint-missing-doc.rs:57:5 + --> $DIR/lint-missing-doc.rs:61:5 | LL | fn foo_no_self_with_impl() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for an associated type - --> $DIR/lint-missing-doc.rs:67:5 + --> $DIR/lint-missing-doc.rs:71:5 | LL | type AssociatedType; | ^^^^^^^^^^^^^^^^^^^ error: missing documentation for an associated type - --> $DIR/lint-missing-doc.rs:68:5 + --> $DIR/lint-missing-doc.rs:72:5 | LL | type AssociatedTypeDef = Self; | ^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for an associated function - --> $DIR/lint-missing-doc.rs:84:5 + --> $DIR/lint-missing-doc.rs:88:5 | LL | pub fn foo() {} | ^^^^^^^^^^^^ error: missing documentation for an enum - --> $DIR/lint-missing-doc.rs:121:1 + --> $DIR/lint-missing-doc.rs:125:1 | LL | pub enum PubBaz { | ^^^^^^^^^^^^^^^ error: missing documentation for a variant - --> $DIR/lint-missing-doc.rs:122:5 + --> $DIR/lint-missing-doc.rs:126:5 | LL | PubBazA { | ^^^^^^^ error: missing documentation for a struct field - --> $DIR/lint-missing-doc.rs:123:9 + --> $DIR/lint-missing-doc.rs:127:9 | LL | a: isize, | ^^^^^^^^ error: missing documentation for a constant - --> $DIR/lint-missing-doc.rs:154:1 + --> $DIR/lint-missing-doc.rs:158:1 | LL | pub const FOO4: u32 = 0; | ^^^^^^^^^^^^^^^^^^^ error: missing documentation for a static - --> $DIR/lint-missing-doc.rs:164:1 + --> $DIR/lint-missing-doc.rs:168:1 | LL | pub static BAR4: u32 = 0; | ^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a function - --> $DIR/lint-missing-doc.rs:170:5 + --> $DIR/lint-missing-doc.rs:174:5 | LL | pub fn undocumented1() {} | ^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a function - --> $DIR/lint-missing-doc.rs:171:5 + --> $DIR/lint-missing-doc.rs:175:5 | LL | pub fn undocumented2() {} | ^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a function - --> $DIR/lint-missing-doc.rs:177:9 + --> $DIR/lint-missing-doc.rs:181:9 | LL | pub fn also_undocumented1() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a function - --> $DIR/lint-missing-doc.rs:192:5 + --> $DIR/lint-missing-doc.rs:196:5 | LL | pub fn extern_fn_undocumented(f: f32) -> f32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a static - --> $DIR/lint-missing-doc.rs:197:5 + --> $DIR/lint-missing-doc.rs:201:5 | LL | pub static EXTERN_STATIC_UNDOCUMENTED: u8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a foreign type - --> $DIR/lint-missing-doc.rs:202:5 + --> $DIR/lint-missing-doc.rs:206:5 | LL | pub type ExternTyUndocumented; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: missing documentation for a trait alias - --> $DIR/lint-missing-doc.rs:206:1 + --> $DIR/lint-missing-doc.rs:210:1 | LL | pub trait T = Sync; | ^^^^^^^^^^^ diff --git a/tests/ui/lint/lint-unnecessary-parens.fixed b/tests/ui/lint/lint-unnecessary-parens.fixed index 9c144324f2f7e..bafac05d8daa7 100644 --- a/tests/ui/lint/lint-unnecessary-parens.fixed +++ b/tests/ui/lint/lint-unnecessary-parens.fixed @@ -35,6 +35,14 @@ pub fn passes_unused_parens_lint() -> &'static (dyn Trait) { panic!() } +pub fn parens_with_keyword(e: &[()]) -> i32 { + if true {} //~ ERROR unnecessary parentheses around `if` + while true {} //~ ERROR unnecessary parentheses around `while` + for _ in e {} //~ ERROR unnecessary parentheses around `for` + match 1 { _ => ()} //~ ERROR unnecessary parentheses around `match` + return 1; //~ ERROR unnecessary parentheses around `return` value +} + macro_rules! baz { ($($foo:expr),+) => { ($($foo),*) diff --git a/tests/ui/lint/lint-unnecessary-parens.rs b/tests/ui/lint/lint-unnecessary-parens.rs index 4fd9cabb3b0b2..ce537a4dc1da0 100644 --- a/tests/ui/lint/lint-unnecessary-parens.rs +++ b/tests/ui/lint/lint-unnecessary-parens.rs @@ -35,6 +35,14 @@ pub fn passes_unused_parens_lint() -> &'static (dyn Trait) { panic!() } +pub fn parens_with_keyword(e: &[()]) -> i32 { + if(true) {} //~ ERROR unnecessary parentheses around `if` + while(true) {} //~ ERROR unnecessary parentheses around `while` + for _ in(e) {} //~ ERROR unnecessary parentheses around `for` + match(1) { _ => ()} //~ ERROR unnecessary parentheses around `match` + return(1); //~ ERROR unnecessary parentheses around `return` value +} + macro_rules! baz { ($($foo:expr),+) => { ($($foo),*) diff --git a/tests/ui/lint/lint-unnecessary-parens.stderr b/tests/ui/lint/lint-unnecessary-parens.stderr index e13620f06ce08..2ad07530f8c8d 100644 --- a/tests/ui/lint/lint-unnecessary-parens.stderr +++ b/tests/ui/lint/lint-unnecessary-parens.stderr @@ -63,8 +63,68 @@ LL - (5) LL + 5 | +error: unnecessary parentheses around `if` condition + --> $DIR/lint-unnecessary-parens.rs:39:7 + | +LL | if(true) {} + | ^ ^ + | +help: remove these parentheses + | +LL - if(true) {} +LL + if true {} + | + +error: unnecessary parentheses around `while` condition + --> $DIR/lint-unnecessary-parens.rs:40:10 + | +LL | while(true) {} + | ^ ^ + | +help: remove these parentheses + | +LL - while(true) {} +LL + while true {} + | + +error: unnecessary parentheses around `for` iterator expression + --> $DIR/lint-unnecessary-parens.rs:41:13 + | +LL | for _ in(e) {} + | ^ ^ + | +help: remove these parentheses + | +LL - for _ in(e) {} +LL + for _ in e {} + | + +error: unnecessary parentheses around `match` scrutinee expression + --> $DIR/lint-unnecessary-parens.rs:42:10 + | +LL | match(1) { _ => ()} + | ^ ^ + | +help: remove these parentheses + | +LL - match(1) { _ => ()} +LL + match 1 { _ => ()} + | + +error: unnecessary parentheses around `return` value + --> $DIR/lint-unnecessary-parens.rs:43:11 + | +LL | return(1); + | ^ ^ + | +help: remove these parentheses + | +LL - return(1); +LL + return 1; + | + error: unnecessary parentheses around assigned value - --> $DIR/lint-unnecessary-parens.rs:44:31 + --> $DIR/lint-unnecessary-parens.rs:52:31 | LL | pub const CONST_ITEM: usize = (10); | ^ ^ @@ -76,7 +136,7 @@ LL + pub const CONST_ITEM: usize = 10; | error: unnecessary parentheses around assigned value - --> $DIR/lint-unnecessary-parens.rs:45:33 + --> $DIR/lint-unnecessary-parens.rs:53:33 | LL | pub static STATIC_ITEM: usize = (10); | ^ ^ @@ -88,7 +148,7 @@ LL + pub static STATIC_ITEM: usize = 10; | error: unnecessary parentheses around function argument - --> $DIR/lint-unnecessary-parens.rs:49:9 + --> $DIR/lint-unnecessary-parens.rs:57:9 | LL | bar((true)); | ^ ^ @@ -100,7 +160,7 @@ LL + bar(true); | error: unnecessary parentheses around `if` condition - --> $DIR/lint-unnecessary-parens.rs:51:8 + --> $DIR/lint-unnecessary-parens.rs:59:8 | LL | if (true) {} | ^ ^ @@ -112,7 +172,7 @@ LL + if true {} | error: unnecessary parentheses around `while` condition - --> $DIR/lint-unnecessary-parens.rs:52:11 + --> $DIR/lint-unnecessary-parens.rs:60:11 | LL | while (true) {} | ^ ^ @@ -124,7 +184,7 @@ LL + while true {} | error: unnecessary parentheses around `match` scrutinee expression - --> $DIR/lint-unnecessary-parens.rs:53:11 + --> $DIR/lint-unnecessary-parens.rs:61:11 | LL | match (true) { | ^ ^ @@ -136,7 +196,7 @@ LL + match true { | error: unnecessary parentheses around `let` scrutinee expression - --> $DIR/lint-unnecessary-parens.rs:56:16 + --> $DIR/lint-unnecessary-parens.rs:64:16 | LL | if let 1 = (1) {} | ^ ^ @@ -148,7 +208,7 @@ LL + if let 1 = 1 {} | error: unnecessary parentheses around `let` scrutinee expression - --> $DIR/lint-unnecessary-parens.rs:57:19 + --> $DIR/lint-unnecessary-parens.rs:65:19 | LL | while let 1 = (2) {} | ^ ^ @@ -160,7 +220,7 @@ LL + while let 1 = 2 {} | error: unnecessary parentheses around method argument - --> $DIR/lint-unnecessary-parens.rs:73:24 + --> $DIR/lint-unnecessary-parens.rs:81:24 | LL | X { y: false }.foo((true)); | ^ ^ @@ -172,7 +232,7 @@ LL + X { y: false }.foo(true); | error: unnecessary parentheses around assigned value - --> $DIR/lint-unnecessary-parens.rs:75:18 + --> $DIR/lint-unnecessary-parens.rs:83:18 | LL | let mut _a = (0); | ^ ^ @@ -184,7 +244,7 @@ LL + let mut _a = 0; | error: unnecessary parentheses around assigned value - --> $DIR/lint-unnecessary-parens.rs:76:10 + --> $DIR/lint-unnecessary-parens.rs:84:10 | LL | _a = (0); | ^ ^ @@ -196,7 +256,7 @@ LL + _a = 0; | error: unnecessary parentheses around assigned value - --> $DIR/lint-unnecessary-parens.rs:77:11 + --> $DIR/lint-unnecessary-parens.rs:85:11 | LL | _a += (1); | ^ ^ @@ -207,5 +267,5 @@ LL - _a += (1); LL + _a += 1; | -error: aborting due to 17 previous errors +error: aborting due to 22 previous errors diff --git a/tests/ui/lint/trivial-casts-featuring-type-ascription.stderr b/tests/ui/lint/trivial-casts-featuring-type-ascription.stderr index 5087807b6c787..159a54873cc70 100644 --- a/tests/ui/lint/trivial-casts-featuring-type-ascription.stderr +++ b/tests/ui/lint/trivial-casts-featuring-type-ascription.stderr @@ -4,7 +4,7 @@ error: trivial numeric cast: `i32` as `i32` LL | let lugubrious = 12i32 as i32; | ^^^^^^^^^^^^ | - = help: cast can be replaced by coercion; this might require type ascription or a temporary variable + = help: cast can be replaced by coercion; this might require a temporary variable note: the lint level is defined here --> $DIR/trivial-casts-featuring-type-ascription.rs:1:24 | @@ -17,7 +17,7 @@ error: trivial cast: `&u32` as `*const u32` LL | let _ = haunted as *const u32; | ^^^^^^^^^^^^^^^^^^^^^ | - = help: cast can be replaced by coercion; this might require type ascription or a temporary variable + = help: cast can be replaced by coercion; this might require a temporary variable note: the lint level is defined here --> $DIR/trivial-casts-featuring-type-ascription.rs:1:9 | diff --git a/tests/ui/lint/undropped_manually_drops.rs b/tests/ui/lint/undropped_manually_drops.rs new file mode 100644 index 0000000000000..7286121a40483 --- /dev/null +++ b/tests/ui/lint/undropped_manually_drops.rs @@ -0,0 +1,19 @@ +// check-fail + +struct S; + +fn main() { + let mut manual1 = std::mem::ManuallyDrop::new(S); + let mut manual2 = std::mem::ManuallyDrop::new(S); + let mut manual3 = std::mem::ManuallyDrop::new(S); + + drop(std::mem::ManuallyDrop::new(S)); //~ ERROR calls to `std::mem::drop` + drop(manual1); //~ ERROR calls to `std::mem::drop` + drop({ manual3 }); //~ ERROR calls to `std::mem::drop` + + // These lines will drop `S` and should be okay. + unsafe { + std::mem::ManuallyDrop::drop(&mut std::mem::ManuallyDrop::new(S)); + std::mem::ManuallyDrop::drop(&mut manual2); + } +} diff --git a/tests/ui/lint/undropped_manually_drops.stderr b/tests/ui/lint/undropped_manually_drops.stderr new file mode 100644 index 0000000000000..156b647ebd3a7 --- /dev/null +++ b/tests/ui/lint/undropped_manually_drops.stderr @@ -0,0 +1,42 @@ +error: calls to `std::mem::drop` with `std::mem::ManuallyDrop` instead of the inner value does nothing + --> $DIR/undropped_manually_drops.rs:10:5 + | +LL | drop(std::mem::ManuallyDrop::new(S)); + | ^^^^^------------------------------^ + | | + | argument has type `ManuallyDrop` + | + = note: `#[deny(undropped_manually_drops)]` on by default +help: use `std::mem::ManuallyDrop::into_inner` to get the inner value + | +LL | drop(std::mem::ManuallyDrop::into_inner(std::mem::ManuallyDrop::new(S))); + | +++++++++++++++++++++++++++++++++++ + + +error: calls to `std::mem::drop` with `std::mem::ManuallyDrop` instead of the inner value does nothing + --> $DIR/undropped_manually_drops.rs:11:5 + | +LL | drop(manual1); + | ^^^^^-------^ + | | + | argument has type `ManuallyDrop` + | +help: use `std::mem::ManuallyDrop::into_inner` to get the inner value + | +LL | drop(std::mem::ManuallyDrop::into_inner(manual1)); + | +++++++++++++++++++++++++++++++++++ + + +error: calls to `std::mem::drop` with `std::mem::ManuallyDrop` instead of the inner value does nothing + --> $DIR/undropped_manually_drops.rs:12:5 + | +LL | drop({ manual3 }); + | ^^^^^-----------^ + | | + | argument has type `ManuallyDrop` + | +help: use `std::mem::ManuallyDrop::into_inner` to get the inner value + | +LL | drop(std::mem::ManuallyDrop::into_inner({ manual3 })); + | +++++++++++++++++++++++++++++++++++ + + +error: aborting due to 3 previous errors + diff --git a/tests/ui/macros/builtin-std-paths-fail.stderr b/tests/ui/macros/builtin-std-paths-fail.stderr index ba6261011907c..004a39043b778 100644 --- a/tests/ui/macros/builtin-std-paths-fail.stderr +++ b/tests/ui/macros/builtin-std-paths-fail.stderr @@ -93,6 +93,9 @@ error[E0433]: failed to resolve: could not find `test` in `std` | LL | #[std::test] | ^^^^ could not find `test` in `std` + | +note: found an item that was configured out + --> $SRC_DIR/std/src/lib.rs:LL:COL error: aborting due to 16 previous errors diff --git a/tests/ui/macros/issue-112342-1.rs b/tests/ui/macros/issue-112342-1.rs new file mode 100644 index 0000000000000..bd2abe7f697f4 --- /dev/null +++ b/tests/ui/macros/issue-112342-1.rs @@ -0,0 +1,49 @@ +// same as #95267, ignore doc comment although it's a bug. + +macro_rules! m1 { + ( + $( + /// + )* + //~^^^ERROR repetition matches empty token tree + ) => {}; +} + +m1! {} + +macro_rules! m2 { + ( + $( + /// + )+ + //~^^^ERROR repetition matches empty token tree + ) => {}; +} + +m2! {} + +macro_rules! m3 { + ( + $( + /// + )? + //~^^^ERROR repetition matches empty token tree + ) => {}; +} + +m3! {} + + +macro_rules! m4 { + ( + $( + /// + /// + )* + //~^^^^ERROR repetition matches empty token tree + ) => {}; +} + +m4! {} + +fn main() {} diff --git a/tests/ui/macros/issue-112342-1.stderr b/tests/ui/macros/issue-112342-1.stderr new file mode 100644 index 0000000000000..f2d82bf599e99 --- /dev/null +++ b/tests/ui/macros/issue-112342-1.stderr @@ -0,0 +1,64 @@ +note: doc comments are ignored in matcher position + --> $DIR/issue-112342-1.rs:6:13 + | +LL | /// + | ^^^ + +error: repetition matches empty token tree + --> $DIR/issue-112342-1.rs:5:10 + | +LL | $( + | __________^ +LL | | /// +LL | | )* + | |_________^ + +note: doc comments are ignored in matcher position + --> $DIR/issue-112342-1.rs:17:13 + | +LL | /// + | ^^^ + +error: repetition matches empty token tree + --> $DIR/issue-112342-1.rs:16:10 + | +LL | $( + | __________^ +LL | | /// +LL | | )+ + | |_________^ + +note: doc comments are ignored in matcher position + --> $DIR/issue-112342-1.rs:28:13 + | +LL | /// + | ^^^ + +error: repetition matches empty token tree + --> $DIR/issue-112342-1.rs:27:10 + | +LL | $( + | __________^ +LL | | /// +LL | | )? + | |_________^ + +note: doc comments are ignored in matcher position + --> $DIR/issue-112342-1.rs:40:13 + | +LL | / /// +LL | | /// + | |_______________^ + +error: repetition matches empty token tree + --> $DIR/issue-112342-1.rs:39:10 + | +LL | $( + | __________^ +LL | | /// +LL | | /// +LL | | )* + | |_________^ + +error: aborting due to 4 previous errors + diff --git a/tests/ui/macros/issue-112342-2.rs b/tests/ui/macros/issue-112342-2.rs new file mode 100644 index 0000000000000..e797aff94d2f3 --- /dev/null +++ b/tests/ui/macros/issue-112342-2.rs @@ -0,0 +1,39 @@ +// check-pass + +// same as #95267, ignore doc comment although it's a bug. + +macro_rules! m1 { + ( + $( + /// + $expr: expr, + )* + ) => {}; +} + +m1! {} + +macro_rules! m2 { + ( + $( + /// + $expr: expr, + /// + )* + ) => {}; +} + +m2! {} + +macro_rules! m3 { + ( + $( + /// + $tt: tt, + )* + ) => {}; +} + +m3! {} + +fn main() {} diff --git a/tests/ui/macros/issue-112342-2.stderr b/tests/ui/macros/issue-112342-2.stderr new file mode 100644 index 0000000000000..8c1b6f9471bce --- /dev/null +++ b/tests/ui/macros/issue-112342-2.stderr @@ -0,0 +1,24 @@ +note: doc comments are ignored in matcher position + --> $DIR/issue-112342-2.rs:8:13 + | +LL | /// + | ^^^ + +note: doc comments are ignored in matcher position + --> $DIR/issue-112342-2.rs:19:13 + | +LL | /// + | ^^^ + +note: doc comments are ignored in matcher position + --> $DIR/issue-112342-2.rs:21:13 + | +LL | /// + | ^^^ + +note: doc comments are ignored in matcher position + --> $DIR/issue-112342-2.rs:31:13 + | +LL | /// + | ^^^ + diff --git a/tests/ui/macros/issue-88228.rs b/tests/ui/macros/issue-88228.rs index 60ba2eab7a7bf..ec55a262509aa 100644 --- a/tests/ui/macros/issue-88228.rs +++ b/tests/ui/macros/issue-88228.rs @@ -1,14 +1,14 @@ // compile-flags: -Z deduplicate-diagnostics=yes // edition:2018 -mod hey { +mod hey { //~ HELP consider importing this derive macro + //~^ HELP consider importing this macro pub use Copy as Bla; pub use std::println as bla; } #[derive(Bla)] //~^ ERROR cannot find derive macro `Bla` -//~| HELP consider importing this derive macro struct A; #[derive(println)] @@ -19,5 +19,4 @@ struct B; fn main() { bla!(); //~^ ERROR cannot find macro `bla` - //~| HELP consider importing this macro } diff --git a/tests/ui/macros/issue-88228.stderr b/tests/ui/macros/issue-88228.stderr index fe8a1deaedd77..1dbe2b77be2d9 100644 --- a/tests/ui/macros/issue-88228.stderr +++ b/tests/ui/macros/issue-88228.stderr @@ -4,8 +4,10 @@ error: cannot find macro `bla` in this scope LL | bla!(); | ^^^ | - = help: consider importing this macro: - crate::hey::bla +help: consider importing this macro + | +LL + use crate::hey::bla; + | error: cannot find derive macro `println` in this scope --> $DIR/issue-88228.rs:14:10 @@ -16,13 +18,15 @@ LL | #[derive(println)] = note: `println` is in scope, but it is a function-like macro error: cannot find derive macro `Bla` in this scope - --> $DIR/issue-88228.rs:9:10 + --> $DIR/issue-88228.rs:10:10 | LL | #[derive(Bla)] | ^^^ | - = help: consider importing this derive macro: - crate::hey::Bla +help: consider importing this derive macro + | +LL + use crate::hey::Bla; + | error: aborting due to 3 previous errors diff --git a/tests/ui/macros/macro-outer-attributes.stderr b/tests/ui/macros/macro-outer-attributes.stderr index 0bdc3416f807e..0418e6116041b 100644 --- a/tests/ui/macros/macro-outer-attributes.stderr +++ b/tests/ui/macros/macro-outer-attributes.stderr @@ -4,6 +4,11 @@ error[E0425]: cannot find function `bar` in module `a` LL | a::bar(); | ^^^ not found in `a` | +note: found an item that was configured out + --> $DIR/macro-outer-attributes.rs:9:14 + | +LL | pub fn bar() { }); + | ^^^ help: consider importing this function | LL + use b::bar; diff --git a/tests/ui/macros/macro-use-wrong-name.stderr b/tests/ui/macros/macro-use-wrong-name.stderr index ca5f0f190e8ba..36339542ac61f 100644 --- a/tests/ui/macros/macro-use-wrong-name.stderr +++ b/tests/ui/macros/macro-use-wrong-name.stderr @@ -2,15 +2,21 @@ error: cannot find macro `macro_two` in this scope --> $DIR/macro-use-wrong-name.rs:7:5 | LL | macro_two!(); - | ^^^^^^^^^ help: a macro with a similar name exists: `macro_one` + | ^^^^^^^^^ | ::: $DIR/auxiliary/two_macros.rs:2:1 | LL | macro_rules! macro_one { () => ("one") } | ---------------------- similarly named macro `macro_one` defined here | - = help: consider importing this macro: - two_macros::macro_two +help: a macro with a similar name exists + | +LL | macro_one!(); + | ~~~~~~~~~ +help: consider importing this macro + | +LL + use two_macros::macro_two; + | error: aborting due to previous error diff --git a/tests/ui/mir/issue-112269.rs b/tests/ui/mir/issue-112269.rs new file mode 100644 index 0000000000000..8b9b16647e4d6 --- /dev/null +++ b/tests/ui/mir/issue-112269.rs @@ -0,0 +1,9 @@ +pub fn main() { + const x: i32 = 4; + let x: i32 = 3; + //~^ ERROR refutable pattern in local binding + + const y: i32 = 3; + let y = 4; + //~^ ERROR refutable pattern in local binding +} diff --git a/tests/ui/mir/issue-112269.stderr b/tests/ui/mir/issue-112269.stderr new file mode 100644 index 0000000000000..f5b796027979c --- /dev/null +++ b/tests/ui/mir/issue-112269.stderr @@ -0,0 +1,31 @@ +error[E0005]: refutable pattern in local binding + --> $DIR/issue-112269.rs:3:9 + | +LL | let x: i32 = 3; + | ^ + | | + | patterns `i32::MIN..=3_i32` and `5_i32..=i32::MAX` not covered + | missing patterns are not covered because `x` is interpreted as a constant pattern, not a new variable + | help: introduce a variable instead: `x_var` + | + = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant + = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html + = note: the matched value is of type `i32` + +error[E0005]: refutable pattern in local binding + --> $DIR/issue-112269.rs:7:9 + | +LL | let y = 4; + | ^ + | | + | patterns `i32::MIN..=2_i32` and `4_i32..=i32::MAX` not covered + | missing patterns are not covered because `y` is interpreted as a constant pattern, not a new variable + | help: introduce a variable instead: `y_var` + | + = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant + = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html + = note: the matched value is of type `i32` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0005`. diff --git a/tests/ui/mir/thir-constparam-temp.rs b/tests/ui/mir/thir-constparam-temp.rs index cdc5910b36cfa..7eedc325d60f6 100644 --- a/tests/ui/mir/thir-constparam-temp.rs +++ b/tests/ui/mir/thir-constparam-temp.rs @@ -3,7 +3,9 @@ #![feature(adt_const_params)] #![allow(incomplete_features)] -#[derive(PartialEq, Eq)] +use std::marker::ConstParamTy; + +#[derive(PartialEq, Eq, ConstParamTy)] struct Yikes; impl Yikes { diff --git a/tests/ui/mir/thir-constparam-temp.stderr b/tests/ui/mir/thir-constparam-temp.stderr index b77d67e084f6c..d50747e5434fa 100644 --- a/tests/ui/mir/thir-constparam-temp.stderr +++ b/tests/ui/mir/thir-constparam-temp.stderr @@ -1,5 +1,5 @@ warning: taking a mutable reference to a `const` item - --> $DIR/thir-constparam-temp.rs:14:5 + --> $DIR/thir-constparam-temp.rs:16:5 | LL | YIKES.mut_self() | ^^^^^^^^^^^^^^^^ @@ -7,12 +7,12 @@ LL | YIKES.mut_self() = note: each usage of a `const` item creates a new temporary = note: the mutable reference will refer to this temporary, not the original `const` item note: mutable reference created due to call to this method - --> $DIR/thir-constparam-temp.rs:10:5 + --> $DIR/thir-constparam-temp.rs:12:5 | LL | fn mut_self(&mut self) {} | ^^^^^^^^^^^^^^^^^^^^^^ note: `const` item defined here - --> $DIR/thir-constparam-temp.rs:13:8 + --> $DIR/thir-constparam-temp.rs:15:8 | LL | fn foo() { | ^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/mismatched_types/suggest-option-asderef-inference-var.rs b/tests/ui/mismatched_types/suggest-option-asderef-inference-var.rs new file mode 100644 index 0000000000000..5febbbe392b24 --- /dev/null +++ b/tests/ui/mismatched_types/suggest-option-asderef-inference-var.rs @@ -0,0 +1,9 @@ +fn deref_int(a: &i32) -> i32 { + *a +} + +fn main() { + // https://github.com/rust-lang/rust/issues/112293 + let _has_inference_vars: Option = Some(0).map(deref_int); + //~^ ERROR type mismatch in function arguments +} diff --git a/tests/ui/mismatched_types/suggest-option-asderef-inference-var.stderr b/tests/ui/mismatched_types/suggest-option-asderef-inference-var.stderr new file mode 100644 index 0000000000000..71c4729e31038 --- /dev/null +++ b/tests/ui/mismatched_types/suggest-option-asderef-inference-var.stderr @@ -0,0 +1,24 @@ +error[E0631]: type mismatch in function arguments + --> $DIR/suggest-option-asderef-inference-var.rs:7:56 + | +LL | fn deref_int(a: &i32) -> i32 { + | ---------------------------- found signature defined here +... +LL | let _has_inference_vars: Option = Some(0).map(deref_int); + | --- ^^^^^^^^^ expected due to this + | | + | required by a bound introduced by this call + | + = note: expected function signature `fn({integer}) -> _` + found function signature `for<'a> fn(&'a i32) -> _` +note: required by a bound in `Option::::map` + --> $SRC_DIR/core/src/option.rs:LL:COL +help: do not borrow the argument + | +LL - fn deref_int(a: &i32) -> i32 { +LL + fn deref_int(a: i32) -> i32 { + | + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0631`. diff --git a/tests/ui/mismatched_types/suggest-option-asderef-unfixable.rs b/tests/ui/mismatched_types/suggest-option-asderef-unfixable.rs new file mode 100644 index 0000000000000..ac0831ce65508 --- /dev/null +++ b/tests/ui/mismatched_types/suggest-option-asderef-unfixable.rs @@ -0,0 +1,34 @@ +fn produces_string() -> Option { + Some("my cool string".to_owned()) +} + +fn takes_str_but_too_many_refs(_: &&str) -> Option<()> { + Some(()) +} + +fn no_args() -> Option<()> { + Some(()) +} + +extern "C" fn takes_str_but_wrong_abi(_: &str) -> Option<()> { + Some(()) +} + +unsafe fn takes_str_but_unsafe(_: &str) -> Option<()> { + Some(()) +} + +struct TypeWithoutDeref; + +fn main() { + let _ = produces_string().and_then(takes_str_but_too_many_refs); + //~^ ERROR type mismatch in function arguments + let _ = produces_string().and_then(takes_str_but_wrong_abi); + //~^ ERROR expected a `FnOnce<(String,)>` closure, found `for<'a> extern "C" fn(&'a str) -> Option<()> {takes_str_but_wrong_abi}` + let _ = produces_string().and_then(takes_str_but_unsafe); + //~^ ERROR expected a `FnOnce<(String,)>` closure, found `for<'a> unsafe fn(&'a str) -> Option<()> {takes_str_but_unsafe}` + let _ = produces_string().and_then(no_args); + //~^ ERROR function is expected to take 1 argument, but it takes 0 arguments + let _ = Some(TypeWithoutDeref).and_then(takes_str_but_too_many_refs); + //~^ ERROR type mismatch in function arguments +} diff --git a/tests/ui/mismatched_types/suggest-option-asderef-unfixable.stderr b/tests/ui/mismatched_types/suggest-option-asderef-unfixable.stderr new file mode 100644 index 0000000000000..ecfbd27b180e6 --- /dev/null +++ b/tests/ui/mismatched_types/suggest-option-asderef-unfixable.stderr @@ -0,0 +1,75 @@ +error[E0631]: type mismatch in function arguments + --> $DIR/suggest-option-asderef-unfixable.rs:24:40 + | +LL | fn takes_str_but_too_many_refs(_: &&str) -> Option<()> { + | ------------------------------------------------------ found signature defined here +... +LL | let _ = produces_string().and_then(takes_str_but_too_many_refs); + | -------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected due to this + | | + | required by a bound introduced by this call + | + = note: expected function signature `fn(String) -> _` + found function signature `for<'a, 'b> fn(&'a &'b str) -> _` +note: required by a bound in `Option::::and_then` + --> $SRC_DIR/core/src/option.rs:LL:COL + +error[E0277]: expected a `FnOnce<(String,)>` closure, found `for<'a> extern "C" fn(&'a str) -> Option<()> {takes_str_but_wrong_abi}` + --> $DIR/suggest-option-asderef-unfixable.rs:26:40 + | +LL | let _ = produces_string().and_then(takes_str_but_wrong_abi); + | -------- ^^^^^^^^^^^^^^^^^^^^^^^ expected an `FnOnce<(String,)>` closure, found `for<'a> extern "C" fn(&'a str) -> Option<()> {takes_str_but_wrong_abi}` + | | + | required by a bound introduced by this call + | + = help: the trait `FnOnce<(String,)>` is not implemented for fn item `for<'a> extern "C" fn(&'a str) -> Option<()> {takes_str_but_wrong_abi}` +note: required by a bound in `Option::::and_then` + --> $SRC_DIR/core/src/option.rs:LL:COL + +error[E0277]: expected a `FnOnce<(String,)>` closure, found `for<'a> unsafe fn(&'a str) -> Option<()> {takes_str_but_unsafe}` + --> $DIR/suggest-option-asderef-unfixable.rs:28:40 + | +LL | let _ = produces_string().and_then(takes_str_but_unsafe); + | -------- ^^^^^^^^^^^^^^^^^^^^ call the function in a closure: `|| unsafe { /* code */ }` + | | + | required by a bound introduced by this call + | + = help: the trait `FnOnce<(String,)>` is not implemented for fn item `for<'a> unsafe fn(&'a str) -> Option<()> {takes_str_but_unsafe}` + = note: unsafe function cannot be called generically without an unsafe block +note: required by a bound in `Option::::and_then` + --> $SRC_DIR/core/src/option.rs:LL:COL + +error[E0593]: function is expected to take 1 argument, but it takes 0 arguments + --> $DIR/suggest-option-asderef-unfixable.rs:30:40 + | +LL | fn no_args() -> Option<()> { + | -------------------------- takes 0 arguments +... +LL | let _ = produces_string().and_then(no_args); + | -------- ^^^^^^^ expected function that takes 1 argument + | | + | required by a bound introduced by this call + | +note: required by a bound in `Option::::and_then` + --> $SRC_DIR/core/src/option.rs:LL:COL + +error[E0631]: type mismatch in function arguments + --> $DIR/suggest-option-asderef-unfixable.rs:32:45 + | +LL | fn takes_str_but_too_many_refs(_: &&str) -> Option<()> { + | ------------------------------------------------------ found signature defined here +... +LL | let _ = Some(TypeWithoutDeref).and_then(takes_str_but_too_many_refs); + | -------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected due to this + | | + | required by a bound introduced by this call + | + = note: expected function signature `fn(TypeWithoutDeref) -> _` + found function signature `for<'a, 'b> fn(&'a &'b str) -> _` +note: required by a bound in `Option::::and_then` + --> $SRC_DIR/core/src/option.rs:LL:COL + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0277, E0593, E0631. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/mismatched_types/suggest-option-asderef.fixed b/tests/ui/mismatched_types/suggest-option-asderef.fixed new file mode 100644 index 0000000000000..5c42ece3c5d09 --- /dev/null +++ b/tests/ui/mismatched_types/suggest-option-asderef.fixed @@ -0,0 +1,39 @@ +// run-rustfix + +fn produces_string() -> Option { + Some("my cool string".to_owned()) +} + +fn takes_str(_: &str) -> Option<()> { + Some(()) +} + +fn takes_str_mut(_: &mut str) -> Option<()> { + Some(()) +} + +fn generic(_: T) -> Option<()> { + Some(()) +} + +fn generic_ref(_: T) -> Option<()> { + //~^ HELP do not borrow the argument + Some(()) +} + +fn main() { + let _: Option<()> = produces_string().as_deref().and_then(takes_str); + //~^ ERROR type mismatch in function arguments + //~| HELP call `Option::as_deref()` first + let _: Option> = produces_string().as_deref().map(takes_str); + //~^ ERROR type mismatch in function arguments + //~| HELP call `Option::as_deref()` first + let _: Option> = produces_string().as_deref_mut().map(takes_str_mut); + //~^ ERROR type mismatch in function arguments + //~| HELP call `Option::as_deref_mut()` first + let _ = produces_string().and_then(generic); + + let _ = produces_string().as_deref().and_then(generic_ref); + //~^ ERROR type mismatch in function arguments + //~| HELP call `Option::as_deref()` first +} diff --git a/tests/ui/mismatched_types/suggest-option-asderef.rs b/tests/ui/mismatched_types/suggest-option-asderef.rs new file mode 100644 index 0000000000000..a5278b8fb1618 --- /dev/null +++ b/tests/ui/mismatched_types/suggest-option-asderef.rs @@ -0,0 +1,39 @@ +// run-rustfix + +fn produces_string() -> Option { + Some("my cool string".to_owned()) +} + +fn takes_str(_: &str) -> Option<()> { + Some(()) +} + +fn takes_str_mut(_: &mut str) -> Option<()> { + Some(()) +} + +fn generic(_: T) -> Option<()> { + Some(()) +} + +fn generic_ref(_: &T) -> Option<()> { + //~^ HELP do not borrow the argument + Some(()) +} + +fn main() { + let _: Option<()> = produces_string().and_then(takes_str); + //~^ ERROR type mismatch in function arguments + //~| HELP call `Option::as_deref()` first + let _: Option> = produces_string().map(takes_str); + //~^ ERROR type mismatch in function arguments + //~| HELP call `Option::as_deref()` first + let _: Option> = produces_string().map(takes_str_mut); + //~^ ERROR type mismatch in function arguments + //~| HELP call `Option::as_deref_mut()` first + let _ = produces_string().and_then(generic); + + let _ = produces_string().and_then(generic_ref); + //~^ ERROR type mismatch in function arguments + //~| HELP call `Option::as_deref()` first +} diff --git a/tests/ui/mismatched_types/suggest-option-asderef.stderr b/tests/ui/mismatched_types/suggest-option-asderef.stderr new file mode 100644 index 0000000000000..01341603dde3f --- /dev/null +++ b/tests/ui/mismatched_types/suggest-option-asderef.stderr @@ -0,0 +1,88 @@ +error[E0631]: type mismatch in function arguments + --> $DIR/suggest-option-asderef.rs:25:52 + | +LL | fn takes_str(_: &str) -> Option<()> { + | ----------------------------------- found signature defined here +... +LL | let _: Option<()> = produces_string().and_then(takes_str); + | -------- ^^^^^^^^^ expected due to this + | | + | required by a bound introduced by this call + | + = note: expected function signature `fn(String) -> _` + found function signature `for<'a> fn(&'a str) -> _` +note: required by a bound in `Option::::and_then` + --> $SRC_DIR/core/src/option.rs:LL:COL +help: call `Option::as_deref()` first + | +LL | let _: Option<()> = produces_string().as_deref().and_then(takes_str); + | +++++++++++ + +error[E0631]: type mismatch in function arguments + --> $DIR/suggest-option-asderef.rs:28:55 + | +LL | fn takes_str(_: &str) -> Option<()> { + | ----------------------------------- found signature defined here +... +LL | let _: Option> = produces_string().map(takes_str); + | --- ^^^^^^^^^ expected due to this + | | + | required by a bound introduced by this call + | + = note: expected function signature `fn(String) -> _` + found function signature `for<'a> fn(&'a str) -> _` +note: required by a bound in `Option::::map` + --> $SRC_DIR/core/src/option.rs:LL:COL +help: call `Option::as_deref()` first + | +LL | let _: Option> = produces_string().as_deref().map(takes_str); + | +++++++++++ + +error[E0631]: type mismatch in function arguments + --> $DIR/suggest-option-asderef.rs:31:55 + | +LL | fn takes_str_mut(_: &mut str) -> Option<()> { + | ------------------------------------------- found signature defined here +... +LL | let _: Option> = produces_string().map(takes_str_mut); + | --- ^^^^^^^^^^^^^ expected due to this + | | + | required by a bound introduced by this call + | + = note: expected function signature `fn(String) -> _` + found function signature `for<'a> fn(&'a mut str) -> _` +note: required by a bound in `Option::::map` + --> $SRC_DIR/core/src/option.rs:LL:COL +help: call `Option::as_deref_mut()` first + | +LL | let _: Option> = produces_string().as_deref_mut().map(takes_str_mut); + | +++++++++++++++ + +error[E0631]: type mismatch in function arguments + --> $DIR/suggest-option-asderef.rs:36:40 + | +LL | fn generic_ref(_: &T) -> Option<()> { + | -------------------------------------- found signature defined here +... +LL | let _ = produces_string().and_then(generic_ref); + | -------- ^^^^^^^^^^^ expected due to this + | | + | required by a bound introduced by this call + | + = note: expected function signature `fn(String) -> _` + found function signature `for<'a> fn(&'a _) -> _` +note: required by a bound in `Option::::and_then` + --> $SRC_DIR/core/src/option.rs:LL:COL +help: do not borrow the argument + | +LL - fn generic_ref(_: &T) -> Option<()> { +LL + fn generic_ref(_: T) -> Option<()> { + | +help: call `Option::as_deref()` first + | +LL | let _ = produces_string().as_deref().and_then(generic_ref); + | +++++++++++ + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0631`. diff --git a/tests/ui/missing/missing-macro-use.stderr b/tests/ui/missing/missing-macro-use.stderr index 99e291cda0377..e1d80f52daddc 100644 --- a/tests/ui/missing/missing-macro-use.stderr +++ b/tests/ui/missing/missing-macro-use.stderr @@ -4,8 +4,10 @@ error: cannot find macro `macro_two` in this scope LL | macro_two!(); | ^^^^^^^^^ | - = help: consider importing this macro: - two_macros::macro_two +help: consider importing this macro + | +LL + use two_macros::macro_two; + | error: aborting due to previous error diff --git a/tests/ui/nll/issue-53119.rs b/tests/ui/nll/issue-53119.rs index 03c9c071c9b10..015b72367f1d7 100644 --- a/tests/ui/nll/issue-53119.rs +++ b/tests/ui/nll/issue-53119.rs @@ -1,4 +1,6 @@ // check-pass +// revisions: current next +//[next] compile-flags: -Ztrait-solver=next use std::ops::Deref; diff --git a/tests/ui/offset-of/offset-of-dst-field.rs b/tests/ui/offset-of/offset-of-dst-field.rs index 3b8dc0b84a460..e393b159e64d5 100644 --- a/tests/ui/offset-of/offset-of-dst-field.rs +++ b/tests/ui/offset-of/offset-of-dst-field.rs @@ -36,6 +36,8 @@ fn main() { offset_of!(Alpha, z); //~ ERROR the size for values of type offset_of!(Beta, z); //~ ERROR the size for values of type offset_of!(Gamma, z); //~ ERROR the size for values of type + offset_of!((u8, dyn Trait), 0); // ok + offset_of!((u8, dyn Trait), 1); //~ ERROR the size for values of type } fn delta() { diff --git a/tests/ui/offset-of/offset-of-dst-field.stderr b/tests/ui/offset-of/offset-of-dst-field.stderr index 128c783d5dd2d..4eaceaa935817 100644 --- a/tests/ui/offset-of/offset-of-dst-field.stderr +++ b/tests/ui/offset-of/offset-of-dst-field.stderr @@ -25,8 +25,17 @@ LL | offset_of!(Gamma, z); = help: the trait `Sized` is not implemented for `Extern` = note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) +error[E0277]: the size for values of type `dyn Trait` cannot be known at compilation time + --> $DIR/offset-of-dst-field.rs:40:5 + | +LL | offset_of!((u8, dyn Trait), 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `dyn Trait` + = note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) + error[E0277]: the size for values of type `Extern` cannot be known at compilation time - --> $DIR/offset-of-dst-field.rs:43:5 + --> $DIR/offset-of-dst-field.rs:45:5 | LL | offset_of!(Delta, z); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time @@ -35,7 +44,7 @@ LL | offset_of!(Delta, z); = note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the size for values of type `dyn Trait` cannot be known at compilation time - --> $DIR/offset-of-dst-field.rs:44:5 + --> $DIR/offset-of-dst-field.rs:46:5 | LL | offset_of!(Delta, z); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time @@ -44,7 +53,7 @@ LL | offset_of!(Delta, z); = note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the size for values of type `[u8]` cannot be known at compilation time - --> $DIR/offset-of-dst-field.rs:42:5 + --> $DIR/offset-of-dst-field.rs:44:5 | LL | offset_of!(Delta, z); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time @@ -58,7 +67,7 @@ LL | struct Alpha { = note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the size for values of type `T` cannot be known at compilation time - --> $DIR/offset-of-dst-field.rs:48:5 + --> $DIR/offset-of-dst-field.rs:50:5 | LL | fn generic_with_maybe_sized() -> usize { | - this type parameter needs to be `std::marker::Sized` @@ -72,6 +81,6 @@ LL - fn generic_with_maybe_sized() -> usize { LL + fn generic_with_maybe_sized() -> usize { | -error: aborting due to 7 previous errors +error: aborting due to 8 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/offset-of/offset-of-tuple-nested.rs b/tests/ui/offset-of/offset-of-tuple-nested.rs new file mode 100644 index 0000000000000..00fbb6bf8f407 --- /dev/null +++ b/tests/ui/offset-of/offset-of-tuple-nested.rs @@ -0,0 +1,32 @@ +// run-pass +// Test for issue #112204 -- make sure this goes through the entire compilation pipeline, +// similar to why `offset-of-unsized.rs` is also build-pass + +#![feature(offset_of)] +#![feature(builtin_syntax)] + +use std::mem::offset_of; + +type ComplexTup = ((u8, (u8, (u8, u16), u8)), (u8, u32, u16)); + +fn main() { + println!("{}", offset_of!(((u8, u8), u8), 0)); + println!("{}", offset_of!(((u8, u8), u8), 1)); + println!("{}", offset_of!(((u8, (u8, u8)), (u8, u8, u8)), 0.1.0)); + + // Complex case: do all combinations of spacings because the spacing determines what gets + // sent to the lexer. + println!("{}", offset_of!(ComplexTup, 0.1.1.1)); + println!("{}", builtin # offset_of(ComplexTup, 0. 1.1.1)); + println!("{}", offset_of!(ComplexTup, 0 . 1.1.1)); + println!("{}", offset_of!(ComplexTup, 0 .1.1.1)); + println!("{}", offset_of!(ComplexTup, 0.1 .1.1)); + println!("{}", offset_of!(ComplexTup, 0.1 . 1.1)); + println!("{}", offset_of!(ComplexTup, 0.1. 1.1)); + println!("{}", builtin # offset_of(ComplexTup, 0.1.1. 1)); + println!("{}", offset_of!(ComplexTup, 0.1.1 . 1)); + println!("{}", offset_of!(ComplexTup, 0.1.1 .1)); + + println!("{}", offset_of!(((u8, u16), (u32, u16, u8)), 0.0)); + println!("{}", offset_of!(((u8, u16), (u32, u16, u8)), 1.2)); +} diff --git a/tests/ui/offset-of/offset-of-tuple.rs b/tests/ui/offset-of/offset-of-tuple.rs new file mode 100644 index 0000000000000..e31b037ee3e01 --- /dev/null +++ b/tests/ui/offset-of/offset-of-tuple.rs @@ -0,0 +1,54 @@ +#![feature(offset_of)] +#![feature(builtin_syntax)] + +use std::mem::offset_of; + +fn main() { + offset_of!((u8, u8), _0); //~ ERROR no field `_0` + offset_of!((u8, u8), 01); //~ ERROR no field `01` + offset_of!((u8, u8), 1e2); //~ ERROR no field `1e2` + offset_of!((u8, u8), 1_u8); //~ ERROR no field `1_` + //~| ERROR suffixes on a tuple index + offset_of!((u8, u8), +1); //~ ERROR no rules expected + offset_of!((u8, u8), -1); //~ ERROR no rules expected + offset_of!((u8, u8), 1.); //~ ERROR expected identifier, found `)` + offset_of!((u8, u8), 1 .); //~ ERROR unexpected end of macro + builtin # offset_of((u8, u8), 1e2); //~ ERROR no field `1e2` + builtin # offset_of((u8, u8), _0); //~ ERROR no field `_0` + builtin # offset_of((u8, u8), 01); //~ ERROR no field `01` + builtin # offset_of((u8, u8), 1_u8); //~ ERROR no field `1_` + //~| ERROR suffixes on a tuple index + // We need to put these into curly braces, otherwise only one of the + // errors will be emitted and the others suppressed. + { builtin # offset_of((u8, u8), +1) }; //~ ERROR expected identifier, found `+` + { builtin # offset_of((u8, u8), 1.) }; //~ ERROR expected identifier, found `)` + { builtin # offset_of((u8, u8), 1 .) }; //~ ERROR expected identifier, found `)` +} + +type ComplexTup = ((u8, (u8, u8)), u8); + +fn nested() { + offset_of!(((u8, u16), (u32, u16, u8)), 0.2); //~ ERROR no field `2` + offset_of!(((u8, u16), (u32, u16, u8)), 1.2); + offset_of!(((u8, u16), (u32, u16, u8)), 1.2.0); //~ ERROR no field `0` + + // All combinations of spaces (this sends different tokens to the parser) + offset_of!(ComplexTup, 0.0.1.); //~ ERROR expected identifier + offset_of!(ComplexTup, 0 .0.1.); //~ ERROR unexpected end of macro + offset_of!(ComplexTup, 0 . 0.1.); //~ ERROR unexpected end of macro + offset_of!(ComplexTup, 0. 0.1.); //~ ERROR no rules expected + offset_of!(ComplexTup, 0.0 .1.); //~ ERROR expected identifier, found `)` + offset_of!(ComplexTup, 0.0 . 1.); //~ ERROR expected identifier, found `)` + offset_of!(ComplexTup, 0.0. 1.); //~ ERROR expected identifier, found `)` + + // Test for builtin too to ensure that the builtin syntax can also handle these cases + // We need to put these into curly braces, otherwise only one of the + // errors will be emitted and the others suppressed. + { builtin # offset_of(ComplexTup, 0.0.1.) }; //~ ERROR expected identifier, found `)` + { builtin # offset_of(ComplexTup, 0 .0.1.) }; //~ ERROR expected identifier, found `)` + { builtin # offset_of(ComplexTup, 0 . 0.1.) }; //~ ERROR expected identifier, found `)` + { builtin # offset_of(ComplexTup, 0. 0.1.) }; //~ ERROR expected identifier, found `)` + { builtin # offset_of(ComplexTup, 0.0 .1.) }; //~ ERROR expected identifier, found `)` + { builtin # offset_of(ComplexTup, 0.0 . 1.) }; //~ ERROR expected identifier, found `)` + { builtin # offset_of(ComplexTup, 0.0. 1.) }; //~ ERROR expected identifier, found `)` +} diff --git a/tests/ui/offset-of/offset-of-tuple.stderr b/tests/ui/offset-of/offset-of-tuple.stderr new file mode 100644 index 0000000000000..954515f80a65e --- /dev/null +++ b/tests/ui/offset-of/offset-of-tuple.stderr @@ -0,0 +1,241 @@ +error: suffixes on a tuple index are invalid + --> $DIR/offset-of-tuple.rs:19:35 + | +LL | builtin # offset_of((u8, u8), 1_u8); + | ^^^^ invalid suffix `u8` + +error: expected identifier, found `+` + --> $DIR/offset-of-tuple.rs:23:37 + | +LL | { builtin # offset_of((u8, u8), +1) }; + | ^ expected identifier + +error: expected identifier, found `)` + --> $DIR/offset-of-tuple.rs:24:39 + | +LL | { builtin # offset_of((u8, u8), 1.) }; + | ^ expected identifier + +error: expected identifier, found `)` + --> $DIR/offset-of-tuple.rs:25:40 + | +LL | { builtin # offset_of((u8, u8), 1 .) }; + | ^ expected identifier + +error: expected identifier, found `)` + --> $DIR/offset-of-tuple.rs:47:45 + | +LL | { builtin # offset_of(ComplexTup, 0.0.1.) }; + | ^ expected identifier + +error: expected identifier, found `)` + --> $DIR/offset-of-tuple.rs:48:46 + | +LL | { builtin # offset_of(ComplexTup, 0 .0.1.) }; + | ^ expected identifier + +error: expected identifier, found `)` + --> $DIR/offset-of-tuple.rs:49:47 + | +LL | { builtin # offset_of(ComplexTup, 0 . 0.1.) }; + | ^ expected identifier + +error: expected identifier, found `)` + --> $DIR/offset-of-tuple.rs:50:46 + | +LL | { builtin # offset_of(ComplexTup, 0. 0.1.) }; + | ^ expected identifier + +error: expected identifier, found `)` + --> $DIR/offset-of-tuple.rs:51:46 + | +LL | { builtin # offset_of(ComplexTup, 0.0 .1.) }; + | ^ expected identifier + +error: expected identifier, found `)` + --> $DIR/offset-of-tuple.rs:52:47 + | +LL | { builtin # offset_of(ComplexTup, 0.0 . 1.) }; + | ^ expected identifier + +error: expected identifier, found `)` + --> $DIR/offset-of-tuple.rs:53:46 + | +LL | { builtin # offset_of(ComplexTup, 0.0. 1.) }; + | ^ expected identifier + +error: suffixes on a tuple index are invalid + --> $DIR/offset-of-tuple.rs:10:26 + | +LL | offset_of!((u8, u8), 1_u8); + | ^^^^ invalid suffix `u8` + +error: no rules expected the token `1` + --> $DIR/offset-of-tuple.rs:12:27 + | +LL | offset_of!((u8, u8), +1); + | ^ no rules expected this token in macro call + | + = note: while trying to match sequence start + +error: no rules expected the token `1` + --> $DIR/offset-of-tuple.rs:13:27 + | +LL | offset_of!((u8, u8), -1); + | ^ no rules expected this token in macro call + | + = note: while trying to match sequence start + +error: expected identifier, found `)` + --> $DIR/offset-of-tuple.rs:14:5 + | +LL | offset_of!((u8, u8), 1.); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expected identifier + | in this macro invocation + | + = note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: unexpected end of macro invocation + --> $DIR/offset-of-tuple.rs:15:29 + | +LL | offset_of!((u8, u8), 1 .); + | ^ missing tokens in macro arguments + | +note: while trying to match meta-variable `$fields:tt` + --> $SRC_DIR/core/src/mem/mod.rs:LL:COL + +error: expected identifier, found `)` + --> $DIR/offset-of-tuple.rs:36:5 + | +LL | offset_of!(ComplexTup, 0.0.1.); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expected identifier + | in this macro invocation + | + = note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: unexpected end of macro invocation + --> $DIR/offset-of-tuple.rs:37:35 + | +LL | offset_of!(ComplexTup, 0 .0.1.); + | ^ missing tokens in macro arguments + | +note: while trying to match meta-variable `$fields:tt` + --> $SRC_DIR/core/src/mem/mod.rs:LL:COL + +error: unexpected end of macro invocation + --> $DIR/offset-of-tuple.rs:38:36 + | +LL | offset_of!(ComplexTup, 0 . 0.1.); + | ^ missing tokens in macro arguments + | +note: while trying to match meta-variable `$fields:tt` + --> $SRC_DIR/core/src/mem/mod.rs:LL:COL + +error: no rules expected the token `0.1` + --> $DIR/offset-of-tuple.rs:39:31 + | +LL | offset_of!(ComplexTup, 0. 0.1.); + | ^^^ no rules expected this token in macro call + | + = note: while trying to match sequence start + +error: expected identifier, found `)` + --> $DIR/offset-of-tuple.rs:40:5 + | +LL | offset_of!(ComplexTup, 0.0 .1.); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expected identifier + | in this macro invocation + | + = note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: expected identifier, found `)` + --> $DIR/offset-of-tuple.rs:41:5 + | +LL | offset_of!(ComplexTup, 0.0 . 1.); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expected identifier + | in this macro invocation + | + = note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: expected identifier, found `)` + --> $DIR/offset-of-tuple.rs:42:5 + | +LL | offset_of!(ComplexTup, 0.0. 1.); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expected identifier + | in this macro invocation + | + = note: this error originates in the macro `offset_of` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0609]: no field `_0` on type `(u8, u8)` + --> $DIR/offset-of-tuple.rs:7:26 + | +LL | offset_of!((u8, u8), _0); + | ^^ + +error[E0609]: no field `01` on type `(u8, u8)` + --> $DIR/offset-of-tuple.rs:8:26 + | +LL | offset_of!((u8, u8), 01); + | ^^ + +error[E0609]: no field `1e2` on type `(u8, u8)` + --> $DIR/offset-of-tuple.rs:9:26 + | +LL | offset_of!((u8, u8), 1e2); + | ^^^ + +error[E0609]: no field `1_` on type `(u8, u8)` + --> $DIR/offset-of-tuple.rs:10:26 + | +LL | offset_of!((u8, u8), 1_u8); + | ^^^^ + +error[E0609]: no field `1e2` on type `(u8, u8)` + --> $DIR/offset-of-tuple.rs:16:35 + | +LL | builtin # offset_of((u8, u8), 1e2); + | ^^^ + +error[E0609]: no field `_0` on type `(u8, u8)` + --> $DIR/offset-of-tuple.rs:17:35 + | +LL | builtin # offset_of((u8, u8), _0); + | ^^ + +error[E0609]: no field `01` on type `(u8, u8)` + --> $DIR/offset-of-tuple.rs:18:35 + | +LL | builtin # offset_of((u8, u8), 01); + | ^^ + +error[E0609]: no field `1_` on type `(u8, u8)` + --> $DIR/offset-of-tuple.rs:19:35 + | +LL | builtin # offset_of((u8, u8), 1_u8); + | ^^^^ + +error[E0609]: no field `2` on type `(u8, u16)` + --> $DIR/offset-of-tuple.rs:31:47 + | +LL | offset_of!(((u8, u16), (u32, u16, u8)), 0.2); + | ^ + +error[E0609]: no field `0` on type `u8` + --> $DIR/offset-of-tuple.rs:33:49 + | +LL | offset_of!(((u8, u16), (u32, u16, u8)), 1.2.0); + | ^ + +error: aborting due to 33 previous errors + +For more information about this error, try `rustc --explain E0609`. diff --git a/tests/ui/offset-of/offset-of-unsized.rs b/tests/ui/offset-of/offset-of-unsized.rs index 666387e615ef2..49c8328da5c55 100644 --- a/tests/ui/offset-of/offset-of-unsized.rs +++ b/tests/ui/offset-of/offset-of-unsized.rs @@ -1,5 +1,6 @@ // build-pass -// regression test for #112051 +// regression test for #112051, not in `offset-of-dst` as the issue is in codegen, +// and isn't triggered in the presence of typeck errors #![feature(offset_of)] diff --git a/tests/ui/parser/issue-112188.fixed b/tests/ui/parser/issue-112188.fixed new file mode 100644 index 0000000000000..5e73d8e38de81 --- /dev/null +++ b/tests/ui/parser/issue-112188.fixed @@ -0,0 +1,14 @@ +// run-rustfix + +#![allow(unused)] + +struct Foo { x: i32 } + +fn main() { + let f = Foo { x: 0 }; + let Foo { .. } = f; + let Foo { .. } = f; //~ ERROR expected `}`, found `,` + let Foo { x, .. } = f; + let Foo { x, .. } = f; //~ ERROR expected `}`, found `,` + let Foo { x, .. } = f; //~ ERROR expected `}`, found `,` +} diff --git a/tests/ui/parser/issue-112188.rs b/tests/ui/parser/issue-112188.rs new file mode 100644 index 0000000000000..27ca192e52263 --- /dev/null +++ b/tests/ui/parser/issue-112188.rs @@ -0,0 +1,14 @@ +// run-rustfix + +#![allow(unused)] + +struct Foo { x: i32 } + +fn main() { + let f = Foo { x: 0 }; + let Foo { .. } = f; + let Foo { .., } = f; //~ ERROR expected `}`, found `,` + let Foo { x, .. } = f; + let Foo { .., x } = f; //~ ERROR expected `}`, found `,` + let Foo { .., x, .. } = f; //~ ERROR expected `}`, found `,` +} diff --git a/tests/ui/parser/issue-112188.stderr b/tests/ui/parser/issue-112188.stderr new file mode 100644 index 0000000000000..6d2d8e6a3b055 --- /dev/null +++ b/tests/ui/parser/issue-112188.stderr @@ -0,0 +1,37 @@ +error: expected `}`, found `,` + --> $DIR/issue-112188.rs:10:17 + | +LL | let Foo { .., } = f; + | --^ + | | | + | | expected `}` + | | help: remove this comma + | `..` must be at the end and cannot have a trailing comma + +error: expected `}`, found `,` + --> $DIR/issue-112188.rs:12:17 + | +LL | let Foo { .., x } = f; + | --^ + | | | + | | expected `}` + | `..` must be at the end and cannot have a trailing comma + | +help: move the `..` to the end of the field list + | +LL - let Foo { .., x } = f; +LL + let Foo { x, .. } = f; + | + +error: expected `}`, found `,` + --> $DIR/issue-112188.rs:13:17 + | +LL | let Foo { .., x, .. } = f; + | --^- + | | | + | | expected `}` + | `..` must be at the end and cannot have a trailing comma + | help: remove the starting `..` + +error: aborting due to 3 previous errors + diff --git a/tests/ui/parser/issue-49257.stderr b/tests/ui/parser/issue-49257.stderr index 846467f7f2268..97e16f88b8d32 100644 --- a/tests/ui/parser/issue-49257.stderr +++ b/tests/ui/parser/issue-49257.stderr @@ -25,7 +25,7 @@ LL | let Point { .., y } = p; help: move the `..` to the end of the field list | LL - let Point { .., y } = p; -LL + let Point { y , .. } = p; +LL + let Point { y, .. } = p; | error: expected `}`, found `,` diff --git a/tests/ui/parser/issues/issue-112458.rs b/tests/ui/parser/issues/issue-112458.rs new file mode 100644 index 0000000000000..36895450cd94f --- /dev/null +++ b/tests/ui/parser/issues/issue-112458.rs @@ -0,0 +1,4 @@ +fn main() { + println!("{}", x.); //~ ERROR unexpected token: `)` + //~^ ERROR cannot find value `x` in this scope +} diff --git a/tests/ui/parser/issues/issue-112458.stderr b/tests/ui/parser/issues/issue-112458.stderr new file mode 100644 index 0000000000000..54a8f1d03b02a --- /dev/null +++ b/tests/ui/parser/issues/issue-112458.stderr @@ -0,0 +1,15 @@ +error: unexpected token: `)` + --> $DIR/issue-112458.rs:2:22 + | +LL | println!("{}", x.); + | ^ + +error[E0425]: cannot find value `x` in this scope + --> $DIR/issue-112458.rs:2:20 + | +LL | println!("{}", x.); + | ^ not found in this scope + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/privacy/effective_visibilities_full_priv.rs b/tests/ui/privacy/effective_visibilities_full_priv.rs new file mode 100644 index 0000000000000..cc708917586ff --- /dev/null +++ b/tests/ui/privacy/effective_visibilities_full_priv.rs @@ -0,0 +1,21 @@ +#![feature(rustc_attrs)] +#![allow(private_in_public)] + +struct SemiPriv; + +mod m { + #[rustc_effective_visibility] + struct Priv; + //~^ ERROR Direct: pub(self), Reexported: pub(self), Reachable: pub(crate), ReachableThroughImplTrait: pub(crate) + //~| ERROR not in the table + + #[rustc_effective_visibility] + pub fn foo() {} //~ ERROR Direct: pub(crate), Reexported: pub(crate), Reachable: pub(crate), ReachableThroughImplTrait: pub(crate) + + #[rustc_effective_visibility] + impl crate::SemiPriv { //~ ERROR Direct: pub(crate), Reexported: pub(crate), Reachable: pub(crate), ReachableThroughImplTrait: pub(crate) + pub fn f(_: Priv) {} + } +} + +fn main() {} diff --git a/tests/ui/privacy/effective_visibilities_full_priv.stderr b/tests/ui/privacy/effective_visibilities_full_priv.stderr new file mode 100644 index 0000000000000..a856aa20d92c5 --- /dev/null +++ b/tests/ui/privacy/effective_visibilities_full_priv.stderr @@ -0,0 +1,26 @@ +error: Direct: pub(self), Reexported: pub(self), Reachable: pub(crate), ReachableThroughImplTrait: pub(crate) + --> $DIR/effective_visibilities_full_priv.rs:8:5 + | +LL | struct Priv; + | ^^^^^^^^^^^ + +error: not in the table + --> $DIR/effective_visibilities_full_priv.rs:8:5 + | +LL | struct Priv; + | ^^^^^^^^^^^ + +error: Direct: pub(crate), Reexported: pub(crate), Reachable: pub(crate), ReachableThroughImplTrait: pub(crate) + --> $DIR/effective_visibilities_full_priv.rs:13:5 + | +LL | pub fn foo() {} + | ^^^^^^^^^^^^ + +error: Direct: pub(crate), Reexported: pub(crate), Reachable: pub(crate), ReachableThroughImplTrait: pub(crate) + --> $DIR/effective_visibilities_full_priv.rs:16:5 + | +LL | impl crate::SemiPriv { + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/tests/ui/proc-macro/derive-helper-shadowing.stderr b/tests/ui/proc-macro/derive-helper-shadowing.stderr index de2c27a878c67..7e7870b295127 100644 --- a/tests/ui/proc-macro/derive-helper-shadowing.stderr +++ b/tests/ui/proc-macro/derive-helper-shadowing.stderr @@ -16,9 +16,11 @@ error: cannot find attribute `empty_helper` in this scope LL | #[derive(GenHelperUse)] | ^^^^^^^^^^^^ | - = help: consider importing this attribute macro: - empty_helper = note: this error originates in the derive macro `GenHelperUse` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider importing this attribute macro + | +LL + use empty_helper; + | error: cannot find attribute `empty_helper` in this scope --> $DIR/derive-helper-shadowing.rs:14:11 @@ -29,9 +31,11 @@ LL | #[empty_helper] LL | gen_helper_use!(); | ----------------- in this macro invocation | - = help: consider importing this attribute macro: - crate::empty_helper = note: this error originates in the macro `gen_helper_use` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider importing this attribute macro + | +LL + use crate::empty_helper; + | error[E0659]: `empty_helper` is ambiguous --> $DIR/derive-helper-shadowing.rs:26:13 diff --git a/tests/ui/proc-macro/span-preservation.stderr b/tests/ui/proc-macro/span-preservation.stderr index 66c68be2f09de..8c15cb9de9823 100644 --- a/tests/ui/proc-macro/span-preservation.stderr +++ b/tests/ui/proc-macro/span-preservation.stderr @@ -32,7 +32,7 @@ error[E0560]: struct `Foo` has no field named `b` LL | let y = Foo { a: 10, b: 10isize }; | ^ `Foo` does not have this field | - = note: available fields are: `a` + = note: all struct fields are already assigned error[E0308]: mismatched types --> $DIR/span-preservation.rs:39:5 diff --git a/tests/ui/recursion/issue-83150.rs b/tests/ui/recursion/issue-83150.rs index 38353d161c133..75dcdc59f21c1 100644 --- a/tests/ui/recursion/issue-83150.rs +++ b/tests/ui/recursion/issue-83150.rs @@ -2,6 +2,7 @@ // compile-flags: -Copt-level=0 // normalize-stderr-test: "long-type-\d+" -> "long-type-hash" //~^^^ ERROR overflow evaluating the requirement +// ignore-compare-mode-next-solver (hangs) fn main() { let mut iter = 0u8..1; diff --git a/tests/ui/recursion/issue-83150.stderr b/tests/ui/recursion/issue-83150.stderr index 64683ae3a6ebd..eae58771a4169 100644 --- a/tests/ui/recursion/issue-83150.stderr +++ b/tests/ui/recursion/issue-83150.stderr @@ -1,5 +1,5 @@ warning: function cannot return without recursing - --> $DIR/issue-83150.rs:11:1 + --> $DIR/issue-83150.rs:12:1 | LL | fn func>(iter: &mut T) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing @@ -9,10 +9,10 @@ LL | func(&mut iter.map(|x| x + 1)) = help: a `loop` may express intention better if this is on purpose = note: `#[warn(unconditional_recursion)]` on by default -error[E0275]: overflow evaluating the requirement `Map<&mut std::ops::Range, [closure@$DIR/issue-83150.rs:12:24: 12:27]>: Iterator` +error[E0275]: overflow evaluating the requirement `Map<&mut std::ops::Range, [closure@$DIR/issue-83150.rs:13:24: 13:27]>: Iterator` | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_83150`) - = note: required for `&mut Map<&mut std::ops::Range, [closure@$DIR/issue-83150.rs:12:24: 12:27]>` to implement `Iterator` + = note: required for `&mut Map<&mut std::ops::Range, [closure@$DIR/issue-83150.rs:13:24: 13:27]>` to implement `Iterator` = note: 65 redundant requirements hidden = note: required for `&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<..., ...>, ...>, ...>, ...>, ...>, ...>, ...>` to implement `Iterator` = note: the full type name has been written to '$TEST_BUILD_DIR/recursion/issue-83150/issue-83150.long-type-hash.txt' diff --git a/tests/ui/resolve/hidden_glob_reexports.rs b/tests/ui/resolve/hidden_glob_reexports.rs index 361243fcd7bdf..102b565624526 100644 --- a/tests/ui/resolve/hidden_glob_reexports.rs +++ b/tests/ui/resolve/hidden_glob_reexports.rs @@ -6,10 +6,10 @@ pub mod upstream_a { pub struct Bar {} } - pub use self::inner::*; - struct Foo; //~^ WARN private item shadows public glob re-export + + pub use self::inner::*; } pub mod upstream_b { diff --git a/tests/ui/resolve/hidden_glob_reexports.stderr b/tests/ui/resolve/hidden_glob_reexports.stderr index ddf7bcda827a8..11fa94d6fee98 100644 --- a/tests/ui/resolve/hidden_glob_reexports.stderr +++ b/tests/ui/resolve/hidden_glob_reexports.stderr @@ -1,31 +1,54 @@ warning: private item shadows public glob re-export - --> $DIR/hidden_glob_reexports.rs:11:5 + --> $DIR/hidden_glob_reexports.rs:9:5 | -LL | pub use self::inner::*; - | -------------- the name `Foo` in the type namespace is supposed to be publicly re-exported here -LL | LL | struct Foo; - | ^^^^^^^^^^^ but the private item here shadows it + | ^^^^^^^^^^^ + | +note: the name `Foo` in the type namespace is supposed to be publicly re-exported here + --> $DIR/hidden_glob_reexports.rs:12:13 + | +LL | pub use self::inner::*; + | ^^^^^^^^^^^^^^ +note: but the private item here shadows it + --> $DIR/hidden_glob_reexports.rs:9:5 | +LL | struct Foo; + | ^^^^^^^^^^^ = note: `#[warn(hidden_glob_reexports)]` on by default warning: private item shadows public glob re-export --> $DIR/hidden_glob_reexports.rs:27:9 | +LL | use self::other::Foo; + | ^^^^^^^^^^^^^^^^ + | +note: the name `Foo` in the type namespace is supposed to be publicly re-exported here + --> $DIR/hidden_glob_reexports.rs:25:13 + | LL | pub use self::inner::*; - | -------------- the name `Foo` in the type namespace is supposed to be publicly re-exported here -LL | + | ^^^^^^^^^^^^^^ +note: but the private item here shadows it + --> $DIR/hidden_glob_reexports.rs:27:9 + | LL | use self::other::Foo; - | ^^^^^^^^^^^^^^^^ but the private item here shadows it + | ^^^^^^^^^^^^^^^^ warning: private item shadows public glob re-export --> $DIR/hidden_glob_reexports.rs:40:9 | +LL | use std::primitive::u8; + | ^^^^^^^^^^^^^^^^^^ + | +note: the name `u8` in the type namespace is supposed to be publicly re-exported here + --> $DIR/hidden_glob_reexports.rs:38:13 + | LL | pub use self::no_def_id::*; - | ------------------ the name `u8` in the type namespace is supposed to be publicly re-exported here -LL | + | ^^^^^^^^^^^^^^^^^^ +note: but the private item here shadows it + --> $DIR/hidden_glob_reexports.rs:40:9 + | LL | use std::primitive::u8; - | ^^^^^^^^^^^^^^^^^^ but the private item here shadows it + | ^^^^^^^^^^^^^^^^^^ warning: 3 warnings emitted diff --git a/tests/ui/rfc-2005-default-binding-mode/enum.rs b/tests/ui/rfc-2005-default-binding-mode/enum.rs deleted file mode 100644 index 4e57769d6e24c..0000000000000 --- a/tests/ui/rfc-2005-default-binding-mode/enum.rs +++ /dev/null @@ -1,22 +0,0 @@ -enum Wrapper { - Wrap(i32), -} - -use Wrapper::Wrap; - -pub fn main() { - let Wrap(x) = &Wrap(3); - *x += 1; //~ ERROR cannot assign to `*x`, which is behind a `&` reference - - - if let Some(x) = &Some(3) { - *x += 1; //~ ERROR cannot assign to `*x`, which is behind a `&` reference - } else { - panic!(); - } - - while let Some(x) = &Some(3) { - *x += 1; //~ ERROR cannot assign to `*x`, which is behind a `&` reference - break; - } -} diff --git a/tests/ui/rfc-2005-default-binding-mode/for.rs b/tests/ui/rfc-2005-default-binding-mode/for.rs deleted file mode 100644 index d6c5a13b1bdb0..0000000000000 --- a/tests/ui/rfc-2005-default-binding-mode/for.rs +++ /dev/null @@ -1,9 +0,0 @@ -struct Foo {} - -pub fn main() { - let mut tups = vec![(Foo {}, Foo {})]; - // The below desugars to &(ref n, mut m). - for (n, mut m) in &tups { - //~^ ERROR cannot move out of a shared reference - } -} diff --git a/tests/ui/rfc-2005-default-binding-mode/lit.rs b/tests/ui/rfc-2005-default-binding-mode/lit.rs deleted file mode 100644 index ce79cfbdc1a30..0000000000000 --- a/tests/ui/rfc-2005-default-binding-mode/lit.rs +++ /dev/null @@ -1,24 +0,0 @@ -// FIXME(tschottdorf): we want these to compile, but they don't. - -fn with_str() { - let s: &'static str = "abc"; - - match &s { - "abc" => true, //~ ERROR mismatched types - _ => panic!(), - }; -} - -fn with_bytes() { - let s: &'static [u8] = b"abc"; - - match &s { - b"abc" => true, //~ ERROR mismatched types - _ => panic!(), - }; -} - -pub fn main() { - with_str(); - with_bytes(); -} diff --git a/tests/ui/rfc-2005-default-binding-mode/slice.rs b/tests/ui/rfc-2005-default-binding-mode/slice.rs deleted file mode 100644 index 363a0e3e649d9..0000000000000 --- a/tests/ui/rfc-2005-default-binding-mode/slice.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub fn main() { - let sl: &[u8] = b"foo"; - - match sl { //~ ERROR non-exhaustive patterns - [first, remainder @ ..] => {}, - }; -} diff --git a/tests/ui/rfc-0107-bind-by-move-pattern-guards/bind-by-move-no-guards.rs b/tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/bind-by-move-no-guards.rs similarity index 100% rename from tests/ui/rfc-0107-bind-by-move-pattern-guards/bind-by-move-no-guards.rs rename to tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/bind-by-move-no-guards.rs diff --git a/tests/ui/rfc-0107-bind-by-move-pattern-guards/former-E0008-now-pass.rs b/tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/former-E0008-now-pass.rs similarity index 100% rename from tests/ui/rfc-0107-bind-by-move-pattern-guards/former-E0008-now-pass.rs rename to tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/former-E0008-now-pass.rs diff --git a/tests/ui/rfc-0107-bind-by-move-pattern-guards/rfc-basic-examples.rs b/tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/rfc-basic-examples.rs similarity index 100% rename from tests/ui/rfc-0107-bind-by-move-pattern-guards/rfc-basic-examples.rs rename to tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/rfc-basic-examples.rs diff --git a/tests/ui/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-across-arms.rs b/tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-across-arms.rs similarity index 100% rename from tests/ui/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-across-arms.rs rename to tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-across-arms.rs diff --git a/tests/ui/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-across-arms.stderr b/tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-across-arms.stderr similarity index 100% rename from tests/ui/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-across-arms.stderr rename to tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-across-arms.stderr diff --git a/tests/ui/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-in-first-arm.rs b/tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-in-first-arm.rs similarity index 100% rename from tests/ui/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-in-first-arm.rs rename to tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-in-first-arm.rs diff --git a/tests/ui/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-in-first-arm.stderr b/tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-in-first-arm.stderr similarity index 100% rename from tests/ui/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-in-first-arm.stderr rename to tests/ui/rfcs/rfc-0107-bind-by-move-pattern-guards/rfc-reject-double-move-in-first-arm.stderr diff --git a/tests/ui/rfcs/rfc-1014-2.rs b/tests/ui/rfcs/rfc-1014-stdout-existential-crisis/rfc-1014-2.rs similarity index 100% rename from tests/ui/rfcs/rfc-1014-2.rs rename to tests/ui/rfcs/rfc-1014-stdout-existential-crisis/rfc-1014-2.rs diff --git a/tests/ui/rfcs/rfc-1014.rs b/tests/ui/rfcs/rfc-1014-stdout-existential-crisis/rfc-1014.rs similarity index 100% rename from tests/ui/rfcs/rfc-1014.rs rename to tests/ui/rfcs/rfc-1014-stdout-existential-crisis/rfc-1014.rs diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/allow-hide-behind-direct-unsafe-ptr-embedded.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/allow-hide-behind-direct-unsafe-ptr-embedded.rs similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/allow-hide-behind-direct-unsafe-ptr-embedded.rs rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/allow-hide-behind-direct-unsafe-ptr-embedded.rs diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/allow-hide-behind-direct-unsafe-ptr-param.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/allow-hide-behind-direct-unsafe-ptr-param.rs similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/allow-hide-behind-direct-unsafe-ptr-param.rs rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/allow-hide-behind-direct-unsafe-ptr-param.rs diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/allow-hide-behind-indirect-unsafe-ptr-embedded.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/allow-hide-behind-indirect-unsafe-ptr-embedded.rs similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/allow-hide-behind-indirect-unsafe-ptr-embedded.rs rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/allow-hide-behind-indirect-unsafe-ptr-embedded.rs diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/allow-hide-behind-indirect-unsafe-ptr-param.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/allow-hide-behind-indirect-unsafe-ptr-param.rs similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/allow-hide-behind-indirect-unsafe-ptr-param.rs rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/allow-hide-behind-indirect-unsafe-ptr-param.rs diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/allow-use-behind-cousin-variant.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/allow-use-behind-cousin-variant.rs similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/allow-use-behind-cousin-variant.rs rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/allow-use-behind-cousin-variant.rs diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-embedded.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-embedded.rs similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-embedded.rs rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-embedded.rs diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-embedded.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-embedded.stderr similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-embedded.stderr rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-embedded.stderr diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-param.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-param.rs similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-param.rs rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-param.rs diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-param.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-param.stderr similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-param.stderr rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-direct-struct-param.stderr diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-embedded.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-embedded.rs similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-embedded.rs rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-embedded.rs diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-embedded.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-embedded.stderr similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-embedded.stderr rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-embedded.stderr diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-param.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-param.rs similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-param.rs rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-param.rs diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-param.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-param.stderr similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-param.stderr rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-doubly-indirect-param.stderr diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-embedded.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-embedded.rs similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-embedded.rs rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-embedded.rs diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-embedded.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-embedded.stderr similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-embedded.stderr rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-embedded.stderr diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-param.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-param.rs similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-param.rs rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-param.rs diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-param.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-param.stderr similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-param.stderr rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/cant-hide-behind-indirect-struct-param.stderr diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/feature-gate.no_gate.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/feature-gate.no_gate.stderr similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/feature-gate.no_gate.stderr rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/feature-gate.no_gate.stderr diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/feature-gate.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/feature-gate.rs similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/feature-gate.rs rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/feature-gate.rs diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/feature-gate.with_gate.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/feature-gate.with_gate.stderr similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/feature-gate.with_gate.stderr rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/feature-gate.with_gate.stderr diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/fn-ptr-is-structurally-matchable.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/fn-ptr-is-structurally-matchable.rs similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/fn-ptr-is-structurally-matchable.rs rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/fn-ptr-is-structurally-matchable.rs diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/issue-61188-match-slice-forbidden-without-eq.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-61188-match-slice-forbidden-without-eq.rs similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/issue-61188-match-slice-forbidden-without-eq.rs rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-61188-match-slice-forbidden-without-eq.rs diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/issue-61188-match-slice-forbidden-without-eq.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-61188-match-slice-forbidden-without-eq.stderr similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/issue-61188-match-slice-forbidden-without-eq.stderr rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-61188-match-slice-forbidden-without-eq.stderr diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/issue-62307-match-ref-ref-forbidden-without-eq.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-62307-match-ref-ref-forbidden-without-eq.rs similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/issue-62307-match-ref-ref-forbidden-without-eq.rs rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-62307-match-ref-ref-forbidden-without-eq.rs diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/issue-62307-match-ref-ref-forbidden-without-eq.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-62307-match-ref-ref-forbidden-without-eq.stderr similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/issue-62307-match-ref-ref-forbidden-without-eq.stderr rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-62307-match-ref-ref-forbidden-without-eq.stderr diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/issue-63479-match-fnptr.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-63479-match-fnptr.rs similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/issue-63479-match-fnptr.rs rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-63479-match-fnptr.rs diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/issue-63479-match-fnptr.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-63479-match-fnptr.stderr similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/issue-63479-match-fnptr.stderr rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-63479-match-fnptr.stderr diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/issue-6804.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804.rs similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/issue-6804.rs rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804.rs diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/issue-6804.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804.stderr similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/issue-6804.stderr rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804.stderr diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/match-empty-array-allowed-without-eq-issue-62336.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/match-empty-array-allowed-without-eq-issue-62336.rs similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/match-empty-array-allowed-without-eq-issue-62336.rs rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/match-empty-array-allowed-without-eq-issue-62336.rs diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/match-forbidden-without-eq.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/match-forbidden-without-eq.rs similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/match-forbidden-without-eq.rs rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/match-forbidden-without-eq.rs diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/match-forbidden-without-eq.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/match-forbidden-without-eq.stderr similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/match-forbidden-without-eq.stderr rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/match-forbidden-without-eq.stderr diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/match-nonempty-array-forbidden-without-eq.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/match-nonempty-array-forbidden-without-eq.rs similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/match-nonempty-array-forbidden-without-eq.rs rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/match-nonempty-array-forbidden-without-eq.rs diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/match-nonempty-array-forbidden-without-eq.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/match-nonempty-array-forbidden-without-eq.stderr similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/match-nonempty-array-forbidden-without-eq.stderr rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/match-nonempty-array-forbidden-without-eq.stderr diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/match-requires-both-partialeq-and-eq.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/match-requires-both-partialeq-and-eq.rs similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/match-requires-both-partialeq-and-eq.rs rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/match-requires-both-partialeq-and-eq.rs diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/match-requires-both-partialeq-and-eq.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/match-requires-both-partialeq-and-eq.stderr similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/match-requires-both-partialeq-and-eq.stderr rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/match-requires-both-partialeq-and-eq.stderr diff --git a/tests/ui/rfc-1445-restrict-constants-in-patterns/phantom-data-is-structurally-matchable.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/phantom-data-is-structurally-matchable.rs similarity index 100% rename from tests/ui/rfc-1445-restrict-constants-in-patterns/phantom-data-is-structurally-matchable.rs rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/phantom-data-is-structurally-matchable.rs diff --git a/tests/ui/rfcs/rfc1445/eq-allows-match-on-ty-in-macro.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/rfc1445/eq-allows-match-on-ty-in-macro.rs similarity index 100% rename from tests/ui/rfcs/rfc1445/eq-allows-match-on-ty-in-macro.rs rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/rfc1445/eq-allows-match-on-ty-in-macro.rs diff --git a/tests/ui/rfcs/rfc1445/eq-allows-match.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/rfc1445/eq-allows-match.rs similarity index 100% rename from tests/ui/rfcs/rfc1445/eq-allows-match.rs rename to tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/rfc1445/eq-allows-match.rs diff --git a/tests/ui/rfcs/rfc1623-2.rs b/tests/ui/rfcs/rfc-1623-static/rfc1623-2.rs similarity index 100% rename from tests/ui/rfcs/rfc1623-2.rs rename to tests/ui/rfcs/rfc-1623-static/rfc1623-2.rs diff --git a/tests/ui/rfcs/rfc1623-2.stderr b/tests/ui/rfcs/rfc-1623-static/rfc1623-2.stderr similarity index 100% rename from tests/ui/rfcs/rfc1623-2.stderr rename to tests/ui/rfcs/rfc-1623-static/rfc1623-2.stderr diff --git a/tests/ui/rfcs/rfc1623-3.rs b/tests/ui/rfcs/rfc-1623-static/rfc1623-3.rs similarity index 100% rename from tests/ui/rfcs/rfc1623-3.rs rename to tests/ui/rfcs/rfc-1623-static/rfc1623-3.rs diff --git a/tests/ui/rfcs/rfc1623-3.stderr b/tests/ui/rfcs/rfc-1623-static/rfc1623-3.stderr similarity index 100% rename from tests/ui/rfcs/rfc1623-3.stderr rename to tests/ui/rfcs/rfc-1623-static/rfc1623-3.stderr diff --git a/tests/ui/rfcs/rfc1623.rs b/tests/ui/rfcs/rfc-1623-static/rfc1623.rs similarity index 100% rename from tests/ui/rfcs/rfc1623.rs rename to tests/ui/rfcs/rfc-1623-static/rfc1623.rs diff --git a/tests/ui/rfcs/rfc1717/library-override.rs b/tests/ui/rfcs/rfc-1717-dllimport/1717-dllimport/library-override.rs similarity index 100% rename from tests/ui/rfcs/rfc1717/library-override.rs rename to tests/ui/rfcs/rfc-1717-dllimport/1717-dllimport/library-override.rs diff --git a/tests/ui/rfc-1717-dllimport/missing-link-attr.rs b/tests/ui/rfcs/rfc-1717-dllimport/missing-link-attr.rs similarity index 100% rename from tests/ui/rfc-1717-dllimport/missing-link-attr.rs rename to tests/ui/rfcs/rfc-1717-dllimport/missing-link-attr.rs diff --git a/tests/ui/rfc-1717-dllimport/missing-link-attr.stderr b/tests/ui/rfcs/rfc-1717-dllimport/missing-link-attr.stderr similarity index 100% rename from tests/ui/rfc-1717-dllimport/missing-link-attr.stderr rename to tests/ui/rfcs/rfc-1717-dllimport/missing-link-attr.stderr diff --git a/tests/ui/rfc-1717-dllimport/multiple-renames.rs b/tests/ui/rfcs/rfc-1717-dllimport/multiple-renames.rs similarity index 100% rename from tests/ui/rfc-1717-dllimport/multiple-renames.rs rename to tests/ui/rfcs/rfc-1717-dllimport/multiple-renames.rs diff --git a/tests/ui/rfc-1717-dllimport/multiple-renames.stderr b/tests/ui/rfcs/rfc-1717-dllimport/multiple-renames.stderr similarity index 100% rename from tests/ui/rfc-1717-dllimport/multiple-renames.stderr rename to tests/ui/rfcs/rfc-1717-dllimport/multiple-renames.stderr diff --git a/tests/ui/rfc-1717-dllimport/rename-modifiers.rs b/tests/ui/rfcs/rfc-1717-dllimport/rename-modifiers.rs similarity index 100% rename from tests/ui/rfc-1717-dllimport/rename-modifiers.rs rename to tests/ui/rfcs/rfc-1717-dllimport/rename-modifiers.rs diff --git a/tests/ui/rfc-1717-dllimport/rename-modifiers.stderr b/tests/ui/rfcs/rfc-1717-dllimport/rename-modifiers.stderr similarity index 100% rename from tests/ui/rfc-1717-dllimport/rename-modifiers.stderr rename to tests/ui/rfcs/rfc-1717-dllimport/rename-modifiers.stderr diff --git a/tests/ui/rfc-1717-dllimport/rename-to-empty.rs b/tests/ui/rfcs/rfc-1717-dllimport/rename-to-empty.rs similarity index 100% rename from tests/ui/rfc-1717-dllimport/rename-to-empty.rs rename to tests/ui/rfcs/rfc-1717-dllimport/rename-to-empty.rs diff --git a/tests/ui/rfc-1717-dllimport/rename-to-empty.stderr b/tests/ui/rfcs/rfc-1717-dllimport/rename-to-empty.stderr similarity index 100% rename from tests/ui/rfc-1717-dllimport/rename-to-empty.stderr rename to tests/ui/rfcs/rfc-1717-dllimport/rename-to-empty.stderr diff --git a/tests/ui/rfcs/rfc1857-drop-order.rs b/tests/ui/rfcs/rfc-1857-stabilize-drop-order/drop-order.rs similarity index 100% rename from tests/ui/rfcs/rfc1857-drop-order.rs rename to tests/ui/rfcs/rfc-1857-stabilize-drop-order/drop-order.rs diff --git a/tests/ui/rfc-1937-termination-trait/issue-103052-1.rs b/tests/ui/rfcs/rfc-1937-termination-trait/issue-103052-1.rs similarity index 100% rename from tests/ui/rfc-1937-termination-trait/issue-103052-1.rs rename to tests/ui/rfcs/rfc-1937-termination-trait/issue-103052-1.rs diff --git a/tests/ui/rfc-1937-termination-trait/issue-103052-1.stderr b/tests/ui/rfcs/rfc-1937-termination-trait/issue-103052-1.stderr similarity index 100% rename from tests/ui/rfc-1937-termination-trait/issue-103052-1.stderr rename to tests/ui/rfcs/rfc-1937-termination-trait/issue-103052-1.stderr diff --git a/tests/ui/rfc-1937-termination-trait/issue-103052-2.current.stderr b/tests/ui/rfcs/rfc-1937-termination-trait/issue-103052-2.current.stderr similarity index 100% rename from tests/ui/rfc-1937-termination-trait/issue-103052-2.current.stderr rename to tests/ui/rfcs/rfc-1937-termination-trait/issue-103052-2.current.stderr diff --git a/tests/ui/rfc-1937-termination-trait/issue-103052-2.next.stderr b/tests/ui/rfcs/rfc-1937-termination-trait/issue-103052-2.next.stderr similarity index 100% rename from tests/ui/rfc-1937-termination-trait/issue-103052-2.next.stderr rename to tests/ui/rfcs/rfc-1937-termination-trait/issue-103052-2.next.stderr diff --git a/tests/ui/rfc-1937-termination-trait/issue-103052-2.rs b/tests/ui/rfcs/rfc-1937-termination-trait/issue-103052-2.rs similarity index 100% rename from tests/ui/rfc-1937-termination-trait/issue-103052-2.rs rename to tests/ui/rfcs/rfc-1937-termination-trait/issue-103052-2.rs diff --git a/tests/ui/rfc-1937-termination-trait/termination-trait-for-box-dyn-error.rs b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-box-dyn-error-err.rs similarity index 100% rename from tests/ui/rfc-1937-termination-trait/termination-trait-for-box-dyn-error.rs rename to tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-box-dyn-error-err.rs diff --git a/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-box-dyn-error.rs b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-box-dyn-error-ok.rs similarity index 100% rename from tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-box-dyn-error.rs rename to tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-box-dyn-error-ok.rs diff --git a/tests/ui/rfc-1937-termination-trait/termination-trait-for-never.rs b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-never.rs similarity index 100% rename from tests/ui/rfc-1937-termination-trait/termination-trait-for-never.rs rename to tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-never.rs diff --git a/tests/ui/rfc-1937-termination-trait/termination-trait-for-result-box-error_err.rs b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-result-box-error_err.rs similarity index 100% rename from tests/ui/rfc-1937-termination-trait/termination-trait-for-result-box-error_err.rs rename to tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-result-box-error_err.rs diff --git a/tests/ui/rfc-1937-termination-trait/termination-trait-for-str.rs b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-str-err.rs similarity index 100% rename from tests/ui/rfc-1937-termination-trait/termination-trait-for-str.rs rename to tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-str-err.rs diff --git a/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-str.rs b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-str-ok.rs similarity index 100% rename from tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-str.rs rename to tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-str-ok.rs diff --git a/tests/ui/rfc-1937-termination-trait/termination-trait-impl-trait.rs b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-impl-trait.rs similarity index 100% rename from tests/ui/rfc-1937-termination-trait/termination-trait-impl-trait.rs rename to tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-impl-trait.rs diff --git a/tests/ui/rfc-1937-termination-trait/termination-trait-impl-trait.stderr b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-impl-trait.stderr similarity index 100% rename from tests/ui/rfc-1937-termination-trait/termination-trait-impl-trait.stderr rename to tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-impl-trait.stderr diff --git a/tests/ui/rfc-1937-termination-trait/termination-trait-in-test-should-panic.rs b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-in-test-should-panic.rs similarity index 100% rename from tests/ui/rfc-1937-termination-trait/termination-trait-in-test-should-panic.rs rename to tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-in-test-should-panic.rs diff --git a/tests/ui/rfc-1937-termination-trait/termination-trait-in-test-should-panic.stderr b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-in-test-should-panic.stderr similarity index 100% rename from tests/ui/rfc-1937-termination-trait/termination-trait-in-test-should-panic.stderr rename to tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-in-test-should-panic.stderr diff --git a/tests/ui/rfc-1937-termination-trait/termination-trait-in-test.rs b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-in-test.rs similarity index 100% rename from tests/ui/rfc-1937-termination-trait/termination-trait-in-test.rs rename to tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-in-test.rs diff --git a/tests/ui/rfc-1937-termination-trait/termination-trait-main-i32.rs b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-main-i32.rs similarity index 100% rename from tests/ui/rfc-1937-termination-trait/termination-trait-main-i32.rs rename to tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-main-i32.rs diff --git a/tests/ui/rfc-1937-termination-trait/termination-trait-main-i32.stderr b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-main-i32.stderr similarity index 100% rename from tests/ui/rfc-1937-termination-trait/termination-trait-main-i32.stderr rename to tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-main-i32.stderr diff --git a/tests/ui/rfc-1937-termination-trait/termination-trait-main-wrong-type.rs b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-main-wrong-type.rs similarity index 100% rename from tests/ui/rfc-1937-termination-trait/termination-trait-main-wrong-type.rs rename to tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-main-wrong-type.rs diff --git a/tests/ui/rfc-1937-termination-trait/termination-trait-main-wrong-type.stderr b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-main-wrong-type.stderr similarity index 100% rename from tests/ui/rfc-1937-termination-trait/termination-trait-main-wrong-type.stderr rename to tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-main-wrong-type.stderr diff --git a/tests/ui/rfc-1937-termination-trait/termination-trait-not-satisfied.rs b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-not-satisfied.rs similarity index 100% rename from tests/ui/rfc-1937-termination-trait/termination-trait-not-satisfied.rs rename to tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-not-satisfied.rs diff --git a/tests/ui/rfc-1937-termination-trait/termination-trait-not-satisfied.stderr b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-not-satisfied.stderr similarity index 100% rename from tests/ui/rfc-1937-termination-trait/termination-trait-not-satisfied.stderr rename to tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-not-satisfied.stderr diff --git a/tests/ui/rfc-1937-termination-trait/termination-trait-test-wrong-type.rs b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-test-wrong-type.rs similarity index 100% rename from tests/ui/rfc-1937-termination-trait/termination-trait-test-wrong-type.rs rename to tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-test-wrong-type.rs diff --git a/tests/ui/rfc-1937-termination-trait/termination-trait-test-wrong-type.stderr b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-test-wrong-type.stderr similarity index 100% rename from tests/ui/rfc-1937-termination-trait/termination-trait-test-wrong-type.stderr rename to tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-test-wrong-type.stderr diff --git a/tests/ui/rfc-2005-default-binding-mode/borrowck-issue-49631.rs b/tests/ui/rfcs/rfc-2005-default-binding-mode/borrowck-issue-49631.rs similarity index 100% rename from tests/ui/rfc-2005-default-binding-mode/borrowck-issue-49631.rs rename to tests/ui/rfcs/rfc-2005-default-binding-mode/borrowck-issue-49631.rs diff --git a/tests/ui/rfc-2005-default-binding-mode/borrowck-issue-49631.stderr b/tests/ui/rfcs/rfc-2005-default-binding-mode/borrowck-issue-49631.stderr similarity index 100% rename from tests/ui/rfc-2005-default-binding-mode/borrowck-issue-49631.stderr rename to tests/ui/rfcs/rfc-2005-default-binding-mode/borrowck-issue-49631.stderr diff --git a/tests/ui/rfc-2005-default-binding-mode/const.rs b/tests/ui/rfcs/rfc-2005-default-binding-mode/const.rs similarity index 100% rename from tests/ui/rfc-2005-default-binding-mode/const.rs rename to tests/ui/rfcs/rfc-2005-default-binding-mode/const.rs diff --git a/tests/ui/rfc-2005-default-binding-mode/const.stderr b/tests/ui/rfcs/rfc-2005-default-binding-mode/const.stderr similarity index 100% rename from tests/ui/rfc-2005-default-binding-mode/const.stderr rename to tests/ui/rfcs/rfc-2005-default-binding-mode/const.stderr diff --git a/tests/ui/rfcs/rfc-2005-default-binding-mode/enum-ok.rs b/tests/ui/rfcs/rfc-2005-default-binding-mode/enum-ok.rs new file mode 100644 index 0000000000000..52fbb90ed541b --- /dev/null +++ b/tests/ui/rfcs/rfc-2005-default-binding-mode/enum-ok.rs @@ -0,0 +1,45 @@ +// run-pass +enum Wrapper { + Wrap(i32), +} + +use Wrapper::Wrap; + +pub fn main() { + let Wrap(x) = &Wrap(3); + println!("{}", *x); + + let Wrap(x) = &mut Wrap(3); + println!("{}", *x); + + if let Some(x) = &Some(3) { + println!("{}", *x); + } else { + panic!(); + } + + if let Some(x) = &mut Some(3) { + println!("{}", *x); + } else { + panic!(); + } + + if let Some(x) = &mut Some(3) { + *x += 1; + } else { + panic!(); + } + + while let Some(x) = &Some(3) { + println!("{}", *x); + break; + } + while let Some(x) = &mut Some(3) { + println!("{}", *x); + break; + } + while let Some(x) = &mut Some(3) { + *x += 1; + break; + } +} diff --git a/tests/ui/rfcs/rfc-2005-default-binding-mode/enum.rs b/tests/ui/rfcs/rfc-2005-default-binding-mode/enum.rs index 52fbb90ed541b..4e57769d6e24c 100644 --- a/tests/ui/rfcs/rfc-2005-default-binding-mode/enum.rs +++ b/tests/ui/rfcs/rfc-2005-default-binding-mode/enum.rs @@ -1,4 +1,3 @@ -// run-pass enum Wrapper { Wrap(i32), } @@ -7,39 +6,17 @@ use Wrapper::Wrap; pub fn main() { let Wrap(x) = &Wrap(3); - println!("{}", *x); + *x += 1; //~ ERROR cannot assign to `*x`, which is behind a `&` reference - let Wrap(x) = &mut Wrap(3); - println!("{}", *x); if let Some(x) = &Some(3) { - println!("{}", *x); - } else { - panic!(); - } - - if let Some(x) = &mut Some(3) { - println!("{}", *x); - } else { - panic!(); - } - - if let Some(x) = &mut Some(3) { - *x += 1; + *x += 1; //~ ERROR cannot assign to `*x`, which is behind a `&` reference } else { panic!(); } while let Some(x) = &Some(3) { - println!("{}", *x); - break; - } - while let Some(x) = &mut Some(3) { - println!("{}", *x); - break; - } - while let Some(x) = &mut Some(3) { - *x += 1; + *x += 1; //~ ERROR cannot assign to `*x`, which is behind a `&` reference break; } } diff --git a/tests/ui/rfc-2005-default-binding-mode/enum.stderr b/tests/ui/rfcs/rfc-2005-default-binding-mode/enum.stderr similarity index 100% rename from tests/ui/rfc-2005-default-binding-mode/enum.stderr rename to tests/ui/rfcs/rfc-2005-default-binding-mode/enum.stderr diff --git a/tests/ui/rfc-2005-default-binding-mode/explicit-mut.rs b/tests/ui/rfcs/rfc-2005-default-binding-mode/explicit-mut.rs similarity index 100% rename from tests/ui/rfc-2005-default-binding-mode/explicit-mut.rs rename to tests/ui/rfcs/rfc-2005-default-binding-mode/explicit-mut.rs diff --git a/tests/ui/rfc-2005-default-binding-mode/explicit-mut.stderr b/tests/ui/rfcs/rfc-2005-default-binding-mode/explicit-mut.stderr similarity index 100% rename from tests/ui/rfc-2005-default-binding-mode/explicit-mut.stderr rename to tests/ui/rfcs/rfc-2005-default-binding-mode/explicit-mut.stderr diff --git a/tests/ui/rfcs/rfc-2005-default-binding-mode/for-ok.rs b/tests/ui/rfcs/rfc-2005-default-binding-mode/for-ok.rs new file mode 100644 index 0000000000000..a5a24a806340f --- /dev/null +++ b/tests/ui/rfcs/rfc-2005-default-binding-mode/for-ok.rs @@ -0,0 +1,20 @@ +// run-pass +pub fn main() { + let mut tups = vec![(0u8, 1u8)]; + + for (n, m) in &tups { + let _: &u8 = n; + let _: &u8 = m; + } + + for (n, m) in &mut tups { + *n += 1; + *m += 2; + } + + assert_eq!(tups, vec![(1u8, 3u8)]); + + for (n, m) in tups { + println!("{} {}", m, n); + } +} diff --git a/tests/ui/rfcs/rfc-2005-default-binding-mode/for.rs b/tests/ui/rfcs/rfc-2005-default-binding-mode/for.rs index a5a24a806340f..d6c5a13b1bdb0 100644 --- a/tests/ui/rfcs/rfc-2005-default-binding-mode/for.rs +++ b/tests/ui/rfcs/rfc-2005-default-binding-mode/for.rs @@ -1,20 +1,9 @@ -// run-pass -pub fn main() { - let mut tups = vec![(0u8, 1u8)]; - - for (n, m) in &tups { - let _: &u8 = n; - let _: &u8 = m; - } +struct Foo {} - for (n, m) in &mut tups { - *n += 1; - *m += 2; - } - - assert_eq!(tups, vec![(1u8, 3u8)]); - - for (n, m) in tups { - println!("{} {}", m, n); +pub fn main() { + let mut tups = vec![(Foo {}, Foo {})]; + // The below desugars to &(ref n, mut m). + for (n, mut m) in &tups { + //~^ ERROR cannot move out of a shared reference } } diff --git a/tests/ui/rfc-2005-default-binding-mode/for.stderr b/tests/ui/rfcs/rfc-2005-default-binding-mode/for.stderr similarity index 100% rename from tests/ui/rfc-2005-default-binding-mode/for.stderr rename to tests/ui/rfcs/rfc-2005-default-binding-mode/for.stderr diff --git a/tests/ui/rfc-2005-default-binding-mode/issue-44912-or.rs b/tests/ui/rfcs/rfc-2005-default-binding-mode/issue-44912-or.rs similarity index 100% rename from tests/ui/rfc-2005-default-binding-mode/issue-44912-or.rs rename to tests/ui/rfcs/rfc-2005-default-binding-mode/issue-44912-or.rs diff --git a/tests/ui/rfc-2005-default-binding-mode/issue-44912-or.stderr b/tests/ui/rfcs/rfc-2005-default-binding-mode/issue-44912-or.stderr similarity index 100% rename from tests/ui/rfc-2005-default-binding-mode/issue-44912-or.stderr rename to tests/ui/rfcs/rfc-2005-default-binding-mode/issue-44912-or.stderr diff --git a/tests/ui/rfcs/rfc-2005-default-binding-mode/lit-ok.rs b/tests/ui/rfcs/rfc-2005-default-binding-mode/lit-ok.rs new file mode 100644 index 0000000000000..9379753598eb0 --- /dev/null +++ b/tests/ui/rfcs/rfc-2005-default-binding-mode/lit-ok.rs @@ -0,0 +1,34 @@ +// run-pass +#![allow(dead_code)] +fn with_u8() { + let s = 5u8; + let r = match &s { + 4 => false, + 5 => true, + _ => false, + }; + assert!(r); +} + +// A string literal isn't mistaken for a non-ref pattern (in which case we'd +// deref `s` and mess things up). +fn with_str() { + let s: &'static str = "abc"; + match s { + "abc" => true, + _ => panic!(), + }; +} + +// Ditto with byte strings. +fn with_bytes() { + let s: &'static [u8] = b"abc"; + match s { + b"abc" => true, + _ => panic!(), + }; +} + +pub fn main() { + with_str(); +} diff --git a/tests/ui/rfcs/rfc-2005-default-binding-mode/lit.rs b/tests/ui/rfcs/rfc-2005-default-binding-mode/lit.rs index 9379753598eb0..ce79cfbdc1a30 100644 --- a/tests/ui/rfcs/rfc-2005-default-binding-mode/lit.rs +++ b/tests/ui/rfcs/rfc-2005-default-binding-mode/lit.rs @@ -1,34 +1,24 @@ -// run-pass -#![allow(dead_code)] -fn with_u8() { - let s = 5u8; - let r = match &s { - 4 => false, - 5 => true, - _ => false, - }; - assert!(r); -} +// FIXME(tschottdorf): we want these to compile, but they don't. -// A string literal isn't mistaken for a non-ref pattern (in which case we'd -// deref `s` and mess things up). fn with_str() { let s: &'static str = "abc"; - match s { - "abc" => true, + + match &s { + "abc" => true, //~ ERROR mismatched types _ => panic!(), }; } -// Ditto with byte strings. fn with_bytes() { let s: &'static [u8] = b"abc"; - match s { - b"abc" => true, + + match &s { + b"abc" => true, //~ ERROR mismatched types _ => panic!(), }; } pub fn main() { with_str(); + with_bytes(); } diff --git a/tests/ui/rfc-2005-default-binding-mode/lit.stderr b/tests/ui/rfcs/rfc-2005-default-binding-mode/lit.stderr similarity index 100% rename from tests/ui/rfc-2005-default-binding-mode/lit.stderr rename to tests/ui/rfcs/rfc-2005-default-binding-mode/lit.stderr diff --git a/tests/ui/rfc-2005-default-binding-mode/no-double-error.rs b/tests/ui/rfcs/rfc-2005-default-binding-mode/no-double-error.rs similarity index 100% rename from tests/ui/rfc-2005-default-binding-mode/no-double-error.rs rename to tests/ui/rfcs/rfc-2005-default-binding-mode/no-double-error.rs diff --git a/tests/ui/rfc-2005-default-binding-mode/no-double-error.stderr b/tests/ui/rfcs/rfc-2005-default-binding-mode/no-double-error.stderr similarity index 100% rename from tests/ui/rfc-2005-default-binding-mode/no-double-error.stderr rename to tests/ui/rfcs/rfc-2005-default-binding-mode/no-double-error.stderr diff --git a/tests/ui/rfcs/rfc-2005-default-binding-mode/slice-ok.rs b/tests/ui/rfcs/rfc-2005-default-binding-mode/slice-ok.rs new file mode 100644 index 0000000000000..33229a205f4d8 --- /dev/null +++ b/tests/ui/rfcs/rfc-2005-default-binding-mode/slice-ok.rs @@ -0,0 +1,25 @@ +// run-pass + +fn slice_pat() { + let sl: &[u8] = b"foo"; + + match sl { + [first, remainder @ ..] => { + let _: &u8 = first; + assert_eq!(first, &b'f'); + assert_eq!(remainder, b"oo"); + } + [] => panic!(), + } +} + +fn slice_pat_omission() { + match &[0, 1, 2] { + [..] => {} + }; +} + +fn main() { + slice_pat(); + slice_pat_omission(); +} diff --git a/tests/ui/rfcs/rfc-2005-default-binding-mode/slice.rs b/tests/ui/rfcs/rfc-2005-default-binding-mode/slice.rs index 33229a205f4d8..363a0e3e649d9 100644 --- a/tests/ui/rfcs/rfc-2005-default-binding-mode/slice.rs +++ b/tests/ui/rfcs/rfc-2005-default-binding-mode/slice.rs @@ -1,25 +1,7 @@ -// run-pass - -fn slice_pat() { +pub fn main() { let sl: &[u8] = b"foo"; - match sl { - [first, remainder @ ..] => { - let _: &u8 = first; - assert_eq!(first, &b'f'); - assert_eq!(remainder, b"oo"); - } - [] => panic!(), - } -} - -fn slice_pat_omission() { - match &[0, 1, 2] { - [..] => {} - }; -} - -fn main() { - slice_pat(); - slice_pat_omission(); + match sl { //~ ERROR non-exhaustive patterns + [first, remainder @ ..] => {}, + }; } diff --git a/tests/ui/rfc-2005-default-binding-mode/slice.stderr b/tests/ui/rfcs/rfc-2005-default-binding-mode/slice.stderr similarity index 100% rename from tests/ui/rfc-2005-default-binding-mode/slice.stderr rename to tests/ui/rfcs/rfc-2005-default-binding-mode/slice.stderr diff --git a/tests/ui/rfc-2008-non-exhaustive/auxiliary/enums.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/auxiliary/enums.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/auxiliary/enums.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/auxiliary/enums.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/auxiliary/monovariants.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/auxiliary/monovariants.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/auxiliary/monovariants.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/auxiliary/monovariants.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/auxiliary/structs.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/auxiliary/structs.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/auxiliary/structs.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/auxiliary/structs.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/auxiliary/unstable.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/auxiliary/unstable.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/auxiliary/unstable.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/auxiliary/unstable.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/auxiliary/variants.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/auxiliary/variants.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/auxiliary/variants.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/auxiliary/variants.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/borrowck-exhaustive.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/borrowck-exhaustive.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/borrowck-exhaustive.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/borrowck-exhaustive.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/borrowck-non-exhaustive.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/borrowck-non-exhaustive.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/borrowck-non-exhaustive.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/borrowck-non-exhaustive.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/borrowck-non-exhaustive.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/borrowck-non-exhaustive.stderr similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/borrowck-non-exhaustive.stderr rename to tests/ui/rfcs/rfc-2008-non-exhaustive/borrowck-non-exhaustive.stderr diff --git a/tests/ui/rfc-2008-non-exhaustive/enum-as-cast.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/enum-as-cast.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/enum-as-cast.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/enum-as-cast.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/enum-as-cast.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/enum-as-cast.stderr similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/enum-as-cast.stderr rename to tests/ui/rfcs/rfc-2008-non-exhaustive/enum-as-cast.stderr diff --git a/tests/ui/rfc-2008-non-exhaustive/enum.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/enum.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/enum.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/enum.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/enum.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/enum.stderr similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/enum.stderr rename to tests/ui/rfcs/rfc-2008-non-exhaustive/enum.stderr diff --git a/tests/ui/rfc-2008-non-exhaustive/enum_same_crate.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/enum_same_crate.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/enum_same_crate.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/enum_same_crate.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/enum_same_crate_empty_match.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/enum_same_crate_empty_match.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/enum_same_crate_empty_match.stderr similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.stderr rename to tests/ui/rfcs/rfc-2008-non-exhaustive/enum_same_crate_empty_match.stderr diff --git a/tests/ui/rfc-2008-non-exhaustive/improper_ctypes/auxiliary/types.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/improper_ctypes/auxiliary/types.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/improper_ctypes/auxiliary/types.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/improper_ctypes/auxiliary/types.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/improper_ctypes/extern_crate_improper.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/improper_ctypes/extern_crate_improper.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/improper_ctypes/extern_crate_improper.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/improper_ctypes/extern_crate_improper.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/improper_ctypes/extern_crate_improper.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/improper_ctypes/extern_crate_improper.stderr similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/improper_ctypes/extern_crate_improper.stderr rename to tests/ui/rfcs/rfc-2008-non-exhaustive/improper_ctypes/extern_crate_improper.stderr diff --git a/tests/ui/rfc-2008-non-exhaustive/improper_ctypes/same_crate_proper.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/improper_ctypes/same_crate_proper.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/improper_ctypes/same_crate_proper.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/improper_ctypes/same_crate_proper.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/invalid-attribute.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/invalid-attribute.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/invalid-attribute.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/invalid-attribute.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/invalid-attribute.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/invalid-attribute.stderr similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/invalid-attribute.stderr rename to tests/ui/rfcs/rfc-2008-non-exhaustive/invalid-attribute.stderr diff --git a/tests/ui/rfc-2008-non-exhaustive/omitted-patterns.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/omitted-patterns.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/omitted-patterns.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/omitted-patterns.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/omitted-patterns.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/omitted-patterns.stderr similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/omitted-patterns.stderr rename to tests/ui/rfcs/rfc-2008-non-exhaustive/omitted-patterns.stderr diff --git a/tests/ui/rfc-2008-non-exhaustive/stable-omitted-patterns.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/stable-omitted-patterns.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/stable-omitted-patterns.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/stable-omitted-patterns.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/stable-omitted-patterns.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/stable-omitted-patterns.stderr similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/stable-omitted-patterns.stderr rename to tests/ui/rfcs/rfc-2008-non-exhaustive/stable-omitted-patterns.stderr diff --git a/tests/ui/rfc-2008-non-exhaustive/struct.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/struct.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/struct.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/struct.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/struct.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/struct.stderr similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/struct.stderr rename to tests/ui/rfcs/rfc-2008-non-exhaustive/struct.stderr diff --git a/tests/ui/rfc-2008-non-exhaustive/structs_same_crate.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/structs_same_crate.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/structs_same_crate.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/structs_same_crate.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/uninhabited/auxiliary/uninhabited.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/auxiliary/uninhabited.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/uninhabited/auxiliary/uninhabited.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/auxiliary/uninhabited.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/uninhabited/coercions.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/coercions.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/uninhabited/coercions.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/coercions.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/uninhabited/coercions.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/coercions.stderr similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/uninhabited/coercions.stderr rename to tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/coercions.stderr diff --git a/tests/ui/rfc-2008-non-exhaustive/uninhabited/coercions_same_crate.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/coercions_same_crate.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/uninhabited/coercions_same_crate.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/coercions_same_crate.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/uninhabited/coercions_same_crate.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/coercions_same_crate.stderr similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/uninhabited/coercions_same_crate.stderr rename to tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/coercions_same_crate.stderr diff --git a/tests/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/indirect_match.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/indirect_match.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/indirect_match.stderr similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match.stderr rename to tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/indirect_match.stderr diff --git a/tests/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_same_crate.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/indirect_match_same_crate.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_same_crate.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/indirect_match_same_crate.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_same_crate.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/indirect_match_same_crate.stderr similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_same_crate.stderr rename to tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/indirect_match_same_crate.stderr diff --git a/tests/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.stderr similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.stderr rename to tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns.stderr diff --git a/tests/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns_same_crate.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns_same_crate.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns_same_crate.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/indirect_match_with_exhaustive_patterns_same_crate.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/uninhabited/issue-65157-repeated-match-arm.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/issue-65157-repeated-match-arm.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/uninhabited/issue-65157-repeated-match-arm.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/issue-65157-repeated-match-arm.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/uninhabited/issue-65157-repeated-match-arm.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/issue-65157-repeated-match-arm.stderr similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/uninhabited/issue-65157-repeated-match-arm.stderr rename to tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/issue-65157-repeated-match-arm.stderr diff --git a/tests/ui/rfc-2008-non-exhaustive/uninhabited/match.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/uninhabited/match.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/uninhabited/match.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match.stderr similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/uninhabited/match.stderr rename to tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match.stderr diff --git a/tests/ui/rfc-2008-non-exhaustive/uninhabited/match_same_crate.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_same_crate.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/uninhabited/match_same_crate.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_same_crate.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/uninhabited/match_same_crate.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_same_crate.stderr similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/uninhabited/match_same_crate.stderr rename to tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_same_crate.stderr diff --git a/tests/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr rename to tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr diff --git a/tests/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns_same_crate.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns_same_crate.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns_same_crate.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns_same_crate.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/uninhabited/patterns.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/patterns.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/uninhabited/patterns.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/patterns.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/uninhabited/patterns_same_crate.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/patterns_same_crate.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/uninhabited/patterns_same_crate.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/patterns_same_crate.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/uninhabited/patterns_same_crate.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/patterns_same_crate.stderr similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/uninhabited/patterns_same_crate.stderr rename to tests/ui/rfcs/rfc-2008-non-exhaustive/uninhabited/patterns_same_crate.stderr diff --git a/tests/ui/rfc-2008-non-exhaustive/variant.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/variant.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/variant.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/variant.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/variant.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/variant.stderr similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/variant.stderr rename to tests/ui/rfcs/rfc-2008-non-exhaustive/variant.stderr diff --git a/tests/ui/rfc-2008-non-exhaustive/variants_fictive_visibility.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/variants_fictive_visibility.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/variants_fictive_visibility.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/variants_fictive_visibility.rs diff --git a/tests/ui/rfc-2008-non-exhaustive/variants_same_crate.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/variants_same_crate.rs similarity index 100% rename from tests/ui/rfc-2008-non-exhaustive/variants_same_crate.rs rename to tests/ui/rfcs/rfc-2008-non-exhaustive/variants_same_crate.rs diff --git a/tests/ui/rfc-2027-object-safe-for-dispatch/downcast-unsafe-trait-objects.rs b/tests/ui/rfcs/rfc-2027-object-safe-for-dispatch/downcast-unsafe-trait-objects.rs similarity index 100% rename from tests/ui/rfc-2027-object-safe-for-dispatch/downcast-unsafe-trait-objects.rs rename to tests/ui/rfcs/rfc-2027-object-safe-for-dispatch/downcast-unsafe-trait-objects.rs diff --git a/tests/ui/rfc-2027-object-safe-for-dispatch/manual-self-impl-for-unsafe-obj.rs b/tests/ui/rfcs/rfc-2027-object-safe-for-dispatch/manual-self-impl-for-unsafe-obj.rs similarity index 100% rename from tests/ui/rfc-2027-object-safe-for-dispatch/manual-self-impl-for-unsafe-obj.rs rename to tests/ui/rfcs/rfc-2027-object-safe-for-dispatch/manual-self-impl-for-unsafe-obj.rs diff --git a/tests/ui/rfc-2027-object-safe-for-dispatch/static-dispatch-unsafe-object.rs b/tests/ui/rfcs/rfc-2027-object-safe-for-dispatch/static-dispatch-unsafe-object.rs similarity index 100% rename from tests/ui/rfc-2027-object-safe-for-dispatch/static-dispatch-unsafe-object.rs rename to tests/ui/rfcs/rfc-2027-object-safe-for-dispatch/static-dispatch-unsafe-object.rs diff --git a/tests/ui/rfc-2091-track-caller/call-chain.rs b/tests/ui/rfcs/rfc-2091-track-caller/call-chain.rs similarity index 100% rename from tests/ui/rfc-2091-track-caller/call-chain.rs rename to tests/ui/rfcs/rfc-2091-track-caller/call-chain.rs diff --git a/tests/ui/rfc-2091-track-caller/caller-location-fnptr-rt-ctfe-equiv.rs b/tests/ui/rfcs/rfc-2091-track-caller/caller-location-fnptr-rt-ctfe-equiv.rs similarity index 100% rename from tests/ui/rfc-2091-track-caller/caller-location-fnptr-rt-ctfe-equiv.rs rename to tests/ui/rfcs/rfc-2091-track-caller/caller-location-fnptr-rt-ctfe-equiv.rs diff --git a/tests/ui/rfc-2091-track-caller/caller-location-fnptr-rt-ctfe-equiv.stderr b/tests/ui/rfcs/rfc-2091-track-caller/caller-location-fnptr-rt-ctfe-equiv.stderr similarity index 100% rename from tests/ui/rfc-2091-track-caller/caller-location-fnptr-rt-ctfe-equiv.stderr rename to tests/ui/rfcs/rfc-2091-track-caller/caller-location-fnptr-rt-ctfe-equiv.stderr diff --git a/tests/ui/rfc-2091-track-caller/caller-location-intrinsic.rs b/tests/ui/rfcs/rfc-2091-track-caller/caller-location-intrinsic.rs similarity index 100% rename from tests/ui/rfc-2091-track-caller/caller-location-intrinsic.rs rename to tests/ui/rfcs/rfc-2091-track-caller/caller-location-intrinsic.rs diff --git a/tests/ui/rfc-2091-track-caller/const-caller-location.rs b/tests/ui/rfcs/rfc-2091-track-caller/const-caller-location.rs similarity index 100% rename from tests/ui/rfc-2091-track-caller/const-caller-location.rs rename to tests/ui/rfcs/rfc-2091-track-caller/const-caller-location.rs diff --git a/tests/ui/rfc-2091-track-caller/diverging-caller-location.rs b/tests/ui/rfcs/rfc-2091-track-caller/diverging-caller-location.rs similarity index 100% rename from tests/ui/rfc-2091-track-caller/diverging-caller-location.rs rename to tests/ui/rfcs/rfc-2091-track-caller/diverging-caller-location.rs diff --git a/tests/ui/rfc-2091-track-caller/error-odd-syntax.rs b/tests/ui/rfcs/rfc-2091-track-caller/error-odd-syntax.rs similarity index 100% rename from tests/ui/rfc-2091-track-caller/error-odd-syntax.rs rename to tests/ui/rfcs/rfc-2091-track-caller/error-odd-syntax.rs diff --git a/tests/ui/rfc-2091-track-caller/error-odd-syntax.stderr b/tests/ui/rfcs/rfc-2091-track-caller/error-odd-syntax.stderr similarity index 100% rename from tests/ui/rfc-2091-track-caller/error-odd-syntax.stderr rename to tests/ui/rfcs/rfc-2091-track-caller/error-odd-syntax.stderr diff --git a/tests/ui/rfc-2091-track-caller/error-with-invalid-abi.rs b/tests/ui/rfcs/rfc-2091-track-caller/error-with-invalid-abi.rs similarity index 100% rename from tests/ui/rfc-2091-track-caller/error-with-invalid-abi.rs rename to tests/ui/rfcs/rfc-2091-track-caller/error-with-invalid-abi.rs diff --git a/tests/ui/rfc-2091-track-caller/error-with-invalid-abi.stderr b/tests/ui/rfcs/rfc-2091-track-caller/error-with-invalid-abi.stderr similarity index 100% rename from tests/ui/rfc-2091-track-caller/error-with-invalid-abi.stderr rename to tests/ui/rfcs/rfc-2091-track-caller/error-with-invalid-abi.stderr diff --git a/tests/ui/rfc-2091-track-caller/error-with-main.rs b/tests/ui/rfcs/rfc-2091-track-caller/error-with-main.rs similarity index 100% rename from tests/ui/rfc-2091-track-caller/error-with-main.rs rename to tests/ui/rfcs/rfc-2091-track-caller/error-with-main.rs diff --git a/tests/ui/rfc-2091-track-caller/error-with-main.stderr b/tests/ui/rfcs/rfc-2091-track-caller/error-with-main.stderr similarity index 100% rename from tests/ui/rfc-2091-track-caller/error-with-main.stderr rename to tests/ui/rfcs/rfc-2091-track-caller/error-with-main.stderr diff --git a/tests/ui/rfc-2091-track-caller/error-with-naked.rs b/tests/ui/rfcs/rfc-2091-track-caller/error-with-naked.rs similarity index 100% rename from tests/ui/rfc-2091-track-caller/error-with-naked.rs rename to tests/ui/rfcs/rfc-2091-track-caller/error-with-naked.rs diff --git a/tests/ui/rfc-2091-track-caller/error-with-naked.stderr b/tests/ui/rfcs/rfc-2091-track-caller/error-with-naked.stderr similarity index 100% rename from tests/ui/rfc-2091-track-caller/error-with-naked.stderr rename to tests/ui/rfcs/rfc-2091-track-caller/error-with-naked.stderr diff --git a/tests/ui/rfc-2091-track-caller/error-with-start.rs b/tests/ui/rfcs/rfc-2091-track-caller/error-with-start.rs similarity index 100% rename from tests/ui/rfc-2091-track-caller/error-with-start.rs rename to tests/ui/rfcs/rfc-2091-track-caller/error-with-start.rs diff --git a/tests/ui/rfc-2091-track-caller/error-with-start.stderr b/tests/ui/rfcs/rfc-2091-track-caller/error-with-start.stderr similarity index 100% rename from tests/ui/rfc-2091-track-caller/error-with-start.stderr rename to tests/ui/rfcs/rfc-2091-track-caller/error-with-start.stderr diff --git a/tests/ui/rfc-2091-track-caller/intrinsic-wrapper.rs b/tests/ui/rfcs/rfc-2091-track-caller/intrinsic-wrapper.rs similarity index 100% rename from tests/ui/rfc-2091-track-caller/intrinsic-wrapper.rs rename to tests/ui/rfcs/rfc-2091-track-caller/intrinsic-wrapper.rs diff --git a/tests/ui/rfc-2091-track-caller/macro-declaration.rs b/tests/ui/rfcs/rfc-2091-track-caller/macro-declaration.rs similarity index 100% rename from tests/ui/rfc-2091-track-caller/macro-declaration.rs rename to tests/ui/rfcs/rfc-2091-track-caller/macro-declaration.rs diff --git a/tests/ui/rfc-2091-track-caller/mir-inlined-macro.rs b/tests/ui/rfcs/rfc-2091-track-caller/mir-inlined-macro.rs similarity index 100% rename from tests/ui/rfc-2091-track-caller/mir-inlined-macro.rs rename to tests/ui/rfcs/rfc-2091-track-caller/mir-inlined-macro.rs diff --git a/tests/ui/rfc-2091-track-caller/only-for-fns.rs b/tests/ui/rfcs/rfc-2091-track-caller/only-for-fns.rs similarity index 100% rename from tests/ui/rfc-2091-track-caller/only-for-fns.rs rename to tests/ui/rfcs/rfc-2091-track-caller/only-for-fns.rs diff --git a/tests/ui/rfc-2091-track-caller/only-for-fns.stderr b/tests/ui/rfcs/rfc-2091-track-caller/only-for-fns.stderr similarity index 100% rename from tests/ui/rfc-2091-track-caller/only-for-fns.stderr rename to tests/ui/rfcs/rfc-2091-track-caller/only-for-fns.stderr diff --git a/tests/ui/rfc-2091-track-caller/pass.rs b/tests/ui/rfcs/rfc-2091-track-caller/pass.rs similarity index 100% rename from tests/ui/rfc-2091-track-caller/pass.rs rename to tests/ui/rfcs/rfc-2091-track-caller/pass.rs diff --git a/tests/ui/rfc-2091-track-caller/std-panic-locations.rs b/tests/ui/rfcs/rfc-2091-track-caller/std-panic-locations.rs similarity index 100% rename from tests/ui/rfc-2091-track-caller/std-panic-locations.rs rename to tests/ui/rfcs/rfc-2091-track-caller/std-panic-locations.rs diff --git a/tests/ui/rfc-2091-track-caller/track-caller-attribute.rs b/tests/ui/rfcs/rfc-2091-track-caller/track-caller-attribute.rs similarity index 100% rename from tests/ui/rfc-2091-track-caller/track-caller-attribute.rs rename to tests/ui/rfcs/rfc-2091-track-caller/track-caller-attribute.rs diff --git a/tests/ui/rfc-2091-track-caller/track-caller-ffi.rs b/tests/ui/rfcs/rfc-2091-track-caller/track-caller-ffi.rs similarity index 100% rename from tests/ui/rfc-2091-track-caller/track-caller-ffi.rs rename to tests/ui/rfcs/rfc-2091-track-caller/track-caller-ffi.rs diff --git a/tests/ui/rfc-2091-track-caller/tracked-closure.rs b/tests/ui/rfcs/rfc-2091-track-caller/tracked-closure.rs similarity index 100% rename from tests/ui/rfc-2091-track-caller/tracked-closure.rs rename to tests/ui/rfcs/rfc-2091-track-caller/tracked-closure.rs diff --git a/tests/ui/rfc-2091-track-caller/tracked-fn-ptr-with-arg.rs b/tests/ui/rfcs/rfc-2091-track-caller/tracked-fn-ptr-with-arg.rs similarity index 100% rename from tests/ui/rfc-2091-track-caller/tracked-fn-ptr-with-arg.rs rename to tests/ui/rfcs/rfc-2091-track-caller/tracked-fn-ptr-with-arg.rs diff --git a/tests/ui/rfc-2091-track-caller/tracked-fn-ptr.rs b/tests/ui/rfcs/rfc-2091-track-caller/tracked-fn-ptr.rs similarity index 100% rename from tests/ui/rfc-2091-track-caller/tracked-fn-ptr.rs rename to tests/ui/rfcs/rfc-2091-track-caller/tracked-fn-ptr.rs diff --git a/tests/ui/rfc-2091-track-caller/tracked-trait-impls.rs b/tests/ui/rfcs/rfc-2091-track-caller/tracked-trait-impls.rs similarity index 100% rename from tests/ui/rfc-2091-track-caller/tracked-trait-impls.rs rename to tests/ui/rfcs/rfc-2091-track-caller/tracked-trait-impls.rs diff --git a/tests/ui/rfc-2091-track-caller/tracked-trait-obj.rs b/tests/ui/rfcs/rfc-2091-track-caller/tracked-trait-obj.rs similarity index 100% rename from tests/ui/rfc-2091-track-caller/tracked-trait-obj.rs rename to tests/ui/rfcs/rfc-2091-track-caller/tracked-trait-obj.rs diff --git a/tests/ui/rfc-2093-infer-outlives/cross-crate.rs b/tests/ui/rfcs/rfc-2093-infer-outlives/cross-crate.rs similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/cross-crate.rs rename to tests/ui/rfcs/rfc-2093-infer-outlives/cross-crate.rs diff --git a/tests/ui/rfc-2093-infer-outlives/cross-crate.stderr b/tests/ui/rfcs/rfc-2093-infer-outlives/cross-crate.stderr similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/cross-crate.stderr rename to tests/ui/rfcs/rfc-2093-infer-outlives/cross-crate.stderr diff --git a/tests/ui/rfc-2093-infer-outlives/dont-infer-static.rs b/tests/ui/rfcs/rfc-2093-infer-outlives/dont-infer-static.rs similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/dont-infer-static.rs rename to tests/ui/rfcs/rfc-2093-infer-outlives/dont-infer-static.rs diff --git a/tests/ui/rfc-2093-infer-outlives/dont-infer-static.stderr b/tests/ui/rfcs/rfc-2093-infer-outlives/dont-infer-static.stderr similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/dont-infer-static.stderr rename to tests/ui/rfcs/rfc-2093-infer-outlives/dont-infer-static.stderr diff --git a/tests/ui/rfc-2093-infer-outlives/enum.rs b/tests/ui/rfcs/rfc-2093-infer-outlives/enum.rs similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/enum.rs rename to tests/ui/rfcs/rfc-2093-infer-outlives/enum.rs diff --git a/tests/ui/rfc-2093-infer-outlives/enum.stderr b/tests/ui/rfcs/rfc-2093-infer-outlives/enum.stderr similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/enum.stderr rename to tests/ui/rfcs/rfc-2093-infer-outlives/enum.stderr diff --git a/tests/ui/rfc-2093-infer-outlives/explicit-dyn.rs b/tests/ui/rfcs/rfc-2093-infer-outlives/explicit-dyn.rs similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/explicit-dyn.rs rename to tests/ui/rfcs/rfc-2093-infer-outlives/explicit-dyn.rs diff --git a/tests/ui/rfc-2093-infer-outlives/explicit-dyn.stderr b/tests/ui/rfcs/rfc-2093-infer-outlives/explicit-dyn.stderr similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/explicit-dyn.stderr rename to tests/ui/rfcs/rfc-2093-infer-outlives/explicit-dyn.stderr diff --git a/tests/ui/rfc-2093-infer-outlives/explicit-enum.rs b/tests/ui/rfcs/rfc-2093-infer-outlives/explicit-enum.rs similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/explicit-enum.rs rename to tests/ui/rfcs/rfc-2093-infer-outlives/explicit-enum.rs diff --git a/tests/ui/rfc-2093-infer-outlives/explicit-enum.stderr b/tests/ui/rfcs/rfc-2093-infer-outlives/explicit-enum.stderr similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/explicit-enum.stderr rename to tests/ui/rfcs/rfc-2093-infer-outlives/explicit-enum.stderr diff --git a/tests/ui/rfc-2093-infer-outlives/explicit-projection.rs b/tests/ui/rfcs/rfc-2093-infer-outlives/explicit-projection.rs similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/explicit-projection.rs rename to tests/ui/rfcs/rfc-2093-infer-outlives/explicit-projection.rs diff --git a/tests/ui/rfc-2093-infer-outlives/explicit-projection.stderr b/tests/ui/rfcs/rfc-2093-infer-outlives/explicit-projection.stderr similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/explicit-projection.stderr rename to tests/ui/rfcs/rfc-2093-infer-outlives/explicit-projection.stderr diff --git a/tests/ui/rfc-2093-infer-outlives/explicit-struct.rs b/tests/ui/rfcs/rfc-2093-infer-outlives/explicit-struct.rs similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/explicit-struct.rs rename to tests/ui/rfcs/rfc-2093-infer-outlives/explicit-struct.rs diff --git a/tests/ui/rfc-2093-infer-outlives/explicit-struct.stderr b/tests/ui/rfcs/rfc-2093-infer-outlives/explicit-struct.stderr similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/explicit-struct.stderr rename to tests/ui/rfcs/rfc-2093-infer-outlives/explicit-struct.stderr diff --git a/tests/ui/rfc-2093-infer-outlives/explicit-union.rs b/tests/ui/rfcs/rfc-2093-infer-outlives/explicit-union.rs similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/explicit-union.rs rename to tests/ui/rfcs/rfc-2093-infer-outlives/explicit-union.rs diff --git a/tests/ui/rfc-2093-infer-outlives/explicit-union.stderr b/tests/ui/rfcs/rfc-2093-infer-outlives/explicit-union.stderr similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/explicit-union.stderr rename to tests/ui/rfcs/rfc-2093-infer-outlives/explicit-union.stderr diff --git a/tests/ui/rfc-2093-infer-outlives/issue-54467.rs b/tests/ui/rfcs/rfc-2093-infer-outlives/issue-54467.rs similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/issue-54467.rs rename to tests/ui/rfcs/rfc-2093-infer-outlives/issue-54467.rs diff --git a/tests/ui/rfc-2093-infer-outlives/nested-enum.rs b/tests/ui/rfcs/rfc-2093-infer-outlives/nested-enum.rs similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/nested-enum.rs rename to tests/ui/rfcs/rfc-2093-infer-outlives/nested-enum.rs diff --git a/tests/ui/rfc-2093-infer-outlives/nested-enum.stderr b/tests/ui/rfcs/rfc-2093-infer-outlives/nested-enum.stderr similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/nested-enum.stderr rename to tests/ui/rfcs/rfc-2093-infer-outlives/nested-enum.stderr diff --git a/tests/ui/rfc-2093-infer-outlives/nested-regions.rs b/tests/ui/rfcs/rfc-2093-infer-outlives/nested-regions.rs similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/nested-regions.rs rename to tests/ui/rfcs/rfc-2093-infer-outlives/nested-regions.rs diff --git a/tests/ui/rfc-2093-infer-outlives/nested-regions.stderr b/tests/ui/rfcs/rfc-2093-infer-outlives/nested-regions.stderr similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/nested-regions.stderr rename to tests/ui/rfcs/rfc-2093-infer-outlives/nested-regions.stderr diff --git a/tests/ui/rfc-2093-infer-outlives/nested-structs.rs b/tests/ui/rfcs/rfc-2093-infer-outlives/nested-structs.rs similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/nested-structs.rs rename to tests/ui/rfcs/rfc-2093-infer-outlives/nested-structs.rs diff --git a/tests/ui/rfc-2093-infer-outlives/nested-structs.stderr b/tests/ui/rfcs/rfc-2093-infer-outlives/nested-structs.stderr similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/nested-structs.stderr rename to tests/ui/rfcs/rfc-2093-infer-outlives/nested-structs.stderr diff --git a/tests/ui/rfc-2093-infer-outlives/nested-union.rs b/tests/ui/rfcs/rfc-2093-infer-outlives/nested-union.rs similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/nested-union.rs rename to tests/ui/rfcs/rfc-2093-infer-outlives/nested-union.rs diff --git a/tests/ui/rfc-2093-infer-outlives/nested-union.stderr b/tests/ui/rfcs/rfc-2093-infer-outlives/nested-union.stderr similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/nested-union.stderr rename to tests/ui/rfcs/rfc-2093-infer-outlives/nested-union.stderr diff --git a/tests/ui/rfc-2093-infer-outlives/privacy.rs b/tests/ui/rfcs/rfc-2093-infer-outlives/privacy.rs similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/privacy.rs rename to tests/ui/rfcs/rfc-2093-infer-outlives/privacy.rs diff --git a/tests/ui/rfc-2093-infer-outlives/projection.rs b/tests/ui/rfcs/rfc-2093-infer-outlives/projection.rs similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/projection.rs rename to tests/ui/rfcs/rfc-2093-infer-outlives/projection.rs diff --git a/tests/ui/rfc-2093-infer-outlives/projection.stderr b/tests/ui/rfcs/rfc-2093-infer-outlives/projection.stderr similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/projection.stderr rename to tests/ui/rfcs/rfc-2093-infer-outlives/projection.stderr diff --git a/tests/ui/rfc-2093-infer-outlives/reference.rs b/tests/ui/rfcs/rfc-2093-infer-outlives/reference.rs similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/reference.rs rename to tests/ui/rfcs/rfc-2093-infer-outlives/reference.rs diff --git a/tests/ui/rfc-2093-infer-outlives/reference.stderr b/tests/ui/rfcs/rfc-2093-infer-outlives/reference.stderr similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/reference.stderr rename to tests/ui/rfcs/rfc-2093-infer-outlives/reference.stderr diff --git a/tests/ui/rfc-2093-infer-outlives/regions-enum-not-wf.rs b/tests/ui/rfcs/rfc-2093-infer-outlives/regions-enum-not-wf.rs similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/regions-enum-not-wf.rs rename to tests/ui/rfcs/rfc-2093-infer-outlives/regions-enum-not-wf.rs diff --git a/tests/ui/rfc-2093-infer-outlives/regions-enum-not-wf.stderr b/tests/ui/rfcs/rfc-2093-infer-outlives/regions-enum-not-wf.stderr similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/regions-enum-not-wf.stderr rename to tests/ui/rfcs/rfc-2093-infer-outlives/regions-enum-not-wf.stderr diff --git a/tests/ui/rfc-2093-infer-outlives/regions-outlives-nominal-type-region-rev.rs b/tests/ui/rfcs/rfc-2093-infer-outlives/regions-outlives-nominal-type-region-rev.rs similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/regions-outlives-nominal-type-region-rev.rs rename to tests/ui/rfcs/rfc-2093-infer-outlives/regions-outlives-nominal-type-region-rev.rs diff --git a/tests/ui/rfc-2093-infer-outlives/regions-outlives-nominal-type-region-rev.stderr b/tests/ui/rfcs/rfc-2093-infer-outlives/regions-outlives-nominal-type-region-rev.stderr similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/regions-outlives-nominal-type-region-rev.stderr rename to tests/ui/rfcs/rfc-2093-infer-outlives/regions-outlives-nominal-type-region-rev.stderr diff --git a/tests/ui/rfc-2093-infer-outlives/regions-outlives-nominal-type-region.rs b/tests/ui/rfcs/rfc-2093-infer-outlives/regions-outlives-nominal-type-region.rs similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/regions-outlives-nominal-type-region.rs rename to tests/ui/rfcs/rfc-2093-infer-outlives/regions-outlives-nominal-type-region.rs diff --git a/tests/ui/rfc-2093-infer-outlives/regions-outlives-nominal-type-region.stderr b/tests/ui/rfcs/rfc-2093-infer-outlives/regions-outlives-nominal-type-region.stderr similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/regions-outlives-nominal-type-region.stderr rename to tests/ui/rfcs/rfc-2093-infer-outlives/regions-outlives-nominal-type-region.stderr diff --git a/tests/ui/rfc-2093-infer-outlives/regions-outlives-nominal-type-type-rev.rs b/tests/ui/rfcs/rfc-2093-infer-outlives/regions-outlives-nominal-type-type-rev.rs similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/regions-outlives-nominal-type-type-rev.rs rename to tests/ui/rfcs/rfc-2093-infer-outlives/regions-outlives-nominal-type-type-rev.rs diff --git a/tests/ui/rfc-2093-infer-outlives/regions-outlives-nominal-type-type-rev.stderr b/tests/ui/rfcs/rfc-2093-infer-outlives/regions-outlives-nominal-type-type-rev.stderr similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/regions-outlives-nominal-type-type-rev.stderr rename to tests/ui/rfcs/rfc-2093-infer-outlives/regions-outlives-nominal-type-type-rev.stderr diff --git a/tests/ui/rfc-2093-infer-outlives/regions-outlives-nominal-type-type.rs b/tests/ui/rfcs/rfc-2093-infer-outlives/regions-outlives-nominal-type-type.rs similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/regions-outlives-nominal-type-type.rs rename to tests/ui/rfcs/rfc-2093-infer-outlives/regions-outlives-nominal-type-type.rs diff --git a/tests/ui/rfc-2093-infer-outlives/regions-outlives-nominal-type-type.stderr b/tests/ui/rfcs/rfc-2093-infer-outlives/regions-outlives-nominal-type-type.stderr similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/regions-outlives-nominal-type-type.stderr rename to tests/ui/rfcs/rfc-2093-infer-outlives/regions-outlives-nominal-type-type.stderr diff --git a/tests/ui/rfc-2093-infer-outlives/regions-struct-not-wf.rs b/tests/ui/rfcs/rfc-2093-infer-outlives/regions-struct-not-wf.rs similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/regions-struct-not-wf.rs rename to tests/ui/rfcs/rfc-2093-infer-outlives/regions-struct-not-wf.rs diff --git a/tests/ui/rfc-2093-infer-outlives/regions-struct-not-wf.stderr b/tests/ui/rfcs/rfc-2093-infer-outlives/regions-struct-not-wf.stderr similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/regions-struct-not-wf.stderr rename to tests/ui/rfcs/rfc-2093-infer-outlives/regions-struct-not-wf.stderr diff --git a/tests/ui/rfc-2093-infer-outlives/self-dyn.rs b/tests/ui/rfcs/rfc-2093-infer-outlives/self-dyn.rs similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/self-dyn.rs rename to tests/ui/rfcs/rfc-2093-infer-outlives/self-dyn.rs diff --git a/tests/ui/rfc-2093-infer-outlives/self-dyn.stderr b/tests/ui/rfcs/rfc-2093-infer-outlives/self-dyn.stderr similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/self-dyn.stderr rename to tests/ui/rfcs/rfc-2093-infer-outlives/self-dyn.stderr diff --git a/tests/ui/rfc-2093-infer-outlives/self-structs.rs b/tests/ui/rfcs/rfc-2093-infer-outlives/self-structs.rs similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/self-structs.rs rename to tests/ui/rfcs/rfc-2093-infer-outlives/self-structs.rs diff --git a/tests/ui/rfc-2093-infer-outlives/self-structs.stderr b/tests/ui/rfcs/rfc-2093-infer-outlives/self-structs.stderr similarity index 100% rename from tests/ui/rfc-2093-infer-outlives/self-structs.stderr rename to tests/ui/rfcs/rfc-2093-infer-outlives/self-structs.stderr diff --git a/tests/ui/rfc-2126-crate-paths/crate-path-non-absolute.rs b/tests/ui/rfcs/rfc-2126-crate-paths/crate-path-non-absolute.rs similarity index 100% rename from tests/ui/rfc-2126-crate-paths/crate-path-non-absolute.rs rename to tests/ui/rfcs/rfc-2126-crate-paths/crate-path-non-absolute.rs diff --git a/tests/ui/rfc-2126-crate-paths/crate-path-non-absolute.stderr b/tests/ui/rfcs/rfc-2126-crate-paths/crate-path-non-absolute.stderr similarity index 100% rename from tests/ui/rfc-2126-crate-paths/crate-path-non-absolute.stderr rename to tests/ui/rfcs/rfc-2126-crate-paths/crate-path-non-absolute.stderr diff --git a/tests/ui/rfc-2126-crate-paths/keyword-crate-as-identifier.rs b/tests/ui/rfcs/rfc-2126-crate-paths/keyword-crate-as-identifier.rs similarity index 100% rename from tests/ui/rfc-2126-crate-paths/keyword-crate-as-identifier.rs rename to tests/ui/rfcs/rfc-2126-crate-paths/keyword-crate-as-identifier.rs diff --git a/tests/ui/rfc-2126-crate-paths/keyword-crate-as-identifier.stderr b/tests/ui/rfcs/rfc-2126-crate-paths/keyword-crate-as-identifier.stderr similarity index 100% rename from tests/ui/rfc-2126-crate-paths/keyword-crate-as-identifier.stderr rename to tests/ui/rfcs/rfc-2126-crate-paths/keyword-crate-as-identifier.stderr diff --git a/tests/ui/rfc-2126-extern-absolute-paths/auxiliary/xcrate.rs b/tests/ui/rfcs/rfc-2126-extern-absolute-paths/auxiliary/xcrate.rs similarity index 100% rename from tests/ui/rfc-2126-extern-absolute-paths/auxiliary/xcrate.rs rename to tests/ui/rfcs/rfc-2126-extern-absolute-paths/auxiliary/xcrate.rs diff --git a/tests/ui/rfc-2126-extern-absolute-paths/non-existent-1.rs b/tests/ui/rfcs/rfc-2126-extern-absolute-paths/non-existent-1.rs similarity index 100% rename from tests/ui/rfc-2126-extern-absolute-paths/non-existent-1.rs rename to tests/ui/rfcs/rfc-2126-extern-absolute-paths/non-existent-1.rs diff --git a/tests/ui/rfc-2126-extern-absolute-paths/non-existent-1.stderr b/tests/ui/rfcs/rfc-2126-extern-absolute-paths/non-existent-1.stderr similarity index 100% rename from tests/ui/rfc-2126-extern-absolute-paths/non-existent-1.stderr rename to tests/ui/rfcs/rfc-2126-extern-absolute-paths/non-existent-1.stderr diff --git a/tests/ui/rfc-2126-extern-absolute-paths/non-existent-2.rs b/tests/ui/rfcs/rfc-2126-extern-absolute-paths/non-existent-2.rs similarity index 100% rename from tests/ui/rfc-2126-extern-absolute-paths/non-existent-2.rs rename to tests/ui/rfcs/rfc-2126-extern-absolute-paths/non-existent-2.rs diff --git a/tests/ui/rfc-2126-extern-absolute-paths/non-existent-2.stderr b/tests/ui/rfcs/rfc-2126-extern-absolute-paths/non-existent-2.stderr similarity index 100% rename from tests/ui/rfc-2126-extern-absolute-paths/non-existent-2.stderr rename to tests/ui/rfcs/rfc-2126-extern-absolute-paths/non-existent-2.stderr diff --git a/tests/ui/rfc-2126-extern-absolute-paths/non-existent-3.rs b/tests/ui/rfcs/rfc-2126-extern-absolute-paths/non-existent-3.rs similarity index 100% rename from tests/ui/rfc-2126-extern-absolute-paths/non-existent-3.rs rename to tests/ui/rfcs/rfc-2126-extern-absolute-paths/non-existent-3.rs diff --git a/tests/ui/rfc-2126-extern-absolute-paths/non-existent-3.stderr b/tests/ui/rfcs/rfc-2126-extern-absolute-paths/non-existent-3.stderr similarity index 100% rename from tests/ui/rfc-2126-extern-absolute-paths/non-existent-3.stderr rename to tests/ui/rfcs/rfc-2126-extern-absolute-paths/non-existent-3.stderr diff --git a/tests/ui/rfc-2126-extern-absolute-paths/not-allowed.rs b/tests/ui/rfcs/rfc-2126-extern-absolute-paths/not-allowed.rs similarity index 100% rename from tests/ui/rfc-2126-extern-absolute-paths/not-allowed.rs rename to tests/ui/rfcs/rfc-2126-extern-absolute-paths/not-allowed.rs diff --git a/tests/ui/rfc-2126-extern-absolute-paths/not-allowed.stderr b/tests/ui/rfcs/rfc-2126-extern-absolute-paths/not-allowed.stderr similarity index 100% rename from tests/ui/rfc-2126-extern-absolute-paths/not-allowed.stderr rename to tests/ui/rfcs/rfc-2126-extern-absolute-paths/not-allowed.stderr diff --git a/tests/ui/rfc-2126-extern-absolute-paths/single-segment.rs b/tests/ui/rfcs/rfc-2126-extern-absolute-paths/single-segment.rs similarity index 100% rename from tests/ui/rfc-2126-extern-absolute-paths/single-segment.rs rename to tests/ui/rfcs/rfc-2126-extern-absolute-paths/single-segment.rs diff --git a/tests/ui/rfc-2126-extern-absolute-paths/single-segment.stderr b/tests/ui/rfcs/rfc-2126-extern-absolute-paths/single-segment.stderr similarity index 100% rename from tests/ui/rfc-2126-extern-absolute-paths/single-segment.stderr rename to tests/ui/rfcs/rfc-2126-extern-absolute-paths/single-segment.stderr diff --git a/tests/ui/rfc-2294-if-let-guard/bindings.rs b/tests/ui/rfcs/rfc-2294-if-let-guard/bindings.rs similarity index 100% rename from tests/ui/rfc-2294-if-let-guard/bindings.rs rename to tests/ui/rfcs/rfc-2294-if-let-guard/bindings.rs diff --git a/tests/ui/rfc-2294-if-let-guard/bindings.stderr b/tests/ui/rfcs/rfc-2294-if-let-guard/bindings.stderr similarity index 100% rename from tests/ui/rfc-2294-if-let-guard/bindings.stderr rename to tests/ui/rfcs/rfc-2294-if-let-guard/bindings.stderr diff --git a/tests/ui/rfc-2294-if-let-guard/feature-gate.rs b/tests/ui/rfcs/rfc-2294-if-let-guard/feature-gate.rs similarity index 100% rename from tests/ui/rfc-2294-if-let-guard/feature-gate.rs rename to tests/ui/rfcs/rfc-2294-if-let-guard/feature-gate.rs diff --git a/tests/ui/rfc-2294-if-let-guard/feature-gate.stderr b/tests/ui/rfcs/rfc-2294-if-let-guard/feature-gate.stderr similarity index 100% rename from tests/ui/rfc-2294-if-let-guard/feature-gate.stderr rename to tests/ui/rfcs/rfc-2294-if-let-guard/feature-gate.stderr diff --git a/tests/ui/rfc-2294-if-let-guard/run-pass.rs b/tests/ui/rfcs/rfc-2294-if-let-guard/run-pass.rs similarity index 100% rename from tests/ui/rfc-2294-if-let-guard/run-pass.rs rename to tests/ui/rfcs/rfc-2294-if-let-guard/run-pass.rs diff --git a/tests/ui/rfc-2294-if-let-guard/typeck.rs b/tests/ui/rfcs/rfc-2294-if-let-guard/typeck.rs similarity index 100% rename from tests/ui/rfc-2294-if-let-guard/typeck.rs rename to tests/ui/rfcs/rfc-2294-if-let-guard/typeck.rs diff --git a/tests/ui/rfc-2294-if-let-guard/typeck.stderr b/tests/ui/rfcs/rfc-2294-if-let-guard/typeck.stderr similarity index 100% rename from tests/ui/rfc-2294-if-let-guard/typeck.stderr rename to tests/ui/rfcs/rfc-2294-if-let-guard/typeck.stderr diff --git a/tests/ui/rfc-2294-if-let-guard/warns.rs b/tests/ui/rfcs/rfc-2294-if-let-guard/warns.rs similarity index 100% rename from tests/ui/rfc-2294-if-let-guard/warns.rs rename to tests/ui/rfcs/rfc-2294-if-let-guard/warns.rs diff --git a/tests/ui/rfc-2294-if-let-guard/warns.stderr b/tests/ui/rfcs/rfc-2294-if-let-guard/warns.stderr similarity index 100% rename from tests/ui/rfc-2294-if-let-guard/warns.stderr rename to tests/ui/rfcs/rfc-2294-if-let-guard/warns.stderr diff --git a/tests/ui/rfcs/rfc-2302-self-struct-ctor.rs b/tests/ui/rfcs/rfc-2302-self-struct-ctor/rfc-2302-self-struct-ctor.rs similarity index 100% rename from tests/ui/rfcs/rfc-2302-self-struct-ctor.rs rename to tests/ui/rfcs/rfc-2302-self-struct-ctor/rfc-2302-self-struct-ctor.rs diff --git a/tests/ui/rfc-2306/convert-id-const-with-gate.rs b/tests/ui/rfcs/rfc-2306-convert-id/convert-id-const-with-gate.rs similarity index 100% rename from tests/ui/rfc-2306/convert-id-const-with-gate.rs rename to tests/ui/rfcs/rfc-2306-convert-id/convert-id-const-with-gate.rs diff --git a/tests/ui/rfc-2361-dbg-macro/dbg-macro-expected-behavior.rs b/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-expected-behavior.rs similarity index 100% rename from tests/ui/rfc-2361-dbg-macro/dbg-macro-expected-behavior.rs rename to tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-expected-behavior.rs diff --git a/tests/ui/rfc-2361-dbg-macro/dbg-macro-expected-behavior.run.stderr b/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-expected-behavior.run.stderr similarity index 100% rename from tests/ui/rfc-2361-dbg-macro/dbg-macro-expected-behavior.run.stderr rename to tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-expected-behavior.run.stderr diff --git a/tests/ui/rfc-2361-dbg-macro/dbg-macro-move-semantics.rs b/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-move-semantics.rs similarity index 100% rename from tests/ui/rfc-2361-dbg-macro/dbg-macro-move-semantics.rs rename to tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-move-semantics.rs diff --git a/tests/ui/rfc-2361-dbg-macro/dbg-macro-move-semantics.stderr b/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-move-semantics.stderr similarity index 100% rename from tests/ui/rfc-2361-dbg-macro/dbg-macro-move-semantics.stderr rename to tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-move-semantics.stderr diff --git a/tests/ui/rfc-2361-dbg-macro/dbg-macro-requires-debug.rs b/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-requires-debug.rs similarity index 100% rename from tests/ui/rfc-2361-dbg-macro/dbg-macro-requires-debug.rs rename to tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-requires-debug.rs diff --git a/tests/ui/rfc-2361-dbg-macro/dbg-macro-requires-debug.stderr b/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-requires-debug.stderr similarity index 100% rename from tests/ui/rfc-2361-dbg-macro/dbg-macro-requires-debug.stderr rename to tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-requires-debug.stderr diff --git a/tests/ui/rfc-2397-do-not-recommend/feature-gate-do_not_recommend.rs b/tests/ui/rfcs/rfc-2397-do-not-recommend/feature-gate-do_not_recommend.rs similarity index 100% rename from tests/ui/rfc-2397-do-not-recommend/feature-gate-do_not_recommend.rs rename to tests/ui/rfcs/rfc-2397-do-not-recommend/feature-gate-do_not_recommend.rs diff --git a/tests/ui/rfc-2397-do-not-recommend/feature-gate-do_not_recommend.stderr b/tests/ui/rfcs/rfc-2397-do-not-recommend/feature-gate-do_not_recommend.stderr similarity index 100% rename from tests/ui/rfc-2397-do-not-recommend/feature-gate-do_not_recommend.stderr rename to tests/ui/rfcs/rfc-2397-do-not-recommend/feature-gate-do_not_recommend.stderr diff --git a/tests/ui/rfc-2397-do-not-recommend/incorrect-locations.rs b/tests/ui/rfcs/rfc-2397-do-not-recommend/incorrect-locations.rs similarity index 100% rename from tests/ui/rfc-2397-do-not-recommend/incorrect-locations.rs rename to tests/ui/rfcs/rfc-2397-do-not-recommend/incorrect-locations.rs diff --git a/tests/ui/rfc-2397-do-not-recommend/incorrect-locations.stderr b/tests/ui/rfcs/rfc-2397-do-not-recommend/incorrect-locations.stderr similarity index 100% rename from tests/ui/rfc-2397-do-not-recommend/incorrect-locations.stderr rename to tests/ui/rfcs/rfc-2397-do-not-recommend/incorrect-locations.stderr diff --git a/tests/ui/rfc-2397-do-not-recommend/unstable-feature.rs b/tests/ui/rfcs/rfc-2397-do-not-recommend/unstable-feature.rs similarity index 100% rename from tests/ui/rfc-2397-do-not-recommend/unstable-feature.rs rename to tests/ui/rfcs/rfc-2397-do-not-recommend/unstable-feature.rs diff --git a/tests/ui/rfc-2397-do-not-recommend/unstable-feature.stderr b/tests/ui/rfcs/rfc-2397-do-not-recommend/unstable-feature.stderr similarity index 100% rename from tests/ui/rfc-2397-do-not-recommend/unstable-feature.stderr rename to tests/ui/rfcs/rfc-2397-do-not-recommend/unstable-feature.stderr diff --git a/tests/ui/rfcs/rfc-2421-unreserve-pure-offsetof-sizeof-alignof.rs b/tests/ui/rfcs/rfc-2421-unreserve-pure-offsetof-sizeof-alignof/offsetof-alignof-sizeof-pure-can-be-used-as-idents.rs similarity index 100% rename from tests/ui/rfcs/rfc-2421-unreserve-pure-offsetof-sizeof-alignof.rs rename to tests/ui/rfcs/rfc-2421-unreserve-pure-offsetof-sizeof-alignof/offsetof-alignof-sizeof-pure-can-be-used-as-idents.rs diff --git a/tests/ui/rfc-2457/auxiliary/mod_file_nonascii_with_path_allowed-aux.rs b/tests/ui/rfcs/rfc-2457-non-ascii-idents/auxiliary/mod_file_nonascii_with_path_allowed-aux.rs similarity index 100% rename from tests/ui/rfc-2457/auxiliary/mod_file_nonascii_with_path_allowed-aux.rs rename to tests/ui/rfcs/rfc-2457-non-ascii-idents/auxiliary/mod_file_nonascii_with_path_allowed-aux.rs diff --git a/tests/ui/rfc-2457/crate_name_nonascii_forbidden-1.rs b/tests/ui/rfcs/rfc-2457-non-ascii-idents/crate_name_nonascii_forbidden-1.rs similarity index 100% rename from tests/ui/rfc-2457/crate_name_nonascii_forbidden-1.rs rename to tests/ui/rfcs/rfc-2457-non-ascii-idents/crate_name_nonascii_forbidden-1.rs diff --git a/tests/ui/rfc-2457/crate_name_nonascii_forbidden-1.stderr b/tests/ui/rfcs/rfc-2457-non-ascii-idents/crate_name_nonascii_forbidden-1.stderr similarity index 100% rename from tests/ui/rfc-2457/crate_name_nonascii_forbidden-1.stderr rename to tests/ui/rfcs/rfc-2457-non-ascii-idents/crate_name_nonascii_forbidden-1.stderr diff --git a/tests/ui/rfc-2457/crate_name_nonascii_forbidden-2.rs b/tests/ui/rfcs/rfc-2457-non-ascii-idents/crate_name_nonascii_forbidden-2.rs similarity index 100% rename from tests/ui/rfc-2457/crate_name_nonascii_forbidden-2.rs rename to tests/ui/rfcs/rfc-2457-non-ascii-idents/crate_name_nonascii_forbidden-2.rs diff --git a/tests/ui/rfc-2457/crate_name_nonascii_forbidden-2.stderr b/tests/ui/rfcs/rfc-2457-non-ascii-idents/crate_name_nonascii_forbidden-2.stderr similarity index 100% rename from tests/ui/rfc-2457/crate_name_nonascii_forbidden-2.stderr rename to tests/ui/rfcs/rfc-2457-non-ascii-idents/crate_name_nonascii_forbidden-2.stderr diff --git a/tests/ui/rfc-2457/extern_block_nonascii_forbidden.rs b/tests/ui/rfcs/rfc-2457-non-ascii-idents/extern_block_nonascii_forbidden.rs similarity index 100% rename from tests/ui/rfc-2457/extern_block_nonascii_forbidden.rs rename to tests/ui/rfcs/rfc-2457-non-ascii-idents/extern_block_nonascii_forbidden.rs diff --git a/tests/ui/rfc-2457/extern_block_nonascii_forbidden.stderr b/tests/ui/rfcs/rfc-2457-non-ascii-idents/extern_block_nonascii_forbidden.stderr similarity index 100% rename from tests/ui/rfc-2457/extern_block_nonascii_forbidden.stderr rename to tests/ui/rfcs/rfc-2457-non-ascii-idents/extern_block_nonascii_forbidden.stderr diff --git a/tests/ui/rfc-2457/idents-normalized.rs b/tests/ui/rfcs/rfc-2457-non-ascii-idents/idents-normalized.rs similarity index 100% rename from tests/ui/rfc-2457/idents-normalized.rs rename to tests/ui/rfcs/rfc-2457-non-ascii-idents/idents-normalized.rs diff --git a/tests/ui/rfc-2457/mod_file_nonascii_forbidden.rs b/tests/ui/rfcs/rfc-2457-non-ascii-idents/mod_file_nonascii_forbidden.rs similarity index 100% rename from tests/ui/rfc-2457/mod_file_nonascii_forbidden.rs rename to tests/ui/rfcs/rfc-2457-non-ascii-idents/mod_file_nonascii_forbidden.rs diff --git a/tests/ui/rfc-2457/mod_file_nonascii_forbidden.stderr b/tests/ui/rfcs/rfc-2457-non-ascii-idents/mod_file_nonascii_forbidden.stderr similarity index 100% rename from tests/ui/rfc-2457/mod_file_nonascii_forbidden.stderr rename to tests/ui/rfcs/rfc-2457-non-ascii-idents/mod_file_nonascii_forbidden.stderr diff --git a/tests/ui/rfc-2457/mod_file_nonascii_with_path_allowed.rs b/tests/ui/rfcs/rfc-2457-non-ascii-idents/mod_file_nonascii_with_path_allowed.rs similarity index 100% rename from tests/ui/rfc-2457/mod_file_nonascii_with_path_allowed.rs rename to tests/ui/rfcs/rfc-2457-non-ascii-idents/mod_file_nonascii_with_path_allowed.rs diff --git a/tests/ui/rfc-2457/mod_inline_nonascii_allowed.rs b/tests/ui/rfcs/rfc-2457-non-ascii-idents/mod_inline_nonascii_allowed.rs similarity index 100% rename from tests/ui/rfc-2457/mod_inline_nonascii_allowed.rs rename to tests/ui/rfcs/rfc-2457-non-ascii-idents/mod_inline_nonascii_allowed.rs diff --git a/tests/ui/rfc-2457/no_mangle_nonascii_forbidden.rs b/tests/ui/rfcs/rfc-2457-non-ascii-idents/no_mangle_nonascii_forbidden.rs similarity index 100% rename from tests/ui/rfc-2457/no_mangle_nonascii_forbidden.rs rename to tests/ui/rfcs/rfc-2457-non-ascii-idents/no_mangle_nonascii_forbidden.rs diff --git a/tests/ui/rfc-2457/no_mangle_nonascii_forbidden.stderr b/tests/ui/rfcs/rfc-2457-non-ascii-idents/no_mangle_nonascii_forbidden.stderr similarity index 100% rename from tests/ui/rfc-2457/no_mangle_nonascii_forbidden.stderr rename to tests/ui/rfcs/rfc-2457-non-ascii-idents/no_mangle_nonascii_forbidden.stderr diff --git a/tests/ui/rfc-2497-if-let-chains/ast-lowering-does-not-wrap-let-chains.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/ast-lowering-does-not-wrap-let-chains.rs similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/ast-lowering-does-not-wrap-let-chains.rs rename to tests/ui/rfcs/rfc-2497-if-let-chains/ast-lowering-does-not-wrap-let-chains.rs diff --git a/tests/ui/rfc-2497-if-let-chains/ast-pretty-check.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/ast-pretty-check.rs similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/ast-pretty-check.rs rename to tests/ui/rfcs/rfc-2497-if-let-chains/ast-pretty-check.rs diff --git a/tests/ui/rfc-2497-if-let-chains/ast-pretty-check.stdout b/tests/ui/rfcs/rfc-2497-if-let-chains/ast-pretty-check.stdout similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/ast-pretty-check.stdout rename to tests/ui/rfcs/rfc-2497-if-let-chains/ast-pretty-check.stdout diff --git a/tests/ui/rfc-2497-if-let-chains/ast-validate-guards.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.rs similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/ast-validate-guards.rs rename to tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.rs diff --git a/tests/ui/rfc-2497-if-let-chains/ast-validate-guards.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.stderr similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/ast-validate-guards.stderr rename to tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.stderr diff --git a/tests/ui/rfc-2497-if-let-chains/chains-without-let.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/chains-without-let.rs similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/chains-without-let.rs rename to tests/ui/rfcs/rfc-2497-if-let-chains/chains-without-let.rs diff --git a/tests/ui/rfc-2497-if-let-chains/chains-without-let.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/chains-without-let.stderr similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/chains-without-let.stderr rename to tests/ui/rfcs/rfc-2497-if-let-chains/chains-without-let.stderr diff --git a/tests/ui/rfc-2497-if-let-chains/disallowed-positions.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.rs similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/disallowed-positions.rs rename to tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.rs diff --git a/tests/ui/rfc-2497-if-let-chains/disallowed-positions.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.stderr similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/disallowed-positions.stderr rename to tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.stderr diff --git a/tests/ui/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.rs similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.rs rename to tests/ui/rfcs/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.rs diff --git a/tests/ui/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.stderr similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.stderr rename to tests/ui/rfcs/rfc-2497-if-let-chains/ensure-that-let-else-does-not-interact-with-let-chains.stderr diff --git a/tests/ui/rfc-2497-if-let-chains/feature-gate.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/feature-gate.rs similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/feature-gate.rs rename to tests/ui/rfcs/rfc-2497-if-let-chains/feature-gate.rs diff --git a/tests/ui/rfc-2497-if-let-chains/feature-gate.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/feature-gate.stderr similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/feature-gate.stderr rename to tests/ui/rfcs/rfc-2497-if-let-chains/feature-gate.stderr diff --git a/tests/ui/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.rs similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.rs rename to tests/ui/rfcs/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.rs diff --git a/tests/ui/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.stderr similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.stderr rename to tests/ui/rfcs/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.stderr diff --git a/tests/ui/rfc-2497-if-let-chains/irrefutable-lets.disallowed.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/irrefutable-lets.disallowed.stderr similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/irrefutable-lets.disallowed.stderr rename to tests/ui/rfcs/rfc-2497-if-let-chains/irrefutable-lets.disallowed.stderr diff --git a/tests/ui/rfc-2497-if-let-chains/irrefutable-lets.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/irrefutable-lets.rs similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/irrefutable-lets.rs rename to tests/ui/rfcs/rfc-2497-if-let-chains/irrefutable-lets.rs diff --git a/tests/ui/rfc-2497-if-let-chains/issue-88498.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/issue-88498.rs similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/issue-88498.rs rename to tests/ui/rfcs/rfc-2497-if-let-chains/issue-88498.rs diff --git a/tests/ui/rfc-2497-if-let-chains/issue-90722.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/issue-90722.rs similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/issue-90722.rs rename to tests/ui/rfcs/rfc-2497-if-let-chains/issue-90722.rs diff --git a/tests/ui/rfc-2497-if-let-chains/issue-92145.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/issue-92145.rs similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/issue-92145.rs rename to tests/ui/rfcs/rfc-2497-if-let-chains/issue-92145.rs diff --git a/tests/ui/rfc-2497-if-let-chains/issue-93150.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/issue-93150.rs similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/issue-93150.rs rename to tests/ui/rfcs/rfc-2497-if-let-chains/issue-93150.rs diff --git a/tests/ui/rfc-2497-if-let-chains/issue-93150.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/issue-93150.stderr similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/issue-93150.stderr rename to tests/ui/rfcs/rfc-2497-if-let-chains/issue-93150.stderr diff --git a/tests/ui/rfc-2497-if-let-chains/issue-99938.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/issue-99938.rs similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/issue-99938.rs rename to tests/ui/rfcs/rfc-2497-if-let-chains/issue-99938.rs diff --git a/tests/ui/rfc-2497-if-let-chains/no-double-assigments.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/no-double-assigments.rs similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/no-double-assigments.rs rename to tests/ui/rfcs/rfc-2497-if-let-chains/no-double-assigments.rs diff --git a/tests/ui/rfc-2497-if-let-chains/protect-precedences.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/protect-precedences.rs similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/protect-precedences.rs rename to tests/ui/rfcs/rfc-2497-if-let-chains/protect-precedences.rs diff --git a/tests/ui/rfc-2497-if-let-chains/protect-precedences.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/protect-precedences.stderr similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/protect-precedences.stderr rename to tests/ui/rfcs/rfc-2497-if-let-chains/protect-precedences.stderr diff --git a/tests/ui/rfc-2497-if-let-chains/then-else-blocks.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/then-else-blocks.rs similarity index 100% rename from tests/ui/rfc-2497-if-let-chains/then-else-blocks.rs rename to tests/ui/rfcs/rfc-2497-if-let-chains/then-else-blocks.rs diff --git a/tests/ui/rfc-2565-param-attrs/attr-without-param.rs b/tests/ui/rfcs/rfc-2565-param-attrs/attr-without-param.rs similarity index 100% rename from tests/ui/rfc-2565-param-attrs/attr-without-param.rs rename to tests/ui/rfcs/rfc-2565-param-attrs/attr-without-param.rs diff --git a/tests/ui/rfc-2565-param-attrs/attr-without-param.stderr b/tests/ui/rfcs/rfc-2565-param-attrs/attr-without-param.stderr similarity index 100% rename from tests/ui/rfc-2565-param-attrs/attr-without-param.stderr rename to tests/ui/rfcs/rfc-2565-param-attrs/attr-without-param.stderr diff --git a/tests/ui/rfc-2565-param-attrs/auxiliary/ident-mac.rs b/tests/ui/rfcs/rfc-2565-param-attrs/auxiliary/ident-mac.rs similarity index 100% rename from tests/ui/rfc-2565-param-attrs/auxiliary/ident-mac.rs rename to tests/ui/rfcs/rfc-2565-param-attrs/auxiliary/ident-mac.rs diff --git a/tests/ui/rfc-2565-param-attrs/auxiliary/param-attrs.rs b/tests/ui/rfcs/rfc-2565-param-attrs/auxiliary/param-attrs.rs similarity index 100% rename from tests/ui/rfc-2565-param-attrs/auxiliary/param-attrs.rs rename to tests/ui/rfcs/rfc-2565-param-attrs/auxiliary/param-attrs.rs diff --git a/tests/ui/rfc-2565-param-attrs/issue-64682-dropping-first-attrs-in-impl-fns.rs b/tests/ui/rfcs/rfc-2565-param-attrs/issue-64682-dropping-first-attrs-in-impl-fns.rs similarity index 100% rename from tests/ui/rfc-2565-param-attrs/issue-64682-dropping-first-attrs-in-impl-fns.rs rename to tests/ui/rfcs/rfc-2565-param-attrs/issue-64682-dropping-first-attrs-in-impl-fns.rs diff --git a/tests/ui/rfc-2565-param-attrs/param-attrs-2018.rs b/tests/ui/rfcs/rfc-2565-param-attrs/param-attrs-2018.rs similarity index 100% rename from tests/ui/rfc-2565-param-attrs/param-attrs-2018.rs rename to tests/ui/rfcs/rfc-2565-param-attrs/param-attrs-2018.rs diff --git a/tests/ui/rfc-2565-param-attrs/param-attrs-2018.stderr b/tests/ui/rfcs/rfc-2565-param-attrs/param-attrs-2018.stderr similarity index 100% rename from tests/ui/rfc-2565-param-attrs/param-attrs-2018.stderr rename to tests/ui/rfcs/rfc-2565-param-attrs/param-attrs-2018.stderr diff --git a/tests/ui/rfc-2565-param-attrs/param-attrs-allowed.rs b/tests/ui/rfcs/rfc-2565-param-attrs/param-attrs-allowed.rs similarity index 100% rename from tests/ui/rfc-2565-param-attrs/param-attrs-allowed.rs rename to tests/ui/rfcs/rfc-2565-param-attrs/param-attrs-allowed.rs diff --git a/tests/ui/rfc-2565-param-attrs/param-attrs-builtin-attrs.rs b/tests/ui/rfcs/rfc-2565-param-attrs/param-attrs-builtin-attrs.rs similarity index 100% rename from tests/ui/rfc-2565-param-attrs/param-attrs-builtin-attrs.rs rename to tests/ui/rfcs/rfc-2565-param-attrs/param-attrs-builtin-attrs.rs diff --git a/tests/ui/rfc-2565-param-attrs/param-attrs-builtin-attrs.stderr b/tests/ui/rfcs/rfc-2565-param-attrs/param-attrs-builtin-attrs.stderr similarity index 100% rename from tests/ui/rfc-2565-param-attrs/param-attrs-builtin-attrs.stderr rename to tests/ui/rfcs/rfc-2565-param-attrs/param-attrs-builtin-attrs.stderr diff --git a/tests/ui/rfc-2565-param-attrs/param-attrs-cfg.rs b/tests/ui/rfcs/rfc-2565-param-attrs/param-attrs-cfg.rs similarity index 100% rename from tests/ui/rfc-2565-param-attrs/param-attrs-cfg.rs rename to tests/ui/rfcs/rfc-2565-param-attrs/param-attrs-cfg.rs diff --git a/tests/ui/rfc-2565-param-attrs/param-attrs-cfg.stderr b/tests/ui/rfcs/rfc-2565-param-attrs/param-attrs-cfg.stderr similarity index 100% rename from tests/ui/rfc-2565-param-attrs/param-attrs-cfg.stderr rename to tests/ui/rfcs/rfc-2565-param-attrs/param-attrs-cfg.stderr diff --git a/tests/ui/rfc-2565-param-attrs/param-attrs-pretty.rs b/tests/ui/rfcs/rfc-2565-param-attrs/param-attrs-pretty.rs similarity index 100% rename from tests/ui/rfc-2565-param-attrs/param-attrs-pretty.rs rename to tests/ui/rfcs/rfc-2565-param-attrs/param-attrs-pretty.rs diff --git a/tests/ui/rfc-2565-param-attrs/proc-macro-cannot-be-used.rs b/tests/ui/rfcs/rfc-2565-param-attrs/proc-macro-cannot-be-used.rs similarity index 100% rename from tests/ui/rfc-2565-param-attrs/proc-macro-cannot-be-used.rs rename to tests/ui/rfcs/rfc-2565-param-attrs/proc-macro-cannot-be-used.rs diff --git a/tests/ui/rfc-2565-param-attrs/proc-macro-cannot-be-used.stderr b/tests/ui/rfcs/rfc-2565-param-attrs/proc-macro-cannot-be-used.stderr similarity index 100% rename from tests/ui/rfc-2565-param-attrs/proc-macro-cannot-be-used.stderr rename to tests/ui/rfcs/rfc-2565-param-attrs/proc-macro-cannot-be-used.stderr diff --git a/tests/ui/rfc-2627-raw-dylib/dlltool-failed.rs b/tests/ui/rfcs/rfc-2627-raw-dylib/dlltool-failed.rs similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/dlltool-failed.rs rename to tests/ui/rfcs/rfc-2627-raw-dylib/dlltool-failed.rs diff --git a/tests/ui/rfc-2627-raw-dylib/dlltool-failed.stderr b/tests/ui/rfcs/rfc-2627-raw-dylib/dlltool-failed.stderr similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/dlltool-failed.stderr rename to tests/ui/rfcs/rfc-2627-raw-dylib/dlltool-failed.stderr diff --git a/tests/ui/rfc-2627-raw-dylib/import-name-type-invalid-format.rs b/tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-invalid-format.rs similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/import-name-type-invalid-format.rs rename to tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-invalid-format.rs diff --git a/tests/ui/rfc-2627-raw-dylib/import-name-type-invalid-format.stderr b/tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-invalid-format.stderr similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/import-name-type-invalid-format.stderr rename to tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-invalid-format.stderr diff --git a/tests/ui/rfc-2627-raw-dylib/import-name-type-multiple.rs b/tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-multiple.rs similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/import-name-type-multiple.rs rename to tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-multiple.rs diff --git a/tests/ui/rfc-2627-raw-dylib/import-name-type-multiple.stderr b/tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-multiple.stderr similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/import-name-type-multiple.stderr rename to tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-multiple.stderr diff --git a/tests/ui/rfc-2627-raw-dylib/import-name-type-unknown-value.rs b/tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-unknown-value.rs similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/import-name-type-unknown-value.rs rename to tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-unknown-value.rs diff --git a/tests/ui/rfc-2627-raw-dylib/import-name-type-unknown-value.stderr b/tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-unknown-value.stderr similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/import-name-type-unknown-value.stderr rename to tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-unknown-value.stderr diff --git a/tests/ui/rfc-2627-raw-dylib/import-name-type-unsupported-link-kind.rs b/tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-unsupported-link-kind.rs similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/import-name-type-unsupported-link-kind.rs rename to tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-unsupported-link-kind.rs diff --git a/tests/ui/rfc-2627-raw-dylib/import-name-type-unsupported-link-kind.stderr b/tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-unsupported-link-kind.stderr similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/import-name-type-unsupported-link-kind.stderr rename to tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-unsupported-link-kind.stderr diff --git a/tests/ui/rfc-2627-raw-dylib/import-name-type-x86-only.rs b/tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-x86-only.rs similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/import-name-type-x86-only.rs rename to tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-x86-only.rs diff --git a/tests/ui/rfc-2627-raw-dylib/import-name-type-x86-only.stderr b/tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-x86-only.stderr similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/import-name-type-x86-only.stderr rename to tests/ui/rfcs/rfc-2627-raw-dylib/import-name-type-x86-only.stderr diff --git a/tests/ui/rfc-2627-raw-dylib/invalid-dlltool.rs b/tests/ui/rfcs/rfc-2627-raw-dylib/invalid-dlltool.rs similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/invalid-dlltool.rs rename to tests/ui/rfcs/rfc-2627-raw-dylib/invalid-dlltool.rs diff --git a/tests/ui/rfc-2627-raw-dylib/invalid-dlltool.stderr b/tests/ui/rfcs/rfc-2627-raw-dylib/invalid-dlltool.stderr similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/invalid-dlltool.stderr rename to tests/ui/rfcs/rfc-2627-raw-dylib/invalid-dlltool.stderr diff --git a/tests/ui/rfc-2627-raw-dylib/link-ordinal-and-name.rs b/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-and-name.rs similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/link-ordinal-and-name.rs rename to tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-and-name.rs diff --git a/tests/ui/rfc-2627-raw-dylib/link-ordinal-and-name.stderr b/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-and-name.stderr similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/link-ordinal-and-name.stderr rename to tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-and-name.stderr diff --git a/tests/ui/rfc-2627-raw-dylib/link-ordinal-invalid-format.rs b/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-invalid-format.rs similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/link-ordinal-invalid-format.rs rename to tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-invalid-format.rs diff --git a/tests/ui/rfc-2627-raw-dylib/link-ordinal-invalid-format.stderr b/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-invalid-format.stderr similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/link-ordinal-invalid-format.stderr rename to tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-invalid-format.stderr diff --git a/tests/ui/rfc-2627-raw-dylib/link-ordinal-missing-argument.rs b/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-missing-argument.rs similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/link-ordinal-missing-argument.rs rename to tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-missing-argument.rs diff --git a/tests/ui/rfc-2627-raw-dylib/link-ordinal-missing-argument.stderr b/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-missing-argument.stderr similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/link-ordinal-missing-argument.stderr rename to tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-missing-argument.stderr diff --git a/tests/ui/rfc-2627-raw-dylib/link-ordinal-multiple.rs b/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-multiple.rs similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/link-ordinal-multiple.rs rename to tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-multiple.rs diff --git a/tests/ui/rfc-2627-raw-dylib/link-ordinal-multiple.stderr b/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-multiple.stderr similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/link-ordinal-multiple.stderr rename to tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-multiple.stderr diff --git a/tests/ui/rfc-2627-raw-dylib/link-ordinal-not-foreign-fn.rs b/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-not-foreign-fn.rs similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/link-ordinal-not-foreign-fn.rs rename to tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-not-foreign-fn.rs diff --git a/tests/ui/rfc-2627-raw-dylib/link-ordinal-not-foreign-fn.stderr b/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-not-foreign-fn.stderr similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/link-ordinal-not-foreign-fn.stderr rename to tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-not-foreign-fn.stderr diff --git a/tests/ui/rfc-2627-raw-dylib/link-ordinal-too-large.rs b/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-too-large.rs similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/link-ordinal-too-large.rs rename to tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-too-large.rs diff --git a/tests/ui/rfc-2627-raw-dylib/link-ordinal-too-large.stderr b/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-too-large.stderr similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/link-ordinal-too-large.stderr rename to tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-too-large.stderr diff --git a/tests/ui/rfc-2627-raw-dylib/link-ordinal-too-many-arguments.rs b/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-too-many-arguments.rs similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/link-ordinal-too-many-arguments.rs rename to tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-too-many-arguments.rs diff --git a/tests/ui/rfc-2627-raw-dylib/link-ordinal-too-many-arguments.stderr b/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-too-many-arguments.stderr similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/link-ordinal-too-many-arguments.stderr rename to tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-too-many-arguments.stderr diff --git a/tests/ui/rfc-2627-raw-dylib/link-ordinal-unsupported-link-kind.rs b/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-unsupported-link-kind.rs similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/link-ordinal-unsupported-link-kind.rs rename to tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-unsupported-link-kind.rs diff --git a/tests/ui/rfc-2627-raw-dylib/link-ordinal-unsupported-link-kind.stderr b/tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-unsupported-link-kind.stderr similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/link-ordinal-unsupported-link-kind.stderr rename to tests/ui/rfcs/rfc-2627-raw-dylib/link-ordinal-unsupported-link-kind.stderr diff --git a/tests/ui/rfc-2627-raw-dylib/multiple-declarations.rs b/tests/ui/rfcs/rfc-2627-raw-dylib/multiple-declarations.rs similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/multiple-declarations.rs rename to tests/ui/rfcs/rfc-2627-raw-dylib/multiple-declarations.rs diff --git a/tests/ui/rfc-2627-raw-dylib/multiple-declarations.stderr b/tests/ui/rfcs/rfc-2627-raw-dylib/multiple-declarations.stderr similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/multiple-declarations.stderr rename to tests/ui/rfcs/rfc-2627-raw-dylib/multiple-declarations.stderr diff --git a/tests/ui/rfc-2627-raw-dylib/raw-dylib-windows-only.rs b/tests/ui/rfcs/rfc-2627-raw-dylib/raw-dylib-windows-only.rs similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/raw-dylib-windows-only.rs rename to tests/ui/rfcs/rfc-2627-raw-dylib/raw-dylib-windows-only.rs diff --git a/tests/ui/rfc-2627-raw-dylib/raw-dylib-windows-only.stderr b/tests/ui/rfcs/rfc-2627-raw-dylib/raw-dylib-windows-only.stderr similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/raw-dylib-windows-only.stderr rename to tests/ui/rfcs/rfc-2627-raw-dylib/raw-dylib-windows-only.stderr diff --git a/tests/ui/rfc-2627-raw-dylib/unsupported-abi.rs b/tests/ui/rfcs/rfc-2627-raw-dylib/unsupported-abi.rs similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/unsupported-abi.rs rename to tests/ui/rfcs/rfc-2627-raw-dylib/unsupported-abi.rs diff --git a/tests/ui/rfc-2627-raw-dylib/unsupported-abi.stderr b/tests/ui/rfcs/rfc-2627-raw-dylib/unsupported-abi.stderr similarity index 100% rename from tests/ui/rfc-2627-raw-dylib/unsupported-abi.stderr rename to tests/ui/rfcs/rfc-2627-raw-dylib/unsupported-abi.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/assoc-type-const-bound-usage.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/assoc-type-const-bound-usage.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type-const-bound-usage.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/assoc-type.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/assoc-type.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/assoc-type.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/assoc-type.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/assoc-type.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/attr-misuse.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/attr-misuse.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/attr-misuse.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/attr-misuse.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/attr-misuse.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/attr-misuse.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/attr-misuse.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/attr-misuse.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/auxiliary/cross-crate.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/auxiliary/cross-crate.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/auxiliary/cross-crate.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/auxiliary/cross-crate.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/auxiliary/staged-api.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/auxiliary/staged-api.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/auxiliary/staged-api.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/auxiliary/staged-api.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/call-const-trait-method-fail.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-const-trait-method-fail.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/call-const-trait-method-fail.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/call-const-trait-method-fail.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/call-const-trait-method-fail.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-const-trait-method-fail.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/call-const-trait-method-fail.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/call-const-trait-method-fail.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/call-const-trait-method-pass.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-const-trait-method-pass.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/call-const-trait-method-pass.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/call-const-trait-method-pass.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/call-const-trait-method-pass.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-const-trait-method-pass.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/call-const-trait-method-pass.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/call-const-trait-method-pass.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/call-generic-in-impl.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-in-impl.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/call-generic-in-impl.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-in-impl.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/call-generic-in-impl.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-in-impl.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/call-generic-in-impl.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-in-impl.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/call-generic-method-chain.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-chain.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/call-generic-method-chain.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-chain.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/call-generic-method-chain.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-chain.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/call-generic-method-chain.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-chain.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/call-generic-method-dup-bound.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-dup-bound.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/call-generic-method-dup-bound.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-dup-bound.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/call-generic-method-dup-bound.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-dup-bound.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/call-generic-method-dup-bound.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-dup-bound.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/call-generic-method-fail.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-fail.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/call-generic-method-fail.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-fail.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/call-generic-method-fail.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-fail.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/call-generic-method-fail.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-fail.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/call-generic-method-nonconst-bound.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-nonconst-bound.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/call-generic-method-nonconst-bound.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-nonconst-bound.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/call-generic-method-nonconst.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-nonconst.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/call-generic-method-nonconst.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-nonconst.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/call-generic-method-nonconst.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-nonconst.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/call-generic-method-nonconst.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-nonconst.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/call-generic-method-pass.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-pass.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/call-generic-method-pass.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-pass.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/call-generic-method-pass.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-pass.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/call-generic-method-pass.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/call-generic-method-pass.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/call.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/call.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/call.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/call.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/const-and-non-const-impl.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-and-non-const-impl.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-and-non-const-impl.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-and-non-const-impl.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/const-and-non-const-impl.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-and-non-const-impl.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-and-non-const-impl.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-and-non-const-impl.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/const-check-fns-in-const-impl.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-check-fns-in-const-impl.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-check-fns-in-const-impl.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-check-fns-in-const-impl.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/const-check-fns-in-const-impl.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-check-fns-in-const-impl.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-check-fns-in-const-impl.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-check-fns-in-const-impl.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/const-closure-parse-not-item.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-closure-parse-not-item.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-closure-parse-not-item.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-closure-parse-not-item.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/const-closure-trait-method-fail.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-closure-trait-method-fail.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-closure-trait-method-fail.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-closure-trait-method-fail.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/const-closure-trait-method-fail.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-closure-trait-method-fail.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-closure-trait-method-fail.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-closure-trait-method-fail.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/const-closure-trait-method.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-closure-trait-method.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-closure-trait-method.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-closure-trait-method.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/const-closures.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-closures.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-closures.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-closures.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/const-default-method-bodies.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-default-method-bodies.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-default-method-bodies.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-default-method-bodies.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/const-default-method-bodies.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-default-method-bodies.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-default-method-bodies.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-default-method-bodies.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/const-drop-bound.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop-bound.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-drop-bound.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop-bound.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/const-drop-fail-2.precise.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop-fail-2.precise.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-drop-fail-2.precise.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop-fail-2.precise.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/const-drop-fail-2.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop-fail-2.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-drop-fail-2.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop-fail-2.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/const-drop-fail-2.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop-fail-2.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-drop-fail-2.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop-fail-2.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/const-drop-fail-2.stock.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop-fail-2.stock.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-drop-fail-2.stock.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop-fail-2.stock.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/const-drop-fail.precise.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop-fail.precise.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-drop-fail.precise.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop-fail.precise.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/const-drop-fail.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop-fail.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-drop-fail.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop-fail.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/const-drop-fail.stock.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop-fail.stock.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-drop-fail.stock.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop-fail.stock.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/const-drop.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-drop.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-drop.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/const-impl-norecover.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-norecover.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-impl-norecover.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-norecover.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/const-impl-norecover.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-norecover.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-impl-norecover.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-norecover.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/const-impl-recovery.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-recovery.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-impl-recovery.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-recovery.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/const-impl-recovery.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-recovery.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-impl-recovery.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-recovery.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/const-impl-requires-const-trait.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-requires-const-trait.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-impl-requires-const-trait.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-requires-const-trait.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/const-impl-requires-const-trait.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-requires-const-trait.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-impl-requires-const-trait.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-requires-const-trait.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/const-impl-trait.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-trait.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-impl-trait.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-trait.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/const-impl-trait.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-trait.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const-impl-trait.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const-impl-trait.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/const_derives/derive-const-gate.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/derive-const-gate.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const_derives/derive-const-gate.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/derive-const-gate.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/const_derives/derive-const-gate.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/derive-const-gate.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const_derives/derive-const-gate.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/derive-const-gate.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/const_derives/derive-const-non-const-type.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/derive-const-non-const-type.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const_derives/derive-const-non-const-type.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/derive-const-non-const-type.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/const_derives/derive-const-non-const-type.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/derive-const-non-const-type.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const_derives/derive-const-non-const-type.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/derive-const-non-const-type.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/const_derives/derive-const-use.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/derive-const-use.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const_derives/derive-const-use.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/derive-const-use.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/const_derives/derive-const-use.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/derive-const-use.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const_derives/derive-const-use.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/derive-const-use.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/const_derives/derive-const-with-params.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/derive-const-with-params.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const_derives/derive-const-with-params.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/derive-const-with-params.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/const_derives/derive-const-with-params.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/derive-const-with-params.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/const_derives/derive-const-with-params.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/const_derives/derive-const-with-params.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/cross-crate-default-method-body-is-const.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/cross-crate-default-method-body-is-const.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/cross-crate-default-method-body-is-const.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/cross-crate-default-method-body-is-const.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/cross-crate.gatednc.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/cross-crate.gatednc.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/cross-crate.gatednc.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/cross-crate.gatednc.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/cross-crate.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/cross-crate.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/cross-crate.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/cross-crate.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/cross-crate.stock.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/cross-crate.stock.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/cross-crate.stock.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/cross-crate.stock.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/cross-crate.stocknc.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/cross-crate.stocknc.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/cross-crate.stocknc.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/cross-crate.stocknc.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/default-method-body-is-const-body-checking.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/default-method-body-is-const-body-checking.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/default-method-body-is-const-body-checking.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/default-method-body-is-const-body-checking.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/default-method-body-is-const-body-checking.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/default-method-body-is-const-body-checking.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/default-method-body-is-const-body-checking.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/default-method-body-is-const-body-checking.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/default-method-body-is-const-same-trait-ck.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/default-method-body-is-const-same-trait-ck.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/default-method-body-is-const-same-trait-ck.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/default-method-body-is-const-same-trait-ck.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/default-method-body-is-const-same-trait-ck.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/default-method-body-is-const-same-trait-ck.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/default-method-body-is-const-same-trait-ck.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/default-method-body-is-const-same-trait-ck.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/default-method-body-is-const-with-staged-api.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/default-method-body-is-const-with-staged-api.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/default-method-body-is-const-with-staged-api.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/default-method-body-is-const-with-staged-api.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/do-not-const-check-override.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/do-not-const-check-override.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/do-not-const-check-override.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/do-not-const-check-override.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/do-not-const-check.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/do-not-const-check.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/do-not-const-check.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/do-not-const-check.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/feature-gate.gated.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/feature-gate.gated.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/feature-gate.gated.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/feature-gate.gated.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/feature-gate.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/feature-gate.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/feature-gate.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/feature-gate.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/feature-gate.stock.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/feature-gate.stock.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/feature-gate.stock.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/feature-gate.stock.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/function-pointer-does-not-require-const.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/function-pointer-does-not-require-const.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/function-pointer-does-not-require-const.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/function-pointer-does-not-require-const.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/gate.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/gate.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/gate.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/gate.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/gate.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/gate.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/gate.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/gate.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/generic-bound.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/generic-bound.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/generic-bound.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/generic-bound.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/generic-bound.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/generic-bound.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/generic-bound.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/generic-bound.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/hir-const-check.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/hir-const-check.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/hir-const-check.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/hir-const-check.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/hir-const-check.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/hir-const-check.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/hir-const-check.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/hir-const-check.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/impl-tilde-const-trait.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/impl-tilde-const-trait.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/impl-tilde-const-trait.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/impl-tilde-const-trait.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/impl-tilde-const-trait.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/impl-tilde-const-trait.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/impl-tilde-const-trait.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/impl-tilde-const-trait.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/impl-with-default-fn-fail.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/impl-with-default-fn-fail.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/impl-with-default-fn-fail.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/impl-with-default-fn-fail.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/impl-with-default-fn-fail.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/impl-with-default-fn-fail.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/impl-with-default-fn-fail.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/impl-with-default-fn-fail.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/impl-with-default-fn-pass.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/impl-with-default-fn-pass.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/impl-with-default-fn-pass.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/impl-with-default-fn-pass.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/inherent-impl-const-bounds.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/inherent-impl-const-bounds.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/inherent-impl-const-bounds.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/inherent-impl-const-bounds.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/inherent-impl.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/inherent-impl.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/inherent-impl.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/inherent-impl.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/inherent-impl.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/inherent-impl.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/inherent-impl.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/inherent-impl.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/issue-100222.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-100222.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/issue-100222.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/issue-100222.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/issue-102156.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-102156.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/issue-102156.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/issue-102156.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/issue-102156.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-102156.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/issue-102156.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/issue-102156.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/issue-102985.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-102985.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/issue-102985.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/issue-102985.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/issue-102985.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-102985.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/issue-102985.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/issue-102985.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/issue-103677.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-103677.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/issue-103677.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/issue-103677.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/issue-79450.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-79450.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/issue-79450.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/issue-79450.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/issue-79450.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-79450.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/issue-79450.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/issue-79450.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/issue-88155.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-88155.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/issue-88155.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/issue-88155.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/issue-88155.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-88155.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/issue-88155.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/issue-88155.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/issue-90052.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-90052.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/issue-90052.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/issue-90052.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/issue-90052.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-90052.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/issue-90052.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/issue-90052.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/issue-92111.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-92111.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/issue-92111.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/issue-92111.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/issue-92230-wf-super-trait-env.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-92230-wf-super-trait-env.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/issue-92230-wf-super-trait-env.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/issue-92230-wf-super-trait-env.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/nested-closure.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/nested-closure.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/nested-closure.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/nested-closure.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/non-const-op-const-closure-non-const-outer.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/non-const-op-const-closure-non-const-outer.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/non-const-op-const-closure-non-const-outer.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/non-const-op-const-closure-non-const-outer.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/non-const-op-const-closure-non-const-outer.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/non-const-op-const-closure-non-const-outer.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/non-const-op-const-closure-non-const-outer.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/non-const-op-const-closure-non-const-outer.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/non-const-op-in-closure-in-const.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/non-const-op-in-closure-in-const.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/non-const-op-in-closure-in-const.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/non-const-op-in-closure-in-const.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/non-const-op-in-closure-in-const.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/non-const-op-in-closure-in-const.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/non-const-op-in-closure-in-const.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/non-const-op-in-closure-in-const.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/specialization/const-default-bound-non-const-specialized-bound.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/const-default-bound-non-const-specialized-bound.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/specialization/const-default-bound-non-const-specialized-bound.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/const-default-bound-non-const-specialized-bound.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/specialization/const-default-bound-non-const-specialized-bound.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/const-default-bound-non-const-specialized-bound.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/specialization/const-default-bound-non-const-specialized-bound.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/const-default-bound-non-const-specialized-bound.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/specialization/const-default-const-specialized.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/const-default-const-specialized.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/specialization/const-default-const-specialized.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/const-default-const-specialized.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/specialization/const-default-impl-non-const-specialized-impl.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/const-default-impl-non-const-specialized-impl.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/specialization/const-default-impl-non-const-specialized-impl.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/const-default-impl-non-const-specialized-impl.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/specialization/const-default-impl-non-const-specialized-impl.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/const-default-impl-non-const-specialized-impl.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/specialization/const-default-impl-non-const-specialized-impl.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/const-default-impl-non-const-specialized-impl.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/specialization/default-keyword.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/default-keyword.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/specialization/default-keyword.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/default-keyword.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/specialization/issue-95186-specialize-on-tilde-const.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/issue-95186-specialize-on-tilde-const.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/specialization/issue-95186-specialize-on-tilde-const.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/issue-95186-specialize-on-tilde-const.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/specialization/issue-95187-same-trait-bound-different-constness.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/issue-95187-same-trait-bound-different-constness.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/specialization/issue-95187-same-trait-bound-different-constness.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/issue-95187-same-trait-bound-different-constness.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/specialization/non-const-default-const-specialized.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/non-const-default-const-specialized.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/specialization/non-const-default-const-specialized.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/specialization/non-const-default-const-specialized.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/specializing-constness-2.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/specializing-constness-2.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/specializing-constness-2.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/specializing-constness-2.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/specializing-constness-2.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/specializing-constness-2.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/specializing-constness-2.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/specializing-constness-2.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/specializing-constness.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/specializing-constness.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/specializing-constness.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/specializing-constness.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/specializing-constness.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/specializing-constness.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/specializing-constness.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/specializing-constness.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/staged-api-user-crate.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/staged-api-user-crate.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/staged-api-user-crate.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/staged-api-user-crate.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/staged-api-user-crate.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/staged-api-user-crate.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/staged-api-user-crate.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/staged-api-user-crate.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/staged-api.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/staged-api.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/staged-api.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/staged-api.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/staged-api.stable.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/staged-api.stable.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/staged-api.stable.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/staged-api.stable.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/staged-api.unstable.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/staged-api.unstable.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/staged-api.unstable.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/staged-api.unstable.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/static-const-trait-bound.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/static-const-trait-bound.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/static-const-trait-bound.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/static-const-trait-bound.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/std-impl-gate.gated.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/std-impl-gate.gated.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/std-impl-gate.gated.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/std-impl-gate.gated.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/std-impl-gate.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/std-impl-gate.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/std-impl-gate.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/std-impl-gate.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/std-impl-gate.stock.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/std-impl-gate.stock.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/std-impl-gate.stock.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/std-impl-gate.stock.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/super-traits-fail-2.nn.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.nn.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/super-traits-fail-2.nn.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.nn.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/super-traits-fail-2.ny.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.ny.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/super-traits-fail-2.ny.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.ny.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/super-traits-fail-2.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/super-traits-fail-2.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/super-traits-fail-2.yn.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.yn.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/super-traits-fail-2.yn.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.yn.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/super-traits-fail-2.yy.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.yy.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/super-traits-fail-2.yy.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-2.yy.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/super-traits-fail-3.nn.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.nn.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/super-traits-fail-3.nn.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.nn.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/super-traits-fail-3.ny.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.ny.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/super-traits-fail-3.ny.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.ny.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/super-traits-fail-3.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/super-traits-fail-3.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/super-traits-fail-3.yn.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.yn.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/super-traits-fail-3.yn.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail-3.yn.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/super-traits-fail.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/super-traits-fail.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/super-traits-fail.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/super-traits-fail.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits-fail.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/super-traits.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/super-traits.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/super-traits.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/syntax.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/syntax.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/syntax.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/syntax.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/tilde-const-and-const-params.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-and-const-params.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/tilde-const-and-const-params.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-and-const-params.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/tilde-const-and-const-params.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-and-const-params.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/tilde-const-and-const-params.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-and-const-params.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/tilde-const-invalid-places.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-invalid-places.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/tilde-const-invalid-places.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-invalid-places.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/tilde-const-invalid-places.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-invalid-places.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/tilde-const-invalid-places.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-invalid-places.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/tilde-const-syntax.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-syntax.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/tilde-const-syntax.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-const-syntax.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/tilde-twice.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-twice.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/tilde-twice.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-twice.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/tilde-twice.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-twice.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/tilde-twice.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/tilde-twice.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/tilde_const_on_impl_bound.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/tilde_const_on_impl_bound.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/tilde_const_on_impl_bound.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/tilde_const_on_impl_bound.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/trait-default-body-stability.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/trait-default-body-stability.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/trait-default-body-stability.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/trait-default-body-stability.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/trait-default-body-stability.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/trait-default-body-stability.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/trait-default-body-stability.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/trait-default-body-stability.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/trait-method-ptr-in-consts-ice.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/trait-method-ptr-in-consts-ice.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/trait-method-ptr-in-consts-ice.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/trait-method-ptr-in-consts-ice.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/trait-where-clause-const.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause-const.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/trait-where-clause-const.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause-const.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/trait-where-clause-const.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause-const.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/trait-where-clause-const.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause-const.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/trait-where-clause-run.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause-run.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/trait-where-clause-run.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause-run.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/trait-where-clause-self-referential.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause-self-referential.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/trait-where-clause-self-referential.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause-self-referential.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/trait-where-clause.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/trait-where-clause.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/trait-where-clause.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/trait-where-clause.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/trait-where-clause.stderr diff --git a/tests/ui/rfc-2632-const-trait-impl/without-tilde.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/without-tilde.rs similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/without-tilde.rs rename to tests/ui/rfcs/rfc-2632-const-trait-impl/without-tilde.rs diff --git a/tests/ui/rfc-2632-const-trait-impl/without-tilde.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/without-tilde.stderr similarity index 100% rename from tests/ui/rfc-2632-const-trait-impl/without-tilde.stderr rename to tests/ui/rfcs/rfc-2632-const-trait-impl/without-tilde.stderr diff --git a/tests/ui/sanitize/issue-111184-generator-witness.rs b/tests/ui/sanitize/issue-111184-generator-witness.rs new file mode 100644 index 0000000000000..8f4118057cec4 --- /dev/null +++ b/tests/ui/sanitize/issue-111184-generator-witness.rs @@ -0,0 +1,17 @@ +// Regression test for issue 111184, where ty::GeneratorWitness were not expected to occur in +// encode_ty and caused the compiler to ICE. +// +// needs-sanitizer-cfi +// compile-flags: -Clto -Ctarget-feature=-crt-static -Zsanitizer=cfi --edition=2021 +// no-prefer-dynamic +// only-x86_64-unknown-linux-gnu +// run-pass + +use std::future::Future; + +async fn foo() {} +fn bar(_: impl Future) {} + +fn main() { + bar(foo()); +} diff --git a/tests/ui/specialization/specialization-default-items-drop-coherence.coherence.stderr b/tests/ui/specialization/specialization-default-items-drop-coherence.coherence.stderr new file mode 100644 index 0000000000000..578db0cc65e89 --- /dev/null +++ b/tests/ui/specialization/specialization-default-items-drop-coherence.coherence.stderr @@ -0,0 +1,12 @@ +error[E0119]: conflicting implementations of trait `Overlap` for type `u32` + --> $DIR/specialization-default-items-drop-coherence.rs:29:1 + | +LL | impl Overlap for u32 { + | -------------------- first implementation here +... +LL | impl Overlap for ::Id { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `u32` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/specialization/specialization-default-items-drop-coherence.next.stderr b/tests/ui/specialization/specialization-default-items-drop-coherence.next.stderr new file mode 100644 index 0000000000000..578db0cc65e89 --- /dev/null +++ b/tests/ui/specialization/specialization-default-items-drop-coherence.next.stderr @@ -0,0 +1,12 @@ +error[E0119]: conflicting implementations of trait `Overlap` for type `u32` + --> $DIR/specialization-default-items-drop-coherence.rs:29:1 + | +LL | impl Overlap for u32 { + | -------------------- first implementation here +... +LL | impl Overlap for ::Id { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `u32` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/specialization/specialization-default-items-drop-coherence.rs b/tests/ui/specialization/specialization-default-items-drop-coherence.rs index 16ad942d5ab95..44c598f19cb9b 100644 --- a/tests/ui/specialization/specialization-default-items-drop-coherence.rs +++ b/tests/ui/specialization/specialization-default-items-drop-coherence.rs @@ -1,5 +1,8 @@ -// check-pass -// known-bug: #105782 +// revisions: classic coherence next +//[next] compile-flags: -Ztrait-solver=next +//[coherence] compile-flags: -Ztrait-solver=next-coherence +//[classic] check-pass +//[classic] known-bug: #105782 // Should fail. Default items completely drop candidates instead of ambiguity, // which is unsound during coherence, since coherence requires completeness. @@ -24,6 +27,8 @@ impl Overlap for u32 { } impl Overlap for ::Id { + //[coherence]~^ ERROR conflicting implementations of trait `Overlap` for type `u32` + //[next]~^^ ERROR conflicting implementations of trait `Overlap` for type `u32` type Assoc = Box; } diff --git a/tests/ui/structs/struct-field-cfg.stderr b/tests/ui/structs/struct-field-cfg.stderr index 5ec47c093a9b0..2b9ba85ddcb88 100644 --- a/tests/ui/structs/struct-field-cfg.stderr +++ b/tests/ui/structs/struct-field-cfg.stderr @@ -10,7 +10,7 @@ error[E0560]: struct `Foo` has no field named `absent` LL | let _ = Foo { present: (), #[cfg(all())] absent: () }; | ^^^^^^ `Foo` does not have this field | - = note: available fields are: `present` + = note: all struct fields are already assigned error[E0027]: pattern does not mention field `present` --> $DIR/struct-field-cfg.rs:13:9 diff --git a/tests/ui/structs/struct-fields-shorthand.stderr b/tests/ui/structs/struct-fields-shorthand.stderr index a285a392168c7..d89d45b39033a 100644 --- a/tests/ui/structs/struct-fields-shorthand.stderr +++ b/tests/ui/structs/struct-fields-shorthand.stderr @@ -4,7 +4,7 @@ error[E0560]: struct `Foo` has no field named `z` LL | x, y, z | ^ `Foo` does not have this field | - = note: available fields are: `x`, `y` + = note: all struct fields are already assigned error: aborting due to previous error diff --git a/tests/ui/structs/struct-fields-too-many.stderr b/tests/ui/structs/struct-fields-too-many.stderr index a1b7a7a311080..9342607ebce25 100644 --- a/tests/ui/structs/struct-fields-too-many.stderr +++ b/tests/ui/structs/struct-fields-too-many.stderr @@ -4,7 +4,7 @@ error[E0560]: struct `BuildData` has no field named `bar` LL | bar: 0 | ^^^ `BuildData` does not have this field | - = note: available fields are: `foo` + = note: all struct fields are already assigned error: aborting due to previous error diff --git a/tests/ui/suggestions/as-ref.rs b/tests/ui/suggestions/as-ref.rs index a053534418589..0d9790ac22974 100644 --- a/tests/ui/suggestions/as-ref.rs +++ b/tests/ui/suggestions/as-ref.rs @@ -24,4 +24,6 @@ fn main() { let multiple_ref_result = &&Ok(Foo); multiple_ref_result.map(|arg| takes_ref(arg)); //~ ERROR mismatched types [E0308] multiple_ref_result.and_then(|arg| Ok(takes_ref(arg))); //~ ERROR mismatched types [E0308] + + let _: Result<&usize, _> = &Ok(42); //~ ERROR mismatched types [E0308] } diff --git a/tests/ui/suggestions/as-ref.stderr b/tests/ui/suggestions/as-ref.stderr index 2147d2d92e389..c5b2bb1260f35 100644 --- a/tests/ui/suggestions/as-ref.stderr +++ b/tests/ui/suggestions/as-ref.stderr @@ -74,14 +74,16 @@ error[E0308]: mismatched types --> $DIR/as-ref.rs:13:29 | LL | let y: Option<&usize> = x; - | -------------- ^ - | | | - | | expected `Option<&usize>`, found `&Option` - | | help: you can convert from `&Option` to `Option<&T>` using `.as_ref()`: `x.as_ref()` + | -------------- ^ expected `Option<&usize>`, found `&Option` + | | | expected due to this | = note: expected enum `Option<&usize>` found reference `&Option` +help: try using `.as_ref()` to convert `&Option` to `Option<&usize>` + | +LL | let y: Option<&usize> = x.as_ref(); + | +++++++++ error[E0308]: mismatched types --> $DIR/as-ref.rs:15:37 @@ -93,10 +95,10 @@ LL | let y: Result<&usize, &usize> = x; | = note: expected enum `Result<&usize, &usize>` found reference `&Result` -help: you can convert from `&Result` to `Result<&T, &E>` using `.as_ref()` +help: try using `.as_ref()` to convert `&Result` to `Result<&usize, &usize>` | LL | let y: Result<&usize, &usize> = x.as_ref(); - | ~~~~~~~~~~ + | +++++++++ error[E0308]: mismatched types --> $DIR/as-ref.rs:19:36 @@ -181,6 +183,22 @@ help: consider using `as_ref` instead LL | multiple_ref_result.as_ref().and_then(|arg| Ok(takes_ref(arg))); | +++++++++ -error: aborting due to 11 previous errors +error[E0308]: mismatched types + --> $DIR/as-ref.rs:28:32 + | +LL | let _: Result<&usize, _> = &Ok(42); + | ----------------- ^^^^^^^ expected `Result<&usize, _>`, found `&Result<{integer}, _>` + | | + | expected due to this + | + = note: expected enum `Result<&usize, _>` + found reference `&Result<{integer}, _>` +help: try using `.as_ref()` to convert `&Result<{integer}, _>` to `Result<&usize, _>` + | +LL - let _: Result<&usize, _> = &Ok(42); +LL + let _: Result<&usize, _> = Ok(42).as_ref(); + | + +error: aborting due to 12 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/suggestions/dont-suggest-try_into-in-macros.stderr b/tests/ui/suggestions/dont-suggest-try_into-in-macros.stderr index bc6342004f4db..319d866003ba8 100644 --- a/tests/ui/suggestions/dont-suggest-try_into-in-macros.stderr +++ b/tests/ui/suggestions/dont-suggest-try_into-in-macros.stderr @@ -1,13 +1,13 @@ error[E0308]: mismatched types - --> $DIR/dont-suggest-try_into-in-macros.rs:2:5 + --> $DIR/dont-suggest-try_into-in-macros.rs:2:23 | LL | assert_eq!(10u64, 10usize); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | expected `u64`, found `usize` - | expected because this is `u64` + | ^^^^^^^ expected `u64`, found `usize` | - = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) +help: change the type of the numeric literal from `usize` to `u64` + | +LL | assert_eq!(10u64, 10u64); + | ~~~ error: aborting due to previous error diff --git a/tests/ui/suggestions/while-let-typo.rs b/tests/ui/suggestions/while-let-typo.rs index dbbcdee3c19f6..21b254054bbc0 100644 --- a/tests/ui/suggestions/while-let-typo.rs +++ b/tests/ui/suggestions/while-let-typo.rs @@ -2,7 +2,7 @@ fn main() { let foo = Some(0); let bar = None; while Some(x) = foo {} //~ ERROR cannot find value `x` in this scope - while Some(foo) = bar {} + while Some(foo) = bar {} //~ ERROR mismatched types while 3 = foo {} //~ ERROR mismatched types while Some(3) = foo {} //~ ERROR invalid left-hand side of assignment while x = 5 {} //~ ERROR cannot find value `x` in this scope diff --git a/tests/ui/suggestions/while-let-typo.stderr b/tests/ui/suggestions/while-let-typo.stderr index 7cc2ed3149b14..69a7e5761d421 100644 --- a/tests/ui/suggestions/while-let-typo.stderr +++ b/tests/ui/suggestions/while-let-typo.stderr @@ -20,6 +20,17 @@ help: you might have meant to use pattern matching LL | while let x = 5 {} | +++ +error[E0308]: mismatched types + --> $DIR/while-let-typo.rs:5:11 + | +LL | while Some(foo) = bar {} + | ^^^^^^^^^^^^^^^ expected `bool`, found `()` + | +help: consider adding `let` + | +LL | while let Some(foo) = bar {} + | +++ + error[E0308]: mismatched types --> $DIR/while-let-typo.rs:6:11 | @@ -39,7 +50,7 @@ help: you might have meant to use pattern destructuring LL | while let Some(3) = foo {} | +++ -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors Some errors have detailed explanations: E0070, E0308, E0425. For more information about an error, try `rustc --explain E0070`. diff --git a/tests/ui/switched-expectations.stderr b/tests/ui/switched-expectations.stderr index 744d8483bd3de..6e1bbf701d77f 100644 --- a/tests/ui/switched-expectations.stderr +++ b/tests/ui/switched-expectations.stderr @@ -2,7 +2,9 @@ error[E0308]: mismatched types --> $DIR/switched-expectations.rs:3:30 | LL | let ref string: String = var; - | ^^^ expected `String`, found `i32` + | ^^^- help: try using a conversion method: `.to_string()` + | | + | expected `String`, found `i32` error: aborting due to previous error diff --git a/tests/ui/symbol-names/const-generics-structural-demangling.rs b/tests/ui/symbol-names/const-generics-structural-demangling.rs index df09ba494a74f..947fddf3f31b5 100644 --- a/tests/ui/symbol-names/const-generics-structural-demangling.rs +++ b/tests/ui/symbol-names/const-generics-structural-demangling.rs @@ -1,14 +1,13 @@ // build-fail // compile-flags: -C symbol-mangling-version=v0 --crate-name=c -// NOTE(eddyb) we need `core` for `core::option::Option`, normalize away its -// disambiguator hash, which can/should change (including between stage{1,2}). -// normalize-stderr-test: "core\[[0-9a-f]+\]" -> "core[HASH]" // normalize-stderr-test: "c\[[0-9a-f]+\]" -> "c[HASH]" #![feature(adt_const_params, decl_macro, rustc_attrs)] #![allow(incomplete_features)] +use std::marker::ConstParamTy; + pub struct RefByte; #[rustc_symbol_name] @@ -43,25 +42,31 @@ pub struct TupleByteBool; //~| ERROR demangling-alt(>) impl TupleByteBool<{(1, false)}> {} -pub struct OptionUsize>; +#[derive(PartialEq, Eq, ConstParamTy)] +pub enum MyOption { + Some(T), + None, +} + +pub struct OptionUsize>; // HACK(eddyb) the full mangling is only in `.stderr` because we can normalize // the `core` disambiguator hash away there, but not here. #[rustc_symbol_name] //~^ ERROR symbol-name //~| ERROR demangling -//~| ERROR demangling-alt(::None}>>) -impl OptionUsize<{None}> {} +//~| ERROR demangling-alt(::None}>>) +impl OptionUsize<{MyOption::None}> {} // HACK(eddyb) the full mangling is only in `.stderr` because we can normalize // the `core` disambiguator hash away there, but not here. #[rustc_symbol_name] //~^ ERROR symbol-name //~| ERROR demangling -//~| ERROR demangling-alt(::Some(0)}>>) -impl OptionUsize<{Some(0)}> {} +//~| ERROR demangling-alt(::Some(0)}>>) +impl OptionUsize<{MyOption::Some(0)}> {} -#[derive(PartialEq, Eq)] +#[derive(PartialEq, Eq, ConstParamTy)] pub struct Foo { s: &'static str, ch: char, @@ -78,7 +83,7 @@ impl Foo_<{Foo { s: "abc", ch: 'x', slice: &[1, 2, 3] }}> {} // NOTE(eddyb) this tests specifically the use of disambiguators in field names, // using macros 2.0 hygiene to create a `struct` with conflicting field names. macro duplicate_field_name_test($x:ident) { - #[derive(PartialEq, Eq)] + #[derive(PartialEq, Eq, ConstParamTy)] pub struct Bar { $x: u8, x: u16, diff --git a/tests/ui/symbol-names/const-generics-structural-demangling.stderr b/tests/ui/symbol-names/const-generics-structural-demangling.stderr index a4c997477ee79..96dea154d05c0 100644 --- a/tests/ui/symbol-names/const-generics-structural-demangling.stderr +++ b/tests/ui/symbol-names/const-generics-structural-demangling.stderr @@ -1,131 +1,131 @@ error: symbol-name(_RMCsCRATE_HASH_1cINtB_7RefByteKRh7b_E) - --> $DIR/const-generics-structural-demangling.rs:14:1 + --> $DIR/const-generics-structural-demangling.rs:13:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: demangling(>) - --> $DIR/const-generics-structural-demangling.rs:14:1 + --> $DIR/const-generics-structural-demangling.rs:13:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: demangling-alt(>) - --> $DIR/const-generics-structural-demangling.rs:14:1 + --> $DIR/const-generics-structural-demangling.rs:13:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: symbol-name(_RMs_CsCRATE_HASH_1cINtB_6RefZstKRAEE) - --> $DIR/const-generics-structural-demangling.rs:24:1 + --> $DIR/const-generics-structural-demangling.rs:23:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: demangling(>) - --> $DIR/const-generics-structural-demangling.rs:24:1 + --> $DIR/const-generics-structural-demangling.rs:23:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: demangling-alt(>) - --> $DIR/const-generics-structural-demangling.rs:24:1 + --> $DIR/const-generics-structural-demangling.rs:23:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: symbol-name(_RMs0_CsCRATE_HASH_1cINtB_11Array3BytesKAh1_h2_h3_EE) - --> $DIR/const-generics-structural-demangling.rs:32:1 + --> $DIR/const-generics-structural-demangling.rs:31:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: demangling(>) - --> $DIR/const-generics-structural-demangling.rs:32:1 + --> $DIR/const-generics-structural-demangling.rs:31:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: demangling-alt(>) - --> $DIR/const-generics-structural-demangling.rs:32:1 + --> $DIR/const-generics-structural-demangling.rs:31:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: symbol-name(_RMs1_CsCRATE_HASH_1cINtB_13TupleByteBoolKTh1_b0_EE) - --> $DIR/const-generics-structural-demangling.rs:40:1 + --> $DIR/const-generics-structural-demangling.rs:39:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: demangling(>) - --> $DIR/const-generics-structural-demangling.rs:40:1 + --> $DIR/const-generics-structural-demangling.rs:39:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: demangling-alt(>) - --> $DIR/const-generics-structural-demangling.rs:40:1 + --> $DIR/const-generics-structural-demangling.rs:39:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ -error: symbol-name(_RMs2_CsCRATE_HASH_1cINtB_11OptionUsizeKVNtINtNtCsCRATE_HASH_4core6option6OptionjE4NoneUE) - --> $DIR/const-generics-structural-demangling.rs:50:1 +error: symbol-name(_RMs2_CsCRATE_HASH_1cINtB_11OptionUsizeKVNtINtB_8MyOptionjE4NoneUE) + --> $DIR/const-generics-structural-demangling.rs:55:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ -error: demangling(::None}>>) - --> $DIR/const-generics-structural-demangling.rs:50:1 +error: demangling(::None}>>) + --> $DIR/const-generics-structural-demangling.rs:55:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ -error: demangling-alt(::None}>>) - --> $DIR/const-generics-structural-demangling.rs:50:1 +error: demangling-alt(::None}>>) + --> $DIR/const-generics-structural-demangling.rs:55:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ -error: symbol-name(_RMs3_CsCRATE_HASH_1cINtB_11OptionUsizeKVNtINtNtCsCRATE_HASH_4core6option6OptionjE4SomeTj0_EE) - --> $DIR/const-generics-structural-demangling.rs:58:1 +error: symbol-name(_RMs3_CsCRATE_HASH_1cINtB_11OptionUsizeKVNtINtB_8MyOptionjE4SomeTj0_EE) + --> $DIR/const-generics-structural-demangling.rs:63:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ -error: demangling(::Some(0usize)}>>) - --> $DIR/const-generics-structural-demangling.rs:58:1 +error: demangling(::Some(0usize)}>>) + --> $DIR/const-generics-structural-demangling.rs:63:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ -error: demangling-alt(::Some(0)}>>) - --> $DIR/const-generics-structural-demangling.rs:58:1 +error: demangling-alt(::Some(0)}>>) + --> $DIR/const-generics-structural-demangling.rs:63:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: symbol-name(_RMs4_CsCRATE_HASH_1cINtB_4Foo_KVNtB_3FooS1sRe616263_2chc78_5sliceRAh1_h2_h3_EEE) - --> $DIR/const-generics-structural-demangling.rs:72:1 + --> $DIR/const-generics-structural-demangling.rs:77:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: demangling(>) - --> $DIR/const-generics-structural-demangling.rs:72:1 + --> $DIR/const-generics-structural-demangling.rs:77:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ error: demangling-alt(>) - --> $DIR/const-generics-structural-demangling.rs:72:1 + --> $DIR/const-generics-structural-demangling.rs:77:1 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ -error: symbol-name(_RMs9_CsCRATE_HASH_1cINtB_4Bar_KVNtB_3BarS1xh7b_s_1xt1000_EE) - --> $DIR/const-generics-structural-demangling.rs:88:5 +error: symbol-name(_RMsf_CsCRATE_HASH_1cINtB_4Bar_KVNtB_3BarS1xh7b_s_1xt1000_EE) + --> $DIR/const-generics-structural-demangling.rs:93:5 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ @@ -136,7 +136,7 @@ LL | duplicate_field_name_test!(x); = note: this error originates in the macro `duplicate_field_name_test` (in Nightly builds, run with -Z macro-backtrace for more info) error: demangling(>) - --> $DIR/const-generics-structural-demangling.rs:88:5 + --> $DIR/const-generics-structural-demangling.rs:93:5 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ @@ -147,7 +147,7 @@ LL | duplicate_field_name_test!(x); = note: this error originates in the macro `duplicate_field_name_test` (in Nightly builds, run with -Z macro-backtrace for more info) error: demangling-alt(>) - --> $DIR/const-generics-structural-demangling.rs:88:5 + --> $DIR/const-generics-structural-demangling.rs:93:5 | LL | #[rustc_symbol_name] | ^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/traits/issue-91949-hangs-on-recursion.rs b/tests/ui/traits/issue-91949-hangs-on-recursion.rs index 4eca643a92d4c..312d5d08c7df8 100644 --- a/tests/ui/traits/issue-91949-hangs-on-recursion.rs +++ b/tests/ui/traits/issue-91949-hangs-on-recursion.rs @@ -3,6 +3,7 @@ // error-pattern: overflow evaluating the requirement ` as Iterator>::Item == ()` // error-pattern: function cannot return without recursing // normalize-stderr-test: "long-type-\d+" -> "long-type-hash" +// ignore-compare-mode-next-solver (hangs) // Regression test for #91949. // This hanged *forever* on 1.56, fixed by #90423. diff --git a/tests/ui/traits/issue-91949-hangs-on-recursion.stderr b/tests/ui/traits/issue-91949-hangs-on-recursion.stderr index 144990d50f000..c721dd41a2c15 100644 --- a/tests/ui/traits/issue-91949-hangs-on-recursion.stderr +++ b/tests/ui/traits/issue-91949-hangs-on-recursion.stderr @@ -1,5 +1,5 @@ warning: function cannot return without recursing - --> $DIR/issue-91949-hangs-on-recursion.rs:23:1 + --> $DIR/issue-91949-hangs-on-recursion.rs:24:1 | LL | / fn recurse(elements: T) -> Vec LL | | where @@ -16,7 +16,7 @@ error[E0275]: overflow evaluating the requirement ` as Iter | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "512"]` attribute to your crate (`issue_91949_hangs_on_recursion`) note: required for `IteratorOfWrapped<(), std::iter::Empty<()>>` to implement `Iterator` - --> $DIR/issue-91949-hangs-on-recursion.rs:16:32 + --> $DIR/issue-91949-hangs-on-recursion.rs:17:32 | LL | impl> Iterator for IteratorOfWrapped { | -------- ^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/traits/new-solver/array-default.rs b/tests/ui/traits/new-solver/array-default.rs new file mode 100644 index 0000000000000..5077137b09b4b --- /dev/null +++ b/tests/ui/traits/new-solver/array-default.rs @@ -0,0 +1,8 @@ +// compile-flags: -Ztrait-solver=next +// check-pass + +fn has_default() where [(); N]: Default {} + +fn main() { + has_default::<1>(); +} diff --git a/tests/ui/traits/new-solver/dedup-regions.rs b/tests/ui/traits/new-solver/dedup-regions.rs new file mode 100644 index 0000000000000..f376f39a5a662 --- /dev/null +++ b/tests/ui/traits/new-solver/dedup-regions.rs @@ -0,0 +1,31 @@ +// compile-flags: -Ztrait-solver=next +// check-pass + +struct A(*mut ()); + +unsafe impl Send for A where A: 'static {} + +macro_rules! mk { + ($name:ident $ty:ty) => { + struct $name($ty, $ty, $ty, $ty, $ty, $ty, $ty, $ty, $ty, $ty); + }; +} + +mk!(B A); +mk!(C B); +mk!(D C); +mk!(E D); +mk!(F E); +mk!(G F); +mk!(H G); +mk!(I H); +mk!(J I); +mk!(K J); +mk!(L K); +mk!(M L); + +fn needs_send() {} + +fn main() { + needs_send::(); +} diff --git a/tests/ui/traits/new-solver/normalized-const-built-in-op.rs b/tests/ui/traits/new-solver/normalized-const-built-in-op.rs new file mode 100644 index 0000000000000..2443e51781391 --- /dev/null +++ b/tests/ui/traits/new-solver/normalized-const-built-in-op.rs @@ -0,0 +1,11 @@ +// compile-flags: -Ztrait-solver=next +// check-pass + +const fn foo() { + let mut x = [1, 2, 3]; + // We need to fix up `<<[i32; 3] as Index>::Output as AddAssign>` + // to be treated like a built-in operation. + x[1] += 5; +} + +fn main() {} diff --git a/tests/ui/traits/new-solver/slice-match-byte-lit.rs b/tests/ui/traits/new-solver/slice-match-byte-lit.rs new file mode 100644 index 0000000000000..4f848062595da --- /dev/null +++ b/tests/ui/traits/new-solver/slice-match-byte-lit.rs @@ -0,0 +1,11 @@ +// compile-flags: -Ztrait-solver=next +// check-pass + +fn test(s: &[u8]) { + match &s[0..3] { + b"uwu" => {} + _ => {} + } +} + +fn main() {} diff --git a/tests/ui/traits/new-solver/structural-resolve-field.rs b/tests/ui/traits/new-solver/structural-resolve-field.rs index c492d927696bd..01899c9ad645f 100644 --- a/tests/ui/traits/new-solver/structural-resolve-field.rs +++ b/tests/ui/traits/new-solver/structural-resolve-field.rs @@ -1,35 +1,13 @@ // compile-flags: -Ztrait-solver=next // check-pass +#[derive(Default)] struct Foo { x: i32, } -impl MyDefault for Foo { - fn my_default() -> Self { - Self { - x: 0, - } - } -} - -trait MyDefault { - fn my_default() -> Self; -} - -impl MyDefault for [Foo; 0] { - fn my_default() -> Self { - [] - } -} -impl MyDefault for [Foo; 1] { - fn my_default() -> Self { - [Foo::my_default(); 1] - } -} - fn main() { - let mut xs = <[Foo; 1]>::my_default(); + let mut xs = <[Foo; 1]>::default(); xs[0].x = 1; (&mut xs[0]).x = 2; } diff --git a/tests/ui/traits/new-solver/tait-eq-proj-2.rs b/tests/ui/traits/new-solver/tait-eq-proj-2.rs new file mode 100644 index 0000000000000..99a3d02bd1aea --- /dev/null +++ b/tests/ui/traits/new-solver/tait-eq-proj-2.rs @@ -0,0 +1,21 @@ +// compile-flags: -Ztrait-solver=next +// check-pass + +#![feature(type_alias_impl_trait)] + +// Similar to tests/ui/traits/new-solver/tait-eq-proj.rs +// but check the alias-sub relation in the other direction. + +type Tait = impl Iterator; + +fn mk() -> T { todo!() } + +fn a() { + let x: Tait = mk(); + let mut array = mk(); + let mut z = IntoIterator::into_iter(array); + z = x; + array = [0i32; 32]; +} + +fn main() {} diff --git a/tests/ui/traits/new-solver/tait-eq-proj.rs b/tests/ui/traits/new-solver/tait-eq-proj.rs new file mode 100644 index 0000000000000..01141b2819a8d --- /dev/null +++ b/tests/ui/traits/new-solver/tait-eq-proj.rs @@ -0,0 +1,35 @@ +// compile-flags: -Ztrait-solver=next +// check-pass + +#![feature(type_alias_impl_trait)] + +type Tait = impl Iterator; + +/* + +Consider the goal - AliasRelate(Tait, <[i32; 32] as IntoIterator>::IntoIter) +which is registered on the line above. + +A. SubstRelate - fails (of course). + +B. NormalizesToRhs - Tait normalizes-to <[i32; 32] as IntoIterator>::IntoIter + * infer definition - Tait := <[i32; 32] as IntoIterator>::IntoIter + +C. NormalizesToLhs - <[i32; 32] as IntoIterator>::IntoIter normalizes-to Tait + * Find impl candidate, after substitute - std::array::IntoIter + * Equate std::array::IntoIter and Tait + * infer definition - Tait := std::array::IntoIter + +B and C are not equal, but they are equivalent modulo normalization. + +We get around this by evaluating both the NormalizesToRhs and NormalizesToLhs +goals together. Essentially: + A alias-relate B if A normalizes-to B and B normalizes-to A. + +*/ + +fn a() { + let _: Tait = IntoIterator::into_iter([0i32; 32]); +} + +fn main() {} diff --git a/tests/ui/traits/new-solver/tait-eq-tait.rs b/tests/ui/traits/new-solver/tait-eq-tait.rs new file mode 100644 index 0000000000000..532c4c39bd499 --- /dev/null +++ b/tests/ui/traits/new-solver/tait-eq-tait.rs @@ -0,0 +1,17 @@ +// compile-flags: -Ztrait-solver=next +// check-pass + +// Not exactly sure if this is the inference behavior we *want*, +// but it is a side-effect of the lazy normalization of TAITs. + +#![feature(type_alias_impl_trait)] + +type Tait = impl Sized; +type Tait2 = impl Sized; + +fn mk() -> T { todo!() } + +fn main() { + let x: Tait = 1u32; + let y: Tait2 = x; +} diff --git a/tests/ui/traits/new-solver/unevaluated-const-impl-trait-ref.fails.stderr b/tests/ui/traits/new-solver/unevaluated-const-impl-trait-ref.fails.stderr new file mode 100644 index 0000000000000..072ac32a5de97 --- /dev/null +++ b/tests/ui/traits/new-solver/unevaluated-const-impl-trait-ref.fails.stderr @@ -0,0 +1,18 @@ +error[E0277]: the trait bound `(): Trait<1>` is not satisfied + --> $DIR/unevaluated-const-impl-trait-ref.rs:20:13 + | +LL | needs::<1>(); + | ^ the trait `Trait<1>` is not implemented for `()` + | + = help: the following other types implement trait `Trait`: + <() as Trait<0>> + <() as Trait<2>> +note: required by a bound in `needs` + --> $DIR/unevaluated-const-impl-trait-ref.rs:10:38 + | +LL | fn needs() where (): Trait {} + | ^^^^^^^^ required by this bound in `needs` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/new-solver/unevaluated-const-impl-trait-ref.rs b/tests/ui/traits/new-solver/unevaluated-const-impl-trait-ref.rs new file mode 100644 index 0000000000000..26c595bc97428 --- /dev/null +++ b/tests/ui/traits/new-solver/unevaluated-const-impl-trait-ref.rs @@ -0,0 +1,22 @@ +// compile-flags: -Ztrait-solver=next +// revisions: works fails +//[works] check-pass + +trait Trait {} + +impl Trait<{ 1 - 1 }> for () {} +impl Trait<{ 1 + 1 }> for () {} + +fn needs() where (): Trait {} + +#[cfg(works)] +fn main() { + needs::<0>(); + needs::<2>(); +} + +#[cfg(fails)] +fn main() { + needs::<1>(); + //[fails]~^ ERROR the trait bound `(): Trait<1>` is not satisfied +} diff --git a/tests/ui/typeck/derive-sugg-arg-arity.rs b/tests/ui/typeck/derive-sugg-arg-arity.rs new file mode 100644 index 0000000000000..094c93a8535d8 --- /dev/null +++ b/tests/ui/typeck/derive-sugg-arg-arity.rs @@ -0,0 +1,8 @@ +pub struct A; + +fn main() { + match () { + _ => match A::partial_cmp() {}, + //~^ ERROR the function or associated item `partial_cmp` exists for struct `A`, but its trait bounds were not satisfied + } +} diff --git a/tests/ui/typeck/derive-sugg-arg-arity.stderr b/tests/ui/typeck/derive-sugg-arg-arity.stderr new file mode 100644 index 0000000000000..5b4c481719830 --- /dev/null +++ b/tests/ui/typeck/derive-sugg-arg-arity.stderr @@ -0,0 +1,31 @@ +error[E0599]: the function or associated item `partial_cmp` exists for struct `A`, but its trait bounds were not satisfied + --> $DIR/derive-sugg-arg-arity.rs:5:23 + | +LL | pub struct A; + | ------------ + | | + | function or associated item `partial_cmp` not found for this struct + | doesn't satisfy `A: Iterator` + | doesn't satisfy `A: PartialOrd<_>` +... +LL | _ => match A::partial_cmp() {}, + | ^^^^^^^^^^^ function or associated item cannot be called on `A` due to unsatisfied trait bounds + | + = note: the following trait bounds were not satisfied: + `A: PartialOrd<_>` + which is required by `&A: PartialOrd<&_>` + `A: PartialOrd<_>` + which is required by `&mut A: PartialOrd<&mut _>` + `A: Iterator` + which is required by `&mut A: Iterator` +note: the trait `Iterator` must be implemented + --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL +help: consider annotating `A` with `#[derive(PartialEq, PartialOrd)]` + | +LL + #[derive(PartialEq, PartialOrd)] +LL | pub struct A; + | + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/typeck/issue-112385-while-assign-lhs-place-expr-ice.rs b/tests/ui/typeck/issue-112385-while-assign-lhs-place-expr-ice.rs new file mode 100644 index 0000000000000..3cb011dc06fcd --- /dev/null +++ b/tests/ui/typeck/issue-112385-while-assign-lhs-place-expr-ice.rs @@ -0,0 +1,9 @@ +// Previously, the while loop with an assignment statement (mistakenly) as the condition +// which has a place expr as the LHS would trigger an ICE in typeck. +// Reduced from https://github.com/rust-lang/rust/issues/112385. + +fn main() { + let foo = Some(()); + while Some(foo) = None {} + //~^ ERROR mismatched types +} diff --git a/tests/ui/typeck/issue-112385-while-assign-lhs-place-expr-ice.stderr b/tests/ui/typeck/issue-112385-while-assign-lhs-place-expr-ice.stderr new file mode 100644 index 0000000000000..cf2648d084079 --- /dev/null +++ b/tests/ui/typeck/issue-112385-while-assign-lhs-place-expr-ice.stderr @@ -0,0 +1,14 @@ +error[E0308]: mismatched types + --> $DIR/issue-112385-while-assign-lhs-place-expr-ice.rs:7:11 + | +LL | while Some(foo) = None {} + | ^^^^^^^^^^^^^^^^ expected `bool`, found `()` + | +help: consider adding `let` + | +LL | while let Some(foo) = None {} + | +++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/typeck/issue-89856.stderr b/tests/ui/typeck/issue-89856.stderr index bd76f17246821..0db3e67ede0d4 100644 --- a/tests/ui/typeck/issue-89856.stderr +++ b/tests/ui/typeck/issue-89856.stderr @@ -13,7 +13,7 @@ note: function defined here | LL | fn take_str_maybe(_: Option<&str>) { } | ^^^^^^^^^^^^^^ --------------- -help: try converting the passed type into a `&str` +help: try using `.as_deref()` to convert `Option` to `Option<&str>` | LL | take_str_maybe(option.as_deref()); | +++++++++++ diff --git a/tests/ui/typeck/return-dyn-type-mismatch-2.rs b/tests/ui/typeck/return-dyn-type-mismatch-2.rs new file mode 100644 index 0000000000000..328f154dcbc3e --- /dev/null +++ b/tests/ui/typeck/return-dyn-type-mismatch-2.rs @@ -0,0 +1,11 @@ +trait Trait {} + +fn foo() -> dyn Trait +where + dyn Trait: Sized, // pesky sized predicate +{ + 42 + //~^ ERROR mismatched types +} + +fn main() {} diff --git a/tests/ui/typeck/return-dyn-type-mismatch-2.stderr b/tests/ui/typeck/return-dyn-type-mismatch-2.stderr new file mode 100644 index 0000000000000..9c368e83834b1 --- /dev/null +++ b/tests/ui/typeck/return-dyn-type-mismatch-2.stderr @@ -0,0 +1,15 @@ +error[E0308]: mismatched types + --> $DIR/return-dyn-type-mismatch-2.rs:7:5 + | +LL | fn foo() -> dyn Trait + | ------------ expected `(dyn Trait + 'static)` because of return type +... +LL | 42 + | ^^ expected `dyn Trait`, found integer + | + = note: expected trait object `(dyn Trait + 'static)` + found type `{integer}` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/typeck/return-dyn-type-mismatch.rs b/tests/ui/typeck/return-dyn-type-mismatch.rs new file mode 100644 index 0000000000000..93718f70f4193 --- /dev/null +++ b/tests/ui/typeck/return-dyn-type-mismatch.rs @@ -0,0 +1,21 @@ +pub trait TestTrait { + type MyType; + + fn func() -> Option + where + Self: Sized; +} + +impl dyn TestTrait +where + Self: Sized, // pesky sized predicate +{ + fn other_func() -> dyn TestTrait { + match Self::func() { + None => None, + //~^ ERROR mismatched types + } + } +} + +fn main() {} diff --git a/tests/ui/typeck/return-dyn-type-mismatch.stderr b/tests/ui/typeck/return-dyn-type-mismatch.stderr new file mode 100644 index 0000000000000..9d0a609d87f81 --- /dev/null +++ b/tests/ui/typeck/return-dyn-type-mismatch.stderr @@ -0,0 +1,15 @@ +error[E0308]: mismatched types + --> $DIR/return-dyn-type-mismatch.rs:15:21 + | +LL | fn other_func() -> dyn TestTrait { + | ------------------------- expected `(dyn TestTrait + 'static)` because of return type +LL | match Self::func() { +LL | None => None, + | ^^^^ expected `dyn TestTrait`, found `Option<_>` + | + = note: expected trait object `(dyn TestTrait + 'static)` + found enum `Option<_>` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/union/union-fields-2.mirunsafeck.stderr b/tests/ui/union/union-fields-2.mirunsafeck.stderr index 90ad16402f7db..1157f0c2ae770 100644 --- a/tests/ui/union/union-fields-2.mirunsafeck.stderr +++ b/tests/ui/union/union-fields-2.mirunsafeck.stderr @@ -16,7 +16,7 @@ error[E0560]: union `U` has no field named `c` LL | let u = U { a: 0, b: 1, c: 2 }; | ^ `U` does not have this field | - = note: available fields are: `a`, `b` + = note: all struct fields are already assigned error[E0784]: union expressions should have exactly one field --> $DIR/union-fields-2.rs:13:13 diff --git a/tests/ui/union/union-fields-2.thirunsafeck.stderr b/tests/ui/union/union-fields-2.thirunsafeck.stderr index 90ad16402f7db..1157f0c2ae770 100644 --- a/tests/ui/union/union-fields-2.thirunsafeck.stderr +++ b/tests/ui/union/union-fields-2.thirunsafeck.stderr @@ -16,7 +16,7 @@ error[E0560]: union `U` has no field named `c` LL | let u = U { a: 0, b: 1, c: 2 }; | ^ `U` does not have this field | - = note: available fields are: `a`, `b` + = note: all struct fields are already assigned error[E0784]: union expressions should have exactly one field --> $DIR/union-fields-2.rs:13:13 diff --git a/tests/ui/unsized-locals/issue-67981.stderr b/tests/ui/unsized-locals/issue-67981.stderr index a4b179ae2fd12..13fdc037ac569 100644 --- a/tests/ui/unsized-locals/issue-67981.stderr +++ b/tests/ui/unsized-locals/issue-67981.stderr @@ -5,10 +5,7 @@ LL | let f: fn([u8]) = |_| {}; | ^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `[u8]` -help: function arguments must have a statically known size, borrowed types always have a known size - | -LL | let f: fn([u8]) = |&_| {}; - | + + = note: all function arguments must have a statically known size error: aborting due to previous error diff --git a/triagebot.toml b/triagebot.toml index b1f4e9c77fbd5..0f0b31e9f38a1 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -372,6 +372,14 @@ cc = ["@GuillaumeGomez"] message = "Some changes might have occurred in exhaustiveness checking" cc = ["@Nadrieril"] +[mentions."library/portable-simd"] +message = """ +Portable SIMD is developed in its own repository. If possible, consider \ +making this change to [rust-lang/portable-simd](https://github.com/rust-lang/portable-simd) \ +instead. +""" +cc = ["@calebzulawski", "@programmerjake"] + [mentions."src/librustdoc/clean/types.rs"] cc = ["@camelid"] @@ -496,6 +504,7 @@ compiler-team-contributors = [ "@TaKO8Ki", "@WaffleLapkin", "@b-naber", + "@fee1-dead", ] compiler = [ "compiler-team", diff --git a/x.py b/x.py index b8cdf67712c43..7163df5cfe99b 100755 --- a/x.py +++ b/x.py @@ -9,12 +9,17 @@ if __name__ == '__main__': import os import sys + import warnings + from inspect import cleandoc + + major = sys.version_info.major + minor = sys.version_info.minor # If this is python2, check if python3 is available and re-execute with that # interpreter. Only python3 allows downloading CI LLVM. # # This matters if someone's system `python` is python2. - if sys.version_info.major < 3: + if major < 3: try: os.execvp("py", ["py", "-3"] + sys.argv) except OSError: @@ -24,6 +29,19 @@ # Python 3 isn't available, fall back to python 2 pass + # soft deprecation of old python versions + skip_check = os.environ.get("RUST_IGNORE_OLD_PYTHON") == "1" + if major < 3 or (major == 3 and minor < 6): + msg = cleandoc(""" + Using python {}.{} but >= 3.6 is recommended. Your python version + should continue to work for the near future, but this will + eventually change. If python >= 3.6 is not available on your system, + please file an issue to help us understand timelines. + + This message can be suppressed by setting `RUST_IGNORE_OLD_PYTHON=1` + """.format(major, minor)) + warnings.warn(msg) + rust_dir = os.path.dirname(os.path.abspath(__file__)) # For the import below, have Python search in src/bootstrap first. sys.path.insert(0, os.path.join(rust_dir, "src", "bootstrap"))