diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 293e65ce8726..2c176828c841 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -3,11 +3,12 @@ //! Note that HIR pretty printing is layered on top of this crate. mod expr; +mod fixup; mod item; use crate::pp::Breaks::{Consistent, Inconsistent}; use crate::pp::{self, Breaks}; -use crate::pprust::state::expr::FixupContext; +use crate::pprust::state::fixup::FixupContext; use ast::TraitBoundModifiers; use rustc_ast::attr::AttrIdGenerator; use rustc_ast::ptr::P; @@ -15,7 +16,6 @@ use rustc_ast::token::{self, BinOpToken, CommentKind, Delimiter, Nonterminal, To use rustc_ast::tokenstream::{Spacing, TokenStream, TokenTree}; use rustc_ast::util::classify; use rustc_ast::util::comments::{Comment, CommentStyle}; -use rustc_ast::util::parser; use rustc_ast::{self as ast, AttrArgs, AttrArgsEq, BlockCheckMode, PatKind}; use rustc_ast::{attr, BindingMode, ByRef, DelimArgs, RangeEnd, RangeSyntax, Term}; use rustc_ast::{GenericArg, GenericBound, SelfKind}; @@ -1252,22 +1252,14 @@ impl<'a> State<'a> { ast::StmtKind::Item(item) => self.print_item(item), ast::StmtKind::Expr(expr) => { self.space_if_not_bol(); - self.print_expr_outer_attr_style( - expr, - false, - FixupContext { stmt: true, ..FixupContext::default() }, - ); + self.print_expr_outer_attr_style(expr, false, FixupContext::new_stmt()); if classify::expr_requires_semi_to_be_stmt(expr) { self.word(";"); } } ast::StmtKind::Semi(expr) => { self.space_if_not_bol(); - self.print_expr_outer_attr_style( - expr, - false, - FixupContext { stmt: true, ..FixupContext::default() }, - ); + self.print_expr_outer_attr_style(expr, false, FixupContext::new_stmt()); self.word(";"); } ast::StmtKind::Empty => { @@ -1319,11 +1311,7 @@ impl<'a> State<'a> { ast::StmtKind::Expr(expr) if i == blk.stmts.len() - 1 => { self.maybe_print_comment(st.span.lo()); self.space_if_not_bol(); - self.print_expr_outer_attr_style( - expr, - false, - FixupContext { stmt: true, ..FixupContext::default() }, - ); + self.print_expr_outer_attr_style(expr, false, FixupContext::new_stmt()); self.maybe_print_trailing_comment(expr.span, Some(blk.span.hi())); } _ => self.print_stmt(st), @@ -1367,8 +1355,7 @@ impl<'a> State<'a> { self.word_space("="); self.print_expr_cond_paren( expr, - fixup.parenthesize_exterior_struct_lit && parser::contains_exterior_struct_lit(expr) - || parser::needs_par_as_let_scrutinee(expr.precedence().order()), + fixup.needs_par_as_let_scrutinee(expr), FixupContext::default(), ); } diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index 6eff70410cbb..4cbdc9f256df 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -1,10 +1,10 @@ use crate::pp::Breaks::Inconsistent; +use crate::pprust::state::fixup::FixupContext; use crate::pprust::state::{AnnNode, PrintState, State, INDENT_UNIT}; use ast::{ForLoopKind, MatchKind}; use itertools::{Itertools, Position}; use rustc_ast::ptr::P; use rustc_ast::token; -use rustc_ast::util::classify; use rustc_ast::util::literal::escape_byte_str_symbol; use rustc_ast::util::parser::{self, AssocOp, Fixity}; use rustc_ast::{self as ast, BlockCheckMode}; @@ -14,78 +14,6 @@ use rustc_ast::{ }; use std::fmt::Write; -#[derive(Copy, Clone, Debug)] -pub(crate) struct FixupContext { - /// Print expression such that it can be parsed back as a statement - /// consisting of the original expression. - /// - /// The effect of this is for binary operators in statement position to set - /// `leftmost_subexpression_in_stmt` when printing their left-hand operand. - /// - /// ```ignore (illustrative) - /// (match x {}) - 1; // match needs parens when LHS of binary operator - /// - /// match x {}; // not when its own statement - /// ``` - pub stmt: bool, - - /// This is the difference between: - /// - /// ```ignore (illustrative) - /// (match x {}) - 1; // subexpression needs parens - /// - /// let _ = match x {} - 1; // no parens - /// ``` - /// - /// There are 3 distinguishable contexts in which `print_expr` might be - /// called with the expression `$match` as its argument, where `$match` - /// represents an expression of kind `ExprKind::Match`: - /// - /// - stmt=false leftmost_subexpression_in_stmt=false - /// - /// Example: `let _ = $match - 1;` - /// - /// No parentheses required. - /// - /// - stmt=false leftmost_subexpression_in_stmt=true - /// - /// Example: `$match - 1;` - /// - /// Must parenthesize `($match)`, otherwise parsing back the output as a - /// statement would terminate the statement after the closing brace of - /// the match, parsing `-1;` as a separate statement. - /// - /// - stmt=true leftmost_subexpression_in_stmt=false - /// - /// Example: `$match;` - /// - /// No parentheses required. - pub leftmost_subexpression_in_stmt: bool, - - /// This is the difference between: - /// - /// ```ignore (illustrative) - /// if let _ = (Struct {}) {} // needs parens - /// - /// match () { - /// () if let _ = Struct {} => {} // no parens - /// } - /// ``` - pub parenthesize_exterior_struct_lit: bool, -} - -/// The default amount of fixing is minimal fixing. Fixups should be turned on -/// in a targeted fashion where needed. -impl Default for FixupContext { - fn default() -> Self { - FixupContext { - stmt: false, - leftmost_subexpression_in_stmt: false, - parenthesize_exterior_struct_lit: false, - } - } -} - impl<'a> State<'a> { fn print_else(&mut self, els: Option<&ast::Expr>) { if let Some(_else) = els { @@ -136,9 +64,7 @@ impl<'a> State<'a> { /// Prints an expr using syntax that's acceptable in a condition position, such as the `cond` in /// `if cond { ... }`. fn print_expr_as_cond(&mut self, expr: &ast::Expr) { - let fixup = - FixupContext { parenthesize_exterior_struct_lit: true, ..FixupContext::default() }; - self.print_expr_cond_paren(expr, Self::cond_needs_par(expr), fixup) + self.print_expr_cond_paren(expr, Self::cond_needs_par(expr), FixupContext::new_cond()) } /// Does `expr` need parentheses when printed in a condition position? @@ -310,15 +236,7 @@ impl<'a> State<'a> { // because the latter is valid syntax but with the incorrect meaning. // It's a match-expression followed by tuple-expression, not a function // call. - self.print_expr_maybe_paren( - func, - prec, - FixupContext { - stmt: false, - leftmost_subexpression_in_stmt: fixup.stmt || fixup.leftmost_subexpression_in_stmt, - ..fixup - }, - ); + self.print_expr_maybe_paren(func, prec, fixup.leftmost_subexpression()); self.print_call_post(args) } @@ -387,33 +305,17 @@ impl<'a> State<'a> { _ => left_prec, }; - self.print_expr_maybe_paren( - lhs, - left_prec, - FixupContext { - stmt: false, - leftmost_subexpression_in_stmt: fixup.stmt || fixup.leftmost_subexpression_in_stmt, - ..fixup - }, - ); + self.print_expr_maybe_paren(lhs, left_prec, fixup.leftmost_subexpression()); self.space(); self.word_space(op.node.as_str()); - self.print_expr_maybe_paren( - rhs, - right_prec, - FixupContext { stmt: false, leftmost_subexpression_in_stmt: false, ..fixup }, - ); + self.print_expr_maybe_paren(rhs, right_prec, fixup.subsequent_subexpression()); } fn print_expr_unary(&mut self, op: ast::UnOp, expr: &ast::Expr, fixup: FixupContext) { self.word(op.as_str()); - self.print_expr_maybe_paren( - expr, - parser::PREC_PREFIX, - FixupContext { stmt: false, leftmost_subexpression_in_stmt: false, ..fixup }, - ); + self.print_expr_maybe_paren(expr, parser::PREC_PREFIX, fixup.subsequent_subexpression()); } fn print_expr_addr_of( @@ -431,11 +333,7 @@ impl<'a> State<'a> { self.print_mutability(mutability, true); } } - self.print_expr_maybe_paren( - expr, - parser::PREC_PREFIX, - FixupContext { stmt: false, leftmost_subexpression_in_stmt: false, ..fixup }, - ); + self.print_expr_maybe_paren(expr, parser::PREC_PREFIX, fixup.subsequent_subexpression()); } pub(super) fn print_expr(&mut self, expr: &ast::Expr, fixup: FixupContext) { @@ -470,8 +368,7 @@ impl<'a> State<'a> { // // Same applies to a small set of other expression kinds which eagerly // terminate a statement which opens with them. - let needs_par = - fixup.leftmost_subexpression_in_stmt && !classify::expr_requires_semi_to_be_stmt(expr); + let needs_par = fixup.would_cause_statement_boundary(expr); if needs_par { self.popen(); fixup = FixupContext::default(); @@ -519,16 +416,7 @@ impl<'a> State<'a> { } ast::ExprKind::Cast(expr, ty) => { let prec = AssocOp::As.precedence() as i8; - self.print_expr_maybe_paren( - expr, - prec, - FixupContext { - stmt: false, - leftmost_subexpression_in_stmt: fixup.stmt - || fixup.leftmost_subexpression_in_stmt, - ..fixup - }, - ); + self.print_expr_maybe_paren(expr, prec, fixup.leftmost_subexpression()); self.space(); self.word_space("as"); self.print_type(ty); @@ -660,70 +548,34 @@ impl<'a> State<'a> { self.print_block_with_attrs(blk, attrs); } ast::ExprKind::Await(expr, _) => { - // Same fixups as ExprKind::MethodCall. self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX, fixup); self.word(".await"); } ast::ExprKind::Assign(lhs, rhs, _) => { - // Same fixups as ExprKind::Binary. let prec = AssocOp::Assign.precedence() as i8; - self.print_expr_maybe_paren( - lhs, - prec + 1, - FixupContext { - stmt: false, - leftmost_subexpression_in_stmt: fixup.stmt - || fixup.leftmost_subexpression_in_stmt, - ..fixup - }, - ); + self.print_expr_maybe_paren(lhs, prec + 1, fixup.leftmost_subexpression()); self.space(); self.word_space("="); - self.print_expr_maybe_paren( - rhs, - prec, - FixupContext { stmt: false, leftmost_subexpression_in_stmt: false, ..fixup }, - ); + self.print_expr_maybe_paren(rhs, prec, fixup.subsequent_subexpression()); } ast::ExprKind::AssignOp(op, lhs, rhs) => { - // Same fixups as ExprKind::Binary. let prec = AssocOp::Assign.precedence() as i8; - self.print_expr_maybe_paren( - lhs, - prec + 1, - FixupContext { - stmt: false, - leftmost_subexpression_in_stmt: fixup.stmt - || fixup.leftmost_subexpression_in_stmt, - ..fixup - }, - ); + self.print_expr_maybe_paren(lhs, prec + 1, fixup.leftmost_subexpression()); self.space(); self.word(op.node.as_str()); self.word_space("="); - self.print_expr_maybe_paren( - rhs, - prec, - FixupContext { stmt: false, leftmost_subexpression_in_stmt: false, ..fixup }, - ); + self.print_expr_maybe_paren(rhs, prec, fixup.subsequent_subexpression()); } ast::ExprKind::Field(expr, ident) => { - // Same fixups as ExprKind::MethodCall. self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX, fixup); self.word("."); self.print_ident(*ident); } ast::ExprKind::Index(expr, index, _) => { - // Same fixups as ExprKind::Call. self.print_expr_maybe_paren( expr, parser::PREC_POSTFIX, - FixupContext { - stmt: false, - leftmost_subexpression_in_stmt: fixup.stmt - || fixup.leftmost_subexpression_in_stmt, - ..fixup - }, + fixup.leftmost_subexpression(), ); self.word("["); self.print_expr(index, FixupContext::default()); @@ -736,31 +588,14 @@ impl<'a> State<'a> { // a "normal" binop gets parenthesized. (`LOr` is the lowest-precedence binop.) let fake_prec = AssocOp::LOr.precedence() as i8; if let Some(e) = start { - self.print_expr_maybe_paren( - e, - fake_prec, - FixupContext { - stmt: false, - leftmost_subexpression_in_stmt: fixup.stmt - || fixup.leftmost_subexpression_in_stmt, - ..fixup - }, - ); + self.print_expr_maybe_paren(e, fake_prec, fixup.leftmost_subexpression()); } match limits { ast::RangeLimits::HalfOpen => self.word(".."), ast::RangeLimits::Closed => self.word("..="), } if let Some(e) = end { - self.print_expr_maybe_paren( - e, - fake_prec, - FixupContext { - stmt: false, - leftmost_subexpression_in_stmt: false, - ..fixup - }, - ); + self.print_expr_maybe_paren(e, fake_prec, fixup.subsequent_subexpression()); } } ast::ExprKind::Underscore => self.word("_"), @@ -777,11 +612,7 @@ impl<'a> State<'a> { self.print_expr_maybe_paren( expr, parser::PREC_JUMP, - FixupContext { - stmt: false, - leftmost_subexpression_in_stmt: false, - ..fixup - }, + fixup.subsequent_subexpression(), ); } } @@ -799,11 +630,7 @@ impl<'a> State<'a> { self.print_expr_maybe_paren( expr, parser::PREC_JUMP, - FixupContext { - stmt: false, - leftmost_subexpression_in_stmt: false, - ..fixup - }, + fixup.subsequent_subexpression(), ); } } @@ -816,11 +643,7 @@ impl<'a> State<'a> { self.print_expr_maybe_paren( expr, parser::PREC_JUMP, - FixupContext { - stmt: false, - leftmost_subexpression_in_stmt: false, - ..fixup - }, + fixup.subsequent_subexpression(), ); } } @@ -830,7 +653,7 @@ impl<'a> State<'a> { self.print_expr_maybe_paren( result, parser::PREC_JUMP, - FixupContext { stmt: false, leftmost_subexpression_in_stmt: false, ..fixup }, + fixup.subsequent_subexpression(), ); } ast::ExprKind::InlineAsm(a) => { @@ -884,16 +707,11 @@ impl<'a> State<'a> { self.print_expr_maybe_paren( expr, parser::PREC_JUMP, - FixupContext { - stmt: false, - leftmost_subexpression_in_stmt: false, - ..fixup - }, + fixup.subsequent_subexpression(), ); } } ast::ExprKind::Try(e) => { - // Same fixups as ExprKind::MethodCall. self.print_expr_maybe_paren(e, parser::PREC_POSTFIX, fixup); self.word("?") } @@ -961,7 +779,7 @@ impl<'a> State<'a> { } _ => { self.end(); // Close the ibox for the pattern. - self.print_expr(body, FixupContext { stmt: true, ..FixupContext::default() }); + self.print_expr(body, FixupContext::new_stmt()); self.word(","); } } diff --git a/compiler/rustc_ast_pretty/src/pprust/state/fixup.rs b/compiler/rustc_ast_pretty/src/pprust/state/fixup.rs new file mode 100644 index 000000000000..d21cb82f83b2 --- /dev/null +++ b/compiler/rustc_ast_pretty/src/pprust/state/fixup.rs @@ -0,0 +1,149 @@ +use rustc_ast::util::{classify, parser}; +use rustc_ast::Expr; + +#[derive(Copy, Clone, Debug)] +pub(crate) struct FixupContext { + /// Print expression such that it can be parsed back as a statement + /// consisting of the original expression. + /// + /// The effect of this is for binary operators in statement position to set + /// `leftmost_subexpression_in_stmt` when printing their left-hand operand. + /// + /// ```ignore (illustrative) + /// (match x {}) - 1; // match needs parens when LHS of binary operator + /// + /// match x {}; // not when its own statement + /// ``` + stmt: bool, + + /// This is the difference between: + /// + /// ```ignore (illustrative) + /// (match x {}) - 1; // subexpression needs parens + /// + /// let _ = match x {} - 1; // no parens + /// ``` + /// + /// There are 3 distinguishable contexts in which `print_expr` might be + /// called with the expression `$match` as its argument, where `$match` + /// represents an expression of kind `ExprKind::Match`: + /// + /// - stmt=false leftmost_subexpression_in_stmt=false + /// + /// Example: `let _ = $match - 1;` + /// + /// No parentheses required. + /// + /// - stmt=false leftmost_subexpression_in_stmt=true + /// + /// Example: `$match - 1;` + /// + /// Must parenthesize `($match)`, otherwise parsing back the output as a + /// statement would terminate the statement after the closing brace of + /// the match, parsing `-1;` as a separate statement. + /// + /// - stmt=true leftmost_subexpression_in_stmt=false + /// + /// Example: `$match;` + /// + /// No parentheses required. + leftmost_subexpression_in_stmt: bool, + + /// This is the difference between: + /// + /// ```ignore (illustrative) + /// if let _ = (Struct {}) {} // needs parens + /// + /// match () { + /// () if let _ = Struct {} => {} // no parens + /// } + /// ``` + parenthesize_exterior_struct_lit: bool, +} + +/// The default amount of fixing is minimal fixing. Fixups should be turned on +/// in a targeted fashion where needed. +impl Default for FixupContext { + fn default() -> Self { + FixupContext { + stmt: false, + leftmost_subexpression_in_stmt: false, + parenthesize_exterior_struct_lit: false, + } + } +} + +impl FixupContext { + /// Create the initial fixup for printing an expression in statement + /// position. + /// + /// This is currently also used for printing an expression as a match-arm, + /// but this is incorrect and leads to over-parenthesizing. + pub fn new_stmt() -> Self { + FixupContext { stmt: true, ..FixupContext::default() } + } + + /// Create the initial fixup for printing an expression as the "condition" + /// of an `if` or `while`. There are a few other positions which are + /// grammatically equivalent and also use this, such as the iterator + /// expression in `for` and the scrutinee in `match`. + pub fn new_cond() -> Self { + FixupContext { parenthesize_exterior_struct_lit: true, ..FixupContext::default() } + } + + /// Transform this fixup into the one that should apply when printing the + /// leftmost subexpression of the current expression. + /// + /// The leftmost subexpression is any subexpression that has the same first + /// token as the current expression, but has a different last token. + /// + /// For example in `$a + $b` and `$a.method()`, the subexpression `$a` is a + /// leftmost subexpression. + /// + /// Not every expression has a leftmost subexpression. For example neither + /// `-$a` nor `[$a]` have one. + pub fn leftmost_subexpression(self) -> Self { + FixupContext { + stmt: false, + leftmost_subexpression_in_stmt: self.stmt || self.leftmost_subexpression_in_stmt, + ..self + } + } + + /// Transform this fixup into the one that should apply when printing any + /// subexpression that is neither a leftmost subexpression nor surrounded in + /// delimiters. + /// + /// This is for any subexpression that has a different first token than the + /// current expression, and is not surrounded by a paren/bracket/brace. For + /// example the `$b` in `$a + $b` and `-$b`, but not the one in `[$b]` or + /// `$a.f($b)`. + pub fn subsequent_subexpression(self) -> Self { + FixupContext { stmt: false, leftmost_subexpression_in_stmt: false, ..self } + } + + /// Determine whether parentheses are needed around the given expression to + /// head off an unintended statement boundary. + /// + /// The documentation on `FixupContext::leftmost_subexpression_in_stmt` has + /// examples. + pub fn would_cause_statement_boundary(self, expr: &Expr) -> bool { + self.leftmost_subexpression_in_stmt && !classify::expr_requires_semi_to_be_stmt(expr) + } + + /// Determine whether parentheses are needed around the given `let` + /// scrutinee. + /// + /// In `if let _ = $e {}`, some examples of `$e` that would need parentheses + /// are: + /// + /// - `Struct {}.f()`, because otherwise the `{` would be misinterpreted + /// as the opening of the if's then-block. + /// + /// - `true && false`, because otherwise this would be misinterpreted as a + /// "let chain". + pub fn needs_par_as_let_scrutinee(self, expr: &Expr) -> bool { + self.parenthesize_exterior_struct_lit && parser::contains_exterior_struct_lit(expr) + || parser::needs_par_as_let_scrutinee(expr.precedence().order()) + } +} diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 13f27c1c95c2..10886aace53f 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -1,5 +1,5 @@ use crate::pp::Breaks::Inconsistent; -use crate::pprust::state::expr::FixupContext; +use crate::pprust::state::fixup::FixupContext; use crate::pprust::state::{AnnNode, PrintState, State, INDENT_UNIT}; use ast::StaticItem; diff --git a/compiler/rustc_attr/src/lib.rs b/compiler/rustc_attr/src/lib.rs index fada69c4e6df..dd87a5c4dc38 100644 --- a/compiler/rustc_attr/src/lib.rs +++ b/compiler/rustc_attr/src/lib.rs @@ -7,7 +7,6 @@ #![allow(internal_features)] #![feature(rustdoc_internals)] #![doc(rust_logo)] -#![feature(generic_nonzero)] #![feature(let_chains)] #[macro_use] diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index ec0d4af599ea..61cd085eef5b 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -1320,7 +1320,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { .into_iter() .map(|err| match err.obligation.predicate.kind().skip_binder() { PredicateKind::Clause(ty::ClauseKind::Trait(predicate)) => { - match predicate.self_ty().kind() { + match *predicate.self_ty().kind() { ty::Param(param_ty) => Ok(( generics.type_param(param_ty, tcx), predicate.trait_ref.print_only_trait_path().to_string(), diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index dbea317e7bb2..a505924fabda 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -1070,7 +1070,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // LL | blk(); // | ----- this value implements `FnOnce`, which causes it to be moved when called // ``` - if let ty::Param(param_ty) = self_ty.kind() + if let ty::Param(param_ty) = *self_ty.kind() && let generics = self.infcx.tcx.generics_of(self.mir_def_id()) && let param = generics.type_param(param_ty, self.infcx.tcx) && let Some(hir_generics) = self diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 7fbf4c47ec81..a69b644c4dcc 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -1315,7 +1315,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } AggregateKind::Adt(..) | AggregateKind::Array(..) - | AggregateKind::Tuple { .. } => (), + | AggregateKind::Tuple { .. } + | AggregateKind::RawPtr(..) => (), } for operand in operands { diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 0600a105459b..61fa84666744 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1921,7 +1921,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } AggregateKind::Array(ty) => Ok(ty), - AggregateKind::Tuple => { + AggregateKind::Tuple | AggregateKind::RawPtr(..) => { unreachable!("This should have been covered in check_rvalues"); } } @@ -2518,6 +2518,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { AggregateKind::Closure(_, _) => None, AggregateKind::Coroutine(_, _) => None, AggregateKind::CoroutineClosure(_, _) => None, + AggregateKind::RawPtr(_, _) => None, }, } } @@ -2539,6 +2540,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { return; } + if let AggregateKind::RawPtr(..) = aggregate_kind { + bug!("RawPtr should only be in runtime MIR"); + } + for (i, operand) in operands.iter_enumerated() { let field_ty = match self.aggregate_field_ty(aggregate_kind, i, location) { Ok(field_ty) => field_ty, @@ -2757,7 +2762,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ), ), - AggregateKind::Array(_) | AggregateKind::Tuple => { + AggregateKind::Array(_) | AggregateKind::Tuple | AggregateKind::RawPtr(..) => { (CRATE_DEF_ID.to_def_id(), ty::InstantiatedPredicates::empty()) } }; diff --git a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs index 8b0b9123ac7d..efa4be7e15ac 100644 --- a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs +++ b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs @@ -1,4 +1,13 @@ -#![feature(no_core, lang_items, never_type, linkage, extern_types, thread_local, repr_simd)] +#![feature( + no_core, + lang_items, + never_type, + linkage, + extern_types, + thread_local, + repr_simd, + raw_ref_op +)] #![no_core] #![allow(dead_code, non_camel_case_types, internal_features)] @@ -112,9 +121,7 @@ fn start( static mut NUM: u8 = 6 * 7; -// FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_refs` lint -#[allow(static_mut_refs)] -static NUM_REF: &'static u8 = unsafe { &NUM }; +static NUM_REF: &'static u8 = unsafe { &*&raw const NUM }; unsafe fn zeroed() -> T { let mut uninit = MaybeUninit { uninit: () }; diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index f07421431dae..f428c4c7c0de 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -813,6 +813,19 @@ fn codegen_stmt<'tcx>( ); lval.write_cvalue(fx, val); } + Rvalue::Aggregate(ref kind, ref operands) + if matches!(**kind, AggregateKind::RawPtr(..)) => + { + let ty = to_place_and_rval.1.ty(&fx.mir.local_decls, fx.tcx); + let layout = fx.layout_of(fx.monomorphize(ty)); + let [data, meta] = &*operands.raw else { + bug!("RawPtr fields: {operands:?}"); + }; + let data = codegen_operand(fx, data); + let meta = codegen_operand(fx, meta); + let ptr_val = CValue::pointer_from_data_and_meta(data, meta, layout); + lval.write_cvalue(fx, ptr_val); + } Rvalue::Aggregate(ref kind, ref operands) => { let (variant_index, variant_dest, active_field_index) = match **kind { mir::AggregateKind::Adt(_, variant_index, _, _, active_field_index) => { diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs index ad863903ceec..8d52fd9d7a8e 100644 --- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs +++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs @@ -94,6 +94,23 @@ impl<'tcx> CValue<'tcx> { CValue(CValueInner::ByValPair(value, extra), layout) } + /// For `AggregateKind::RawPtr`, create a pointer from its parts. + /// + /// Panics if the `layout` is not a raw pointer. + pub(crate) fn pointer_from_data_and_meta( + data: CValue<'tcx>, + meta: CValue<'tcx>, + layout: TyAndLayout<'tcx>, + ) -> CValue<'tcx> { + assert!(layout.ty.is_unsafe_ptr()); + let inner = match (data.0, meta.0) { + (CValueInner::ByVal(p), CValueInner::ByVal(m)) => CValueInner::ByValPair(p, m), + (p @ CValueInner::ByVal(_), CValueInner::ByRef(..)) if meta.1.is_zst() => p, + _ => bug!("RawPtr operands {data:?} {meta:?}"), + }; + CValue(inner, layout) + } + pub(crate) fn layout(&self) -> TyAndLayout<'tcx> { self.1 } diff --git a/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs b/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs index add77880716c..5a7ddc4cd7fa 100644 --- a/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs +++ b/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs @@ -2,7 +2,7 @@ #![feature( no_core, unboxed_closures, start, lang_items, never_type, linkage, - extern_types, thread_local + extern_types, thread_local, raw_ref_op )] #![no_core] #![allow(dead_code, internal_features, non_camel_case_types)] @@ -99,9 +99,7 @@ fn start( static mut NUM: u8 = 6 * 7; -// FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_refs` lint -#[allow(static_mut_refs)] -static NUM_REF: &'static u8 = unsafe { &NUM }; +static NUM_REF: &'static u8 = unsafe { &* &raw const NUM }; macro_rules! assert { ($e:expr) => { diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 160f361b9b57..4c2bdb2f5ec2 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -17,7 +17,7 @@ use rustc_data_structures::small_c_str::SmallCStr; use rustc_hir::def_id::DefId; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::ty::layout::{ - FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOfHelpers, TyAndLayout, + FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasTyCtxt, LayoutError, LayoutOfHelpers, TyAndLayout, }; use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; use rustc_sanitizers::{cfi, kcfi}; @@ -1702,4 +1702,128 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { }; kcfi_bundle } + + pub(crate) fn mcdc_parameters( + &mut self, + fn_name: &'ll Value, + hash: &'ll Value, + bitmap_bytes: &'ll Value, + ) -> &'ll Value { + debug!("mcdc_parameters() with args ({:?}, {:?}, {:?})", fn_name, hash, bitmap_bytes); + + assert!(llvm_util::get_version() >= (18, 0, 0), "MCDC intrinsics require LLVM 18 or later"); + + let llfn = unsafe { llvm::LLVMRustGetInstrProfMCDCParametersIntrinsic(self.cx().llmod) }; + let llty = self.cx.type_func( + &[self.cx.type_ptr(), self.cx.type_i64(), self.cx.type_i32()], + self.cx.type_void(), + ); + let args = &[fn_name, hash, bitmap_bytes]; + let args = self.check_call("call", llty, llfn, args); + + unsafe { + let _ = llvm::LLVMRustBuildCall( + self.llbuilder, + llty, + llfn, + args.as_ptr() as *const &llvm::Value, + args.len() as c_uint, + [].as_ptr(), + 0 as c_uint, + ); + // Create condition bitmap named `mcdc.addr`. + let mut bx = Builder::with_cx(self.cx); + bx.position_at_start(llvm::LLVMGetFirstBasicBlock(self.llfn())); + let cond_bitmap = { + let alloca = + llvm::LLVMBuildAlloca(bx.llbuilder, bx.cx.type_i32(), c"mcdc.addr".as_ptr()); + llvm::LLVMSetAlignment(alloca, 4); + alloca + }; + bx.store(self.const_i32(0), cond_bitmap, self.tcx().data_layout.i32_align.abi); + cond_bitmap + } + } + + pub(crate) fn mcdc_tvbitmap_update( + &mut self, + fn_name: &'ll Value, + hash: &'ll Value, + bitmap_bytes: &'ll Value, + bitmap_index: &'ll Value, + mcdc_temp: &'ll Value, + ) { + debug!( + "mcdc_tvbitmap_update() with args ({:?}, {:?}, {:?}, {:?}, {:?})", + fn_name, hash, bitmap_bytes, bitmap_index, mcdc_temp + ); + assert!(llvm_util::get_version() >= (18, 0, 0), "MCDC intrinsics require LLVM 18 or later"); + + let llfn = + unsafe { llvm::LLVMRustGetInstrProfMCDCTVBitmapUpdateIntrinsic(self.cx().llmod) }; + let llty = self.cx.type_func( + &[ + self.cx.type_ptr(), + self.cx.type_i64(), + self.cx.type_i32(), + self.cx.type_i32(), + self.cx.type_ptr(), + ], + self.cx.type_void(), + ); + let args = &[fn_name, hash, bitmap_bytes, bitmap_index, mcdc_temp]; + let args = self.check_call("call", llty, llfn, args); + unsafe { + let _ = llvm::LLVMRustBuildCall( + self.llbuilder, + llty, + llfn, + args.as_ptr() as *const &llvm::Value, + args.len() as c_uint, + [].as_ptr(), + 0 as c_uint, + ); + } + let i32_align = self.tcx().data_layout.i32_align.abi; + self.store(self.const_i32(0), mcdc_temp, i32_align); + } + + pub(crate) fn mcdc_condbitmap_update( + &mut self, + fn_name: &'ll Value, + hash: &'ll Value, + cond_loc: &'ll Value, + mcdc_temp: &'ll Value, + bool_value: &'ll Value, + ) { + debug!( + "mcdc_condbitmap_update() with args ({:?}, {:?}, {:?}, {:?}, {:?})", + fn_name, hash, cond_loc, mcdc_temp, bool_value + ); + assert!(llvm_util::get_version() >= (18, 0, 0), "MCDC intrinsics require LLVM 18 or later"); + let llfn = unsafe { llvm::LLVMRustGetInstrProfMCDCCondBitmapIntrinsic(self.cx().llmod) }; + let llty = self.cx.type_func( + &[ + self.cx.type_ptr(), + self.cx.type_i64(), + self.cx.type_i32(), + self.cx.type_ptr(), + self.cx.type_i1(), + ], + self.cx.type_void(), + ); + let args = &[fn_name, hash, cond_loc, mcdc_temp, bool_value]; + self.check_call("call", llty, llfn, args); + unsafe { + let _ = llvm::LLVMRustBuildCall( + self.llbuilder, + llty, + llfn, + args.as_ptr() as *const &llvm::Value, + args.len() as c_uint, + [].as_ptr(), + 0 as c_uint, + ); + } + } } diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index a62dfe132047..9e85c2d88f9b 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -260,7 +260,8 @@ impl<'ll> CodegenCx<'ll, '_> { #[instrument(level = "debug", skip(self, llty))] pub(crate) fn get_static_inner(&self, def_id: DefId, llty: &'ll Type) -> &'ll Value { - if let Some(&g) = self.instances.borrow().get(&Instance::mono(self.tcx, def_id)) { + let instance = Instance::mono(self.tcx, def_id); + if let Some(&g) = self.instances.borrow().get(&instance) { trace!("used cached value"); return g; } @@ -273,7 +274,7 @@ impl<'ll> CodegenCx<'ll, '_> { statics defined in the same CGU, but did not for `{def_id:?}`" ); - let sym = self.tcx.symbol_name(Instance::mono(self.tcx, def_id)).name; + let sym = self.tcx.symbol_name(instance).name; let fn_attrs = self.tcx.codegen_fn_attrs(def_id); debug!(?sym, ?fn_attrs); @@ -363,7 +364,7 @@ impl<'ll> CodegenCx<'ll, '_> { } } - self.instances.borrow_mut().insert(Instance::mono(self.tcx, def_id), g); + self.instances.borrow_mut().insert(instance, g); g } diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs index 2af28146a51e..12a846a49ec1 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs @@ -1,4 +1,6 @@ -use rustc_middle::mir::coverage::{CodeRegion, CounterId, CovTerm, ExpressionId, MappingKind}; +use rustc_middle::mir::coverage::{ + CodeRegion, ConditionInfo, CounterId, CovTerm, DecisionInfo, ExpressionId, MappingKind, +}; /// Must match the layout of `LLVMRustCounterKind`. #[derive(Copy, Clone, Debug)] @@ -99,6 +101,86 @@ pub enum RegionKind { /// associated with two counters, each representing the number of times the /// expression evaluates to true or false. BranchRegion = 4, + + /// A DecisionRegion represents a top-level boolean expression and is + /// associated with a variable length bitmap index and condition number. + MCDCDecisionRegion = 5, + + /// A Branch Region can be extended to include IDs to facilitate MC/DC. + MCDCBranchRegion = 6, +} + +pub mod mcdc { + use rustc_middle::mir::coverage::{ConditionInfo, DecisionInfo}; + + /// Must match the layout of `LLVMRustMCDCDecisionParameters`. + #[repr(C)] + #[derive(Clone, Copy, Debug, Default)] + pub struct DecisionParameters { + bitmap_idx: u32, + conditions_num: u16, + } + + // ConditionId in llvm is `unsigned int` at 18 while `int16_t` at [19](https://github.com/llvm/llvm-project/pull/81257) + type LLVMConditionId = i16; + + /// Must match the layout of `LLVMRustMCDCBranchParameters`. + #[repr(C)] + #[derive(Clone, Copy, Debug, Default)] + pub struct BranchParameters { + condition_id: LLVMConditionId, + condition_ids: [LLVMConditionId; 2], + } + + #[repr(C)] + #[derive(Clone, Copy, Debug)] + pub enum ParameterTag { + None = 0, + Decision = 1, + Branch = 2, + } + /// Same layout with `LLVMRustMCDCParameters` + #[repr(C)] + #[derive(Clone, Copy, Debug)] + pub struct Parameters { + tag: ParameterTag, + decision_params: DecisionParameters, + branch_params: BranchParameters, + } + + impl Parameters { + pub fn none() -> Self { + Self { + tag: ParameterTag::None, + decision_params: Default::default(), + branch_params: Default::default(), + } + } + pub fn decision(decision_params: DecisionParameters) -> Self { + Self { tag: ParameterTag::Decision, decision_params, branch_params: Default::default() } + } + pub fn branch(branch_params: BranchParameters) -> Self { + Self { tag: ParameterTag::Branch, decision_params: Default::default(), branch_params } + } + } + + impl From for BranchParameters { + fn from(value: ConditionInfo) -> Self { + Self { + condition_id: value.condition_id.as_u32() as LLVMConditionId, + condition_ids: [ + value.false_next_id.as_u32() as LLVMConditionId, + value.true_next_id.as_u32() as LLVMConditionId, + ], + } + } + } + + impl From for DecisionParameters { + fn from(value: DecisionInfo) -> Self { + Self { bitmap_idx: value.bitmap_idx, conditions_num: value.conditions_num } + } + } } /// This struct provides LLVM's representation of a "CoverageMappingRegion", encoded into the @@ -122,6 +204,7 @@ pub struct CounterMappingRegion { /// for the false branch of the region. false_counter: Counter, + mcdc_params: mcdc::Parameters, /// An indirect reference to the source filename. In the LLVM Coverage Mapping Format, the /// file_id is an index into a function-specific `virtual_file_mapping` array of indexes /// that, in turn, are used to look up the filename for this region. @@ -173,6 +256,26 @@ impl CounterMappingRegion { end_line, end_col, ), + MappingKind::MCDCBranch { true_term, false_term, mcdc_params } => { + Self::mcdc_branch_region( + Counter::from_term(true_term), + Counter::from_term(false_term), + mcdc_params, + local_file_id, + start_line, + start_col, + end_line, + end_col, + ) + } + MappingKind::MCDCDecision(decision_info) => Self::decision_region( + decision_info, + local_file_id, + start_line, + start_col, + end_line, + end_col, + ), } } @@ -187,6 +290,7 @@ impl CounterMappingRegion { Self { counter, false_counter: Counter::ZERO, + mcdc_params: mcdc::Parameters::none(), file_id, expanded_file_id: 0, start_line, @@ -209,6 +313,7 @@ impl CounterMappingRegion { Self { counter, false_counter, + mcdc_params: mcdc::Parameters::none(), file_id, expanded_file_id: 0, start_line, @@ -219,6 +324,54 @@ impl CounterMappingRegion { } } + pub(crate) fn mcdc_branch_region( + counter: Counter, + false_counter: Counter, + condition_info: ConditionInfo, + file_id: u32, + start_line: u32, + start_col: u32, + end_line: u32, + end_col: u32, + ) -> Self { + Self { + counter, + false_counter, + mcdc_params: mcdc::Parameters::branch(condition_info.into()), + file_id, + expanded_file_id: 0, + start_line, + start_col, + end_line, + end_col, + kind: RegionKind::MCDCBranchRegion, + } + } + + pub(crate) fn decision_region( + decision_info: DecisionInfo, + file_id: u32, + start_line: u32, + start_col: u32, + end_line: u32, + end_col: u32, + ) -> Self { + let mcdc_params = mcdc::Parameters::decision(decision_info.into()); + + Self { + counter: Counter::ZERO, + false_counter: Counter::ZERO, + mcdc_params, + file_id, + expanded_file_id: 0, + start_line, + start_col, + end_line, + end_col, + kind: RegionKind::MCDCDecisionRegion, + } + } + // This function might be used in the future; the LLVM API is still evolving, as is coverage // support. #[allow(dead_code)] @@ -233,6 +386,7 @@ impl CounterMappingRegion { Self { counter: Counter::ZERO, false_counter: Counter::ZERO, + mcdc_params: mcdc::Parameters::none(), file_id, expanded_file_id, start_line, @@ -256,6 +410,7 @@ impl CounterMappingRegion { Self { counter: Counter::ZERO, false_counter: Counter::ZERO, + mcdc_params: mcdc::Parameters::none(), file_id, expanded_file_id: 0, start_line, @@ -280,6 +435,7 @@ impl CounterMappingRegion { Self { counter, false_counter: Counter::ZERO, + mcdc_params: mcdc::Parameters::none(), file_id, expanded_file_id: 0, start_line, diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs index 140566e8da9b..085ce15d81fe 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs @@ -13,7 +13,7 @@ use rustc_codegen_ssa::traits::{ use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_llvm::RustString; use rustc_middle::bug; -use rustc_middle::mir::coverage::CoverageKind; +use rustc_middle::mir::coverage::{CoverageKind, FunctionCoverageInfo}; use rustc_middle::ty::layout::HasTyCtxt; use rustc_middle::ty::Instance; use rustc_target::abi::Align; @@ -30,6 +30,7 @@ pub struct CrateCoverageContext<'ll, 'tcx> { pub(crate) function_coverage_map: RefCell, FunctionCoverageCollector<'tcx>>>, pub(crate) pgo_func_name_var_map: RefCell, &'ll llvm::Value>>, + pub(crate) mcdc_condition_bitmap_map: RefCell, &'ll llvm::Value>>, } impl<'ll, 'tcx> CrateCoverageContext<'ll, 'tcx> { @@ -37,6 +38,7 @@ impl<'ll, 'tcx> CrateCoverageContext<'ll, 'tcx> { Self { function_coverage_map: Default::default(), pgo_func_name_var_map: Default::default(), + mcdc_condition_bitmap_map: Default::default(), } } @@ -45,6 +47,12 @@ impl<'ll, 'tcx> CrateCoverageContext<'ll, 'tcx> { ) -> FxIndexMap, FunctionCoverageCollector<'tcx>> { self.function_coverage_map.replace(FxIndexMap::default()) } + + /// LLVM use a temp value to record evaluated mcdc test vector of each decision, which is called condition bitmap. + /// This value is named `mcdc.addr` (same as clang) and is a 32-bit integer. + fn try_get_mcdc_condition_bitmap(&self, instance: &Instance<'tcx>) -> Option<&'ll llvm::Value> { + self.mcdc_condition_bitmap_map.borrow().get(instance).copied() + } } // These methods used to be part of trait `CoverageInfoMethods`, which no longer @@ -90,6 +98,10 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { return; }; + if function_coverage_info.mcdc_bitmap_bytes > 0 { + ensure_mcdc_parameters(bx, instance, function_coverage_info); + } + let Some(coverage_context) = bx.coverage_context() else { return }; let mut coverage_map = coverage_context.function_coverage_map.borrow_mut(); let func_coverage = coverage_map @@ -131,10 +143,66 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { CoverageKind::ExpressionUsed { id } => { func_coverage.mark_expression_id_seen(id); } + CoverageKind::CondBitmapUpdate { id, value, .. } => { + drop(coverage_map); + assert_ne!( + id.as_u32(), + 0, + "ConditionId of evaluated conditions should never be zero" + ); + let cond_bitmap = coverage_context + .try_get_mcdc_condition_bitmap(&instance) + .expect("mcdc cond bitmap should have been allocated for updating"); + let cond_loc = bx.const_i32(id.as_u32() as i32 - 1); + let bool_value = bx.const_bool(value); + let fn_name = bx.get_pgo_func_name_var(instance); + let hash = bx.const_u64(function_coverage_info.function_source_hash); + bx.mcdc_condbitmap_update(fn_name, hash, cond_loc, cond_bitmap, bool_value); + } + CoverageKind::TestVectorBitmapUpdate { bitmap_idx } => { + drop(coverage_map); + let cond_bitmap = coverage_context + .try_get_mcdc_condition_bitmap(&instance) + .expect("mcdc cond bitmap should have been allocated for merging into the global bitmap"); + let bitmap_bytes = bx.tcx().coverage_ids_info(instance.def).mcdc_bitmap_bytes; + assert!(bitmap_idx < bitmap_bytes, "bitmap index of the decision out of range"); + assert!( + bitmap_bytes <= function_coverage_info.mcdc_bitmap_bytes, + "bitmap length disagreement: query says {bitmap_bytes} but function info only has {}", + function_coverage_info.mcdc_bitmap_bytes + ); + + let fn_name = bx.get_pgo_func_name_var(instance); + let hash = bx.const_u64(function_coverage_info.function_source_hash); + let bitmap_bytes = bx.const_u32(bitmap_bytes); + let bitmap_index = bx.const_u32(bitmap_idx); + bx.mcdc_tvbitmap_update(fn_name, hash, bitmap_bytes, bitmap_index, cond_bitmap); + } } } } +fn ensure_mcdc_parameters<'ll, 'tcx>( + bx: &mut Builder<'_, 'll, 'tcx>, + instance: Instance<'tcx>, + function_coverage_info: &FunctionCoverageInfo, +) { + let Some(cx) = bx.coverage_context() else { return }; + if cx.mcdc_condition_bitmap_map.borrow().contains_key(&instance) { + return; + } + + let fn_name = bx.get_pgo_func_name_var(instance); + let hash = bx.const_u64(function_coverage_info.function_source_hash); + let bitmap_bytes = bx.const_u32(function_coverage_info.mcdc_bitmap_bytes); + let cond_bitmap = bx.mcdc_parameters(fn_name, hash, bitmap_bytes); + bx.coverage_context() + .expect("already checked above") + .mcdc_condition_bitmap_map + .borrow_mut() + .insert(instance, cond_bitmap); +} + /// Calls llvm::createPGOFuncNameVar() with the given function instance's /// mangled function name. The LLVM API returns an llvm::GlobalVariable /// containing the function name, with the specific variable name and linkage diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 83158f6f1d5b..a10dc61967ed 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1631,6 +1631,10 @@ extern "C" { // Miscellaneous instructions pub fn LLVMRustGetInstrProfIncrementIntrinsic(M: &Module) -> &Value; + pub fn LLVMRustGetInstrProfMCDCParametersIntrinsic(M: &Module) -> &Value; + pub fn LLVMRustGetInstrProfMCDCTVBitmapUpdateIntrinsic(M: &Module) -> &Value; + pub fn LLVMRustGetInstrProfMCDCCondBitmapIntrinsic(M: &Module) -> &Value; + pub fn LLVMRustBuildCall<'a>( B: &Builder<'a>, Ty: &'a Type, diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index fad6f4394418..85fcc4f4f404 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -902,52 +902,45 @@ impl<'a> Linker for MsvcLinker<'a> { } } - fn debuginfo(&mut self, strip: Strip, natvis_debugger_visualizers: &[PathBuf]) { - match strip { - Strip::None => { - // This will cause the Microsoft linker to generate a PDB file - // from the CodeView line tables in the object files. - self.cmd.arg("/DEBUG"); + fn debuginfo(&mut self, _strip: Strip, natvis_debugger_visualizers: &[PathBuf]) { + // This will cause the Microsoft linker to generate a PDB file + // from the CodeView line tables in the object files. + self.cmd.arg("/DEBUG"); - // Default to emitting only the file name of the PDB file into - // the binary instead of the full path. Emitting the full path - // may leak private information (such as user names). - // See https://github.com/rust-lang/rust/issues/87825. - // - // This default behavior can be overridden by explicitly passing - // `-Clink-arg=/PDBALTPATH:...` to rustc. - self.cmd.arg("/PDBALTPATH:%_PDB%"); + // Default to emitting only the file name of the PDB file into + // the binary instead of the full path. Emitting the full path + // may leak private information (such as user names). + // See https://github.com/rust-lang/rust/issues/87825. + // + // This default behavior can be overridden by explicitly passing + // `-Clink-arg=/PDBALTPATH:...` to rustc. + self.cmd.arg("/PDBALTPATH:%_PDB%"); - // This will cause the Microsoft linker to embed .natvis info into the PDB file - let natvis_dir_path = self.sess.sysroot.join("lib\\rustlib\\etc"); - if let Ok(natvis_dir) = fs::read_dir(&natvis_dir_path) { - for entry in natvis_dir { - match entry { - Ok(entry) => { - let path = entry.path(); - if path.extension() == Some("natvis".as_ref()) { - let mut arg = OsString::from("/NATVIS:"); - arg.push(path); - self.cmd.arg(arg); - } - } - Err(error) => { - self.sess.dcx().emit_warn(errors::NoNatvisDirectory { error }); - } + // This will cause the Microsoft linker to embed .natvis info into the PDB file + let natvis_dir_path = self.sess.sysroot.join("lib\\rustlib\\etc"); + if let Ok(natvis_dir) = fs::read_dir(&natvis_dir_path) { + for entry in natvis_dir { + match entry { + Ok(entry) => { + let path = entry.path(); + if path.extension() == Some("natvis".as_ref()) { + let mut arg = OsString::from("/NATVIS:"); + arg.push(path); + self.cmd.arg(arg); } } + Err(error) => { + self.sess.dcx().emit_warn(errors::NoNatvisDirectory { error }); + } } + } + } - // This will cause the Microsoft linker to embed .natvis info for all crates into the PDB file - for path in natvis_debugger_visualizers { - let mut arg = OsString::from("/NATVIS:"); - arg.push(path); - self.cmd.arg(arg); - } - } - Strip::Debuginfo | Strip::Symbols => { - self.cmd.arg("/DEBUG:NONE"); - } + // This will cause the Microsoft linker to embed .natvis info for all crates into the PDB file + for path in natvis_debugger_visualizers { + let mut arg = OsString::from("/NATVIS:"); + arg.push(path); + self.cmd.arg(arg); } } diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index b19f52182b65..cce3f0e6f2d1 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -363,6 +363,24 @@ fn exported_symbols_provider_local( }, )); } + MonoItem::Fn(Instance { + def: InstanceDef::AsyncDropGlueCtorShim(def_id, Some(ty)), + args, + }) => { + // A little sanity-check + debug_assert_eq!( + args.non_erasable_generics(tcx, def_id).skip(1).next(), + Some(GenericArgKind::Type(ty)) + ); + symbols.push(( + ExportedSymbol::AsyncDropGlueCtorShim(ty), + SymbolExportInfo { + level: SymbolExportLevel::Rust, + kind: SymbolExportKind::Text, + used: false, + }, + )); + } _ => { // Any other symbols don't qualify for sharing } @@ -385,6 +403,7 @@ fn upstream_monomorphizations_provider( let mut instances: DefIdMap> = Default::default(); let drop_in_place_fn_def_id = tcx.lang_items().drop_in_place_fn(); + let async_drop_in_place_fn_def_id = tcx.lang_items().async_drop_in_place_fn(); for &cnum in cnums.iter() { for (exported_symbol, _) in tcx.exported_symbols(cnum).iter() { @@ -399,6 +418,18 @@ fn upstream_monomorphizations_provider( continue; } } + ExportedSymbol::AsyncDropGlueCtorShim(ty) => { + if let Some(async_drop_in_place_fn_def_id) = async_drop_in_place_fn_def_id { + ( + async_drop_in_place_fn_def_id, + tcx.mk_args(&[tcx.lifetimes.re_erased.into(), ty.into()]), + ) + } else { + // `drop_in_place` in place does not exist, don't try + // to use it. + continue; + } + } ExportedSymbol::NonGeneric(..) | ExportedSymbol::ThreadLocalShim(..) | ExportedSymbol::NoDefId(..) => { @@ -534,6 +565,13 @@ pub fn symbol_name_for_instance_in_crate<'tcx>( Instance::resolve_drop_in_place(tcx, ty), instantiating_crate, ), + ExportedSymbol::AsyncDropGlueCtorShim(ty) => { + rustc_symbol_mangling::symbol_name_for_instance_in_crate( + tcx, + Instance::resolve_async_drop_in_place(tcx, ty), + instantiating_crate, + ) + } ExportedSymbol::NoDefId(symbol_name) => symbol_name.to_string(), } } @@ -582,6 +620,9 @@ pub fn linking_symbol_name_for_instance_in_crate<'tcx>( // DropGlue always use the Rust calling convention and thus follow the target's default // symbol decoration scheme. ExportedSymbol::DropGlue(..) => None, + // AsyncDropGlueCtorShim always use the Rust calling convention and thus follow the + // target's default symbol decoration scheme. + ExportedSymbol::AsyncDropGlueCtorShim(..) => None, // NoDefId always follow the target's default symbol decoration scheme. ExportedSymbol::NoDefId(..) => None, // ThreadLocalShim always follow the target's default symbol decoration scheme. diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 452398e6d828..64c13fa7c1fb 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -835,7 +835,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let def = instance.map(|i| i.def); - if let Some(ty::InstanceDef::DropGlue(_, None)) = def { + if let Some( + ty::InstanceDef::DropGlue(_, None) | ty::InstanceDef::AsyncDropGlueCtorShim(_, None), + ) = def + { // Empty drop glue; a no-op. let target = target.unwrap(); return helper.funclet_br(self, bx, target, mergeable_succ); diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 6725a6d9e38d..7823d4c249a8 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -9,7 +9,7 @@ use crate::MemFlags; use rustc_hir as hir; use rustc_middle::mir; -use rustc_middle::mir::Operand; +use rustc_middle::mir::{AggregateKind, Operand}; use rustc_middle::ty::cast::{CastTy, IntTy}; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, adjustment::PointerCoercion, Instance, Ty, TyCtxt}; @@ -720,6 +720,24 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { OperandRef { val: OperandValue::Immediate(static_), layout } } mir::Rvalue::Use(ref operand) => self.codegen_operand(bx, operand), + mir::Rvalue::Aggregate(box mir::AggregateKind::RawPtr(..), ref fields) => { + let ty = rvalue.ty(self.mir, self.cx.tcx()); + let layout = self.cx.layout_of(self.monomorphize(ty)); + let [data, meta] = &*fields.raw else { + bug!("RawPtr fields: {fields:?}"); + }; + let data = self.codegen_operand(bx, data); + let meta = self.codegen_operand(bx, meta); + match (data.val, meta.val) { + (p @ OperandValue::Immediate(_), OperandValue::ZeroSized) => { + OperandRef { val: p, layout } + } + (OperandValue::Immediate(p), OperandValue::Immediate(m)) => { + OperandRef { val: OperandValue::Pair(p, m), layout } + } + _ => bug!("RawPtr operands {data:?} {meta:?}"), + } + } mir::Rvalue::Repeat(..) | mir::Rvalue::Aggregate(..) => { // According to `rvalue_creates_operand`, only ZST // aggregate rvalues are allowed to be operands. @@ -1032,6 +1050,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir::Rvalue::ThreadLocalRef(_) | mir::Rvalue::Use(..) => // (*) true, + // This always produces a `ty::RawPtr`, so will be Immediate or Pair + mir::Rvalue::Aggregate(box AggregateKind::RawPtr(..), ..) => true, mir::Rvalue::Repeat(..) | mir::Rvalue::Aggregate(..) => { let ty = rvalue.ty(self.mir, self.cx.tcx()); diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index c3f26da8a79b..b29034e991e3 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -9,7 +9,9 @@ use rustc_middle::mir; use rustc_middle::ty::layout::LayoutOf; use rustc_target::abi::{FieldIdx, FIRST_VARIANT}; -use super::{ImmTy, InterpCx, InterpResult, Machine, PlaceTy, Projectable, Scalar}; +use super::{ + ImmTy, Immediate, InterpCx, InterpResult, Machine, MemPlaceMeta, PlaceTy, Projectable, Scalar, +}; use crate::util; impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { @@ -303,6 +305,27 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let variant_dest = self.project_downcast(dest, variant_index)?; (variant_index, variant_dest, active_field_index) } + mir::AggregateKind::RawPtr(..) => { + // Pointers don't have "fields" in the normal sense, so the + // projection-based code below would either fail in projection + // or in type mismatches. Instead, build an `Immediate` from + // the parts and write that to the destination. + let [data, meta] = &operands.raw else { + bug!("{kind:?} should have 2 operands, had {operands:?}"); + }; + let data = self.eval_operand(data, None)?; + let data = self.read_pointer(&data)?; + let meta = self.eval_operand(meta, None)?; + let meta = if meta.layout.is_zst() { + MemPlaceMeta::None + } else { + MemPlaceMeta::Meta(self.read_scalar(&meta)?) + }; + let ptr_imm = Immediate::new_pointer_with_meta(data, meta, self); + let ptr = ImmTy::from_immediate(ptr_imm, dest.layout); + self.copy_op(&ptr, dest)?; + return Ok(()); + } _ => (FIRST_VARIANT, dest.clone(), None), }; if active_field_index.is_some() { diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index c0e27e86d500..908c4da71f16 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -558,6 +558,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { | ty::InstanceDef::CloneShim(..) | ty::InstanceDef::FnPtrAddrShim(..) | ty::InstanceDef::ThreadLocalShim(..) + | ty::InstanceDef::AsyncDropGlueCtorShim(..) | ty::InstanceDef::Item(_) => { // We need MIR for this fn let Some((body, instance)) = M::find_mir_or_eval_fn( diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index 50420aaec045..d27d42737cd6 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -11,7 +11,6 @@ Rust MIR: a lowered representation of Rust. #![feature(assert_matches)] #![feature(box_patterns)] #![feature(decl_macro)] -#![feature(generic_nonzero)] #![feature(let_chains)] #![feature(slice_ptr_get)] #![feature(strict_provenance)] diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index a499e4b980fc..bf5592c828f1 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -923,6 +923,47 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } } + AggregateKind::RawPtr(pointee_ty, mutability) => { + if !matches!(self.mir_phase, MirPhase::Runtime(_)) { + // It would probably be fine to support this in earlier phases, + // but at the time of writing it's only ever introduced from intrinsic lowering, + // so earlier things just `bug!` on it. + self.fail(location, "RawPtr should be in runtime MIR only"); + } + + if fields.len() != 2 { + self.fail(location, "raw pointer aggregate must have 2 fields"); + } else { + let data_ptr_ty = fields.raw[0].ty(self.body, self.tcx); + let metadata_ty = fields.raw[1].ty(self.body, self.tcx); + if let ty::RawPtr(in_pointee, in_mut) = data_ptr_ty.kind() { + if *in_mut != mutability { + self.fail(location, "input and output mutability must match"); + } + + // FIXME: check `Thin` instead of `Sized` + if !in_pointee.is_sized(self.tcx, self.param_env) { + self.fail(location, "input pointer must be thin"); + } + } else { + self.fail( + location, + "first operand to raw pointer aggregate must be a raw pointer", + ); + } + + // FIXME: Check metadata more generally + if pointee_ty.is_slice() { + if !self.mir_assign_valid_types(metadata_ty, self.tcx.types.usize) { + self.fail(location, "slice metadata must be usize"); + } + } else if pointee_ty.is_sized(self.tcx, self.param_env) { + if metadata_ty != self.tcx.types.unit { + self.fail(location, "metadata for pointer-to-thin must be unit"); + } + } + } + } }, Rvalue::Ref(_, BorrowKind::Fake, _) => { if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) { diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index b82a9a909e6d..2b799d6f5d3b 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -20,7 +20,6 @@ #![feature(cfg_match)] #![feature(core_intrinsics)] #![feature(extend_one)] -#![feature(generic_nonzero)] #![feature(hash_raw_entry)] #![feature(hasher_prefixfree_extras)] #![feature(lazy_cell)] diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 5d345e788e94..a17e0da47a5c 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -730,7 +730,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { } } #[rustc_lint_diagnostics] - fn highlighted_note(&mut self, msg: Vec) -> &mut Self { + pub fn highlighted_note(&mut self, msg: Vec) -> &mut Self { self.sub_with_highlights(Level::Note, msg, MultiSpan::new()); self } diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 8d6b22a9fa98..e3363e895947 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -15,7 +15,6 @@ #![feature(box_patterns)] #![feature(error_reporter)] #![feature(extract_if)] -#![feature(generic_nonzero)] #![feature(let_chains)] #![feature(negative_impls)] #![feature(never_type)] diff --git a/compiler/rustc_feature/src/lib.rs b/compiler/rustc_feature/src/lib.rs index cb28bb4e8e4f..36ef8fe7816f 100644 --- a/compiler/rustc_feature/src/lib.rs +++ b/compiler/rustc_feature/src/lib.rs @@ -12,7 +12,6 @@ //! symbol to the `accepted` or `removed` modules respectively. #![allow(internal_features)] -#![feature(generic_nonzero)] #![feature(rustdoc_internals)] #![doc(rust_logo)] #![feature(lazy_cell)] diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 2a796ca5465c..879476f890e8 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -162,6 +162,18 @@ language_item_table! { Drop, sym::drop, drop_trait, Target::Trait, GenericRequirement::None; Destruct, sym::destruct, destruct_trait, Target::Trait, GenericRequirement::None; + AsyncDrop, sym::async_drop, async_drop_trait, Target::Trait, GenericRequirement::Exact(0); + AsyncDestruct, sym::async_destruct, async_destruct_trait, Target::Trait, GenericRequirement::Exact(0); + AsyncDropInPlace, sym::async_drop_in_place, async_drop_in_place_fn, Target::Fn, GenericRequirement::Exact(1); + SurfaceAsyncDropInPlace, sym::surface_async_drop_in_place, surface_async_drop_in_place_fn, Target::Fn, GenericRequirement::Exact(1); + AsyncDropSurfaceDropInPlace, sym::async_drop_surface_drop_in_place, async_drop_surface_drop_in_place_fn, Target::Fn, GenericRequirement::Exact(1); + AsyncDropSlice, sym::async_drop_slice, async_drop_slice_fn, Target::Fn, GenericRequirement::Exact(1); + AsyncDropChain, sym::async_drop_chain, async_drop_chain_fn, Target::Fn, GenericRequirement::Exact(2); + AsyncDropNoop, sym::async_drop_noop, async_drop_noop_fn, Target::Fn, GenericRequirement::Exact(0); + AsyncDropFuse, sym::async_drop_fuse, async_drop_fuse_fn, Target::Fn, GenericRequirement::Exact(1); + AsyncDropDefer, sym::async_drop_defer, async_drop_defer_fn, Target::Fn, GenericRequirement::Exact(1); + AsyncDropEither, sym::async_drop_either, async_drop_either_fn, Target::Fn, GenericRequirement::Exact(3); + 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); @@ -281,6 +293,7 @@ language_item_table! { ExchangeMalloc, sym::exchange_malloc, exchange_malloc_fn, Target::Fn, GenericRequirement::None; DropInPlace, sym::drop_in_place, drop_in_place_fn, Target::Fn, GenericRequirement::Minimum(1); + FallbackSurfaceDrop, sym::fallback_surface_drop, fallback_surface_drop_fn, Target::Fn, GenericRequirement::None; AllocLayout, sym::alloc_layout, alloc_layout, Target::Struct, GenericRequirement::None; Start, sym::start, start_fn, Target::Fn, GenericRequirement::Exact(1); diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 0ff78ebff995..06b5ee299b85 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -351,7 +351,7 @@ hir_analysis_param_in_ty_of_assoc_const_binding = *[normal] the {$param_def_kind} `{$param_name}` is defined here } -hir_analysis_param_not_captured = `impl Trait` must mention all {$kind} parameters in scope +hir_analysis_param_not_captured = `impl Trait` must mention all {$kind} parameters in scope in `use<...>` .label = {$kind} parameter is implicitly captured by this `impl Trait` .note = currently, all {$kind} parameters are required to be mentioned in the precise captures list @@ -405,6 +405,10 @@ hir_analysis_self_in_impl_self = `Self` is not valid in the self type of an impl block .note = replace `Self` with a different type +hir_analysis_self_ty_not_captured = `impl Trait` must mention the `Self` type of the trait in `use<...>` + .label = `Self` type parameter is implicitly captured by this `impl Trait` + .note = currently, all type parameters are required to be mentioned in the precise captures list + hir_analysis_simd_ffi_highly_experimental = use of SIMD type{$snip} in FFI is highly experimental and may result in invalid code .help = add `#![feature(simd_ffi)]` to the crate attributes to enable diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 3881e240cedb..a43a67d50d81 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -492,6 +492,7 @@ fn check_opaque_precise_captures<'tcx>(tcx: TyCtxt<'tcx>, opaque_def_id: LocalDe }; let mut expected_captures = UnordSet::default(); + let mut shadowed_captures = UnordSet::default(); let mut seen_params = UnordMap::default(); let mut prev_non_lifetime_param = None; for arg in precise_capturing_args { @@ -530,6 +531,21 @@ fn check_opaque_precise_captures<'tcx>(tcx: TyCtxt<'tcx>, opaque_def_id: LocalDe match tcx.named_bound_var(hir_id) { Some(ResolvedArg::EarlyBound(def_id)) => { expected_captures.insert(def_id); + + // Make sure we allow capturing these lifetimes through `Self` and + // `T::Assoc` projection syntax, too. These will occur when we only + // see lifetimes are captured after hir-lowering -- this aligns with + // the cases that were stabilized with the `impl_trait_projection` + // feature -- see . + if let DefKind::LifetimeParam = tcx.def_kind(def_id) + && let ty::ReEarlyParam(ty::EarlyParamRegion { def_id, .. }) + | ty::ReLateParam(ty::LateParamRegion { + bound_region: ty::BoundRegionKind::BrNamed(def_id, _), + .. + }) = *tcx.map_opaque_lifetime_to_parent_lifetime(def_id.expect_local()) + { + shadowed_captures.insert(def_id); + } } _ => { tcx.dcx().span_delayed_bug( @@ -555,39 +571,61 @@ fn check_opaque_precise_captures<'tcx>(tcx: TyCtxt<'tcx>, opaque_def_id: LocalDe ); continue; } + // If a param is shadowed by a early-bound (duplicated) lifetime, then + // it may or may not be captured as invariant, depending on if it shows + // up through `Self` or `T::Assoc` syntax. + if shadowed_captures.contains(¶m.def_id) { + continue; + } match param.kind { ty::GenericParamDefKind::Lifetime => { + let use_span = tcx.def_span(param.def_id); + let opaque_span = tcx.def_span(opaque_def_id); // Check if the lifetime param was captured but isn't named in the precise captures list. if variances[param.index as usize] == ty::Invariant { - let param_span = - if let ty::ReEarlyParam(ty::EarlyParamRegion { def_id, .. }) + if let DefKind::OpaqueTy = tcx.def_kind(tcx.parent(param.def_id)) + && let ty::ReEarlyParam(ty::EarlyParamRegion { def_id, .. }) | ty::ReLateParam(ty::LateParamRegion { bound_region: ty::BoundRegionKind::BrNamed(def_id, _), .. }) = *tcx .map_opaque_lifetime_to_parent_lifetime(param.def_id.expect_local()) - { - Some(tcx.def_span(def_id)) - } else { - None - }; - // FIXME(precise_capturing): Structured suggestion for this would be useful - tcx.dcx().emit_err(errors::LifetimeNotCaptured { - use_span: tcx.def_span(param.def_id), - param_span, - opaque_span: tcx.def_span(opaque_def_id), - }); + { + tcx.dcx().emit_err(errors::LifetimeNotCaptured { + opaque_span, + use_span, + param_span: tcx.def_span(def_id), + }); + } else { + // If the `use_span` is actually just the param itself, then we must + // have not duplicated the lifetime but captured the original. + // The "effective" `use_span` will be the span of the opaque itself, + // and the param span will be the def span of the param. + tcx.dcx().emit_err(errors::LifetimeNotCaptured { + opaque_span, + use_span: opaque_span, + param_span: use_span, + }); + } continue; } } ty::GenericParamDefKind::Type { .. } => { - // FIXME(precise_capturing): Structured suggestion for this would be useful - tcx.dcx().emit_err(errors::ParamNotCaptured { - param_span: tcx.def_span(param.def_id), - opaque_span: tcx.def_span(opaque_def_id), - kind: "type", - }); + if matches!(tcx.def_kind(param.def_id), DefKind::Trait | DefKind::TraitAlias) { + // FIXME(precise_capturing): Structured suggestion for this would be useful + tcx.dcx().emit_err(errors::SelfTyNotCaptured { + trait_span: tcx.def_span(param.def_id), + opaque_span: tcx.def_span(opaque_def_id), + }); + } else { + // FIXME(precise_capturing): Structured suggestion for this would be useful + tcx.dcx().emit_err(errors::ParamNotCaptured { + param_span: tcx.def_span(param.def_id), + opaque_span: tcx.def_span(opaque_def_id), + kind: "type", + }); + } } ty::GenericParamDefKind::Const { .. } => { // FIXME(precise_capturing): Structured suggestion for this would be useful diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index bd64621f0773..fb4a76bf089f 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -128,6 +128,7 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) - | sym::variant_count | sym::is_val_statically_known | sym::ptr_mask + | sym::aggregate_raw_ptr | sym::ub_checks | sym::fadd_algebraic | sym::fsub_algebraic @@ -574,6 +575,10 @@ pub fn check_intrinsic_type( (0, 0, vec![Ty::new_imm_ptr(tcx, Ty::new_unit(tcx))], tcx.types.usize) } + // This type check is not particularly useful, but the `where` bounds + // on the definition in `core` do the heavy lifting for checking it. + sym::aggregate_raw_ptr => (3, 1, vec![param(1), param(2)], param(0)), + sym::ub_checks => (0, 1, Vec::new(), tcx.types.bool), sym::simd_eq diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index c2205815ba6b..02291cc603e1 100644 --- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs +++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs @@ -4,7 +4,7 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_hir as hir; use rustc_infer::traits::util; use rustc_middle::ty::GenericArgs; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeFolder}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::Span; @@ -214,7 +214,7 @@ impl<'tcx> TypeFolder> for AssocTyToOpaque<'tcx> { { self.tcx.type_of(projection_ty.def_id).instantiate(self.tcx, projection_ty.args) } else { - ty + ty.super_fold_with(self) } } } diff --git a/compiler/rustc_hir_analysis/src/errors/precise_captures.rs b/compiler/rustc_hir_analysis/src/errors/precise_captures.rs index 520bf1d9f404..8a9b5fe63696 100644 --- a/compiler/rustc_hir_analysis/src/errors/precise_captures.rs +++ b/compiler/rustc_hir_analysis/src/errors/precise_captures.rs @@ -6,19 +6,29 @@ use rustc_span::{Span, Symbol}; #[note] pub struct ParamNotCaptured { #[primary_span] - pub param_span: Span, - #[label] pub opaque_span: Span, + #[label] + pub param_span: Span, pub kind: &'static str, } +#[derive(Diagnostic)] +#[diag(hir_analysis_self_ty_not_captured)] +#[note] +pub struct SelfTyNotCaptured { + #[primary_span] + pub opaque_span: Span, + #[label] + pub trait_span: Span, +} + #[derive(Diagnostic)] #[diag(hir_analysis_lifetime_not_captured)] pub struct LifetimeNotCaptured { #[primary_span] pub use_span: Span, #[label(hir_analysis_param_label)] - pub param_span: Option, + pub param_span: Span, #[label] pub opaque_span: Span, } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs index d340a08ee79b..dd76862451c6 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs @@ -228,8 +228,8 @@ pub fn lower_generic_args<'tcx: 'a, 'a>( // Check whether this segment takes generic arguments and the user has provided any. let (generic_args, infer_args) = ctx.args_for_def_id(def_id); - let args_iter = generic_args.iter().flat_map(|generic_args| generic_args.args.iter()); - let mut args_iter = args_iter.clone().peekable(); + let mut args_iter = + generic_args.iter().flat_map(|generic_args| generic_args.args.iter()).peekable(); // If we encounter a type or const when we expect a lifetime, we infer the lifetimes. // If we later encounter a lifetime, we know that the arguments were provided in the diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index c374f9762d69..66b86c9eb02a 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -63,7 +63,6 @@ This API is completely unstable and subject to change. #![feature(rustdoc_internals)] #![allow(internal_features)] #![feature(control_flow_enum)] -#![feature(generic_nonzero)] #![feature(if_let_guard)] #![feature(is_sorted)] #![feature(iter_intersperse)] diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index a64d3b9633f2..dfd0b7c2945c 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -38,8 +38,11 @@ pub fn check_legal_trait_for_method_call( receiver: Option, expr_span: Span, trait_id: DefId, + body_id: DefId, ) -> Result<(), ErrorGuaranteed> { - if tcx.lang_items().drop_trait() == Some(trait_id) { + if tcx.lang_items().drop_trait() == Some(trait_id) + && tcx.lang_items().fallback_surface_drop_fn() != Some(body_id) + { let sugg = if let Some(receiver) = receiver.filter(|s| !s.is_empty()) { errors::ExplicitDestructorCallSugg::Snippet { lo: expr_span.shrink_to_lo(), diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 8923137fdd82..48b9142b0142 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -2697,7 +2697,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn point_at_param_definition(&self, err: &mut Diag<'_>, param: ty::ParamTy) { let generics = self.tcx.generics_of(self.body_id); - let generic_param = generics.type_param(¶m, self.tcx); + let generic_param = generics.type_param(param, self.tcx); if let ty::GenericParamDefKind::Type { synthetic: true, .. } = generic_param.kind { return; } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index bd2454f6368d..0b684fdf57be 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -1131,6 +1131,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None, span, container_id, + self.body_id.to_def_id(), ) { self.set_tainted_by_errors(e); } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index d9e5289f6323..b729fbe844a3 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -2144,7 +2144,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let callee_ty = callee_ty.peel_refs(); match *callee_ty.kind() { ty::Param(param) => { - let param = self.tcx.generics_of(self.body_id).type_param(¶m, self.tcx); + let param = self.tcx.generics_of(self.body_id).type_param(param, self.tcx); if param.kind.is_synthetic() { // if it's `impl Fn() -> ..` then just fall down to the def-id based logic def_id = param.def_id; diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index 02759064abd4..d57015282de9 100644 --- a/compiler/rustc_hir_typeck/src/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -628,6 +628,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { Some(self.self_expr.span), self.call_expr.span, trait_def_id, + self.body_id.to_def_id(), ) { self.set_tainted_by_errors(e); } diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 46227e406a32..e74c4d06b5b2 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -125,11 +125,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let Some(arg_ty) = args[0].as_type() else { return false; }; - let ty::Param(param) = arg_ty.kind() else { + let ty::Param(param) = *arg_ty.kind() else { return false; }; // Is `generic_param` the same as the arg for this trait predicate? - generic_param.index == generics.type_param(¶m, tcx).index + generic_param.index == generics.type_param(param, tcx).index } else { false } @@ -156,10 +156,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return false; } - match ty.peel_refs().kind() { + match *ty.peel_refs().kind() { ty::Param(param) => { let generics = self.tcx.generics_of(self.body_id); - let generic_param = generics.type_param(¶m, self.tcx); + let generic_param = generics.type_param(param, self.tcx); for unsatisfied in unsatisfied_predicates.iter() { // The parameter implements `IntoIterator` // but it has called a method that requires it to implement `Iterator` @@ -3232,9 +3232,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .sort_by_key(|&info| (!info.def_id.is_local(), self.tcx.def_path_str(info.def_id))); candidates.dedup(); - let param_type = match rcvr_ty.kind() { + let param_type = match *rcvr_ty.kind() { ty::Param(param) => Some(param), - ty::Ref(_, ty, _) => match ty.kind() { + ty::Ref(_, ty, _) => match *ty.kind() { ty::Param(param) => Some(param), _ => None, }, @@ -3429,7 +3429,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { param_type.map_or_else( || "implement".to_string(), // FIXME: it might only need to be imported into scope, not implemented. - ToString::to_string, + |p| p.to_string(), ) }, }, diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs index 1462037c8c8e..bf6ee044a0bd 100644 --- a/compiler/rustc_incremental/src/persist/fs.rs +++ b/compiler/rustc_incremental/src/persist/fs.rs @@ -162,8 +162,11 @@ pub fn query_cache_path(sess: &Session) -> PathBuf { fn lock_file_path(session_dir: &Path) -> PathBuf { let crate_dir = session_dir.parent().unwrap(); - let directory_name = session_dir.file_name().unwrap().to_string_lossy(); - assert_no_characters_lost(&directory_name); + let directory_name = session_dir + .file_name() + .unwrap() + .to_str() + .expect("malformed session dir name: contains non-Unicode characters"); let dash_indices: Vec<_> = directory_name.match_indices('-').map(|(idx, _)| idx).collect(); if dash_indices.len() != 3 { @@ -329,8 +332,11 @@ pub fn finalize_session_directory(sess: &Session, svh: Option) { debug!("finalize_session_directory() - session directory: {}", incr_comp_session_dir.display()); - let old_sub_dir_name = incr_comp_session_dir.file_name().unwrap().to_string_lossy(); - assert_no_characters_lost(&old_sub_dir_name); + let old_sub_dir_name = incr_comp_session_dir + .file_name() + .unwrap() + .to_str() + .expect("malformed session dir name: contains non-Unicode characters"); // Keep the 's-{timestamp}-{random-number}' prefix, but replace the // '-working' part with the SVH of the crate @@ -527,8 +533,10 @@ where for session_dir in iter { debug!("find_source_directory_in_iter - inspecting `{}`", session_dir.display()); - let directory_name = session_dir.file_name().unwrap().to_string_lossy(); - assert_no_characters_lost(&directory_name); + let Some(directory_name) = session_dir.file_name().unwrap().to_str() else { + debug!("find_source_directory_in_iter - ignoring"); + continue; + }; if source_directories_already_tried.contains(&session_dir) || !is_session_directory(&directory_name) @@ -619,12 +627,6 @@ fn crate_path(sess: &Session) -> PathBuf { incr_dir.join(crate_name) } -fn assert_no_characters_lost(s: &str) { - if s.contains('\u{FFFD}') { - bug!("Could not losslessly convert '{}'.", s) - } -} - fn is_old_enough_to_be_collected(timestamp: SystemTime) -> bool { timestamp < SystemTime::now() - Duration::from_secs(10) } @@ -657,14 +659,14 @@ pub(crate) fn garbage_collect_session_directories(sess: &Session) -> io::Result< }; let entry_name = dir_entry.file_name(); - let entry_name = entry_name.to_string_lossy(); + let Some(entry_name) = entry_name.to_str() else { + continue; + }; if is_session_directory_lock_file(&entry_name) { - assert_no_characters_lost(&entry_name); - lock_files.insert(entry_name.into_owned()); + lock_files.insert(entry_name.to_string()); } else if is_session_directory(&entry_name) { - assert_no_characters_lost(&entry_name); - session_directories.insert(entry_name.into_owned()); + session_directories.insert(entry_name.to_string()); } else { // This is something we don't know, leave it alone } diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 29c9f08a1660..feef64bc6a56 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -61,7 +61,7 @@ use crate::traits::{ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_errors::{ codes::*, pluralize, struct_span_code_err, Applicability, Diag, DiagCtxt, DiagStyledString, - ErrorGuaranteed, IntoDiagArg, + ErrorGuaranteed, IntoDiagArg, StringPart, }; use rustc_hir as hir; use rustc_hir::def::DefKind; @@ -1917,6 +1917,23 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ); if !is_simple_error || terr.must_include_note() { diag.note_expected_found(&expected_label, expected, &found_label, found); + + if let Some(ty::Closure(_, args)) = + exp_found.map(|expected_type_found| expected_type_found.found.kind()) + { + diag.highlighted_note(vec![ + StringPart::normal("closure has signature: `"), + StringPart::highlighted( + self.tcx + .signature_unclosure( + args.as_closure().sig(), + rustc_hir::Unsafety::Normal, + ) + .to_string(), + ), + StringPart::normal("`"), + ]); + } } } } @@ -2354,7 +2371,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // type_param_sugg_span is (span, has_bounds) let (type_scope, type_param_sugg_span) = match bound_kind { - GenericKind::Param(ref param) => { + GenericKind::Param(param) => { let generics = self.tcx.generics_of(generic_param_scope); let def_id = generics.type_param(param, self.tcx).def_id.expect_local(); let scope = self.tcx.local_def_id_to_hir_id(def_id).owner.def_id; 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 008b75b4c9a1..6a42f9b42c38 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 @@ -28,7 +28,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { match err { ArgumentSorts(values, _) | Sorts(values) => { - match (values.expected.kind(), values.found.kind()) { + match (*values.expected.kind(), *values.found.kind()) { (ty::Closure(..), ty::Closure(..)) => { diag.note("no two closures, even if identical, have the same type"); diag.help("consider boxing your closure and/or using it as a trait object"); @@ -452,7 +452,7 @@ impl Trait for X { } (ty::FnPtr(sig), ty::FnDef(def_id, _)) | (ty::FnDef(def_id, _), ty::FnPtr(sig)) => { - if tcx.fn_sig(*def_id).skip_binder().unsafety() < sig.unsafety() { + if tcx.fn_sig(def_id).skip_binder().unsafety() < sig.unsafety() { diag.note( "unsafe functions cannot be coerced into safe function pointers", ); @@ -527,7 +527,7 @@ impl Trait for X { diag: &mut Diag<'_>, msg: impl Fn() -> String, body_owner_def_id: DefId, - proj_ty: &ty::AliasTy<'tcx>, + proj_ty: ty::AliasTy<'tcx>, ty: Ty<'tcx>, ) -> bool { let tcx = self.tcx; @@ -541,7 +541,7 @@ impl Trait for X { }; // Get the `DefId` for the type parameter corresponding to `A` in `::Foo`. // This will also work for `impl Trait`. - let ty::Param(param_ty) = proj_ty.self_ty().kind() else { + let ty::Param(param_ty) = *proj_ty.self_ty().kind() else { return false; }; let generics = tcx.generics_of(body_owner_def_id); @@ -598,7 +598,7 @@ impl Trait for X { fn expected_projection( &self, diag: &mut Diag<'_>, - proj_ty: &ty::AliasTy<'tcx>, + proj_ty: ty::AliasTy<'tcx>, values: ExpectedFound>, body_owner_def_id: DefId, cause_code: &ObligationCauseCode<'_>, @@ -709,7 +709,7 @@ fn foo(&self) -> Self::T { String::new() } &self, diag: &mut Diag<'_>, msg: impl Fn() -> String, - proj_ty: &ty::AliasTy<'tcx>, + proj_ty: ty::AliasTy<'tcx>, ty: Ty<'tcx>, ) -> bool { let tcx = self.tcx; diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 355fcf594952..81130d691510 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -1521,15 +1521,6 @@ impl<'tcx> InferCtxt<'tcx> { closure_kind_ty.to_opt_closure_kind() } - /// Clears the selection, evaluation, and projection caches. This is useful when - /// repeatedly attempting to select an `Obligation` while changing only - /// its `ParamEnv`, since `FulfillmentContext` doesn't use probing. - pub fn clear_caches(&self) { - self.selection_cache.clear(); - self.evaluation_cache.clear(); - self.inner.borrow_mut().projection_cache().clear(); - } - pub fn universe(&self) -> ty::UniverseIndex { self.universe.get() } diff --git a/compiler/rustc_infer/src/traits/project.rs b/compiler/rustc_infer/src/traits/project.rs index 31ceb2343324..c6ffba59638e 100644 --- a/compiler/rustc_infer/src/traits/project.rs +++ b/compiler/rustc_infer/src/traits/project.rs @@ -78,11 +78,12 @@ pub struct ProjectionCacheStorage<'tcx> { #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] pub struct ProjectionCacheKey<'tcx> { ty: ty::AliasTy<'tcx>, + param_env: ty::ParamEnv<'tcx>, } impl<'tcx> ProjectionCacheKey<'tcx> { - pub fn new(ty: ty::AliasTy<'tcx>) -> Self { - Self { ty } + pub fn new(ty: ty::AliasTy<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Self { + Self { ty, param_env } } } diff --git a/compiler/rustc_interface/src/lib.rs b/compiler/rustc_interface/src/lib.rs index d0ce23dacb5e..75df006a56f1 100644 --- a/compiler/rustc_interface/src/lib.rs +++ b/compiler/rustc_interface/src/lib.rs @@ -1,5 +1,4 @@ #![feature(decl_macro)] -#![feature(generic_nonzero)] #![feature(lazy_cell)] #![feature(let_chains)] #![feature(thread_spawn_unchecked)] diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 9d53f0799ea9..8d741ef4c1b6 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -760,7 +760,7 @@ fn test_unstable_options_tracking_hash() { ); tracked!(codegen_backend, Some("abc".to_string())); tracked!(collapse_macro_debuginfo, CollapseMacroDebuginfo::Yes); - tracked!(coverage_options, CoverageOptions { branch: true }); + tracked!(coverage_options, CoverageOptions { branch: true, mcdc: true }); tracked!(crate_attr, vec!["abc".to_string()]); tracked!(cross_crate_inline_threshold, InliningThreshold::Always); tracked!(debug_info_for_profiling, true); diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 31c80c4d75bb..086c3834410f 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -32,7 +32,6 @@ #![feature(box_patterns)] #![feature(control_flow_enum)] #![feature(extract_if)] -#![feature(generic_nonzero)] #![feature(if_let_guard)] #![feature(iter_order_by)] #![feature(let_chains)] diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs index 4b06278330f3..004c2c2e4f44 100644 --- a/compiler/rustc_lint/src/non_local_def.rs +++ b/compiler/rustc_lint/src/non_local_def.rs @@ -264,7 +264,19 @@ fn impl_trait_ref_has_enough_non_local_candidates<'tcx>( binder: EarlyBinder>, mut did_has_local_parent: impl FnMut(DefId) -> bool, ) -> bool { - let infcx = tcx.infer_ctxt().build(); + let infcx = tcx + .infer_ctxt() + // We use the new trait solver since the obligation we are trying to + // prove here may overflow and those are fatal in the old trait solver. + // Which is unacceptable for a lint. + // + // Thanksfully the part we use here are very similar to the + // new-trait-solver-as-coherence, which is in stabilization. + // + // https://github.com/rust-lang/rust/issues/123573 + .with_next_trait_solver(true) + .build(); + let trait_ref = binder.instantiate(tcx, infcx.fresh_args_for_item(infer_span, trait_def_id)); let trait_ref = trait_ref.fold_with(&mut ReplaceLocalTypesWithInfer { diff --git a/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp index 0998b463a886..e842f47f48c2 100644 --- a/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp @@ -4,8 +4,6 @@ #include "llvm/ProfileData/Coverage/CoverageMappingWriter.h" #include "llvm/ProfileData/InstrProf.h" -#include - using namespace llvm; // FFI equivalent of enum `llvm::coverage::Counter::CounterKind` @@ -43,6 +41,8 @@ enum class LLVMRustCounterMappingRegionKind { SkippedRegion = 2, GapRegion = 3, BranchRegion = 4, + MCDCDecisionRegion = 5, + MCDCBranchRegion = 6 }; static coverage::CounterMappingRegion::RegionKind @@ -58,15 +58,102 @@ fromRust(LLVMRustCounterMappingRegionKind Kind) { return coverage::CounterMappingRegion::GapRegion; case LLVMRustCounterMappingRegionKind::BranchRegion: return coverage::CounterMappingRegion::BranchRegion; +#if LLVM_VERSION_GE(18, 0) + case LLVMRustCounterMappingRegionKind::MCDCDecisionRegion: + return coverage::CounterMappingRegion::MCDCDecisionRegion; + case LLVMRustCounterMappingRegionKind::MCDCBranchRegion: + return coverage::CounterMappingRegion::MCDCBranchRegion; +#else + case LLVMRustCounterMappingRegionKind::MCDCDecisionRegion: + break; + case LLVMRustCounterMappingRegionKind::MCDCBranchRegion: + break; +#endif } report_fatal_error("Bad LLVMRustCounterMappingRegionKind!"); } +enum LLVMRustMCDCParametersTag { + None = 0, + Decision = 1, + Branch = 2, +}; + +struct LLVMRustMCDCDecisionParameters { + uint32_t BitmapIdx; + uint16_t NumConditions; +}; + +struct LLVMRustMCDCBranchParameters { + int16_t ConditionID; + int16_t ConditionIDs[2]; +}; + +struct LLVMRustMCDCParameters { + LLVMRustMCDCParametersTag Tag; + LLVMRustMCDCDecisionParameters DecisionParameters; + LLVMRustMCDCBranchParameters BranchParameters; +}; + +// LLVM representations for `MCDCParameters` evolved from LLVM 18 to 19. +// Look at representations in 18 +// https://github.com/rust-lang/llvm-project/blob/66a2881a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L253-L263 +// and representations in 19 +// https://github.com/llvm/llvm-project/blob/843cc474faefad1d639f4c44c1cf3ad7dbda76c8/llvm/include/llvm/ProfileData/Coverage/MCDCTypes.h +#if LLVM_VERSION_GE(18, 0) && LLVM_VERSION_LT(19, 0) +static coverage::CounterMappingRegion::MCDCParameters +fromRust(LLVMRustMCDCParameters Params) { + auto parameter = coverage::CounterMappingRegion::MCDCParameters{}; + switch (Params.Tag) { + case LLVMRustMCDCParametersTag::None: + return parameter; + case LLVMRustMCDCParametersTag::Decision: + parameter.BitmapIdx = + static_cast(Params.DecisionParameters.BitmapIdx), + parameter.NumConditions = + static_cast(Params.DecisionParameters.NumConditions); + return parameter; + case LLVMRustMCDCParametersTag::Branch: + parameter.ID = static_cast( + Params.BranchParameters.ConditionID), + parameter.FalseID = + static_cast( + Params.BranchParameters.ConditionIDs[0]), + parameter.TrueID = + static_cast( + Params.BranchParameters.ConditionIDs[1]); + return parameter; + } + report_fatal_error("Bad LLVMRustMCDCParametersTag!"); +} +#elif LLVM_VERSION_GE(19, 0) +static coverage::mcdc::Parameters fromRust(LLVMRustMCDCParameters Params) { + switch (Params.Tag) { + case LLVMRustMCDCParametersTag::None: + return std::monostate(); + case LLVMRustMCDCParametersTag::Decision: + return coverage::mcdc::DecisionParameters( + Params.DecisionParameters.BitmapIdx, + Params.DecisionParameters.NumConditions); + case LLVMRustMCDCParametersTag::Branch: + return coverage::mcdc::BranchParameters( + static_cast( + Params.BranchParameters.ConditionID), + {static_cast( + Params.BranchParameters.ConditionIDs[0]), + static_cast( + Params.BranchParameters.ConditionIDs[1])}); + } + report_fatal_error("Bad LLVMRustMCDCParametersTag!"); +} +#endif + // FFI equivalent of struct `llvm::coverage::CounterMappingRegion` // https://github.com/rust-lang/llvm-project/blob/ea6fa9c2/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L211-L304 struct LLVMRustCounterMappingRegion { LLVMRustCounter Count; LLVMRustCounter FalseCount; + LLVMRustMCDCParameters MCDCParameters; uint32_t FileID; uint32_t ExpandedFileID; uint32_t LineStart; @@ -135,7 +222,8 @@ extern "C" void LLVMRustCoverageWriteMappingToBuffer( MappingRegions.emplace_back( fromRust(Region.Count), fromRust(Region.FalseCount), #if LLVM_VERSION_GE(18, 0) && LLVM_VERSION_LT(19, 0) - coverage::CounterMappingRegion::MCDCParameters{}, + // LLVM 19 may move this argument to last. + fromRust(Region.MCDCParameters), #endif Region.FileID, Region.ExpandedFileID, // File IDs, then region info. Region.LineStart, Region.ColumnStart, Region.LineEnd, Region.ColumnEnd, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 6e11fd629e4d..565bdc3af03d 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1522,6 +1522,7 @@ extern "C" void LLVMRustFreeOperandBundleDef(OperandBundleDef *Bundle) { delete Bundle; } +// OpBundlesIndirect is an array of pointers (*not* a pointer to an array). extern "C" LLVMValueRef LLVMRustBuildCall(LLVMBuilderRef B, LLVMTypeRef Ty, LLVMValueRef Fn, LLVMValueRef *Args, unsigned NumArgs, OperandBundleDef **OpBundlesIndirect, @@ -1546,6 +1547,33 @@ extern "C" LLVMValueRef LLVMRustGetInstrProfIncrementIntrinsic(LLVMModuleRef M) unwrap(M), llvm::Intrinsic::instrprof_increment)); } +extern "C" LLVMValueRef LLVMRustGetInstrProfMCDCParametersIntrinsic(LLVMModuleRef M) { +#if LLVM_VERSION_GE(18, 0) + return wrap(llvm::Intrinsic::getDeclaration( + unwrap(M), llvm::Intrinsic::instrprof_mcdc_parameters)); +#else + report_fatal_error("LLVM 18.0 is required for mcdc intrinsic functions"); +#endif +} + +extern "C" LLVMValueRef LLVMRustGetInstrProfMCDCTVBitmapUpdateIntrinsic(LLVMModuleRef M) { +#if LLVM_VERSION_GE(18, 0) + return wrap(llvm::Intrinsic::getDeclaration( + unwrap(M), llvm::Intrinsic::instrprof_mcdc_tvbitmap_update)); +#else + report_fatal_error("LLVM 18.0 is required for mcdc intrinsic functions"); +#endif +} + +extern "C" LLVMValueRef LLVMRustGetInstrProfMCDCCondBitmapIntrinsic(LLVMModuleRef M) { +#if LLVM_VERSION_GE(18, 0) + return wrap(llvm::Intrinsic::getDeclaration( + unwrap(M), llvm::Intrinsic::instrprof_mcdc_condbitmap_update)); +#else + report_fatal_error("LLVM 18.0 is required for mcdc intrinsic functions"); +#endif +} + extern "C" LLVMValueRef LLVMRustBuildMemCpy(LLVMBuilderRef B, LLVMValueRef Dst, unsigned DstAlign, LLVMValueRef Src, unsigned SrcAlign, @@ -1574,6 +1602,7 @@ extern "C" LLVMValueRef LLVMRustBuildMemSet(LLVMBuilderRef B, unwrap(Dst), unwrap(Val), unwrap(Size), MaybeAlign(DstAlign), IsVolatile)); } +// OpBundlesIndirect is an array of pointers (*not* a pointer to an array). extern "C" LLVMValueRef LLVMRustBuildInvoke(LLVMBuilderRef B, LLVMTypeRef Ty, LLVMValueRef Fn, LLVMValueRef *Args, unsigned NumArgs, @@ -1596,6 +1625,7 @@ LLVMRustBuildInvoke(LLVMBuilderRef B, LLVMTypeRef Ty, LLVMValueRef Fn, Name)); } +// OpBundlesIndirect is an array of pointers (*not* a pointer to an array). extern "C" LLVMValueRef LLVMRustBuildCallBr(LLVMBuilderRef B, LLVMTypeRef Ty, LLVMValueRef Fn, LLVMBasicBlockRef DefaultDest, diff --git a/compiler/rustc_metadata/src/lib.rs b/compiler/rustc_metadata/src/lib.rs index f133a2f5f73b..7dd03407bd7c 100644 --- a/compiler/rustc_metadata/src/lib.rs +++ b/compiler/rustc_metadata/src/lib.rs @@ -6,7 +6,6 @@ #![feature(error_iter)] #![feature(extract_if)] #![feature(coroutines)] -#![feature(generic_nonzero)] #![feature(iter_from_coroutine)] #![feature(let_chains)] #![feature(if_let_guard)] diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index e52a5863fd03..aadaca18326c 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -35,7 +35,6 @@ #![feature(const_type_name)] #![feature(discriminant_kind)] #![feature(coroutines)] -#![feature(generic_nonzero)] #![feature(if_let_guard)] #![feature(inline_const)] #![feature(iter_from_coroutine)] diff --git a/compiler/rustc_middle/src/middle/exported_symbols.rs b/compiler/rustc_middle/src/middle/exported_symbols.rs index e30b6b203d73..123b32f4aea9 100644 --- a/compiler/rustc_middle/src/middle/exported_symbols.rs +++ b/compiler/rustc_middle/src/middle/exported_symbols.rs @@ -43,6 +43,7 @@ pub enum ExportedSymbol<'tcx> { NonGeneric(DefId), Generic(DefId, GenericArgsRef<'tcx>), DropGlue(Ty<'tcx>), + AsyncDropGlueCtorShim(Ty<'tcx>), ThreadLocalShim(DefId), NoDefId(ty::SymbolName<'tcx>), } @@ -59,6 +60,9 @@ impl<'tcx> ExportedSymbol<'tcx> { ExportedSymbol::DropGlue(ty) => { tcx.symbol_name(ty::Instance::resolve_drop_in_place(tcx, ty)) } + ExportedSymbol::AsyncDropGlueCtorShim(ty) => { + tcx.symbol_name(ty::Instance::resolve_async_drop_in_place(tcx, ty)) + } ExportedSymbol::ThreadLocalShim(def_id) => tcx.symbol_name(ty::Instance { def: ty::InstanceDef::ThreadLocalShim(def_id), args: ty::GenericArgs::empty(), diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs index 582a18066881..04011fd41948 100644 --- a/compiler/rustc_middle/src/mir/coverage.rs +++ b/compiler/rustc_middle/src/mir/coverage.rs @@ -51,6 +51,25 @@ rustc_index::newtype_index! { pub struct ExpressionId {} } +rustc_index::newtype_index! { + /// ID of a mcdc condition. Used by llvm to check mcdc coverage. + /// + /// Note for future: the max limit of 0xFFFF is probably too loose. Actually llvm does not + /// support decisions with too many conditions (7 and more at LLVM 18 while may be hundreds at 19) + /// and represents it with `int16_t`. This max value may be changed once we could + /// figure out an accurate limit. + #[derive(HashStable)] + #[encodable] + #[orderable] + #[max = 0xFFFF] + #[debug_format = "ConditionId({})"] + pub struct ConditionId {} +} + +impl ConditionId { + pub const NONE: Self = Self::from_u32(0); +} + /// Enum that can hold a constant zero value, the ID of an physical coverage /// counter, or the ID of a coverage-counter expression. /// @@ -106,6 +125,22 @@ pub enum CoverageKind { /// mappings. Intermediate expressions with no direct mappings are /// retained/zeroed based on whether they are transitively used.) ExpressionUsed { id: ExpressionId }, + + /// Marks the point in MIR control flow represented by a evaluated condition. + /// + /// This is eventually lowered to `llvm.instrprof.mcdc.condbitmap.update` in LLVM IR. + /// + /// If this statement does not survive MIR optimizations, the condition would never be + /// taken as evaluated. + CondBitmapUpdate { id: ConditionId, value: bool }, + + /// Marks the point in MIR control flow represented by a evaluated decision. + /// + /// This is eventually lowered to `llvm.instrprof.mcdc.tvbitmap.update` in LLVM IR. + /// + /// If this statement does not survive MIR optimizations, the decision would never be + /// taken as evaluated. + TestVectorBitmapUpdate { bitmap_idx: u32 }, } impl Debug for CoverageKind { @@ -116,6 +151,12 @@ impl Debug for CoverageKind { BlockMarker { id } => write!(fmt, "BlockMarker({:?})", id.index()), CounterIncrement { id } => write!(fmt, "CounterIncrement({:?})", id.index()), ExpressionUsed { id } => write!(fmt, "ExpressionUsed({:?})", id.index()), + CondBitmapUpdate { id, value } => { + write!(fmt, "CondBitmapUpdate({:?}, {:?})", id.index(), value) + } + TestVectorBitmapUpdate { bitmap_idx } => { + write!(fmt, "TestVectorUpdate({:?})", bitmap_idx) + } } } } @@ -172,16 +213,23 @@ pub enum MappingKind { Code(CovTerm), /// Associates a branch region with separate counters for true and false. Branch { true_term: CovTerm, false_term: CovTerm }, + /// Associates a branch region with separate counters for true and false. + MCDCBranch { true_term: CovTerm, false_term: CovTerm, mcdc_params: ConditionInfo }, + /// Associates a decision region with a bitmap and number of conditions. + MCDCDecision(DecisionInfo), } impl MappingKind { /// Iterator over all coverage terms in this mapping kind. pub fn terms(&self) -> impl Iterator { - let one = |a| std::iter::once(a).chain(None); - let two = |a, b| std::iter::once(a).chain(Some(b)); + let zero = || None.into_iter().chain(None); + let one = |a| Some(a).into_iter().chain(None); + let two = |a, b| Some(a).into_iter().chain(Some(b)); match *self { Self::Code(term) => one(term), Self::Branch { true_term, false_term } => two(true_term, false_term), + Self::MCDCBranch { true_term, false_term, .. } => two(true_term, false_term), + Self::MCDCDecision(_) => zero(), } } @@ -193,6 +241,12 @@ impl MappingKind { Self::Branch { true_term, false_term } => { Self::Branch { true_term: map_fn(true_term), false_term: map_fn(false_term) } } + Self::MCDCBranch { true_term, false_term, mcdc_params } => Self::MCDCBranch { + true_term: map_fn(true_term), + false_term: map_fn(false_term), + mcdc_params, + }, + Self::MCDCDecision(param) => Self::MCDCDecision(param), } } } @@ -212,7 +266,7 @@ pub struct Mapping { pub struct FunctionCoverageInfo { pub function_source_hash: u64, pub num_counters: usize, - + pub mcdc_bitmap_bytes: u32, pub expressions: IndexVec, pub mappings: Vec, } @@ -226,6 +280,8 @@ pub struct BranchInfo { /// data structures without having to scan the entire body first. pub num_block_markers: usize, pub branch_spans: Vec, + pub mcdc_branch_spans: Vec, + pub mcdc_decision_spans: Vec, } #[derive(Clone, Debug)] @@ -235,3 +291,47 @@ pub struct BranchSpan { pub true_marker: BlockMarkerId, pub false_marker: BlockMarkerId, } + +#[derive(Copy, Clone, Debug)] +#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] +pub struct ConditionInfo { + pub condition_id: ConditionId, + pub true_next_id: ConditionId, + pub false_next_id: ConditionId, +} + +impl Default for ConditionInfo { + fn default() -> Self { + Self { + condition_id: ConditionId::NONE, + true_next_id: ConditionId::NONE, + false_next_id: ConditionId::NONE, + } + } +} + +#[derive(Clone, Debug)] +#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] +pub struct MCDCBranchSpan { + pub span: Span, + /// If `None`, this actually represents a normal branch span inserted for + /// code that was too complex for MC/DC. + pub condition_info: Option, + pub true_marker: BlockMarkerId, + pub false_marker: BlockMarkerId, +} + +#[derive(Copy, Clone, Debug)] +#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] +pub struct DecisionInfo { + pub bitmap_idx: u32, + pub conditions_num: u16, +} + +#[derive(Clone, Debug)] +#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] +pub struct MCDCDecisionSpan { + pub span: Span, + pub conditions_num: usize, + pub end_markers: Vec, +} diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index 43e1318a75ab..9eed70197823 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -65,7 +65,9 @@ impl<'tcx> MonoItem<'tcx> { match instance.def { // "Normal" functions size estimate: the number of // statements, plus one for the terminator. - InstanceDef::Item(..) | InstanceDef::DropGlue(..) => { + InstanceDef::Item(..) + | InstanceDef::DropGlue(..) + | InstanceDef::AsyncDropGlueCtorShim(..) => { let mir = tcx.instance_mir(instance.def); mir.basic_blocks.iter().map(|bb| bb.statements.len() + 1).sum() } @@ -406,7 +408,8 @@ impl<'tcx> CodegenUnit<'tcx> { | InstanceDef::DropGlue(..) | InstanceDef::CloneShim(..) | InstanceDef::ThreadLocalShim(..) - | InstanceDef::FnPtrAddrShim(..) => None, + | InstanceDef::FnPtrAddrShim(..) + | InstanceDef::AsyncDropGlueCtorShim(..) => None, } } MonoItem::Static(def_id) => def_id.as_local().map(Idx::index), diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 15bd5c089652..7a91d7383e51 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -187,6 +187,17 @@ fn dump_path<'tcx>( })); s } + ty::InstanceDef::AsyncDropGlueCtorShim(_, Some(ty)) => { + // Unfortunately, pretty-printed typed are not very filename-friendly. + // We dome some filtering. + let mut s = ".".to_owned(); + s.extend(ty.to_string().chars().filter_map(|c| match c { + ' ' => None, + ':' | '<' | '>' => Some('_'), + c => Some(c), + })); + s + } _ => String::new(), }; @@ -475,7 +486,8 @@ fn write_coverage_branch_info( branch_info: &coverage::BranchInfo, w: &mut dyn io::Write, ) -> io::Result<()> { - let coverage::BranchInfo { branch_spans, .. } = branch_info; + let coverage::BranchInfo { branch_spans, mcdc_branch_spans, mcdc_decision_spans, .. } = + branch_info; for coverage::BranchSpan { span, true_marker, false_marker } in branch_spans { writeln!( @@ -483,7 +495,26 @@ fn write_coverage_branch_info( "{INDENT}coverage branch {{ true: {true_marker:?}, false: {false_marker:?} }} => {span:?}", )?; } - if !branch_spans.is_empty() { + + for coverage::MCDCBranchSpan { span, condition_info, true_marker, false_marker } in + mcdc_branch_spans + { + writeln!( + w, + "{INDENT}coverage mcdc branch {{ condition_id: {:?}, true: {true_marker:?}, false: {false_marker:?} }} => {span:?}", + condition_info.map(|info| info.condition_id) + )?; + } + + for coverage::MCDCDecisionSpan { span, conditions_num, end_markers } in mcdc_decision_spans { + writeln!( + w, + "{INDENT}coverage mcdc decision {{ conditions_num: {conditions_num:?}, end: {end_markers:?} }} => {span:?}" + )?; + } + + if !branch_spans.is_empty() || !mcdc_branch_spans.is_empty() || !mcdc_decision_spans.is_empty() + { writeln!(w)?; } @@ -1074,6 +1105,15 @@ impl<'tcx> Debug for Rvalue<'tcx> { struct_fmt.finish() }), + + AggregateKind::RawPtr(pointee_ty, mutability) => { + let kind_str = match mutability { + Mutability::Mut => "mut", + Mutability::Not => "const", + }; + with_no_trimmed_paths!(write!(fmt, "*{kind_str} {pointee_ty} from "))?; + fmt_tuple(fmt, "") + } } } diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index 105f30f1db82..e3f58729fbd1 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -361,4 +361,8 @@ pub struct CoverageIdsInfo { /// InstrumentCoverage MIR pass, if the highest-numbered counter increments /// were removed by MIR optimizations. pub max_counter_id: mir::coverage::CounterId, + + /// Coverage codegen for mcdc needs to know the size of the global bitmap so that it can + /// set the `bytemap-bytes` argument of the `llvm.instrprof.mcdc.tvbitmap.update` intrinsic. + pub mcdc_bitmap_bytes: u32, } diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 97c3eb556386..db13bb9a3e8e 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -1351,6 +1351,21 @@ pub enum AggregateKind<'tcx> { Closure(DefId, GenericArgsRef<'tcx>), Coroutine(DefId, GenericArgsRef<'tcx>), CoroutineClosure(DefId, GenericArgsRef<'tcx>), + + /// Construct a raw pointer from the data pointer and metadata. + /// + /// The `Ty` here is the type of the *pointee*, not the pointer itself. + /// The `Mutability` indicates whether this produces a `*const` or `*mut`. + /// + /// The [`Rvalue::Aggregate`] operands for thus must be + /// + /// 0. A raw pointer of matching mutability with any [`core::ptr::Thin`] pointee + /// 1. A value of the appropriate [`core::ptr::Pointee::Metadata`] type + /// + /// *Both* operands must always be included, even the unit value if this is + /// creating a thin pointer. If you're just converting between thin pointers, + /// you may want an [`Rvalue::Cast`] with [`CastKind::PtrToPtr`] instead. + RawPtr(Ty<'tcx>, Mutability), } #[derive(Copy, Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)] diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs index 506003ff7c0c..abe99f3e95c2 100644 --- a/compiler/rustc_middle/src/mir/tcx.rs +++ b/compiler/rustc_middle/src/mir/tcx.rs @@ -206,6 +206,7 @@ impl<'tcx> Rvalue<'tcx> { AggregateKind::CoroutineClosure(did, args) => { Ty::new_coroutine_closure(tcx, did, args) } + AggregateKind::RawPtr(ty, mutability) => Ty::new_ptr(tcx, ty, mutability), }, Rvalue::ShallowInitBox(_, ty) => Ty::new_box(tcx, ty), Rvalue::CopyForDeref(ref place) => place.ty(local_decls, tcx).ty, diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 4f7b2f7cbe48..a6d525230b00 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -350,12 +350,14 @@ macro_rules! make_mir_visitor { receiver_by_ref: _, } | ty::InstanceDef::CoroutineKindShim { coroutine_def_id: _def_id } | + ty::InstanceDef::AsyncDropGlueCtorShim(_def_id, None) | ty::InstanceDef::DropGlue(_def_id, None) => {} ty::InstanceDef::FnPtrShim(_def_id, ty) | ty::InstanceDef::DropGlue(_def_id, Some(ty)) | ty::InstanceDef::CloneShim(_def_id, ty) | - ty::InstanceDef::FnPtrAddrShim(_def_id, ty) => { + ty::InstanceDef::FnPtrAddrShim(_def_id, ty) | + ty::InstanceDef::AsyncDropGlueCtorShim(_def_id, Some(ty)) => { // FIXME(eddyb) use a better `TyContext` here. self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)); } @@ -751,6 +753,9 @@ macro_rules! make_mir_visitor { ) => { self.visit_args(coroutine_closure_args, location); } + AggregateKind::RawPtr(ty, _) => { + self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)); + } } for operand in operands { diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 0d625ff0faeb..4ae79399ef48 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1344,6 +1344,14 @@ rustc_queries! { query is_unpin_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { desc { "computing whether `{}` is `Unpin`", env.value } } + /// Query backing `Ty::has_surface_async_drop`. + query has_surface_async_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { + desc { "computing whether `{}` has `AsyncDrop` implementation", env.value } + } + /// Query backing `Ty::has_surface_drop`. + query has_surface_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { + desc { "computing whether `{}` has `Drop` implementation", env.value } + } /// Query backing `Ty::needs_drop`. query needs_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { desc { "computing whether `{}` needs drop", env.value } diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 66130a8dde03..d52b1efce4b0 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -1006,15 +1006,18 @@ impl<'tcx> PatRangeBoundary<'tcx> { // This code is hot when compiling matches with many ranges. So we // special-case extraction of evaluated scalars for speed, for types where - // unsigned int comparisons are appropriate. E.g. `unicode-normalization` has + // we can do scalar comparisons. E.g. `unicode-normalization` has // many ranges such as '\u{037A}'..='\u{037F}', and chars can be compared // in this way. - (Finite(a), Finite(b)) if matches!(ty.kind(), ty::Uint(_) | ty::Char) => { + (Finite(a), Finite(b)) if matches!(ty.kind(), ty::Int(_) | ty::Uint(_) | ty::Char) => { if let (Some(a), Some(b)) = (a.try_to_scalar_int(), b.try_to_scalar_int()) { let sz = ty.primitive_size(tcx); - let a = a.assert_uint(sz); - let b = b.assert_uint(sz); - return Some(a.cmp(&b)); + let cmp = match ty.kind() { + ty::Uint(_) | ty::Char => a.assert_uint(sz).cmp(&b.assert_uint(sz)), + ty::Int(_) => a.assert_int(sz).cmp(&b.assert_int(sz)), + _ => unreachable!(), + }; + return Some(cmp); } } _ => {} diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs index e984f5437017..a99196cb3630 100644 --- a/compiler/rustc_middle/src/ty/generics.rs +++ b/compiler/rustc_middle/src/ty/generics.rs @@ -263,7 +263,7 @@ impl<'tcx> Generics { /// Returns the `GenericParamDef` associated with this `EarlyParamRegion`. pub fn region_param( &'tcx self, - param: &ty::EarlyParamRegion, + param: ty::EarlyParamRegion, tcx: TyCtxt<'tcx>, ) -> &'tcx GenericParamDef { let param = self.param_at(param.index as usize, tcx); @@ -274,7 +274,7 @@ impl<'tcx> Generics { } /// Returns the `GenericParamDef` associated with this `ParamTy`. - pub fn type_param(&'tcx self, param: &ParamTy, tcx: TyCtxt<'tcx>) -> &'tcx GenericParamDef { + pub fn type_param(&'tcx self, param: ParamTy, tcx: TyCtxt<'tcx>) -> &'tcx GenericParamDef { let param = self.param_at(param.index as usize, tcx); match param.kind { GenericParamDefKind::Type { .. } => param, @@ -286,7 +286,7 @@ impl<'tcx> Generics { /// `Generics`. pub fn opt_type_param( &'tcx self, - param: &ParamTy, + param: ParamTy, tcx: TyCtxt<'tcx>, ) -> Option<&'tcx GenericParamDef> { let param = self.opt_param_at(param.index as usize, tcx)?; @@ -297,7 +297,7 @@ impl<'tcx> Generics { } /// Returns the `GenericParamDef` associated with this `ParamConst`. - pub fn const_param(&'tcx self, param: &ParamConst, tcx: TyCtxt<'tcx>) -> &GenericParamDef { + pub fn const_param(&'tcx self, param: ParamConst, tcx: TyCtxt<'tcx>) -> &GenericParamDef { let param = self.param_at(param.index as usize, tcx); match param.kind { GenericParamDefKind::Const { .. } => param, diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index f8f59fbeab44..4002d0da7909 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -168,6 +168,12 @@ pub enum InstanceDef<'tcx> { /// /// The `DefId` is for `FnPtr::addr`, the `Ty` is the type `T`. FnPtrAddrShim(DefId, Ty<'tcx>), + + /// `core::future::async_drop::async_drop_in_place::<'_, T>`. + /// + /// The `DefId` is for `core::future::async_drop::async_drop_in_place`, the `Ty` + /// is the type `T`. + AsyncDropGlueCtorShim(DefId, Option>), } impl<'tcx> Instance<'tcx> { @@ -210,7 +216,9 @@ impl<'tcx> Instance<'tcx> { InstanceDef::Item(def) => tcx .upstream_monomorphizations_for(def) .and_then(|monos| monos.get(&self.args).cloned()), - InstanceDef::DropGlue(_, Some(_)) => tcx.upstream_drop_glue_for(self.args), + InstanceDef::DropGlue(_, Some(_)) | InstanceDef::AsyncDropGlueCtorShim(_, _) => { + tcx.upstream_drop_glue_for(self.args) + } _ => None, } } @@ -235,7 +243,8 @@ impl<'tcx> InstanceDef<'tcx> { | ty::InstanceDef::CoroutineKindShim { coroutine_def_id: def_id } | InstanceDef::DropGlue(def_id, _) | InstanceDef::CloneShim(def_id, _) - | InstanceDef::FnPtrAddrShim(def_id, _) => def_id, + | InstanceDef::FnPtrAddrShim(def_id, _) + | InstanceDef::AsyncDropGlueCtorShim(def_id, _) => def_id, } } @@ -243,9 +252,9 @@ impl<'tcx> InstanceDef<'tcx> { pub fn def_id_if_not_guaranteed_local_codegen(self) -> Option { match self { ty::InstanceDef::Item(def) => Some(def), - ty::InstanceDef::DropGlue(def_id, Some(_)) | InstanceDef::ThreadLocalShim(def_id) => { - Some(def_id) - } + ty::InstanceDef::DropGlue(def_id, Some(_)) + | InstanceDef::AsyncDropGlueCtorShim(def_id, _) + | InstanceDef::ThreadLocalShim(def_id) => Some(def_id), InstanceDef::VTableShim(..) | InstanceDef::ReifyShim(..) | InstanceDef::FnPtrShim(..) @@ -279,6 +288,7 @@ impl<'tcx> InstanceDef<'tcx> { let def_id = match *self { ty::InstanceDef::Item(def) => def, ty::InstanceDef::DropGlue(_, Some(_)) => return false, + ty::InstanceDef::AsyncDropGlueCtorShim(_, Some(_)) => return false, ty::InstanceDef::ThreadLocalShim(_) => return false, _ => return true, }; @@ -347,11 +357,13 @@ impl<'tcx> InstanceDef<'tcx> { | InstanceDef::ThreadLocalShim(..) | InstanceDef::FnPtrAddrShim(..) | InstanceDef::FnPtrShim(..) - | InstanceDef::DropGlue(_, Some(_)) => false, + | InstanceDef::DropGlue(_, Some(_)) + | InstanceDef::AsyncDropGlueCtorShim(_, Some(_)) => false, InstanceDef::ClosureOnceShim { .. } | InstanceDef::ConstructCoroutineInClosureShim { .. } | InstanceDef::CoroutineKindShim { .. } | InstanceDef::DropGlue(..) + | InstanceDef::AsyncDropGlueCtorShim(..) | InstanceDef::Item(_) | InstanceDef::Intrinsic(..) | InstanceDef::ReifyShim(..) @@ -396,6 +408,8 @@ fn fmt_instance( InstanceDef::DropGlue(_, Some(ty)) => write!(f, " - shim(Some({ty}))"), InstanceDef::CloneShim(_, ty) => write!(f, " - shim({ty})"), InstanceDef::FnPtrAddrShim(_, ty) => write!(f, " - shim({ty})"), + InstanceDef::AsyncDropGlueCtorShim(_, None) => write!(f, " - shim(None)"), + InstanceDef::AsyncDropGlueCtorShim(_, Some(ty)) => write!(f, " - shim(Some({ty}))"), } } @@ -638,6 +652,12 @@ impl<'tcx> Instance<'tcx> { Instance::expect_resolve(tcx, ty::ParamEnv::reveal_all(), def_id, args) } + pub fn resolve_async_drop_in_place(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ty::Instance<'tcx> { + let def_id = tcx.require_lang_item(LangItem::AsyncDropInPlace, None); + let args = tcx.mk_args(&[ty.into()]); + Instance::expect_resolve(tcx, ty::ParamEnv::reveal_all(), def_id, args) + } + #[instrument(level = "debug", skip(tcx), ret)] pub fn fn_once_adapter_instance( tcx: TyCtxt<'tcx>, diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index d0a69c6fd2c1..cc026e349d74 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1797,7 +1797,8 @@ impl<'tcx> TyCtxt<'tcx> { | ty::InstanceDef::DropGlue(..) | ty::InstanceDef::CloneShim(..) | ty::InstanceDef::ThreadLocalShim(..) - | ty::InstanceDef::FnPtrAddrShim(..) => self.mir_shims(instance), + | ty::InstanceDef::FnPtrAddrShim(..) + | ty::InstanceDef::AsyncDropGlueCtorShim(..) => self.mir_shims(instance), } } diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 0bd009cd51da..e5450182bf29 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1276,7 +1276,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { fn pretty_print_inherent_projection( &mut self, - alias_ty: &ty::AliasTy<'tcx>, + alias_ty: ty::AliasTy<'tcx>, ) -> Result<(), PrintError> { let def_key = self.tcx().def_key(alias_ty.def_id); self.path_generic_args( @@ -3204,7 +3204,7 @@ define_print_and_forward_display! { ty::AliasTy<'tcx> { if let DefKind::Impl { of_trait: false } = cx.tcx().def_kind(cx.tcx().parent(self.def_id)) { - p!(pretty_print_inherent_projection(self)) + p!(pretty_print_inherent_projection(*self)) } else { // If we're printing verbosely, or don't want to invoke queries // (`is_impl_trait_in_trait`), then fall back to printing the def path. diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 90c68e7ddfc5..14a77d4b37ec 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -427,6 +427,7 @@ TrivialTypeTraversalImpls! { crate::mir::coverage::BlockMarkerId, crate::mir::coverage::CounterId, crate::mir::coverage::ExpressionId, + crate::mir::coverage::ConditionId, crate::mir::Local, crate::mir::Promoted, crate::traits::Reveal, diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 6084e8d7cab2..0184ff549795 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -24,6 +24,7 @@ use rustc_target::abi::{FieldIdx, VariantIdx, FIRST_VARIANT}; use rustc_target::spec::abi::{self, Abi}; use std::assert_matches::debug_assert_matches; use std::borrow::Cow; +use std::iter; use std::ops::{ControlFlow, Deref, Range}; use ty::util::IntTypeExt; @@ -1379,7 +1380,7 @@ impl<'tcx> ParamTy { Ty::new_param(tcx, self.index, self.name) } - pub fn span_from_generics(&self, tcx: TyCtxt<'tcx>, item_with_generics: DefId) -> Span { + pub fn span_from_generics(self, tcx: TyCtxt<'tcx>, item_with_generics: DefId) -> Span { let generics = tcx.generics_of(item_with_generics); let type_param = generics.type_param(self, tcx); tcx.def_span(type_param.def_id) @@ -2316,6 +2317,133 @@ impl<'tcx> Ty<'tcx> { } } + /// Returns the type of the async destructor of this type. + pub fn async_destructor_ty(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Ty<'tcx> { + if self.is_async_destructor_noop(tcx, param_env) || matches!(self.kind(), ty::Error(_)) { + return Ty::async_destructor_combinator(tcx, LangItem::AsyncDropNoop) + .instantiate_identity(); + } + match *self.kind() { + ty::Param(_) | ty::Alias(..) | ty::Infer(ty::TyVar(_)) => { + let assoc_items = tcx + .associated_item_def_ids(tcx.require_lang_item(LangItem::AsyncDestruct, None)); + Ty::new_projection(tcx, assoc_items[0], [self]) + } + + ty::Array(elem_ty, _) | ty::Slice(elem_ty) => { + let dtor = Ty::async_destructor_combinator(tcx, LangItem::AsyncDropSlice) + .instantiate(tcx, &[elem_ty.into()]); + Ty::async_destructor_combinator(tcx, LangItem::AsyncDropFuse) + .instantiate(tcx, &[dtor.into()]) + } + + ty::Adt(adt_def, args) if adt_def.is_enum() || adt_def.is_struct() => self + .adt_async_destructor_ty( + tcx, + adt_def.variants().iter().map(|v| v.fields.iter().map(|f| f.ty(tcx, args))), + param_env, + ), + ty::Tuple(tys) => self.adt_async_destructor_ty(tcx, iter::once(tys), param_env), + ty::Closure(_, args) => self.adt_async_destructor_ty( + tcx, + iter::once(args.as_closure().upvar_tys()), + param_env, + ), + ty::CoroutineClosure(_, args) => self.adt_async_destructor_ty( + tcx, + iter::once(args.as_coroutine_closure().upvar_tys()), + param_env, + ), + + ty::Adt(adt_def, _) => { + assert!(adt_def.is_union()); + + let surface_drop = self.surface_async_dropper_ty(tcx, param_env).unwrap(); + + Ty::async_destructor_combinator(tcx, LangItem::AsyncDropFuse) + .instantiate(tcx, &[surface_drop.into()]) + } + + ty::Bound(..) + | ty::Foreign(_) + | ty::Placeholder(_) + | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + bug!("`async_destructor_ty` applied to unexpected type: {self:?}") + } + + _ => bug!("`async_destructor_ty` is not yet implemented for type: {self:?}"), + } + } + + fn adt_async_destructor_ty( + self, + tcx: TyCtxt<'tcx>, + variants: I, + param_env: ParamEnv<'tcx>, + ) -> Ty<'tcx> + where + I: Iterator + ExactSizeIterator, + I::Item: IntoIterator>, + { + debug_assert!(!self.is_async_destructor_noop(tcx, param_env)); + + let defer = Ty::async_destructor_combinator(tcx, LangItem::AsyncDropDefer); + let chain = Ty::async_destructor_combinator(tcx, LangItem::AsyncDropChain); + + let noop = + Ty::async_destructor_combinator(tcx, LangItem::AsyncDropNoop).instantiate_identity(); + let either = Ty::async_destructor_combinator(tcx, LangItem::AsyncDropEither); + + let variants_dtor = variants + .into_iter() + .map(|variant| { + variant + .into_iter() + .map(|ty| defer.instantiate(tcx, &[ty.into()])) + .reduce(|acc, next| chain.instantiate(tcx, &[acc.into(), next.into()])) + .unwrap_or(noop) + }) + .reduce(|other, matched| { + either.instantiate(tcx, &[other.into(), matched.into(), self.into()]) + }) + .unwrap(); + + let dtor = if let Some(dropper_ty) = self.surface_async_dropper_ty(tcx, param_env) { + Ty::async_destructor_combinator(tcx, LangItem::AsyncDropChain) + .instantiate(tcx, &[dropper_ty.into(), variants_dtor.into()]) + } else { + variants_dtor + }; + + Ty::async_destructor_combinator(tcx, LangItem::AsyncDropFuse) + .instantiate(tcx, &[dtor.into()]) + } + + fn surface_async_dropper_ty( + self, + tcx: TyCtxt<'tcx>, + param_env: ParamEnv<'tcx>, + ) -> Option> { + if self.has_surface_async_drop(tcx, param_env) { + Some(LangItem::SurfaceAsyncDropInPlace) + } else if self.has_surface_drop(tcx, param_env) { + Some(LangItem::AsyncDropSurfaceDropInPlace) + } else { + None + } + .map(|dropper| { + Ty::async_destructor_combinator(tcx, dropper).instantiate(tcx, &[self.into()]) + }) + } + + fn async_destructor_combinator( + tcx: TyCtxt<'tcx>, + lang_item: LangItem, + ) -> ty::EarlyBinder> { + tcx.fn_sig(tcx.require_lang_item(lang_item, None)) + .map_bound(|fn_sig| fn_sig.output().no_bound_vars().unwrap()) + } + /// Returns the type of metadata for (potentially fat) pointers to this type, /// or the struct tail if the metadata type cannot be determined. pub fn ptr_metadata_ty_or_tail( diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 9af665cfb6fe..42e0565db83d 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -432,19 +432,19 @@ impl<'tcx> TyCtxt<'tcx> { .filter(|&(_, k)| { match k.unpack() { GenericArgKind::Lifetime(region) => match region.kind() { - ty::ReEarlyParam(ref ebr) => { + ty::ReEarlyParam(ebr) => { !impl_generics.region_param(ebr, self).pure_wrt_drop } // Error: not a region param _ => false, }, - GenericArgKind::Type(ty) => match ty.kind() { - ty::Param(ref pt) => !impl_generics.type_param(pt, self).pure_wrt_drop, + GenericArgKind::Type(ty) => match *ty.kind() { + ty::Param(pt) => !impl_generics.type_param(pt, self).pure_wrt_drop, // Error: not a type param _ => false, }, GenericArgKind::Const(ct) => match ct.kind() { - ty::ConstKind::Param(ref pc) => { + ty::ConstKind::Param(pc) => { !impl_generics.const_param(pc, self).pure_wrt_drop } // Error: not a const param @@ -1303,6 +1303,98 @@ impl<'tcx> Ty<'tcx> { } } + /// Checks whether values of this type `T` implements the `AsyncDrop` + /// trait. + pub fn has_surface_async_drop(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool { + self.could_have_surface_async_drop() && tcx.has_surface_async_drop_raw(param_env.and(self)) + } + + /// Fast path helper for testing if a type has `AsyncDrop` + /// implementation. + /// + /// Returning `false` means the type is known to not have `AsyncDrop` + /// implementation. Returning `true` means nothing -- could be + /// `AsyncDrop`, might not be. + fn could_have_surface_async_drop(self) -> bool { + !self.is_async_destructor_trivially_noop() + && !matches!( + self.kind(), + ty::Tuple(_) + | ty::Slice(_) + | ty::Array(_, _) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Coroutine(..) + ) + } + + /// Checks whether values of this type `T` implements the `Drop` + /// trait. + pub fn has_surface_drop(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool { + self.could_have_surface_drop() && tcx.has_surface_drop_raw(param_env.and(self)) + } + + /// Fast path helper for testing if a type has `Drop` implementation. + /// + /// Returning `false` means the type is known to not have `Drop` + /// implementation. Returning `true` means nothing -- could be + /// `Drop`, might not be. + fn could_have_surface_drop(self) -> bool { + !self.is_async_destructor_trivially_noop() + && !matches!( + self.kind(), + ty::Tuple(_) + | ty::Slice(_) + | ty::Array(_, _) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Coroutine(..) + ) + } + + /// Checks whether values of this type `T` implement has noop async destructor. + // + // FIXME: implement optimization to make ADTs, which do not need drop, + // to skip fields or to have noop async destructor. + pub fn is_async_destructor_noop( + self, + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) -> bool { + self.is_async_destructor_trivially_noop() + || if let ty::Adt(adt_def, _) = self.kind() { + (adt_def.is_union() || adt_def.is_payloadfree()) + && !self.has_surface_async_drop(tcx, param_env) + && !self.has_surface_drop(tcx, param_env) + } else { + false + } + } + + /// Fast path helper for testing if a type has noop async destructor. + /// + /// Returning `true` means the type is known to have noop async destructor + /// implementation. Returning `true` means nothing -- could be + /// `Drop`, might not be. + fn is_async_destructor_trivially_noop(self) -> bool { + match self.kind() { + ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Bool + | ty::Char + | ty::Str + | ty::Never + | ty::Ref(..) + | ty::RawPtr(..) + | ty::FnDef(..) + | ty::FnPtr(_) => true, + ty::Tuple(tys) => tys.is_empty(), + ty::Adt(adt_def, _) => adt_def.is_manually_drop(), + _ => false, + } + } + /// If `ty.needs_drop(...)` returns `true`, then `ty` is definitely /// non-copy and *might* have a destructor attached; if it returns /// `false`, then `ty` definitely has no destructor (i.e., no drop glue). diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl index 1de691f32a70..34440c60cf37 100644 --- a/compiler/rustc_mir_build/messages.ftl +++ b/compiler/rustc_mir_build/messages.ftl @@ -97,6 +97,8 @@ mir_build_deref_raw_pointer_requires_unsafe_unsafe_op_in_unsafe_fn_allowed = .note = raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior .label = dereference of raw pointer +mir_build_exceeds_mcdc_condition_num_limit = Conditions number of the decision ({$conditions_num}) exceeds limit ({$max_conditions_num}). MCDC analysis will not count this expression. + mir_build_extern_static_requires_unsafe = use of extern static is unsafe and requires unsafe block .note = extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior diff --git a/compiler/rustc_mir_build/src/build/coverageinfo.rs b/compiler/rustc_mir_build/src/build/coverageinfo.rs index ab0043906b19..9e9ccd3dc2d0 100644 --- a/compiler/rustc_mir_build/src/build/coverageinfo.rs +++ b/compiler/rustc_mir_build/src/build/coverageinfo.rs @@ -1,14 +1,20 @@ use std::assert_matches::assert_matches; use std::collections::hash_map::Entry; +use std::collections::VecDeque; use rustc_data_structures::fx::FxHashMap; -use rustc_middle::mir::coverage::{BlockMarkerId, BranchSpan, CoverageKind}; -use rustc_middle::mir::{self, BasicBlock, UnOp}; -use rustc_middle::thir::{ExprId, ExprKind, Thir}; +use rustc_middle::mir::coverage::{ + BlockMarkerId, BranchSpan, ConditionId, ConditionInfo, CoverageKind, MCDCBranchSpan, + MCDCDecisionSpan, +}; +use rustc_middle::mir::{self, BasicBlock, SourceInfo, UnOp}; +use rustc_middle::thir::{ExprId, ExprKind, LogicalOp, Thir}; use rustc_middle::ty::TyCtxt; use rustc_span::def_id::LocalDefId; +use rustc_span::Span; -use crate::build::Builder; +use crate::build::{Builder, CFG}; +use crate::errors::MCDCExceedsConditionNumLimit; pub(crate) struct BranchInfoBuilder { /// Maps condition expressions to their enclosing `!`, for better instrumentation. @@ -16,6 +22,10 @@ pub(crate) struct BranchInfoBuilder { num_block_markers: usize, branch_spans: Vec, + + mcdc_branch_spans: Vec, + mcdc_decision_spans: Vec, + mcdc_state: Option, } #[derive(Clone, Copy)] @@ -33,7 +43,14 @@ impl BranchInfoBuilder { /// is enabled and `def_id` represents a function that is eligible for coverage. pub(crate) fn new_if_enabled(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option { if tcx.sess.instrument_coverage_branch() && tcx.is_eligible_for_coverage(def_id) { - Some(Self { nots: FxHashMap::default(), num_block_markers: 0, branch_spans: vec![] }) + Some(Self { + nots: FxHashMap::default(), + num_block_markers: 0, + branch_spans: vec![], + mcdc_branch_spans: vec![], + mcdc_decision_spans: vec![], + mcdc_state: MCDCState::new_if_enabled(tcx), + }) } else { None } @@ -79,21 +96,242 @@ impl BranchInfoBuilder { } } + fn fetch_mcdc_condition_info( + &mut self, + tcx: TyCtxt<'_>, + true_marker: BlockMarkerId, + false_marker: BlockMarkerId, + ) -> Option { + let mcdc_state = self.mcdc_state.as_mut()?; + let (mut condition_info, decision_result) = + mcdc_state.take_condition(true_marker, false_marker); + if let Some(decision) = decision_result { + match decision.conditions_num { + 0 => { + unreachable!("Decision with no condition is not expected"); + } + 1..=MAX_CONDITIONS_NUM_IN_DECISION => { + self.mcdc_decision_spans.push(decision); + } + _ => { + // Do not generate mcdc mappings and statements for decisions with too many conditions. + let rebase_idx = self.mcdc_branch_spans.len() - decision.conditions_num + 1; + for branch in &mut self.mcdc_branch_spans[rebase_idx..] { + branch.condition_info = None; + } + + // ConditionInfo of this branch shall also be reset. + condition_info = None; + + tcx.dcx().emit_warn(MCDCExceedsConditionNumLimit { + span: decision.span, + conditions_num: decision.conditions_num, + max_conditions_num: MAX_CONDITIONS_NUM_IN_DECISION, + }); + } + } + } + condition_info + } + + fn add_two_way_branch<'tcx>( + &mut self, + cfg: &mut CFG<'tcx>, + source_info: SourceInfo, + true_block: BasicBlock, + false_block: BasicBlock, + ) { + let true_marker = self.inject_block_marker(cfg, source_info, true_block); + let false_marker = self.inject_block_marker(cfg, source_info, false_block); + + self.branch_spans.push(BranchSpan { span: source_info.span, true_marker, false_marker }); + } + fn next_block_marker_id(&mut self) -> BlockMarkerId { let id = BlockMarkerId::from_usize(self.num_block_markers); self.num_block_markers += 1; id } + fn inject_block_marker( + &mut self, + cfg: &mut CFG<'_>, + source_info: SourceInfo, + block: BasicBlock, + ) -> BlockMarkerId { + let id = self.next_block_marker_id(); + + let marker_statement = mir::Statement { + source_info, + kind: mir::StatementKind::Coverage(CoverageKind::BlockMarker { id }), + }; + cfg.push(block, marker_statement); + + id + } + pub(crate) fn into_done(self) -> Option> { - let Self { nots: _, num_block_markers, branch_spans } = self; + let Self { + nots: _, + num_block_markers, + branch_spans, + mcdc_branch_spans, + mcdc_decision_spans, + mcdc_state: _, + } = self; if num_block_markers == 0 { assert!(branch_spans.is_empty()); return None; } - Some(Box::new(mir::coverage::BranchInfo { num_block_markers, branch_spans })) + Some(Box::new(mir::coverage::BranchInfo { + num_block_markers, + branch_spans, + mcdc_branch_spans, + mcdc_decision_spans, + })) + } +} + +/// The MCDC bitmap scales exponentially (2^n) based on the number of conditions seen, +/// So llvm sets a maximum value prevents the bitmap footprint from growing too large without the user's knowledge. +/// This limit may be relaxed if the [upstream change](https://github.com/llvm/llvm-project/pull/82448) is merged. +const MAX_CONDITIONS_NUM_IN_DECISION: usize = 6; + +struct MCDCState { + /// To construct condition evaluation tree. + decision_stack: VecDeque, + processing_decision: Option, +} + +impl MCDCState { + fn new_if_enabled(tcx: TyCtxt<'_>) -> Option { + tcx.sess + .instrument_coverage_mcdc() + .then(|| Self { decision_stack: VecDeque::new(), processing_decision: None }) + } + + // At first we assign ConditionIds for each sub expression. + // If the sub expression is composite, re-assign its ConditionId to its LHS and generate a new ConditionId for its RHS. + // + // Example: "x = (A && B) || (C && D) || (D && F)" + // + // Visit Depth1: + // (A && B) || (C && D) || (D && F) + // ^-------LHS--------^ ^-RHS--^ + // ID=1 ID=2 + // + // Visit LHS-Depth2: + // (A && B) || (C && D) + // ^-LHS--^ ^-RHS--^ + // ID=1 ID=3 + // + // Visit LHS-Depth3: + // (A && B) + // LHS RHS + // ID=1 ID=4 + // + // Visit RHS-Depth3: + // (C && D) + // LHS RHS + // ID=3 ID=5 + // + // Visit RHS-Depth2: (D && F) + // LHS RHS + // ID=2 ID=6 + // + // Visit Depth1: + // (A && B) || (C && D) || (D && F) + // ID=1 ID=4 ID=3 ID=5 ID=2 ID=6 + // + // A node ID of '0' always means MC/DC isn't being tracked. + // + // If a "next" node ID is '0', it means it's the end of the test vector. + // + // As the compiler tracks expression in pre-order, we can ensure that condition info of parents are always properly assigned when their children are visited. + // - If the op is AND, the "false_next" of LHS and RHS should be the parent's "false_next". While "true_next" of the LHS is the RHS, the "true next" of RHS is the parent's "true_next". + // - If the op is OR, the "true_next" of LHS and RHS should be the parent's "true_next". While "false_next" of the LHS is the RHS, the "false next" of RHS is the parent's "false_next". + fn record_conditions(&mut self, op: LogicalOp, span: Span) { + let decision = match self.processing_decision.as_mut() { + Some(decision) => { + decision.span = decision.span.to(span); + decision + } + None => self.processing_decision.insert(MCDCDecisionSpan { + span, + conditions_num: 0, + end_markers: vec![], + }), + }; + + let parent_condition = self.decision_stack.pop_back().unwrap_or_default(); + let lhs_id = if parent_condition.condition_id == ConditionId::NONE { + decision.conditions_num += 1; + ConditionId::from(decision.conditions_num) + } else { + parent_condition.condition_id + }; + + decision.conditions_num += 1; + let rhs_condition_id = ConditionId::from(decision.conditions_num); + + let (lhs, rhs) = match op { + LogicalOp::And => { + let lhs = ConditionInfo { + condition_id: lhs_id, + true_next_id: rhs_condition_id, + false_next_id: parent_condition.false_next_id, + }; + let rhs = ConditionInfo { + condition_id: rhs_condition_id, + true_next_id: parent_condition.true_next_id, + false_next_id: parent_condition.false_next_id, + }; + (lhs, rhs) + } + LogicalOp::Or => { + let lhs = ConditionInfo { + condition_id: lhs_id, + true_next_id: parent_condition.true_next_id, + false_next_id: rhs_condition_id, + }; + let rhs = ConditionInfo { + condition_id: rhs_condition_id, + true_next_id: parent_condition.true_next_id, + false_next_id: parent_condition.false_next_id, + }; + (lhs, rhs) + } + }; + // We visit expressions tree in pre-order, so place the left-hand side on the top. + self.decision_stack.push_back(rhs); + self.decision_stack.push_back(lhs); + } + + fn take_condition( + &mut self, + true_marker: BlockMarkerId, + false_marker: BlockMarkerId, + ) -> (Option, Option) { + let Some(condition_info) = self.decision_stack.pop_back() else { + return (None, None); + }; + let Some(decision) = self.processing_decision.as_mut() else { + bug!("Processing decision should have been created before any conditions are taken"); + }; + if condition_info.true_next_id == ConditionId::NONE { + decision.end_markers.push(true_marker); + } + if condition_info.false_next_id == ConditionId::NONE { + decision.end_markers.push(false_marker); + } + + if self.decision_stack.is_empty() { + (Some(condition_info), self.processing_decision.take()) + } else { + (Some(condition_info), None) + } } } @@ -107,7 +345,7 @@ impl Builder<'_, '_> { mut else_block: BasicBlock, ) { // Bail out if branch coverage is not enabled for this function. - let Some(branch_info) = self.coverage_branch_info.as_ref() else { return }; + let Some(branch_info) = self.coverage_branch_info.as_mut() else { return }; // If this condition expression is nested within one or more `!` expressions, // replace it with the enclosing `!` collected by `visit_unary_not`. @@ -117,30 +355,34 @@ impl Builder<'_, '_> { std::mem::swap(&mut then_block, &mut else_block); } } - let source_info = self.source_info(self.thir[expr_id].span); - // Now that we have `source_info`, we can upgrade to a &mut reference. - let branch_info = self.coverage_branch_info.as_mut().expect("upgrading & to &mut"); + let source_info = SourceInfo { span: self.thir[expr_id].span, scope: self.source_scope }; - let mut inject_branch_marker = |block: BasicBlock| { - let id = branch_info.next_block_marker_id(); + // Separate path for handling branches when MC/DC is enabled. + if branch_info.mcdc_state.is_some() { + let mut inject_block_marker = + |block| branch_info.inject_block_marker(&mut self.cfg, source_info, block); + let true_marker = inject_block_marker(then_block); + let false_marker = inject_block_marker(else_block); + let condition_info = + branch_info.fetch_mcdc_condition_info(self.tcx, true_marker, false_marker); + branch_info.mcdc_branch_spans.push(MCDCBranchSpan { + span: source_info.span, + condition_info, + true_marker, + false_marker, + }); + return; + } - let marker_statement = mir::Statement { - source_info, - kind: mir::StatementKind::Coverage(CoverageKind::BlockMarker { id }), - }; - self.cfg.push(block, marker_statement); + branch_info.add_two_way_branch(&mut self.cfg, source_info, then_block, else_block); + } - id - }; - - let true_marker = inject_branch_marker(then_block); - let false_marker = inject_branch_marker(else_block); - - branch_info.branch_spans.push(BranchSpan { - span: source_info.span, - true_marker, - false_marker, - }); + pub(crate) fn visit_coverage_branch_operation(&mut self, logical_op: LogicalOp, span: Span) { + if let Some(branch_info) = self.coverage_branch_info.as_mut() + && let Some(mcdc_state) = branch_info.mcdc_state.as_mut() + { + mcdc_state.record_conditions(logical_op, span); + } } } diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index 9730473c4288..f46dceeeedf5 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -77,11 +77,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { match expr.kind { ExprKind::LogicalOp { op: LogicalOp::And, lhs, rhs } => { + this.visit_coverage_branch_operation(LogicalOp::And, expr_span); let lhs_then_block = unpack!(this.then_else_break_inner(block, lhs, args)); let rhs_then_block = unpack!(this.then_else_break_inner(lhs_then_block, rhs, args)); rhs_then_block.unit() } ExprKind::LogicalOp { op: LogicalOp::Or, lhs, rhs } => { + this.visit_coverage_branch_operation(LogicalOp::Or, expr_span); let local_scope = this.local_scope(); let (lhs_success_block, failure_block) = this.in_if_then_scope(local_scope, expr_span, |this| { diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index 26f10fdd333c..9ddfb12bf764 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -818,6 +818,15 @@ pub struct NontrivialStructuralMatch<'tcx> { pub non_sm_ty: Ty<'tcx>, } +#[derive(Diagnostic)] +#[diag(mir_build_exceeds_mcdc_condition_num_limit)] +pub(crate) struct MCDCExceedsConditionNumLimit { + #[primary_span] + pub span: Span, + pub conditions_num: usize, + pub max_conditions_num: usize, +} + #[derive(Diagnostic)] #[diag(mir_build_pattern_not_covered, code = E0005)] pub(crate) struct PatternNotCovered<'s, 'tcx> { diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index d382d2c03c24..0b15c52c2814 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -9,7 +9,7 @@ mod tests; use self::counters::{CounterIncrementSite, CoverageCounters}; use self::graph::{BasicCoverageBlock, CoverageGraph}; -use self::spans::{BcbMapping, BcbMappingKind, CoverageSpans}; +use self::spans::{BcbBranchPair, BcbMapping, BcbMappingKind, CoverageSpans}; use crate::MirPass; @@ -100,9 +100,12 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir: &coverage_counters, ); + inject_mcdc_statements(mir_body, &basic_coverage_blocks, &coverage_spans); + mir_body.function_coverage_info = Some(Box::new(FunctionCoverageInfo { function_source_hash: hir_info.function_source_hash, num_counters: coverage_counters.num_counters(), + mcdc_bitmap_bytes: coverage_spans.test_vector_bitmap_bytes(), expressions: coverage_counters.into_expressions(), mappings, })); @@ -136,20 +139,47 @@ fn create_mappings<'tcx>( .as_term() }; - coverage_spans - .all_bcb_mappings() - .filter_map(|&BcbMapping { kind: bcb_mapping_kind, span }| { - let kind = match bcb_mapping_kind { + let mut mappings = Vec::new(); + + mappings.extend(coverage_spans.mappings.iter().filter_map( + |BcbMapping { kind: bcb_mapping_kind, span }| { + let kind = match *bcb_mapping_kind { BcbMappingKind::Code(bcb) => MappingKind::Code(term_for_bcb(bcb)), - BcbMappingKind::Branch { true_bcb, false_bcb } => MappingKind::Branch { + BcbMappingKind::MCDCBranch { true_bcb, false_bcb, condition_info: None } => { + MappingKind::Branch { + true_term: term_for_bcb(true_bcb), + false_term: term_for_bcb(false_bcb), + } + } + BcbMappingKind::MCDCBranch { + true_bcb, + false_bcb, + condition_info: Some(mcdc_params), + } => MappingKind::MCDCBranch { true_term: term_for_bcb(true_bcb), false_term: term_for_bcb(false_bcb), + mcdc_params, }, + BcbMappingKind::MCDCDecision { bitmap_idx, conditions_num, .. } => { + MappingKind::MCDCDecision(DecisionInfo { bitmap_idx, conditions_num }) + } }; + let code_region = make_code_region(source_map, file_name, *span, body_span)?; + Some(Mapping { kind, code_region }) + }, + )); + + mappings.extend(coverage_spans.branch_pairs.iter().filter_map( + |&BcbBranchPair { span, true_bcb, false_bcb }| { + let true_term = term_for_bcb(true_bcb); + let false_term = term_for_bcb(false_bcb); + let kind = MappingKind::Branch { true_term, false_term }; let code_region = make_code_region(source_map, file_name, span, body_span)?; Some(Mapping { kind, code_region }) - }) - .collect::>() + }, + )); + + mappings } /// For each BCB node or BCB edge that has an associated coverage counter, @@ -204,6 +234,55 @@ fn inject_coverage_statements<'tcx>( } } +/// For each conditions inject statements to update condition bitmap after it has been evaluated. +/// For each decision inject statements to update test vector bitmap after it has been evaluated. +fn inject_mcdc_statements<'tcx>( + mir_body: &mut mir::Body<'tcx>, + basic_coverage_blocks: &CoverageGraph, + coverage_spans: &CoverageSpans, +) { + if coverage_spans.test_vector_bitmap_bytes() == 0 { + return; + } + + // Inject test vector update first because `inject_statement` always insert new statement at head. + for (end_bcbs, bitmap_idx) in + coverage_spans.mappings.iter().filter_map(|mapping| match &mapping.kind { + BcbMappingKind::MCDCDecision { end_bcbs, bitmap_idx, .. } => { + Some((end_bcbs, *bitmap_idx)) + } + _ => None, + }) + { + for end in end_bcbs { + let end_bb = basic_coverage_blocks[*end].leader_bb(); + inject_statement(mir_body, CoverageKind::TestVectorBitmapUpdate { bitmap_idx }, end_bb); + } + } + + for (true_bcb, false_bcb, condition_id) in + coverage_spans.mappings.iter().filter_map(|mapping| match mapping.kind { + BcbMappingKind::MCDCBranch { true_bcb, false_bcb, condition_info } => { + Some((true_bcb, false_bcb, condition_info?.condition_id)) + } + _ => None, + }) + { + let true_bb = basic_coverage_blocks[true_bcb].leader_bb(); + inject_statement( + mir_body, + CoverageKind::CondBitmapUpdate { id: condition_id, value: true }, + true_bb, + ); + let false_bb = basic_coverage_blocks[false_bcb].leader_bb(); + inject_statement( + mir_body, + CoverageKind::CondBitmapUpdate { id: condition_id, value: false }, + false_bb, + ); + } +} + /// Given two basic blocks that have a control-flow edge between them, creates /// and returns a new block that sits between those blocks. fn inject_edge_counter_basic_block( diff --git a/compiler/rustc_mir_transform/src/coverage/query.rs b/compiler/rustc_mir_transform/src/coverage/query.rs index 65715253647a..f77ee63d02c2 100644 --- a/compiler/rustc_mir_transform/src/coverage/query.rs +++ b/compiler/rustc_mir_transform/src/coverage/query.rs @@ -61,7 +61,17 @@ fn coverage_ids_info<'tcx>( .max() .unwrap_or(CounterId::ZERO); - CoverageIdsInfo { max_counter_id } + let mcdc_bitmap_bytes = mir_body + .coverage_branch_info + .as_deref() + .map(|info| { + info.mcdc_decision_spans + .iter() + .fold(0, |acc, decision| acc + (1_u32 << decision.conditions_num).div_ceil(8)) + }) + .unwrap_or_default(); + + CoverageIdsInfo { max_counter_id, mcdc_bitmap_bytes } } fn all_coverage_in_mir_body<'a, 'tcx>( diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs index 03ede8866884..88f18b720857 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans.rs @@ -1,7 +1,9 @@ use rustc_data_structures::graph::DirectedGraph; use rustc_index::bit_set::BitSet; use rustc_middle::mir; +use rustc_middle::mir::coverage::ConditionInfo; use rustc_span::{BytePos, Span}; +use std::collections::BTreeSet; use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph, START_BCB}; use crate::coverage::spans::from_mir::SpanFromMir; @@ -9,12 +11,24 @@ use crate::coverage::ExtractedHirInfo; mod from_mir; -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Debug)] pub(super) enum BcbMappingKind { /// Associates an ordinary executable code span with its corresponding BCB. Code(BasicCoverageBlock), - /// Associates a branch span with BCBs for its true and false arms. - Branch { true_bcb: BasicCoverageBlock, false_bcb: BasicCoverageBlock }, + + // Ordinary branch mappings are stored separately, so they don't have a + // variant in this enum. + // + /// Associates a mcdc branch span with condition info besides fields for normal branch. + MCDCBranch { + true_bcb: BasicCoverageBlock, + false_bcb: BasicCoverageBlock, + /// If `None`, this actually represents a normal branch mapping inserted + /// for code that was too complex for MC/DC. + condition_info: Option, + }, + /// Associates a mcdc decision with its join BCB. + MCDCDecision { end_bcbs: BTreeSet, bitmap_idx: u32, conditions_num: u16 }, } #[derive(Debug)] @@ -23,9 +37,21 @@ pub(super) struct BcbMapping { pub(super) span: Span, } +/// This is separate from [`BcbMappingKind`] to help prepare for larger changes +/// that will be needed for improved branch coverage in the future. +/// (See .) +#[derive(Debug)] +pub(super) struct BcbBranchPair { + pub(super) span: Span, + pub(super) true_bcb: BasicCoverageBlock, + pub(super) false_bcb: BasicCoverageBlock, +} + pub(super) struct CoverageSpans { bcb_has_mappings: BitSet, - mappings: Vec, + pub(super) mappings: Vec, + pub(super) branch_pairs: Vec, + test_vector_bitmap_bytes: u32, } impl CoverageSpans { @@ -33,8 +59,8 @@ impl CoverageSpans { self.bcb_has_mappings.contains(bcb) } - pub(super) fn all_bcb_mappings(&self) -> impl Iterator { - self.mappings.iter() + pub(super) fn test_vector_bitmap_bytes(&self) -> u32 { + self.test_vector_bitmap_bytes } } @@ -48,6 +74,7 @@ pub(super) fn generate_coverage_spans( basic_coverage_blocks: &CoverageGraph, ) -> Option { let mut mappings = vec![]; + let mut branch_pairs = vec![]; if hir_info.is_async_fn { // An async function desugars into a function that returns a future, @@ -69,14 +96,20 @@ pub(super) fn generate_coverage_spans( BcbMapping { kind: BcbMappingKind::Code(bcb), span } })); - mappings.extend(from_mir::extract_branch_mappings( + branch_pairs.extend(from_mir::extract_branch_pairs( + mir_body, + hir_info, + basic_coverage_blocks, + )); + + mappings.extend(from_mir::extract_mcdc_mappings( mir_body, hir_info.body_span, basic_coverage_blocks, )); } - if mappings.is_empty() { + if mappings.is_empty() && branch_pairs.is_empty() { return None; } @@ -85,17 +118,29 @@ pub(super) fn generate_coverage_spans( let mut insert = |bcb| { bcb_has_mappings.insert(bcb); }; - for &BcbMapping { kind, span: _ } in &mappings { - match kind { + let mut test_vector_bitmap_bytes = 0; + for BcbMapping { kind, span: _ } in &mappings { + match *kind { BcbMappingKind::Code(bcb) => insert(bcb), - BcbMappingKind::Branch { true_bcb, false_bcb } => { + BcbMappingKind::MCDCBranch { true_bcb, false_bcb, .. } => { insert(true_bcb); insert(false_bcb); } + BcbMappingKind::MCDCDecision { bitmap_idx, conditions_num, .. } => { + // `bcb_has_mappings` is used for inject coverage counters + // but they are not needed for decision BCBs. + // While the length of test vector bitmap should be calculated here. + test_vector_bitmap_bytes = test_vector_bitmap_bytes + .max(bitmap_idx + (1_u32 << conditions_num as u32).div_ceil(8)); + } } } + for &BcbBranchPair { true_bcb, false_bcb, .. } in &branch_pairs { + insert(true_bcb); + insert(false_bcb); + } - Some(CoverageSpans { bcb_has_mappings, mappings }) + Some(CoverageSpans { bcb_has_mappings, mappings, branch_pairs, test_vector_bitmap_bytes }) } #[derive(Debug)] diff --git a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs index adb0c9f1929d..64f21d74b1cd 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs @@ -1,7 +1,9 @@ use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::FxHashSet; use rustc_index::IndexVec; -use rustc_middle::mir::coverage::{BlockMarkerId, BranchSpan, CoverageKind}; +use rustc_middle::mir::coverage::{ + BlockMarkerId, BranchSpan, CoverageKind, MCDCBranchSpan, MCDCDecisionSpan, +}; use rustc_middle::mir::{ self, AggregateKind, BasicBlock, FakeReadCause, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, @@ -11,7 +13,7 @@ use rustc_span::{ExpnKind, MacroKind, Span, Symbol}; use crate::coverage::graph::{ BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph, START_BCB, }; -use crate::coverage::spans::{BcbMapping, BcbMappingKind}; +use crate::coverage::spans::{BcbBranchPair, BcbMapping, BcbMappingKind}; use crate::coverage::ExtractedHirInfo; /// Traverses the MIR body to produce an initial collection of coverage-relevant @@ -227,7 +229,10 @@ fn filtered_statement_span(statement: &Statement<'_>) -> Option { // These coverage statements should not exist prior to coverage instrumentation. StatementKind::Coverage( - CoverageKind::CounterIncrement { .. } | CoverageKind::ExpressionUsed { .. }, + CoverageKind::CounterIncrement { .. } + | CoverageKind::ExpressionUsed { .. } + | CoverageKind::CondBitmapUpdate { .. } + | CoverageKind::TestVectorBitmapUpdate { .. }, ) => bug!( "Unexpected coverage statement found during coverage instrumentation: {statement:?}" ), @@ -361,15 +366,10 @@ impl SpanFromMir { } } -pub(super) fn extract_branch_mappings( +fn resolve_block_markers( + branch_info: &mir::coverage::BranchInfo, mir_body: &mir::Body<'_>, - body_span: Span, - basic_coverage_blocks: &CoverageGraph, -) -> Vec { - let Some(branch_info) = mir_body.coverage_branch_info.as_deref() else { - return vec![]; - }; - +) -> IndexVec> { let mut block_markers = IndexVec::>::from_elem_n( None, branch_info.num_block_markers, @@ -384,6 +384,24 @@ pub(super) fn extract_branch_mappings( } } + block_markers +} + +// FIXME: There is currently a lot of redundancy between +// `extract_branch_pairs` and `extract_mcdc_mappings`. This is needed so +// that they can each be modified without interfering with the other, but in +// the long term we should try to bring them together again when branch coverage +// and MC/DC coverage support are more mature. + +pub(super) fn extract_branch_pairs( + mir_body: &mir::Body<'_>, + hir_info: &ExtractedHirInfo, + basic_coverage_blocks: &CoverageGraph, +) -> Vec { + let Some(branch_info) = mir_body.coverage_branch_info.as_deref() else { return vec![] }; + + let block_markers = resolve_block_markers(branch_info, mir_body); + branch_info .branch_spans .iter() @@ -393,7 +411,8 @@ pub(super) fn extract_branch_mappings( if !raw_span.ctxt().outer_expn_data().is_root() { return None; } - let (span, _) = unexpand_into_body_span_with_visible_macro(raw_span, body_span)?; + let (span, _) = + unexpand_into_body_span_with_visible_macro(raw_span, hir_info.body_span)?; let bcb_from_marker = |marker: BlockMarkerId| basic_coverage_blocks.bcb_from_bb(block_markers[marker]?); @@ -401,7 +420,75 @@ pub(super) fn extract_branch_mappings( let true_bcb = bcb_from_marker(true_marker)?; let false_bcb = bcb_from_marker(false_marker)?; - Some(BcbMapping { kind: BcbMappingKind::Branch { true_bcb, false_bcb }, span }) + Some(BcbBranchPair { span, true_bcb, false_bcb }) }) .collect::>() } + +pub(super) fn extract_mcdc_mappings( + mir_body: &mir::Body<'_>, + body_span: Span, + basic_coverage_blocks: &CoverageGraph, +) -> Vec { + let Some(branch_info) = mir_body.coverage_branch_info.as_deref() else { + return vec![]; + }; + + let block_markers = resolve_block_markers(branch_info, mir_body); + + let bcb_from_marker = + |marker: BlockMarkerId| basic_coverage_blocks.bcb_from_bb(block_markers[marker]?); + + let check_branch_bcb = + |raw_span: Span, true_marker: BlockMarkerId, false_marker: BlockMarkerId| { + // For now, ignore any branch span that was introduced by + // expansion. This makes things like assert macros less noisy. + if !raw_span.ctxt().outer_expn_data().is_root() { + return None; + } + let (span, _) = unexpand_into_body_span_with_visible_macro(raw_span, body_span)?; + + let true_bcb = bcb_from_marker(true_marker)?; + let false_bcb = bcb_from_marker(false_marker)?; + Some((span, true_bcb, false_bcb)) + }; + + let mcdc_branch_filter_map = + |&MCDCBranchSpan { span: raw_span, true_marker, false_marker, condition_info }| { + check_branch_bcb(raw_span, true_marker, false_marker).map( + |(span, true_bcb, false_bcb)| BcbMapping { + kind: BcbMappingKind::MCDCBranch { true_bcb, false_bcb, condition_info }, + span, + }, + ) + }; + + let mut next_bitmap_idx = 0; + + let decision_filter_map = |decision: &MCDCDecisionSpan| { + let (span, _) = unexpand_into_body_span_with_visible_macro(decision.span, body_span)?; + + let end_bcbs = decision + .end_markers + .iter() + .map(|&marker| bcb_from_marker(marker)) + .collect::>()?; + + let bitmap_idx = next_bitmap_idx; + next_bitmap_idx += (1_u32 << decision.conditions_num).div_ceil(8); + + Some(BcbMapping { + kind: BcbMappingKind::MCDCDecision { + end_bcbs, + bitmap_idx, + conditions_num: decision.conditions_num as u16, + }, + span, + }) + }; + + std::iter::empty() + .chain(branch_info.mcdc_branch_spans.iter().filter_map(mcdc_branch_filter_map)) + .chain(branch_info.mcdc_decision_spans.iter().filter_map(decision_filter_map)) + .collect::>() +} diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 8e8d78226c30..24832086b167 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -885,6 +885,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { AggregateKind::Adt(did, ..) => tcx.def_kind(did) != DefKind::Enum, // Coroutines are never ZST, as they at least contain the implicit states. AggregateKind::Coroutine(..) => false, + AggregateKind::RawPtr(..) => bug!("MIR for RawPtr aggregate must have 2 fields"), }; if is_zst { @@ -910,6 +911,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } // Do not track unions. AggregateKind::Adt(_, _, _, _, Some(_)) => return None, + // FIXME: Do the extra work to GVN `from_raw_parts` + AggregateKind::RawPtr(..) => return None, }; let fields: Option> = fields diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 625d8f53939f..7dcff458cedc 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -332,7 +332,8 @@ impl<'tcx> Inliner<'tcx> { | InstanceDef::DropGlue(..) | InstanceDef::CloneShim(..) | InstanceDef::ThreadLocalShim(..) - | InstanceDef::FnPtrAddrShim(..) => return Ok(()), + | InstanceDef::FnPtrAddrShim(..) + | InstanceDef::AsyncDropGlueCtorShim(..) => return Ok(()), } if self.tcx.is_constructor(callee_def_id) { @@ -1083,7 +1084,8 @@ fn try_instance_mir<'tcx>( tcx: TyCtxt<'tcx>, instance: InstanceDef<'tcx>, ) -> Result<&'tcx Body<'tcx>, &'static str> { - if let ty::InstanceDef::DropGlue(_, Some(ty)) = instance + if let ty::InstanceDef::DropGlue(_, Some(ty)) + | ty::InstanceDef::AsyncDropGlueCtorShim(_, Some(ty)) = instance && let ty::Adt(def, args) = ty.kind() { let fields = def.all_fields(); diff --git a/compiler/rustc_mir_transform/src/inline/cycle.rs b/compiler/rustc_mir_transform/src/inline/cycle.rs index 99c7b616f1b2..8c5f965108bd 100644 --- a/compiler/rustc_mir_transform/src/inline/cycle.rs +++ b/compiler/rustc_mir_transform/src/inline/cycle.rs @@ -94,8 +94,10 @@ pub(crate) fn mir_callgraph_reachable<'tcx>( | InstanceDef::CloneShim(..) => {} // This shim does not call any other functions, thus there can be no recursion. - InstanceDef::FnPtrAddrShim(..) => continue, - InstanceDef::DropGlue(..) => { + InstanceDef::FnPtrAddrShim(..) => { + continue; + } + InstanceDef::DropGlue(..) | InstanceDef::AsyncDropGlueCtorShim(..) => { // FIXME: A not fully instantiated drop shim can cause ICEs if one attempts to // have its MIR built. Likely oli-obk just screwed up the `ParamEnv`s, so this // needs some more analysis. diff --git a/compiler/rustc_mir_transform/src/instsimplify.rs b/compiler/rustc_mir_transform/src/instsimplify.rs index ff786d44d6a9..fd768cc96ae3 100644 --- a/compiler/rustc_mir_transform/src/instsimplify.rs +++ b/compiler/rustc_mir_transform/src/instsimplify.rs @@ -36,6 +36,7 @@ impl<'tcx> MirPass<'tcx> for InstSimplify { ctx.simplify_bool_cmp(&statement.source_info, rvalue); ctx.simplify_ref_deref(&statement.source_info, rvalue); ctx.simplify_len(&statement.source_info, rvalue); + ctx.simplify_ptr_aggregate(&statement.source_info, rvalue); ctx.simplify_cast(rvalue); } _ => {} @@ -58,8 +59,17 @@ struct InstSimplifyContext<'tcx, 'a> { impl<'tcx> InstSimplifyContext<'tcx, '_> { fn should_simplify(&self, source_info: &SourceInfo, rvalue: &Rvalue<'tcx>) -> bool { + self.should_simplify_custom(source_info, "Rvalue", rvalue) + } + + fn should_simplify_custom( + &self, + source_info: &SourceInfo, + label: &str, + value: impl std::fmt::Debug, + ) -> bool { self.tcx.consider_optimizing(|| { - format!("InstSimplify - Rvalue: {rvalue:?} SourceInfo: {source_info:?}") + format!("InstSimplify - {label}: {value:?} SourceInfo: {source_info:?}") }) } @@ -111,7 +121,7 @@ impl<'tcx> InstSimplifyContext<'tcx, '_> { if a.const_.ty().is_bool() { a.const_.try_to_bool() } else { None } } - /// Transform "&(*a)" ==> "a". + /// Transform `&(*a)` ==> `a`. fn simplify_ref_deref(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) { if let Rvalue::Ref(_, _, place) = rvalue { if let Some((base, ProjectionElem::Deref)) = place.as_ref().last_projection() { @@ -131,7 +141,7 @@ impl<'tcx> InstSimplifyContext<'tcx, '_> { } } - /// Transform "Len([_; N])" ==> "N". + /// Transform `Len([_; N])` ==> `N`. fn simplify_len(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) { if let Rvalue::Len(ref place) = *rvalue { let place_ty = place.ty(self.local_decls, self.tcx).ty; @@ -147,6 +157,30 @@ impl<'tcx> InstSimplifyContext<'tcx, '_> { } } + /// Transform `Aggregate(RawPtr, [p, ()])` ==> `Cast(PtrToPtr, p)`. + fn simplify_ptr_aggregate(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) { + if let Rvalue::Aggregate(box AggregateKind::RawPtr(pointee_ty, mutability), fields) = rvalue + { + let meta_ty = fields.raw[1].ty(self.local_decls, self.tcx); + if meta_ty.is_unit() { + // The mutable borrows we're holding prevent printing `rvalue` here + if !self.should_simplify_custom( + source_info, + "Aggregate::RawPtr", + (&pointee_ty, *mutability, &fields), + ) { + return; + } + + let mut fields = std::mem::take(fields); + let _meta = fields.pop().unwrap(); + let data = fields.pop().unwrap(); + let ptr_ty = Ty::new_ptr(self.tcx, *pointee_ty, *mutability); + *rvalue = Rvalue::Cast(CastKind::PtrToPtr, data, ptr_ty); + } + } + } + fn simplify_ub_check(&self, source_info: &SourceInfo, rvalue: &mut Rvalue<'tcx>) { if let Rvalue::NullaryOp(NullOp::UbChecks, _) = *rvalue { let const_ = Const::from_bool(self.tcx, self.tcx.sess.ub_checks()); diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index 2744026a7c9d..b8dbf8a759fc 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -603,6 +603,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { AggregateKind::Adt(_, variant, _, _, _) => variant, AggregateKind::Array(_) | AggregateKind::Tuple + | AggregateKind::RawPtr(_, _) | AggregateKind::Closure(_, _) | AggregateKind::Coroutine(_, _) | AggregateKind::CoroutineClosure(_, _) => VariantIdx::ZERO, diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs index 7e8920604c17..da63fcf23d9e 100644 --- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs +++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs @@ -287,6 +287,34 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics { terminator.kind = TerminatorKind::Unreachable; } } + sym::aggregate_raw_ptr => { + let Ok([data, meta]) = <[_; 2]>::try_from(std::mem::take(args)) else { + span_bug!( + terminator.source_info.span, + "Wrong number of arguments for aggregate_raw_ptr intrinsic", + ); + }; + let target = target.unwrap(); + let pointer_ty = generic_args.type_at(0); + let kind = if let ty::RawPtr(pointee_ty, mutability) = pointer_ty.kind() { + AggregateKind::RawPtr(*pointee_ty, *mutability) + } else { + span_bug!( + terminator.source_info.span, + "Return type of aggregate_raw_ptr intrinsic must be a raw pointer", + ); + }; + let fields = [data.node, meta.node]; + block.statements.push(Statement { + source_info: terminator.source_info, + kind: StatementKind::Assign(Box::new(( + *destination, + Rvalue::Aggregate(Box::new(kind), fields.into()), + ))), + }); + + terminator.kind = TerminatorKind::Goto { target }; + } _ => {} } } diff --git a/compiler/rustc_mir_transform/src/match_branches.rs b/compiler/rustc_mir_transform/src/match_branches.rs index a8a576e4efe4..1411d9be2236 100644 --- a/compiler/rustc_mir_transform/src/match_branches.rs +++ b/compiler/rustc_mir_transform/src/match_branches.rs @@ -41,7 +41,10 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification { should_cleanup = true; continue; } - if SimplifyToExp::default().simplify(tcx, body, bb_idx, param_env).is_some() { + // unsound: https://github.com/rust-lang/rust/issues/124150 + if tcx.sess.opts.unstable_opts.unsound_mir_opts + && SimplifyToExp::default().simplify(tcx, body, bb_idx, param_env).is_some() + { should_cleanup = true; continue; } diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index fa6906bdd554..1c85a604053c 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -22,6 +22,8 @@ use crate::{ use rustc_middle::mir::patch::MirPatch; use rustc_mir_dataflow::elaborate_drops::{self, DropElaborator, DropFlagMode, DropStyle}; +mod async_destructor_ctor; + pub fn provide(providers: &mut Providers) { providers.mir_shims = make_shim; } @@ -127,6 +129,9 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<' ty::InstanceDef::ThreadLocalShim(..) => build_thread_local_shim(tcx, instance), ty::InstanceDef::CloneShim(def_id, ty) => build_clone_shim(tcx, def_id, ty), ty::InstanceDef::FnPtrAddrShim(def_id, ty) => build_fn_ptr_addr_shim(tcx, def_id, ty), + ty::InstanceDef::AsyncDropGlueCtorShim(def_id, ty) => { + async_destructor_ctor::build_async_destructor_ctor_shim(tcx, def_id, ty) + } ty::InstanceDef::Virtual(..) => { bug!("InstanceDef::Virtual ({:?}) is for direct calls only", instance) } diff --git a/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs b/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs new file mode 100644 index 000000000000..80eadb9abdcf --- /dev/null +++ b/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs @@ -0,0 +1,618 @@ +use std::iter; + +use itertools::Itertools; +use rustc_ast::Mutability; +use rustc_const_eval::interpret; +use rustc_hir::def_id::DefId; +use rustc_hir::lang_items::LangItem; +use rustc_index::{Idx, IndexVec}; +use rustc_middle::mir::{ + BasicBlock, BasicBlockData, Body, CallSource, CastKind, Const, ConstOperand, ConstValue, Local, + LocalDecl, MirSource, Operand, Place, PlaceElem, Rvalue, SourceInfo, Statement, StatementKind, + Terminator, TerminatorKind, UnwindAction, UnwindTerminateReason, RETURN_PLACE, +}; +use rustc_middle::ty::adjustment::PointerCoercion; +use rustc_middle::ty::util::Discr; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_span::source_map::respan; +use rustc_span::{Span, Symbol}; +use rustc_target::abi::{FieldIdx, VariantIdx}; +use rustc_target::spec::PanicStrategy; + +use super::{local_decls_for_sig, new_body}; + +pub fn build_async_destructor_ctor_shim<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: DefId, + ty: Option>, +) -> Body<'tcx> { + debug!("build_drop_shim(def_id={:?}, ty={:?})", def_id, ty); + + AsyncDestructorCtorShimBuilder::new(tcx, def_id, ty).build() +} + +/// Builder for async_drop_in_place shim. Functions as a stack machine +/// to build up an expression using combinators. Stack contains pairs +/// of locals and types. Combinator is a not yet instantiated pair of a +/// function and a type, is considered to be an operator which consumes +/// operands from the stack by instantiating its function and its type +/// with operand types and moving locals into the function call. Top +/// pair is considered to be the last operand. +// FIXME: add mir-opt tests +struct AsyncDestructorCtorShimBuilder<'tcx> { + tcx: TyCtxt<'tcx>, + def_id: DefId, + self_ty: Option>, + span: Span, + source_info: SourceInfo, + param_env: ty::ParamEnv<'tcx>, + + stack: Vec>, + last_bb: BasicBlock, + top_cleanup_bb: Option, + + locals: IndexVec>, + bbs: IndexVec>, +} + +#[derive(Clone, Copy)] +enum SurfaceDropKind { + Async, + Sync, +} + +impl<'tcx> AsyncDestructorCtorShimBuilder<'tcx> { + const SELF_PTR: Local = Local::from_u32(1); + const INPUT_COUNT: usize = 1; + const MAX_STACK_LEN: usize = 2; + + fn new(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Option>) -> Self { + let args = if let Some(ty) = self_ty { + tcx.mk_args(&[ty.into()]) + } else { + ty::GenericArgs::identity_for_item(tcx, def_id) + }; + let sig = tcx.fn_sig(def_id).instantiate(tcx, args); + let sig = tcx.instantiate_bound_regions_with_erased(sig); + let span = tcx.def_span(def_id); + + let source_info = SourceInfo::outermost(span); + + debug_assert_eq!(sig.inputs().len(), Self::INPUT_COUNT); + let locals = local_decls_for_sig(&sig, span); + + // Usual case: noop() + unwind resume + return + let mut bbs = IndexVec::with_capacity(3); + let param_env = tcx.param_env_reveal_all_normalized(def_id); + AsyncDestructorCtorShimBuilder { + tcx, + def_id, + self_ty, + span, + source_info, + param_env, + + stack: Vec::with_capacity(Self::MAX_STACK_LEN), + last_bb: bbs.push(BasicBlockData::new(None)), + top_cleanup_bb: match tcx.sess.panic_strategy() { + PanicStrategy::Unwind => { + // Don't drop input arg because it's just a pointer + Some(bbs.push(BasicBlockData { + statements: Vec::new(), + terminator: Some(Terminator { + source_info, + kind: TerminatorKind::UnwindResume, + }), + is_cleanup: true, + })) + } + PanicStrategy::Abort => None, + }, + + locals, + bbs, + } + } + + fn build(self) -> Body<'tcx> { + let (tcx, def_id, Some(self_ty)) = (self.tcx, self.def_id, self.self_ty) else { + return self.build_zst_output(); + }; + + let surface_drop_kind = || { + let param_env = tcx.param_env_reveal_all_normalized(def_id); + if self_ty.has_surface_async_drop(tcx, param_env) { + Some(SurfaceDropKind::Async) + } else if self_ty.has_surface_drop(tcx, param_env) { + Some(SurfaceDropKind::Sync) + } else { + None + } + }; + + match self_ty.kind() { + ty::Array(elem_ty, _) => self.build_slice(true, *elem_ty), + ty::Slice(elem_ty) => self.build_slice(false, *elem_ty), + + ty::Tuple(elem_tys) => self.build_chain(None, elem_tys.iter()), + ty::Adt(adt_def, args) if adt_def.is_struct() => { + let field_tys = adt_def.non_enum_variant().fields.iter().map(|f| f.ty(tcx, args)); + self.build_chain(surface_drop_kind(), field_tys) + } + ty::Closure(_, args) => self.build_chain(None, args.as_closure().upvar_tys().iter()), + ty::CoroutineClosure(_, args) => { + self.build_chain(None, args.as_coroutine_closure().upvar_tys().iter()) + } + + ty::Adt(adt_def, args) if adt_def.is_enum() => { + self.build_enum(*adt_def, *args, surface_drop_kind()) + } + + ty::Adt(adt_def, _) => { + assert!(adt_def.is_union()); + match surface_drop_kind().unwrap() { + SurfaceDropKind::Async => self.build_fused_async_surface(), + SurfaceDropKind::Sync => self.build_fused_sync_surface(), + } + } + + ty::Bound(..) + | ty::Foreign(_) + | ty::Placeholder(_) + | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_) | ty::TyVar(_)) + | ty::Param(_) + | ty::Alias(..) => { + bug!("Building async destructor for unexpected type: {self_ty:?}") + } + + _ => { + bug!( + "Building async destructor constructor shim is not yet implemented for type: {self_ty:?}" + ) + } + } + } + + fn build_enum( + mut self, + adt_def: ty::AdtDef<'tcx>, + args: ty::GenericArgsRef<'tcx>, + surface_drop: Option, + ) -> Body<'tcx> { + let tcx = self.tcx; + + let surface = match surface_drop { + None => None, + Some(kind) => { + self.put_self(); + Some(match kind { + SurfaceDropKind::Async => self.combine_async_surface(), + SurfaceDropKind::Sync => self.combine_sync_surface(), + }) + } + }; + + let mut other = None; + for (variant_idx, discr) in adt_def.discriminants(tcx) { + let variant = adt_def.variant(variant_idx); + + let mut chain = None; + for (field_idx, field) in variant.fields.iter_enumerated() { + let field_ty = field.ty(tcx, args); + self.put_variant_field(variant.name, variant_idx, field_idx, field_ty); + let defer = self.combine_defer(field_ty); + chain = Some(match chain { + None => defer, + Some(chain) => self.combine_chain(chain, defer), + }) + } + let variant_dtor = chain.unwrap_or_else(|| self.put_noop()); + + other = Some(match other { + None => variant_dtor, + Some(other) => { + self.put_self(); + self.put_discr(discr); + self.combine_either(other, variant_dtor) + } + }); + } + let variants_dtor = other.unwrap_or_else(|| self.put_noop()); + + let dtor = match surface { + None => variants_dtor, + Some(surface) => self.combine_chain(surface, variants_dtor), + }; + self.combine_fuse(dtor); + self.return_() + } + + fn build_chain(mut self, surface_drop: Option, elem_tys: I) -> Body<'tcx> + where + I: Iterator> + ExactSizeIterator, + { + let surface = match surface_drop { + None => None, + Some(kind) => { + self.put_self(); + Some(match kind { + SurfaceDropKind::Async => self.combine_async_surface(), + SurfaceDropKind::Sync => self.combine_sync_surface(), + }) + } + }; + + let mut chain = None; + for (field_idx, field_ty) in elem_tys.enumerate().map(|(i, ty)| (FieldIdx::new(i), ty)) { + self.put_field(field_idx, field_ty); + let defer = self.combine_defer(field_ty); + chain = Some(match chain { + None => defer, + Some(chain) => self.combine_chain(chain, defer), + }) + } + let chain = chain.unwrap_or_else(|| self.put_noop()); + + let dtor = match surface { + None => chain, + Some(surface) => self.combine_chain(surface, chain), + }; + self.combine_fuse(dtor); + self.return_() + } + + fn build_zst_output(mut self) -> Body<'tcx> { + self.put_zst_output(); + self.return_() + } + + fn build_fused_async_surface(mut self) -> Body<'tcx> { + self.put_self(); + let surface = self.combine_async_surface(); + self.combine_fuse(surface); + self.return_() + } + + fn build_fused_sync_surface(mut self) -> Body<'tcx> { + self.put_self(); + let surface = self.combine_sync_surface(); + self.combine_fuse(surface); + self.return_() + } + + fn build_slice(mut self, is_array: bool, elem_ty: Ty<'tcx>) -> Body<'tcx> { + if is_array { + self.put_array_as_slice(elem_ty) + } else { + self.put_self() + } + let dtor = self.combine_slice(elem_ty); + self.combine_fuse(dtor); + self.return_() + } + + fn put_zst_output(&mut self) { + let return_ty = self.locals[RETURN_PLACE].ty; + self.put_operand(Operand::Constant(Box::new(ConstOperand { + span: self.span, + user_ty: None, + const_: Const::zero_sized(return_ty), + }))); + } + + /// Puts `to_drop: *mut Self` on top of the stack. + fn put_self(&mut self) { + self.put_operand(Operand::Copy(Self::SELF_PTR.into())) + } + + /// Given that `Self is [ElemTy; N]` puts `to_drop: *mut [ElemTy]` + /// on top of the stack. + fn put_array_as_slice(&mut self, elem_ty: Ty<'tcx>) { + let slice_ptr_ty = Ty::new_mut_ptr(self.tcx, Ty::new_slice(self.tcx, elem_ty)); + self.put_temp_rvalue(Rvalue::Cast( + CastKind::PointerCoercion(PointerCoercion::Unsize), + Operand::Copy(Self::SELF_PTR.into()), + slice_ptr_ty, + )) + } + + /// If given Self is a struct puts `to_drop: *mut FieldTy` on top + /// of the stack. + fn put_field(&mut self, field: FieldIdx, field_ty: Ty<'tcx>) { + let place = Place { + local: Self::SELF_PTR, + projection: self + .tcx + .mk_place_elems(&[PlaceElem::Deref, PlaceElem::Field(field, field_ty)]), + }; + self.put_temp_rvalue(Rvalue::AddressOf(Mutability::Mut, place)) + } + + /// If given Self is an enum puts `to_drop: *mut FieldTy` on top of + /// the stack. + fn put_variant_field( + &mut self, + variant_sym: Symbol, + variant: VariantIdx, + field: FieldIdx, + field_ty: Ty<'tcx>, + ) { + let place = Place { + local: Self::SELF_PTR, + projection: self.tcx.mk_place_elems(&[ + PlaceElem::Deref, + PlaceElem::Downcast(Some(variant_sym), variant), + PlaceElem::Field(field, field_ty), + ]), + }; + self.put_temp_rvalue(Rvalue::AddressOf(Mutability::Mut, place)) + } + + /// If given Self is an enum puts `to_drop: *mut FieldTy` on top of + /// the stack. + fn put_discr(&mut self, discr: Discr<'tcx>) { + let (size, _) = discr.ty.int_size_and_signed(self.tcx); + self.put_operand(Operand::const_from_scalar( + self.tcx, + discr.ty, + interpret::Scalar::from_uint(discr.val, size), + self.span, + )); + } + + /// Puts `x: RvalueType` on top of the stack. + fn put_temp_rvalue(&mut self, rvalue: Rvalue<'tcx>) { + let last_bb = &mut self.bbs[self.last_bb]; + debug_assert!(last_bb.terminator.is_none()); + let source_info = self.source_info; + + let local_ty = rvalue.ty(&self.locals, self.tcx); + // We need to create a new local to be able to "consume" it with + // a combinator + let local = self.locals.push(LocalDecl::with_source_info(local_ty, source_info)); + last_bb.statements.extend_from_slice(&[ + Statement { source_info, kind: StatementKind::StorageLive(local) }, + Statement { + source_info, + kind: StatementKind::Assign(Box::new((local.into(), rvalue))), + }, + ]); + + self.put_operand(Operand::Move(local.into())); + } + + /// Puts operand on top of the stack. + fn put_operand(&mut self, operand: Operand<'tcx>) { + if let Some(top_cleanup_bb) = &mut self.top_cleanup_bb { + let source_info = self.source_info; + match &operand { + Operand::Copy(_) | Operand::Constant(_) => { + *top_cleanup_bb = self.bbs.push(BasicBlockData { + statements: Vec::new(), + terminator: Some(Terminator { + source_info, + kind: TerminatorKind::Goto { target: *top_cleanup_bb }, + }), + is_cleanup: true, + }); + } + Operand::Move(place) => { + let local = place.as_local().unwrap(); + *top_cleanup_bb = self.bbs.push(BasicBlockData { + statements: Vec::new(), + terminator: Some(Terminator { + source_info, + kind: if self.locals[local].ty.needs_drop(self.tcx, self.param_env) { + TerminatorKind::Drop { + place: local.into(), + target: *top_cleanup_bb, + unwind: UnwindAction::Terminate( + UnwindTerminateReason::InCleanup, + ), + replace: false, + } + } else { + TerminatorKind::Goto { target: *top_cleanup_bb } + }, + }), + is_cleanup: true, + }); + } + }; + } + self.stack.push(operand); + } + + /// Puts `noop: async_drop::Noop` on top of the stack + fn put_noop(&mut self) -> Ty<'tcx> { + self.apply_combinator(0, LangItem::AsyncDropNoop, &[]) + } + + fn combine_async_surface(&mut self) -> Ty<'tcx> { + self.apply_combinator(1, LangItem::SurfaceAsyncDropInPlace, &[self.self_ty.unwrap().into()]) + } + + fn combine_sync_surface(&mut self) -> Ty<'tcx> { + self.apply_combinator( + 1, + LangItem::AsyncDropSurfaceDropInPlace, + &[self.self_ty.unwrap().into()], + ) + } + + fn combine_fuse(&mut self, inner_future_ty: Ty<'tcx>) -> Ty<'tcx> { + self.apply_combinator(1, LangItem::AsyncDropFuse, &[inner_future_ty.into()]) + } + + fn combine_slice(&mut self, elem_ty: Ty<'tcx>) -> Ty<'tcx> { + self.apply_combinator(1, LangItem::AsyncDropSlice, &[elem_ty.into()]) + } + + fn combine_defer(&mut self, to_drop_ty: Ty<'tcx>) -> Ty<'tcx> { + self.apply_combinator(1, LangItem::AsyncDropDefer, &[to_drop_ty.into()]) + } + + fn combine_chain(&mut self, first: Ty<'tcx>, second: Ty<'tcx>) -> Ty<'tcx> { + self.apply_combinator(2, LangItem::AsyncDropChain, &[first.into(), second.into()]) + } + + fn combine_either(&mut self, other: Ty<'tcx>, matched: Ty<'tcx>) -> Ty<'tcx> { + self.apply_combinator( + 4, + LangItem::AsyncDropEither, + &[other.into(), matched.into(), self.self_ty.unwrap().into()], + ) + } + + fn return_(mut self) -> Body<'tcx> { + let last_bb = &mut self.bbs[self.last_bb]; + debug_assert!(last_bb.terminator.is_none()); + let source_info = self.source_info; + + let (1, Some(output)) = (self.stack.len(), self.stack.pop()) else { + span_bug!( + self.span, + "async destructor ctor shim builder finished with invalid number of stack items: expected 1 found {}", + self.stack.len(), + ) + }; + #[cfg(debug_assertions)] + if let Some(ty) = self.self_ty { + debug_assert_eq!( + output.ty(&self.locals, self.tcx), + ty.async_destructor_ty(self.tcx, self.param_env), + "output async destructor types did not match for type: {ty:?}", + ); + } + + let dead_storage = match &output { + Operand::Move(place) => Some(Statement { + source_info, + kind: StatementKind::StorageDead(place.as_local().unwrap()), + }), + _ => None, + }; + + last_bb.statements.extend( + iter::once(Statement { + source_info, + kind: StatementKind::Assign(Box::new((RETURN_PLACE.into(), Rvalue::Use(output)))), + }) + .chain(dead_storage), + ); + + last_bb.terminator = Some(Terminator { source_info, kind: TerminatorKind::Return }); + + let source = MirSource::from_instance(ty::InstanceDef::AsyncDropGlueCtorShim( + self.def_id, + self.self_ty, + )); + new_body(source, self.bbs, self.locals, Self::INPUT_COUNT, self.span) + } + + fn apply_combinator( + &mut self, + arity: usize, + function: LangItem, + args: &[ty::GenericArg<'tcx>], + ) -> Ty<'tcx> { + let function = self.tcx.require_lang_item(function, Some(self.span)); + let operands_split = self + .stack + .len() + .checked_sub(arity) + .expect("async destructor ctor shim combinator tried to consume too many items"); + let operands = &self.stack[operands_split..]; + + let func_ty = Ty::new_fn_def(self.tcx, function, args.iter().copied()); + let func_sig = func_ty.fn_sig(self.tcx).no_bound_vars().unwrap(); + #[cfg(debug_assertions)] + operands.iter().zip(func_sig.inputs()).for_each(|(operand, expected_ty)| { + let operand_ty = operand.ty(&self.locals, self.tcx); + if operand_ty == *expected_ty { + return; + } + + // If projection of Discriminant then compare with `Ty::discriminant_ty` + if let ty::Alias(ty::AliasKind::Projection, ty::AliasTy { args, def_id, .. }) = + expected_ty.kind() + && Some(*def_id) == self.tcx.lang_items().discriminant_type() + && args.first().unwrap().as_type().unwrap().discriminant_ty(self.tcx) == operand_ty + { + return; + } + + span_bug!( + self.span, + "Operand type and combinator argument type are not equal. + operand_ty: {:?} + argument_ty: {:?} +", + operand_ty, + expected_ty + ); + }); + + let target = self.bbs.push(BasicBlockData { + statements: operands + .iter() + .rev() + .filter_map(|o| { + if let Operand::Move(Place { local, projection }) = o { + assert!(projection.is_empty()); + Some(Statement { + source_info: self.source_info, + kind: StatementKind::StorageDead(*local), + }) + } else { + None + } + }) + .collect(), + terminator: None, + is_cleanup: false, + }); + + let dest_ty = func_sig.output(); + let dest = + self.locals.push(LocalDecl::with_source_info(dest_ty, self.source_info).immutable()); + + let unwind = if let Some(top_cleanup_bb) = &mut self.top_cleanup_bb { + for _ in 0..arity { + *top_cleanup_bb = + self.bbs[*top_cleanup_bb].terminator().successors().exactly_one().ok().unwrap(); + } + UnwindAction::Cleanup(*top_cleanup_bb) + } else { + UnwindAction::Unreachable + }; + + let last_bb = &mut self.bbs[self.last_bb]; + debug_assert!(last_bb.terminator.is_none()); + last_bb.statements.push(Statement { + source_info: self.source_info, + kind: StatementKind::StorageLive(dest), + }); + last_bb.terminator = Some(Terminator { + source_info: self.source_info, + kind: TerminatorKind::Call { + func: Operand::Constant(Box::new(ConstOperand { + span: self.span, + user_ty: None, + const_: Const::Val(ConstValue::ZeroSized, func_ty), + })), + destination: dest.into(), + target: Some(target), + unwind, + call_source: CallSource::Misc, + fn_span: self.span, + args: self.stack.drain(operands_split..).map(|o| respan(self.span, o)).collect(), + }, + }); + + self.put_operand(Operand::Move(dest.into())); + self.last_bb = target; + + dest_ty + } +} diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 36d623fd93e1..a8fa6fe002db 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -966,13 +966,14 @@ fn visit_instance_use<'tcx>( ty::InstanceDef::ThreadLocalShim(..) => { bug!("{:?} being reified", instance); } - ty::InstanceDef::DropGlue(_, None) => { + ty::InstanceDef::DropGlue(_, None) | ty::InstanceDef::AsyncDropGlueCtorShim(_, None) => { // Don't need to emit noop drop glue if we are calling directly. if !is_direct_call { output.push(create_fn_mono_item(tcx, instance, source)); } } ty::InstanceDef::DropGlue(_, Some(_)) + | ty::InstanceDef::AsyncDropGlueCtorShim(_, Some(_)) | ty::InstanceDef::VTableShim(..) | ty::InstanceDef::ReifyShim(..) | ty::InstanceDef::ClosureOnceShim { .. } diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index 5a92657cb40d..23e07890bb6c 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -625,7 +625,8 @@ fn characteristic_def_id_of_mono_item<'tcx>( | ty::InstanceDef::Virtual(..) | ty::InstanceDef::CloneShim(..) | ty::InstanceDef::ThreadLocalShim(..) - | ty::InstanceDef::FnPtrAddrShim(..) => return None, + | ty::InstanceDef::FnPtrAddrShim(..) + | ty::InstanceDef::AsyncDropGlueCtorShim(..) => return None, }; // If this is a method, we want to put it into the same module as @@ -769,7 +770,9 @@ fn mono_item_visibility<'tcx>( }; let def_id = match instance.def { - InstanceDef::Item(def_id) | InstanceDef::DropGlue(def_id, Some(_)) => def_id, + InstanceDef::Item(def_id) + | InstanceDef::DropGlue(def_id, Some(_)) + | InstanceDef::AsyncDropGlueCtorShim(def_id, Some(_)) => def_id, // We match the visibility of statics here InstanceDef::ThreadLocalShim(def_id) => { @@ -786,6 +789,7 @@ fn mono_item_visibility<'tcx>( | InstanceDef::ConstructCoroutineInClosureShim { .. } | InstanceDef::CoroutineKindShim { .. } | InstanceDef::DropGlue(..) + | InstanceDef::AsyncDropGlueCtorShim(..) | InstanceDef::CloneShim(..) | InstanceDef::FnPtrAddrShim(..) => return Visibility::Hidden, }; diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs index e03052bcfede..bce29e2af09b 100644 --- a/compiler/rustc_passes/src/lib.rs +++ b/compiler/rustc_passes/src/lib.rs @@ -8,7 +8,6 @@ #![doc(rust_logo)] #![feature(rustdoc_internals)] #![allow(internal_features)] -#![feature(generic_nonzero)] #![feature(let_chains)] #![feature(map_try_insert)] #![feature(try_blocks)] diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index 3373835d813e..914481d712e8 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -3,7 +3,6 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(rustdoc_internals)] -#![feature(generic_nonzero)] #![feature(min_specialization)] #![feature(rustc_attrs)] #![allow(rustc::potential_query_instability, unused_parens)] diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index 534937003ebf..2b3fa7f6cfad 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -459,7 +459,8 @@ impl DepGraph { } TaskDepsRef::Ignore => return, TaskDepsRef::Forbid => { - panic!("Illegal read of: {dep_node_index:?}") + // Reading is forbidden in this context. ICE with a useful error message. + panic_on_forbidden_read(data, dep_node_index) } }; let task_deps = &mut *task_deps; @@ -1366,3 +1367,45 @@ pub(crate) fn print_markframe_trace(graph: &DepGraph, frame: Option< eprintln!("end of try_mark_green dep node stack"); } + +#[cold] +#[inline(never)] +fn panic_on_forbidden_read(data: &DepGraphData, dep_node_index: DepNodeIndex) -> ! { + // We have to do an expensive reverse-lookup of the DepNode that + // corresponds to `dep_node_index`, but that's OK since we are about + // to ICE anyway. + let mut dep_node = None; + + // First try to find the dep node among those that already existed in the + // previous session + for (prev_index, index) in data.current.prev_index_to_index.lock().iter_enumerated() { + if index == &Some(dep_node_index) { + dep_node = Some(data.previous.index_to_node(prev_index)); + break; + } + } + + if dep_node.is_none() { + // Try to find it among the new nodes + for shard in data.current.new_node_to_index.lock_shards() { + if let Some((node, _)) = shard.iter().find(|(_, index)| **index == dep_node_index) { + dep_node = Some(*node); + break; + } + } + } + + let dep_node = dep_node.map_or_else( + || format!("with index {:?}", dep_node_index), + |dep_node| format!("`{:?}`", dep_node), + ); + + panic!( + "Error: trying to record dependency on DepNode {dep_node} in a \ + context that does not allow it (e.g. during query deserialization). \ + The most common case of recording a dependency on a DepNode `foo` is \ + when the correspondng query `foo` is invoked. Invoking queries is not \ + allowed as part of loading something from the incremental on-disk cache. \ + See ." + ) +} diff --git a/compiler/rustc_query_system/src/lib.rs b/compiler/rustc_query_system/src/lib.rs index 6a959a99e5d4..416f556f57d2 100644 --- a/compiler/rustc_query_system/src/lib.rs +++ b/compiler/rustc_query_system/src/lib.rs @@ -1,6 +1,5 @@ #![feature(assert_matches)] #![feature(core_intrinsics)] -#![feature(generic_nonzero)] #![feature(hash_raw_entry)] #![feature(min_specialization)] #![feature(let_chains)] diff --git a/compiler/rustc_serialize/src/lib.rs b/compiler/rustc_serialize/src/lib.rs index 5a9403e0a859..3e1d8f3828bb 100644 --- a/compiler/rustc_serialize/src/lib.rs +++ b/compiler/rustc_serialize/src/lib.rs @@ -11,7 +11,6 @@ #![cfg_attr(bootstrap, feature(associated_type_bounds))] #![feature(const_option)] #![feature(core_intrinsics)] -#![feature(generic_nonzero)] #![feature(inline_const)] #![feature(min_specialization)] #![feature(never_type)] diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 8ad64897e836..d5b22f841d27 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -148,6 +148,8 @@ pub enum InstrumentCoverage { pub struct CoverageOptions { /// Add branch coverage instrumentation. pub branch: bool, + /// Add mcdc coverage instrumentation. + pub mcdc: bool, } /// Settings for `-Z instrument-xray` flag. diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs index c63af90a7f31..58e1394c0907 100644 --- a/compiler/rustc_session/src/lib.rs +++ b/compiler/rustc_session/src/lib.rs @@ -1,4 +1,3 @@ -#![feature(generic_nonzero)] #![feature(let_chains)] #![feature(lazy_cell)] #![feature(option_get_or_insert_default)] diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 8567591cbd9e..d51080589481 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -398,7 +398,7 @@ mod desc { pub const parse_optimization_fuel: &str = "crate=integer"; pub const parse_dump_mono_stats: &str = "`markdown` (default) or `json`"; pub const parse_instrument_coverage: &str = parse_bool; - pub const parse_coverage_options: &str = "`branch` or `no-branch`"; + pub const parse_coverage_options: &str = "either `no-branch`, `branch` or `mcdc`"; pub const parse_instrument_xray: &str = "either a boolean (`yes`, `no`, `on`, `off`, etc), or a comma separated list of settings: `always` or `never` (mutually exclusive), `ignore-loops`, `instruction-threshold=N`, `skip-entry`, `skip-exit`"; pub const parse_unpretty: &str = "`string` or `string=string`"; pub const parse_treat_err_as_bug: &str = "either no value or a non-negative number"; @@ -949,17 +949,19 @@ mod parse { let Some(v) = v else { return true }; for option in v.split(',') { - let (option, enabled) = match option.strip_prefix("no-") { - Some(without_no) => (without_no, false), - None => (option, true), - }; - let slot = match option { - "branch" => &mut slot.branch, + match option { + "no-branch" => { + slot.branch = false; + slot.mcdc = false; + } + "branch" => slot.branch = true, + "mcdc" => { + slot.branch = true; + slot.mcdc = true; + } _ => return false, - }; - *slot = enabled; + } } - true } diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index b63c119eee0e..2bc14b43234d 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -352,6 +352,10 @@ impl Session { self.instrument_coverage() && self.opts.unstable_opts.coverage_options.branch } + pub fn instrument_coverage_mcdc(&self) -> bool { + self.instrument_coverage() && self.opts.unstable_opts.coverage_options.mcdc + } + pub fn is_sanitizer_cfi_enabled(&self) -> bool { self.opts.unstable_opts.sanitizer.contains(SanitizerSet::CFI) } diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs index 61bbedf9eec9..fd31c020f899 100644 --- a/compiler/rustc_smir/src/rustc_smir/context.rs +++ b/compiler/rustc_smir/src/rustc_smir/context.rs @@ -500,6 +500,12 @@ impl<'tcx> Context for TablesWrapper<'tcx> { matches!(instance.def, ty::InstanceDef::DropGlue(_, None)) } + fn is_empty_async_drop_ctor_shim(&self, def: InstanceDef) -> bool { + let tables = self.0.borrow_mut(); + let instance = tables.instances[def]; + matches!(instance.def, ty::InstanceDef::AsyncDropGlueCtorShim(_, None)) + } + fn mono_instance(&self, def_id: stable_mir::DefId) -> stable_mir::mir::mono::Instance { let mut tables = self.0.borrow_mut(); let def_id = tables[def_id]; diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs index c9f666125902..7c0216211034 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs @@ -543,6 +543,9 @@ impl<'tcx> Stable<'tcx> for mir::AggregateKind<'tcx> { mir::AggregateKind::CoroutineClosure(..) => { todo!("FIXME(async_closures): Lower these to SMIR") } + mir::AggregateKind::RawPtr(ty, mutability) => { + stable_mir::mir::AggregateKind::RawPtr(ty.stable(tables), mutability.stable(tables)) + } } } } diff --git a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs index 112e44f674ef..4abf991fba25 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs @@ -807,7 +807,10 @@ impl<'tcx> Stable<'tcx> for ty::Instance<'tcx> { | ty::InstanceDef::ThreadLocalShim(..) | ty::InstanceDef::DropGlue(..) | ty::InstanceDef::CloneShim(..) - | ty::InstanceDef::FnPtrShim(..) => stable_mir::mir::mono::InstanceKind::Shim, + | ty::InstanceDef::FnPtrShim(..) + | ty::InstanceDef::AsyncDropGlueCtorShim(..) => { + stable_mir::mir::mono::InstanceKind::Shim + } }; stable_mir::mir::mono::Instance { def, kind } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 46bae1c1e989..2d3053ccee6b 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -361,6 +361,7 @@ symbols! { adt_const_params, advanced_slice_patterns, adx_target_feature, + aggregate_raw_ptr, alias, align, align_offset, @@ -424,6 +425,16 @@ symbols! { async_call_mut, async_call_once, async_closure, + async_destruct, + async_drop, + async_drop_chain, + async_drop_defer, + async_drop_either, + async_drop_fuse, + async_drop_in_place, + async_drop_noop, + async_drop_slice, + async_drop_surface_drop_in_place, async_fn, async_fn_in_trait, async_fn_kind_helper, @@ -825,6 +836,7 @@ symbols! { fadd_fast, fake_variadic, fallback, + fallback_surface_drop, fdiv_algebraic, fdiv_fast, feature, @@ -1788,6 +1800,7 @@ symbols! { sub_assign, sub_with_overflow, suggestion, + surface_async_drop_in_place, sym, sync, synthetic, diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs index f68668a91e68..f1c3512315ff 100644 --- a/compiler/rustc_symbol_mangling/src/legacy.rs +++ b/compiler/rustc_symbol_mangling/src/legacy.rs @@ -55,7 +55,9 @@ pub(super) fn mangle<'tcx>( printer .print_def_path( def_id, - if let ty::InstanceDef::DropGlue(_, _) = instance.def { + if let ty::InstanceDef::DropGlue(_, _) | ty::InstanceDef::AsyncDropGlueCtorShim(_, _) = + instance.def + { // Add the name of the dropped type to the symbol name &*instance.args } else { diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 6b171af47de0..1b507bb2a155 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -312,14 +312,15 @@ const RISCV_ALLOWED_FEATURES: &[(&str, Stability)] = &[ const WASM_ALLOWED_FEATURES: &[(&str, Stability)] = &[ // tidy-alphabetical-start ("atomics", Unstable(sym::wasm_target_feature)), - ("bulk-memory", Unstable(sym::wasm_target_feature)), + ("bulk-memory", Stable), ("exception-handling", Unstable(sym::wasm_target_feature)), + ("extended-const", Stable), ("multivalue", Unstable(sym::wasm_target_feature)), - ("mutable-globals", Unstable(sym::wasm_target_feature)), - ("nontrapping-fptoint", Unstable(sym::wasm_target_feature)), + ("mutable-globals", Stable), + ("nontrapping-fptoint", Stable), ("reference-types", Unstable(sym::wasm_target_feature)), ("relaxed-simd", Unstable(sym::wasm_target_feature)), - ("sign-ext", Unstable(sym::wasm_target_feature)), + ("sign-ext", Stable), ("simd128", Stable), // tidy-alphabetical-end ]; diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs index 8b5c029428cf..68b0db211413 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -240,6 +240,11 @@ pub(super) trait GoalKind<'tcx>: goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx>; + fn consider_builtin_async_destruct_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx>; + fn consider_builtin_destruct_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, @@ -520,6 +525,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { G::consider_builtin_coroutine_candidate(self, goal) } else if lang_items.discriminant_kind_trait() == Some(trait_def_id) { G::consider_builtin_discriminant_kind_candidate(self, goal) + } else if lang_items.async_destruct_trait() == Some(trait_def_id) { + G::consider_builtin_async_destruct_candidate(self, goal) } else if lang_items.destruct_trait() == Some(trait_def_id) { G::consider_builtin_destruct_candidate(self, goal) } else if lang_items.transmute_trait() == Some(trait_def_id) { diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs index ebf2a0d96213..c662ab23c53b 100644 --- a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs @@ -814,6 +814,59 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { }) } + fn consider_builtin_async_destruct_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + let self_ty = goal.predicate.self_ty(); + let async_destructor_ty = match *self_ty.kind() { + ty::Bool + | ty::Char + | ty::Int(..) + | ty::Uint(..) + | ty::Float(..) + | ty::Array(..) + | ty::RawPtr(..) + | ty::Ref(..) + | ty::FnDef(..) + | ty::FnPtr(..) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Infer(ty::IntVar(..) | ty::FloatVar(..)) + | ty::Never + | ty::Adt(_, _) + | ty::Str + | ty::Slice(_) + | ty::Tuple(_) + | ty::Error(_) => self_ty.async_destructor_ty(ecx.tcx(), goal.param_env), + + // We do not call `Ty::async_destructor_ty` on alias, param, or placeholder + // types, which return `::AsyncDestructor` + // (or ICE in the case of placeholders). Projecting a type to itself + // is never really productive. + ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => { + return Err(NoSolution); + } + + ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) + | ty::Foreign(..) + | ty::Bound(..) => bug!( + "unexpected self ty `{:?}` when normalizing `::AsyncDestructor`", + goal.predicate.self_ty() + ), + + ty::Pat(..) | ty::Dynamic(..) | ty::Coroutine(..) | ty::CoroutineWitness(..) => bug!( + "`consider_builtin_async_destruct_candidate` is not yet implemented for type: {self_ty:?}" + ), + }; + + ecx.probe_misc_candidate("builtin async destruct").enter(|ecx| { + ecx.eq(goal.param_env, goal.predicate.term, async_destructor_ty.into()) + .expect("expected goal term to be fully unconstrained"); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + } + fn consider_builtin_destruct_candidate( _ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index e522339358ad..ed76ea74f08f 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -544,6 +544,18 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } + fn consider_builtin_async_destruct_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + if goal.predicate.polarity != ty::PredicatePolarity::Positive { + return Err(NoSolution); + } + + // `AsyncDestruct` is automatically implemented for every type. + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + fn consider_builtin_destruct_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 73e94da165fb..f9e7ed9dcbbd 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -152,7 +152,6 @@ impl<'tcx> AutoTraitFinder<'tcx> { with {:?}", trait_ref, full_env ); - infcx.clear_caches(); // At this point, we already have all of the bounds we need. FulfillmentContext is used // to store all of the necessary region/lifetime bounds in the InferContext, as well as @@ -176,9 +175,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { AutoTraitResult::PositiveImpl(auto_trait_callback(info)) } -} -impl<'tcx> AutoTraitFinder<'tcx> { /// The core logic responsible for computing the bounds for our synthesized impl. /// /// To calculate the bounds, we call `SelectionContext.select` in a loop. Like @@ -255,8 +252,6 @@ impl<'tcx> AutoTraitFinder<'tcx> { let dummy_cause = ObligationCause::dummy(); while let Some(pred) = predicates.pop_front() { - infcx.clear_caches(); - if !already_visited.insert(pred) { continue; } 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 db3794c1c40b..9d3caaa01abd 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -124,7 +124,7 @@ pub fn suggest_restriction<'tcx, G: EmissionGuarantee>( msg: &str, err: &mut Diag<'_, G>, fn_sig: Option<&hir::FnSig<'_>>, - projection: Option<&ty::AliasTy<'_>>, + projection: Option>, trait_pred: ty::PolyTraitPredicate<'tcx>, // When we are dealing with a trait, `super_traits` will be `Some`: // Given `trait T: A + B + C {}` @@ -142,7 +142,7 @@ pub fn suggest_restriction<'tcx, G: EmissionGuarantee>( let generics = tcx.generics_of(item_id); // Given `fn foo(t: impl Trait)` where `Trait` requires assoc type `A`... if let Some((param, bound_str, fn_sig)) = - fn_sig.zip(projection).and_then(|(sig, p)| match p.self_ty().kind() { + fn_sig.zip(projection).and_then(|(sig, p)| match *p.self_ty().kind() { // Shenanigans to get the `Trait` from the `impl Trait`. ty::Param(param) => { let param_def = generics.type_param(param, tcx); @@ -252,7 +252,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let trait_pred = self.resolve_numeric_literals_with_default(trait_pred); let self_ty = trait_pred.skip_binder().self_ty(); - let (param_ty, projection) = match self_ty.kind() { + let (param_ty, projection) = match *self_ty.kind() { ty::Param(_) => (true, None), ty::Alias(ty::Projection, projection) => (false, Some(projection)), _ => (false, None), diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index fbff78304acf..8cd9f39d5d83 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -758,9 +758,9 @@ impl<'a, 'tcx> FulfillProcessor<'a, 'tcx> { // no type variables present, can use evaluation for better caching. // FIXME: consider caching errors too. if self.selcx.infcx.predicate_must_hold_considering_regions(obligation) { - if let Some(key) = ProjectionCacheKey::from_poly_projection_predicate( + if let Some(key) = ProjectionCacheKey::from_poly_projection_obligation( &mut self.selcx, - project_obligation.predicate, + &project_obligation, ) { // If `predicate_must_hold_considering_regions` succeeds, then we've // evaluated all sub-obligations. We can therefore mark the 'root' diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 8f5a30c436d3..88ebf8754d3c 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -482,7 +482,7 @@ fn is_impossible_associated_item( type Result = ControlFlow<()>; fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result { // If this is a parameter from the trait item's own generics, then bail - if let ty::Param(param) = t.kind() + if let ty::Param(param) = *t.kind() && let param_def_id = self.generics.type_param(param, self.tcx).def_id && self.tcx.parent(param_def_id) == self.trait_item_def_id { @@ -492,7 +492,7 @@ fn is_impossible_associated_item( } fn visit_region(&mut self, r: ty::Region<'tcx>) -> Self::Result { if let ty::ReEarlyParam(param) = r.kind() - && let param_def_id = self.generics.region_param(¶m, self.tcx).def_id + && let param_def_id = self.generics.region_param(param, self.tcx).def_id && self.tcx.parent(param_def_id) == self.trait_item_def_id { return ControlFlow::Break(()); @@ -501,7 +501,7 @@ fn is_impossible_associated_item( } fn visit_const(&mut self, ct: ty::Const<'tcx>) -> Self::Result { if let ty::ConstKind::Param(param) = ct.kind() - && let param_def_id = self.generics.const_param(¶m, self.tcx).def_id + && let param_def_id = self.generics.const_param(param, self.tcx).def_id && self.tcx.parent(param_def_id) == self.trait_item_def_id { return ControlFlow::Break(()); diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 67865bfcaa3d..116e17c7e432 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -344,7 +344,7 @@ pub(super) fn opt_normalize_projection_type<'a, 'b, 'tcx>( let use_cache = !selcx.is_intercrate(); let projection_ty = infcx.resolve_vars_if_possible(projection_ty); - let cache_key = ProjectionCacheKey::new(projection_ty); + let cache_key = ProjectionCacheKey::new(projection_ty, param_env); // FIXME(#20304) For now, I am caching here, which is good, but it // means we don't capture the type variables that are created in @@ -1074,6 +1074,42 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( | ty::Infer(..) | ty::Error(_) => false, } + } else if lang_items.async_destruct_trait() == Some(trait_ref.def_id) { + match self_ty.kind() { + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Adt(..) + | ty::Str + | ty::Array(..) + | ty::Slice(_) + | ty::RawPtr(..) + | ty::Ref(..) + | ty::FnDef(..) + | ty::FnPtr(..) + | ty::Dynamic(..) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Coroutine(..) + | ty::CoroutineWitness(..) + | ty::Pat(..) + | ty::Never + | ty::Tuple(..) + | ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(..)) => true, + + // type parameters, opaques, and unnormalized projections don't have + // a known async destructor and may need to be normalized further or rely + // on param env for async destructor projections + ty::Param(_) + | ty::Foreign(_) + | ty::Alias(..) + | ty::Bound(..) + | ty::Placeholder(..) + | ty::Infer(_) + | ty::Error(_) => false, + } } else if lang_items.pointee_trait() == Some(trait_ref.def_id) { let tail = selcx.tcx().struct_tail_with_normalize( self_ty, @@ -1488,15 +1524,20 @@ fn confirm_builtin_candidate<'cx, 'tcx>( ) -> Progress<'tcx> { let tcx = selcx.tcx(); let self_ty = obligation.predicate.self_ty(); - let args = tcx.mk_args(&[self_ty.into()]); let lang_items = tcx.lang_items(); let item_def_id = obligation.predicate.def_id; let trait_def_id = tcx.trait_of_item(item_def_id).unwrap(); + let args = tcx.mk_args(&[self_ty.into()]); let (term, obligations) = if lang_items.discriminant_kind_trait() == Some(trait_def_id) { let discriminant_def_id = tcx.require_lang_item(LangItem::Discriminant, None); assert_eq!(discriminant_def_id, item_def_id); (self_ty.discriminant_ty(tcx).into(), Vec::new()) + } else if lang_items.async_destruct_trait() == Some(trait_def_id) { + let destructor_def_id = tcx.associated_item_def_ids(trait_def_id)[0]; + assert_eq!(destructor_def_id, item_def_id); + + (self_ty.async_destructor_ty(tcx, obligation.param_env).into(), Vec::new()) } else if lang_items.pointee_trait() == Some(trait_def_id) { let metadata_def_id = tcx.require_lang_item(LangItem::Metadata, None); assert_eq!(metadata_def_id, item_def_id); @@ -2105,27 +2146,28 @@ fn assoc_ty_own_obligations<'cx, 'tcx>( } pub(crate) trait ProjectionCacheKeyExt<'cx, 'tcx>: Sized { - fn from_poly_projection_predicate( + fn from_poly_projection_obligation( selcx: &mut SelectionContext<'cx, 'tcx>, - predicate: ty::PolyProjectionPredicate<'tcx>, + obligation: &PolyProjectionObligation<'tcx>, ) -> Option; } impl<'cx, 'tcx> ProjectionCacheKeyExt<'cx, 'tcx> for ProjectionCacheKey<'tcx> { - fn from_poly_projection_predicate( + fn from_poly_projection_obligation( selcx: &mut SelectionContext<'cx, 'tcx>, - predicate: ty::PolyProjectionPredicate<'tcx>, + obligation: &PolyProjectionObligation<'tcx>, ) -> Option { let infcx = selcx.infcx; // We don't do cross-snapshot caching of obligations with escaping regions, // so there's no cache key to use - predicate.no_bound_vars().map(|predicate| { + obligation.predicate.no_bound_vars().map(|predicate| { ProjectionCacheKey::new( // We don't attempt to match up with a specific type-variable state // from a specific call to `opt_normalize_projection_type` - if // there's no precise match, the original cache entry is "stranded" // anyway. infcx.resolve_vars_if_possible(predicate.projection_ty), + obligation.param_env, ) }) } diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index c415d288b8f7..40d206b92b8d 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -81,6 +81,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } else if lang_items.discriminant_kind_trait() == Some(def_id) { // `DiscriminantKind` is automatically implemented for every type. candidates.vec.push(BuiltinCandidate { has_nested: false }); + } else if lang_items.async_destruct_trait() == Some(def_id) { + // `AsyncDestruct` is automatically implemented for every type. + candidates.vec.push(BuiltinCandidate { has_nested: false }); } else if lang_items.pointee_trait() == Some(def_id) { // `Pointee` is automatically implemented for every type. candidates.vec.push(BuiltinCandidate { has_nested: false }); diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 10370c7898b0..fc12fed35373 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -815,7 +815,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // `EvaluatedToOkModuloRegions`), and skip re-evaluating the // sub-obligations. if let Some(key) = - ProjectionCacheKey::from_poly_projection_predicate(self, data) + ProjectionCacheKey::from_poly_projection_obligation( + self, + &project_obligation, + ) { if let Some(cached_res) = self .infcx @@ -844,8 +847,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { && (eval_rslt == EvaluatedToOk || eval_rslt == EvaluatedToOkModuloRegions) && let Some(key) = - ProjectionCacheKey::from_poly_projection_predicate( - self, data, + ProjectionCacheKey::from_poly_projection_obligation( + self, + &project_obligation, ) { // If the result is something that we can cache, then mark this diff --git a/compiler/rustc_ty_utils/src/common_traits.rs b/compiler/rustc_ty_utils/src/common_traits.rs index 51b908881eb4..cb95239e9919 100644 --- a/compiler/rustc_ty_utils/src/common_traits.rs +++ b/compiler/rustc_ty_utils/src/common_traits.rs @@ -22,6 +22,17 @@ fn is_unpin_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) is_item_raw(tcx, query, LangItem::Unpin) } +fn has_surface_async_drop_raw<'tcx>( + tcx: TyCtxt<'tcx>, + query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, +) -> bool { + is_item_raw(tcx, query, LangItem::AsyncDrop) +} + +fn has_surface_drop_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { + is_item_raw(tcx, query, LangItem::Drop) +} + fn is_item_raw<'tcx>( tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>, @@ -34,5 +45,13 @@ fn is_item_raw<'tcx>( } pub(crate) fn provide(providers: &mut Providers) { - *providers = Providers { is_copy_raw, is_sized_raw, is_freeze_raw, is_unpin_raw, ..*providers }; + *providers = Providers { + is_copy_raw, + is_sized_raw, + is_freeze_raw, + is_unpin_raw, + has_surface_async_drop_raw, + has_surface_drop_raw, + ..*providers + }; } diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index a8f9afb87dd7..c1661fa63a8b 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -54,6 +54,28 @@ fn resolve_instance<'tcx>( debug!(" => trivial drop glue"); ty::InstanceDef::DropGlue(def_id, None) } + } else if Some(def_id) == tcx.lang_items().async_drop_in_place_fn() { + let ty = args.type_at(0); + + if !ty.is_async_destructor_noop(tcx, param_env) { + match *ty.kind() { + ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Coroutine(..) + | ty::Tuple(..) + | ty::Adt(..) + | ty::Dynamic(..) + | ty::Array(..) + | ty::Slice(..) => {} + // Async destructor ctor shims can only be built from ADTs. + _ => return Ok(None), + } + debug!(" => nontrivial async drop glue ctor"); + ty::InstanceDef::AsyncDropGlueCtorShim(def_id, Some(ty)) + } else { + debug!(" => trivial async drop glue ctor"); + ty::InstanceDef::AsyncDropGlueCtorShim(def_id, None) + } } else { debug!(" => free item"); // FIXME(effects): we may want to erase the effect param if that is present on this item. diff --git a/compiler/rustc_ty_utils/src/opaque_types.rs b/compiler/rustc_ty_utils/src/opaque_types.rs index a652bb781161..d7d31a88c9b5 100644 --- a/compiler/rustc_ty_utils/src/opaque_types.rs +++ b/compiler/rustc_ty_utils/src/opaque_types.rs @@ -130,7 +130,7 @@ impl<'tcx> OpaqueTypeCollector<'tcx> { TaitInBodyFinder { collector: self }.visit_expr(body); } - fn visit_opaque_ty(&mut self, alias_ty: &ty::AliasTy<'tcx>) { + fn visit_opaque_ty(&mut self, alias_ty: ty::AliasTy<'tcx>) { if !self.seen.insert(alias_ty.def_id.expect_local()) { return; } @@ -205,7 +205,7 @@ impl<'tcx> TypeVisitor> for OpaqueTypeCollector<'tcx> { #[instrument(skip(self), ret, level = "trace")] fn visit_ty(&mut self, t: Ty<'tcx>) { t.super_visit_with(self); - match t.kind() { + match *t.kind() { ty::Alias(ty::Opaque, alias_ty) if alias_ty.def_id.is_local() => { self.visit_opaque_ty(alias_ty); } @@ -279,7 +279,7 @@ impl<'tcx> TypeVisitor> for OpaqueTypeCollector<'tcx> { // assumption to the `param_env` of the default method. We also separately // rely on that assumption here. let ty = self.tcx.type_of(alias_ty.def_id).instantiate(self.tcx, alias_ty.args); - let ty::Alias(ty::Opaque, alias_ty) = ty.kind() else { bug!("{ty:?}") }; + let ty::Alias(ty::Opaque, alias_ty) = *ty.kind() else { bug!("{ty:?}") }; self.visit_opaque_ty(alias_ty); } } diff --git a/compiler/stable_mir/src/compiler_interface.rs b/compiler/stable_mir/src/compiler_interface.rs index 94c552199bc5..e82d1f4813ea 100644 --- a/compiler/stable_mir/src/compiler_interface.rs +++ b/compiler/stable_mir/src/compiler_interface.rs @@ -158,6 +158,9 @@ pub trait Context { /// Check if this is an empty DropGlue shim. fn is_empty_drop_shim(&self, def: InstanceDef) -> bool; + /// Check if this is an empty AsyncDropGlueCtor shim. + fn is_empty_async_drop_ctor_shim(&self, def: InstanceDef) -> bool; + /// Convert a non-generic crate item into an instance. /// This function will panic if the item is generic. fn mono_instance(&self, def_id: DefId) -> Instance; diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs index 1ad05633d62d..6f666406c221 100644 --- a/compiler/stable_mir/src/mir/body.rs +++ b/compiler/stable_mir/src/mir/body.rs @@ -602,6 +602,7 @@ impl Rvalue { AggregateKind::Coroutine(def, ref args, mov) => { Ok(Ty::new_coroutine(def, args.clone(), mov)) } + AggregateKind::RawPtr(ty, mutability) => Ok(Ty::new_ptr(ty, mutability)), }, Rvalue::ShallowInitBox(_, ty) => Ok(Ty::new_box(*ty)), Rvalue::CopyForDeref(place) => place.ty(locals), @@ -617,6 +618,7 @@ pub enum AggregateKind { Closure(ClosureDef, GenericArgs), // FIXME(stable_mir): Movability here is redundant Coroutine(CoroutineDef, GenericArgs, Movability), + RawPtr(Ty, Mutability), } #[derive(Clone, Debug, Eq, PartialEq)] diff --git a/compiler/stable_mir/src/mir/mono.rs b/compiler/stable_mir/src/mir/mono.rs index a032a180fcfd..394038926f61 100644 --- a/compiler/stable_mir/src/mir/mono.rs +++ b/compiler/stable_mir/src/mir/mono.rs @@ -157,7 +157,10 @@ impl Instance { /// When generating code for a Drop terminator, users can ignore an empty drop glue. /// These shims are only needed to generate a valid Drop call done via VTable. pub fn is_empty_shim(&self) -> bool { - self.kind == InstanceKind::Shim && with(|cx| cx.is_empty_drop_shim(self.def)) + self.kind == InstanceKind::Shim + && with(|cx| { + cx.is_empty_drop_shim(self.def) || cx.is_empty_async_drop_ctor_shim(self.def) + }) } /// Try to constant evaluate the instance into a constant with the given type. diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index dec04d7e421e..88faf5a9c7d6 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -126,7 +126,6 @@ #![feature(extend_one)] #![feature(fmt_internals)] #![feature(fn_traits)] -#![feature(generic_nonzero)] #![feature(hasher_prefixfree_extras)] #![feature(hint_assert_unchecked)] #![feature(inline_const)] diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 1930be65bfb8..b2e22d8715a8 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -1991,15 +1991,17 @@ impl Vec { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_confusables("push_back", "put", "append")] pub fn push(&mut self, value: T) { + // Inform codegen that the length does not change across grow_one(). + let len = self.len; // This will panic or abort if we would allocate > isize::MAX bytes // or if the length increment would overflow for zero-sized types. - if self.len == self.buf.capacity() { + if len == self.buf.capacity() { self.buf.grow_one(); } unsafe { - let end = self.as_mut_ptr().add(self.len); + let end = self.as_mut_ptr().add(len); ptr::write(end, value); - self.len += 1; + self.len = len + 1; } } diff --git a/library/alloc/tests/lib.rs b/library/alloc/tests/lib.rs index a34bce66496d..b5175a8487f4 100644 --- a/library/alloc/tests/lib.rs +++ b/library/alloc/tests/lib.rs @@ -14,7 +14,6 @@ #![feature(core_intrinsics)] #![feature(extract_if)] #![feature(exact_size_is_empty)] -#![feature(generic_nonzero)] #![feature(linked_list_cursors)] #![feature(map_try_insert)] #![feature(new_uninit)] diff --git a/library/core/benches/num/int_log/mod.rs b/library/core/benches/num/int_log/mod.rs index bb61224b5baa..3807cd5d76cf 100644 --- a/library/core/benches/num/int_log/mod.rs +++ b/library/core/benches/num/int_log/mod.rs @@ -1,7 +1,7 @@ use rand::Rng; use test::{black_box, Bencher}; -macro_rules! int_log_bench { +macro_rules! int_log10_bench { ($t:ty, $predictable:ident, $random:ident, $random_small:ident) => { #[bench] fn $predictable(bench: &mut Bencher) { @@ -51,8 +51,75 @@ macro_rules! int_log_bench { }; } -int_log_bench! {u8, u8_log10_predictable, u8_log10_random, u8_log10_random_small} -int_log_bench! {u16, u16_log10_predictable, u16_log10_random, u16_log10_random_small} -int_log_bench! {u32, u32_log10_predictable, u32_log10_random, u32_log10_random_small} -int_log_bench! {u64, u64_log10_predictable, u64_log10_random, u64_log10_random_small} -int_log_bench! {u128, u128_log10_predictable, u128_log10_random, u128_log10_random_small} +int_log10_bench! {u8, u8_log10_predictable, u8_log10_random, u8_log10_random_small} +int_log10_bench! {u16, u16_log10_predictable, u16_log10_random, u16_log10_random_small} +int_log10_bench! {u32, u32_log10_predictable, u32_log10_random, u32_log10_random_small} +int_log10_bench! {u64, u64_log10_predictable, u64_log10_random, u64_log10_random_small} +int_log10_bench! {u128, u128_log10_predictable, u128_log10_random, u128_log10_random_small} + +macro_rules! int_log_bench { + ($t:ty, $random:ident, $random_small:ident, $geometric:ident) => { + #[bench] + fn $random(bench: &mut Bencher) { + let mut rng = crate::bench_rng(); + /* Exponentially distributed random numbers from the whole range of the type. */ + let numbers: Vec<$t> = (0..256) + .map(|_| { + let x = rng.gen::<$t>() >> rng.gen_range(0..<$t>::BITS); + if x >= 2 { x } else { 2 } + }) + .collect(); + bench.iter(|| { + for &b in &numbers { + for &x in &numbers { + black_box(black_box(x).ilog(b)); + } + } + }); + } + + #[bench] + fn $random_small(bench: &mut Bencher) { + let mut rng = crate::bench_rng(); + /* Exponentially distributed random numbers from the range 0..256. */ + let numbers: Vec<$t> = (0..256) + .map(|_| { + let x = (rng.gen::() >> rng.gen_range(0..u8::BITS)) as $t; + if x >= 2 { x } else { 2 } + }) + .collect(); + bench.iter(|| { + for &b in &numbers { + for &x in &numbers { + black_box(black_box(x).ilog(b)); + } + } + }); + } + + #[bench] + fn $geometric(bench: &mut Bencher) { + let bases: [$t; 16] = [2, 3, 4, 5, 7, 8, 9, 15, 16, 17, 31, 32, 33, 63, 64, 65]; + let base_and_numbers: Vec<($t, Vec<$t>)> = bases + .iter() + .map(|&b| { + let numbers = (0..=<$t>::MAX.ilog(b)).map(|exp| b.pow(exp)).collect(); + (b, numbers) + }) + .collect(); + bench.iter(|| { + for (b, numbers) in &base_and_numbers { + for &x in numbers { + black_box(black_box(x).ilog(black_box(*b))); + } + } + }); + } + }; +} + +int_log_bench! {u8, u8_log_random, u8_log_random_small, u8_log_geometric} +int_log_bench! {u16, u16_log_random, u16_log_random_small, u16_log_geometric} +int_log_bench! {u32, u32_log_random, u32_log_random_small, u32_log_geometric} +int_log_bench! {u64, u64_log_random, u64_log_random_small, u64_log_geometric} +int_log_bench! {u128, u128_log_random, u128_log_random_small, u128_log_geometric} diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 2a447aafe72d..05874ab6c4cb 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -512,7 +512,8 @@ impl [T; N] { /// # Examples /// /// ``` - /// #![feature(array_try_map, generic_nonzero)] + /// #![feature(array_try_map)] + /// /// let a = ["1", "2", "3"]; /// let b = a.try_map(|v| v.parse::()).unwrap().map(|v| v + 1); /// assert_eq!(b, [2, 3, 4]); @@ -522,8 +523,10 @@ impl [T; N] { /// assert!(b.is_err()); /// /// use std::num::NonZero; + /// /// let z = [1, 2, 0, 3, 4]; /// assert_eq!(z.try_map(NonZero::new), None); + /// /// let a = [1, 2, 3]; /// let b = a.try_map(NonZero::new); /// let c = b.map(|x| x.map(NonZero::get)); diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index 65ae48318390..a93b94867ce4 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -927,7 +927,7 @@ impl char { #[must_use] #[inline] pub(crate) fn is_grapheme_extended(self) -> bool { - self > '\x7f' && unicode::Grapheme_Extend(self) + unicode::Grapheme_Extend(self) } /// Returns `true` if this `char` has one of the general categories for numbers. diff --git a/library/core/src/future/async_drop.rs b/library/core/src/future/async_drop.rs new file mode 100644 index 000000000000..0eb8d7bb3289 --- /dev/null +++ b/library/core/src/future/async_drop.rs @@ -0,0 +1,271 @@ +#![unstable(feature = "async_drop", issue = "none")] + +use crate::fmt; +use crate::future::{Future, IntoFuture}; +use crate::intrinsics::discriminant_value; +use crate::marker::{DiscriminantKind, PhantomPinned}; +use crate::mem::MaybeUninit; +use crate::pin::Pin; +use crate::task::{ready, Context, Poll}; + +/// Asynchronously drops a value by running `AsyncDrop::async_drop` +/// on a value and its fields recursively. +#[unstable(feature = "async_drop", issue = "none")] +pub fn async_drop(value: T) -> AsyncDropOwning { + AsyncDropOwning { value: MaybeUninit::new(value), dtor: None, _pinned: PhantomPinned } +} + +/// A future returned by the [`async_drop`]. +#[unstable(feature = "async_drop", issue = "none")] +pub struct AsyncDropOwning { + value: MaybeUninit, + dtor: Option>, + _pinned: PhantomPinned, +} + +#[unstable(feature = "async_drop", issue = "none")] +impl fmt::Debug for AsyncDropOwning { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("AsyncDropOwning").finish_non_exhaustive() + } +} + +#[unstable(feature = "async_drop", issue = "none")] +impl Future for AsyncDropOwning { + type Output = (); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + // SAFETY: Self is pinned thus it is ok to store references to self + unsafe { + let this = self.get_unchecked_mut(); + let dtor = Pin::new_unchecked( + this.dtor.get_or_insert_with(|| async_drop_in_place(this.value.as_mut_ptr())), + ); + // AsyncDestuctors are idempotent so Self gets idempotency as well + dtor.poll(cx) + } + } +} + +#[lang = "async_drop_in_place"] +#[allow(unconditional_recursion)] +// FIXME: Consider if `#[rustc_diagnostic_item = "ptr_drop_in_place"]` is needed? +unsafe fn async_drop_in_place_raw( + to_drop: *mut T, +) -> ::AsyncDestructor { + // Code here does not matter - this is replaced by the + // real async drop glue constructor by the compiler. + + // SAFETY: see comment above + unsafe { async_drop_in_place_raw(to_drop) } +} + +/// Creates the asynchronous destructor of the pointed-to value. +/// +/// # Safety +/// +/// Behavior is undefined if any of the following conditions are violated: +/// +/// * `to_drop` must be [valid](crate::ptr#safety) for both reads and writes. +/// +/// * `to_drop` must be properly aligned, even if `T` has size 0. +/// +/// * `to_drop` must be nonnull, even if `T` has size 0. +/// +/// * The value `to_drop` points to must be valid for async dropping, +/// which may mean it must uphold additional invariants. These +/// invariants depend on the type of the value being dropped. For +/// instance, when dropping a Box, the box's pointer to the heap must +/// be valid. +/// +/// * While `async_drop_in_place` is executing or the returned async +/// destructor is alive, the only way to access parts of `to_drop` +/// is through the `self: Pin<&mut Self>` references supplied to +/// the `AsyncDrop::async_drop` methods that `async_drop_in_place` +/// or `AsyncDropInPlace::poll` invokes. This usually means the +/// returned future stores the `to_drop` pointer and user is required +/// to guarantee that dropped value doesn't move. +/// +#[unstable(feature = "async_drop", issue = "none")] +pub unsafe fn async_drop_in_place(to_drop: *mut T) -> AsyncDropInPlace { + // SAFETY: `async_drop_in_place_raw` has the same safety requirements + unsafe { AsyncDropInPlace(async_drop_in_place_raw(to_drop)) } +} + +/// A future returned by the [`async_drop_in_place`]. +#[unstable(feature = "async_drop", issue = "none")] +pub struct AsyncDropInPlace(::AsyncDestructor); + +#[unstable(feature = "async_drop", issue = "none")] +impl fmt::Debug for AsyncDropInPlace { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("AsyncDropInPlace").finish_non_exhaustive() + } +} + +#[unstable(feature = "async_drop", issue = "none")] +impl Future for AsyncDropInPlace { + type Output = (); + + #[inline(always)] + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + // SAFETY: This code simply forwards poll call to the inner future + unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().0) }.poll(cx) + } +} + +// FIXME(zetanumbers): Add same restrictions on AsyncDrop impls as +// with Drop impls +/// Custom code within the asynchronous destructor. +#[unstable(feature = "async_drop", issue = "none")] +#[lang = "async_drop"] +pub trait AsyncDrop { + /// A future returned by the [`AsyncDrop::async_drop`] to be part + /// of the async destructor. + #[unstable(feature = "async_drop", issue = "none")] + type Dropper<'a>: Future + where + Self: 'a; + + /// Constructs the asynchronous destructor for this type. + #[unstable(feature = "async_drop", issue = "none")] + fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_>; +} + +#[lang = "async_destruct"] +#[rustc_deny_explicit_impl(implement_via_object = false)] +trait AsyncDestruct { + type AsyncDestructor: Future; +} + +/// Basically calls `AsyncDrop::async_drop` with pointer. Used to simplify +/// generation of the code for `async_drop_in_place_raw` +#[lang = "surface_async_drop_in_place"] +async unsafe fn surface_async_drop_in_place(ptr: *mut T) { + // SAFETY: We call this from async drop `async_drop_in_place_raw` + // which has the same safety requirements + unsafe { ::async_drop(Pin::new_unchecked(&mut *ptr)).await } +} + +/// Basically calls `Drop::drop` with pointer. Used to simplify generation +/// of the code for `async_drop_in_place_raw` +#[allow(drop_bounds)] +#[lang = "async_drop_surface_drop_in_place"] +async unsafe fn surface_drop_in_place(ptr: *mut T) { + // SAFETY: We call this from async drop `async_drop_in_place_raw` + // which has the same safety requirements + unsafe { crate::ops::fallback_surface_drop(&mut *ptr) } +} + +/// Wraps a future to continue outputing `Poll::Ready(())` once after +/// wrapped future completes by returning `Poll::Ready(())` on poll. This +/// is useful for constructing async destructors to guarantee this +/// "fuse" property +struct Fuse { + inner: Option, +} + +#[lang = "async_drop_fuse"] +fn fuse(inner: T) -> Fuse { + Fuse { inner: Some(inner) } +} + +impl Future for Fuse +where + T: Future, +{ + type Output = (); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + // SAFETY: pin projection into `self.inner` + unsafe { + let this = self.get_unchecked_mut(); + if let Some(inner) = &mut this.inner { + ready!(Pin::new_unchecked(inner).poll(cx)); + this.inner = None; + } + } + Poll::Ready(()) + } +} + +/// Async destructor for arrays and slices. +#[lang = "async_drop_slice"] +async unsafe fn slice(s: *mut [T]) { + let len = s.len(); + let ptr = s.as_mut_ptr(); + for i in 0..len { + // SAFETY: we iterate over elements of `s` slice + unsafe { async_drop_in_place_raw(ptr.add(i)).await } + } +} + +/// Construct a chain of two futures, which awaits them sequentially as +/// a future. +#[lang = "async_drop_chain"] +async fn chain(first: F, last: G) +where + F: IntoFuture, + G: IntoFuture, +{ + first.await; + last.await; +} + +/// Basically a lazy version of `async_drop_in_place`. Returns a future +/// that would call `AsyncDrop::async_drop` on a first poll. +/// +/// # Safety +/// +/// Same as `async_drop_in_place` except is lazy to avoid creating +/// multiple mutable refernces. +#[lang = "async_drop_defer"] +async unsafe fn defer(to_drop: *mut T) { + // SAFETY: same safety requirements as `async_drop_in_place` + unsafe { async_drop_in_place(to_drop) }.await +} + +/// If `T`'s discriminant is equal to the stored one then awaits `M` +/// otherwise awaits the `O`. +/// +/// # Safety +/// +/// User should carefully manage returned future, since it would +/// try creating an immutable referece from `this` and get pointee's +/// discriminant. +// FIXME(zetanumbers): Send and Sync impls +#[lang = "async_drop_either"] +async unsafe fn either, M: IntoFuture, T>( + other: O, + matched: M, + this: *mut T, + discr: ::Discriminant, +) { + // SAFETY: Guaranteed by the safety section of this funtion's documentation + if unsafe { discriminant_value(&*this) } == discr { + drop(other); + matched.await + } else { + drop(matched); + other.await + } +} + +/// Used for noop async destructors. We don't use [`core::future::Ready`] +/// because it panics after its second poll, which could be potentially +/// bad if that would happen during the cleanup. +#[derive(Clone, Copy)] +struct Noop; + +#[lang = "async_drop_noop"] +fn noop() -> Noop { + Noop +} + +impl Future for Noop { + type Output = (); + + fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll { + Poll::Ready(()) + } +} diff --git a/library/core/src/future/mod.rs b/library/core/src/future/mod.rs index 0f77a2d83433..c3bd18e30aa4 100644 --- a/library/core/src/future/mod.rs +++ b/library/core/src/future/mod.rs @@ -12,6 +12,8 @@ use crate::ptr::NonNull; use crate::task::Context; +#[cfg(not(bootstrap))] +mod async_drop; mod future; mod into_future; mod join; @@ -36,6 +38,10 @@ pub use ready::{ready, Ready}; #[stable(feature = "future_poll_fn", since = "1.64.0")] pub use poll_fn::{poll_fn, PollFn}; +#[cfg(not(bootstrap))] +#[unstable(feature = "async_drop", issue = "none")] +pub use async_drop::{async_drop, async_drop_in_place, AsyncDrop, AsyncDropInPlace}; + /// This type is needed because: /// /// a) Coroutines cannot implement `for<'a, 'b> Coroutine<&'a mut Context<'b>>`, so we need to pass diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 9406efd7ab24..92f1bd274082 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -2779,6 +2779,34 @@ pub unsafe fn vtable_align(_ptr: *const ()) -> usize { unreachable!() } +/// Lowers in MIR to `Rvalue::Aggregate` with `AggregateKind::RawPtr`. +/// +/// This is used to implement functions like `slice::from_raw_parts_mut` and +/// `ptr::from_raw_parts` in a way compatible with the compiler being able to +/// change the possible layouts of pointers. +#[rustc_nounwind] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_const_unstable(feature = "ptr_metadata", issue = "81513")] +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[cfg(not(bootstrap))] +pub const fn aggregate_raw_ptr, D, M>(_data: D, _meta: M) -> P { + // To implement a fallback we'd have to assume the layout of the pointer, + // but the whole point of this intrinsic is that we shouldn't do that. + unreachable!() +} + +#[unstable(feature = "core_intrinsics", issue = "none")] +pub trait AggregateRawPtr { + type Metadata: Copy; +} +impl AggregateRawPtr<*const T> for *const P { + type Metadata =

::Metadata; +} +impl AggregateRawPtr<*mut T> for *mut P { + type Metadata =

::Metadata; +} + // Some functions are defined here because they accidentally got made // available in this module on stable. See . // (`transmute` also falls into this category, but it cannot be wrapped due to the diff --git a/library/core/src/iter/traits/double_ended.rs b/library/core/src/iter/traits/double_ended.rs index 35092ec51cdd..3b1267857284 100644 --- a/library/core/src/iter/traits/double_ended.rs +++ b/library/core/src/iter/traits/double_ended.rs @@ -118,7 +118,8 @@ pub trait DoubleEndedIterator: Iterator { /// Basic usage: /// /// ``` - /// #![feature(generic_nonzero, iter_advance_by)] + /// #![feature(iter_advance_by)] + /// /// use std::num::NonZero; /// /// let a = [3, 4, 5, 6]; diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index 95c03043e3e8..7c1c6122efe4 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -288,7 +288,8 @@ pub trait Iterator { /// # Examples /// /// ``` - /// #![feature(generic_nonzero, iter_advance_by)] + /// #![feature(iter_advance_by)] + /// /// use std::num::NonZero; /// /// let a = [1, 2, 3, 4]; @@ -2939,7 +2940,8 @@ pub trait Iterator { /// This also supports other types which implement [`Try`], not just [`Result`]. /// /// ``` - /// #![feature(generic_nonzero, try_find)] + /// #![feature(try_find)] + /// /// use std::num::NonZero; /// /// let a = [3, 5, 7, 4, 9, 0, 11u32]; diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index e34e9b7fff64..a92da88dd3f5 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -1163,12 +1163,19 @@ macro_rules! int_impl { /// ``` #[stable(feature = "wrapping", since = "1.7.0")] #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + // We could always go back to wrapping + #[rustc_allow_const_fn_unstable(unchecked_shifts)] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] pub const fn checked_shl(self, rhs: u32) -> Option { - let (a, b) = self.overflowing_shl(rhs); - if unlikely!(b) { None } else { Some(a) } + // Not using overflowing_shl as that's a wrapping shift + if rhs < Self::BITS { + // SAFETY: just checked the RHS is in-range + Some(unsafe { self.unchecked_shl(rhs) }) + } else { + None + } } /// Strict shift left. Computes `self << rhs`, panicking if `rhs` is larger @@ -1254,12 +1261,19 @@ macro_rules! int_impl { /// ``` #[stable(feature = "wrapping", since = "1.7.0")] #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + // We could always go back to wrapping + #[rustc_allow_const_fn_unstable(unchecked_shifts)] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] pub const fn checked_shr(self, rhs: u32) -> Option { - let (a, b) = self.overflowing_shr(rhs); - if unlikely!(b) { None } else { Some(a) } + // Not using overflowing_shr as that's a wrapping shift + if rhs < Self::BITS { + // SAFETY: just checked the RHS is in-range + Some(unsafe { self.unchecked_shr(rhs) }) + } else { + None + } } /// Strict shift right. Computes `self >> rhs`, panicking `rhs` is @@ -3109,21 +3123,9 @@ macro_rules! int_impl { if self <= 0 || base <= 1 { None } else { - let mut n = 0; - let mut r = self; - - // Optimization for 128 bit wide integers. - if Self::BITS == 128 { - let b = Self::ilog2(self) / (Self::ilog2(base) + 1); - n += b; - r /= base.pow(b as u32); - } - - while r >= base { - r /= base; - n += 1; - } - Some(n) + // Delegate to the unsigned implementation. + // The condition makes sure that both casts are exact. + (self as $UnsignedT).checked_ilog(base as $UnsignedT) } } @@ -3185,7 +3187,8 @@ macro_rules! int_impl { /// that code in debug mode will trigger a panic on this case and /// optimized code will return #[doc = concat!("`", stringify!($SelfT), "::MIN`")] - /// without a panic. + /// without a panic. If you do not want this behavior, consider + /// using [`unsigned_abs`](Self::unsigned_abs) instead. /// /// # Examples /// diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 9ebbb4ffe80b..443401c5dba8 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -67,15 +67,15 @@ pub use error::ParseIntError; )] pub use nonzero::ZeroablePrimitive; -#[unstable(feature = "generic_nonzero", issue = "120257")] +#[stable(feature = "generic_nonzero", since = "CURRENT_RUSTC_VERSION")] pub use nonzero::NonZero; -#[stable(feature = "nonzero", since = "1.28.0")] -pub use nonzero::{NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize}; - #[stable(feature = "signed_nonzero", since = "1.34.0")] pub use nonzero::{NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize}; +#[stable(feature = "nonzero", since = "1.28.0")] +pub use nonzero::{NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize}; + #[stable(feature = "try_from", since = "1.34.0")] pub use error::TryFromIntError; diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index 62ea7abf6528..b9d771ba359a 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -105,12 +105,11 @@ impl_zeroable_primitive!( /// For example, `Option>` is the same size as `u32`: /// /// ``` -/// #![feature(generic_nonzero)] -/// use core::mem::size_of; +/// use core::{mem::size_of, num::NonZero}; /// -/// assert_eq!(size_of::>>(), size_of::()); +/// assert_eq!(size_of::>>(), size_of::()); /// ``` -#[unstable(feature = "generic_nonzero", issue = "120257")] +#[stable(feature = "generic_nonzero", since = "CURRENT_RUSTC_VERSION")] #[repr(transparent)] #[rustc_nonnull_optimization_guaranteed] #[rustc_diagnostic_item = "NonZero"] @@ -562,7 +561,8 @@ macro_rules! nonzero_integer { /// Basic usage: /// /// ``` - /// #![feature(generic_nonzero, non_zero_count_ones)] + /// #![feature(non_zero_count_ones)] + /// /// # fn main() { test().unwrap(); } /// # fn test() -> Option<()> { /// # use std::num::*; diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index ba6a243041ce..c13763243f03 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -579,8 +579,17 @@ macro_rules! uint_impl { without modifying the original"] #[inline] pub const fn checked_sub(self, rhs: Self) -> Option { - let (a, b) = self.overflowing_sub(rhs); - if unlikely!(b) { None } else { Some(a) } + // Per PR#103299, there's no advantage to the `overflowing` intrinsic + // for *unsigned* subtraction and we just emit the manual check anyway. + // Thus, rather than using `overflowing_sub` that produces a wrapping + // subtraction, check it ourself so we can use an unchecked one. + + if self >= rhs { + // SAFETY: just checked this can't overflow + Some(unsafe { intrinsics::unchecked_sub(self, rhs) }) + } else { + None + } } /// Strict integer subtraction. Computes `self - rhs`, panicking if @@ -1086,18 +1095,25 @@ macro_rules! uint_impl { None } else { let mut n = 0; - let mut r = self; + let mut r = 1; // Optimization for 128 bit wide integers. if Self::BITS == 128 { - let b = Self::ilog2(self) / (Self::ilog2(base) + 1); - n += b; - r /= base.pow(b as u32); + // The following is a correct lower bound for ⌊log(base,self)⌋ because + // + // log(base,self) = log(2,self) / log(2,base) + // ≥ ⌊log(2,self)⌋ / (⌊log(2,base)⌋ + 1) + // + // hence + // + // ⌊log(base,self)⌋ ≥ ⌊ ⌊log(2,self)⌋ / (⌊log(2,base)⌋ + 1) ⌋ . + n = self.ilog2() / (base.ilog2() + 1); + r = base.pow(n); } - while r >= base { - r /= base; + while r <= self / base { n += 1; + r *= base; } Some(n) } @@ -1222,12 +1238,19 @@ macro_rules! uint_impl { /// ``` #[stable(feature = "wrapping", since = "1.7.0")] #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + // We could always go back to wrapping + #[rustc_allow_const_fn_unstable(unchecked_shifts)] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] pub const fn checked_shl(self, rhs: u32) -> Option { - let (a, b) = self.overflowing_shl(rhs); - if unlikely!(b) { None } else { Some(a) } + // Not using overflowing_shl as that's a wrapping shift + if rhs < Self::BITS { + // SAFETY: just checked the RHS is in-range + Some(unsafe { self.unchecked_shl(rhs) }) + } else { + None + } } /// Strict shift left. Computes `self << rhs`, panicking if `rhs` is larger @@ -1313,12 +1336,19 @@ macro_rules! uint_impl { /// ``` #[stable(feature = "wrapping", since = "1.7.0")] #[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")] + // We could always go back to wrapping + #[rustc_allow_const_fn_unstable(unchecked_shifts)] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] pub const fn checked_shr(self, rhs: u32) -> Option { - let (a, b) = self.overflowing_shr(rhs); - if unlikely!(b) { None } else { Some(a) } + // Not using overflowing_shr as that's a wrapping shift + if rhs < Self::BITS { + // SAFETY: just checked the RHS is in-range + Some(unsafe { self.unchecked_shr(rhs) }) + } else { + None + } } /// Strict shift right. Computes `self >> rhs`, panicking `rhs` is diff --git a/library/core/src/ops/drop.rs b/library/core/src/ops/drop.rs index 34dfa9e4c51f..1325d90e4f34 100644 --- a/library/core/src/ops/drop.rs +++ b/library/core/src/ops/drop.rs @@ -238,3 +238,11 @@ pub trait Drop { #[stable(feature = "rust1", since = "1.0.0")] fn drop(&mut self); } + +/// Fallback function to call surface level `Drop::drop` function +#[cfg(not(bootstrap))] +#[allow(drop_bounds)] +#[lang = "fallback_surface_drop"] +pub(crate) fn fallback_surface_drop(x: &mut T) { + ::drop(x) +} diff --git a/library/core/src/ops/mod.rs b/library/core/src/ops/mod.rs index ac808bec50ec..81d5e5c949ec 100644 --- a/library/core/src/ops/mod.rs +++ b/library/core/src/ops/mod.rs @@ -174,6 +174,9 @@ pub use self::deref::Receiver; #[stable(feature = "rust1", since = "1.0.0")] pub use self::drop::Drop; +#[cfg(not(bootstrap))] +pub(crate) use self::drop::fallback_surface_drop; + #[stable(feature = "rust1", since = "1.0.0")] pub use self::function::{Fn, FnMut, FnOnce}; diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs index e8e23f2a7ecc..bda1ee6f4571 100644 --- a/library/core/src/primitive_docs.rs +++ b/library/core/src/primitive_docs.rs @@ -537,7 +537,11 @@ impl () {} /// ## 4. Get it from C. /// /// ``` -/// # #![feature(rustc_private)] +/// # mod libc { +/// # pub unsafe fn malloc(_size: usize) -> *mut core::ffi::c_void { core::ptr::NonNull::dangling().as_ptr() } +/// # pub unsafe fn free(_ptr: *mut core::ffi::c_void) {} +/// # } +/// # #[cfg(any())] /// #[allow(unused_extern_crates)] /// extern crate libc; /// @@ -548,7 +552,7 @@ impl () {} /// if my_num.is_null() { /// panic!("failed to allocate memory"); /// } -/// libc::free(my_num as *mut libc::c_void); +/// libc::free(my_num as *mut core::ffi::c_void); /// } /// ``` /// diff --git a/library/core/src/ptr/metadata.rs b/library/core/src/ptr/metadata.rs index 25a06f121cda..1226c8e24194 100644 --- a/library/core/src/ptr/metadata.rs +++ b/library/core/src/ptr/metadata.rs @@ -2,6 +2,8 @@ use crate::fmt; use crate::hash::{Hash, Hasher}; +#[cfg(not(bootstrap))] +use crate::intrinsics::aggregate_raw_ptr; use crate::marker::Freeze; /// Provides the pointer metadata type of any pointed-to type. @@ -113,10 +115,17 @@ pub const fn from_raw_parts( data_pointer: *const (), metadata: ::Metadata, ) -> *const T { - // SAFETY: Accessing the value from the `PtrRepr` union is safe since *const T - // and PtrComponents have the same memory layouts. Only std can make this - // guarantee. - unsafe { PtrRepr { components: PtrComponents { data_pointer, metadata } }.const_ptr } + #[cfg(bootstrap)] + { + // SAFETY: Accessing the value from the `PtrRepr` union is safe since *const T + // and PtrComponents have the same memory layouts. Only std can make this + // guarantee. + unsafe { PtrRepr { components: PtrComponents { data_pointer, metadata } }.const_ptr } + } + #[cfg(not(bootstrap))] + { + aggregate_raw_ptr(data_pointer, metadata) + } } /// Performs the same functionality as [`from_raw_parts`], except that a @@ -130,10 +139,17 @@ pub const fn from_raw_parts_mut( data_pointer: *mut (), metadata: ::Metadata, ) -> *mut T { - // SAFETY: Accessing the value from the `PtrRepr` union is safe since *const T - // and PtrComponents have the same memory layouts. Only std can make this - // guarantee. - unsafe { PtrRepr { components: PtrComponents { data_pointer, metadata } }.mut_ptr } + #[cfg(bootstrap)] + { + // SAFETY: Accessing the value from the `PtrRepr` union is safe since *const T + // and PtrComponents have the same memory layouts. Only std can make this + // guarantee. + unsafe { PtrRepr { components: PtrComponents { data_pointer, metadata } }.mut_ptr } + } + #[cfg(not(bootstrap))] + { + aggregate_raw_ptr(data_pointer, metadata) + } } #[repr(C)] diff --git a/library/core/src/unicode/unicode_data.rs b/library/core/src/unicode/unicode_data.rs index dd2ad9a58f67..1b3d6729663b 100644 --- a/library/core/src/unicode/unicode_data.rs +++ b/library/core/src/unicode/unicode_data.rs @@ -315,7 +315,11 @@ pub mod grapheme_extend { 15, 0, 7, 1, 17, 2, 7, 1, 2, 1, 5, 100, 1, 160, 7, 0, 1, 61, 4, 0, 4, 0, 7, 109, 7, 0, 96, 128, 240, 0, ]; + #[inline] pub fn lookup(c: char) -> bool { + (c as u32) >= 0x300 && lookup_slow(c) + } + fn lookup_slow(c: char) -> bool { super::skip_search( c as u32, &SHORT_OFFSET_RUNS, diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index e741149e7ce2..7bd962fa2609 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -42,7 +42,6 @@ #![feature(float_minimum_maximum)] #![feature(future_join)] #![feature(generic_assert_internals)] -#![feature(generic_nonzero)] #![feature(array_try_from_fn)] #![feature(hasher_prefixfree_extras)] #![feature(hashmap_internals)] diff --git a/library/core/tests/ptr.rs b/library/core/tests/ptr.rs index 2c82eda9a58c..7b55c2bf8a81 100644 --- a/library/core/tests/ptr.rs +++ b/library/core/tests/ptr.rs @@ -1163,3 +1163,11 @@ fn test_null_array_as_slice() { assert!(ptr.is_null()); assert_eq!(ptr.len(), 4); } + +#[test] +fn test_ptr_from_raw_parts_in_const() { + const EMPTY_SLICE_PTR: *const [i32] = + std::ptr::slice_from_raw_parts(std::ptr::without_provenance(123), 456); + assert_eq!(EMPTY_SLICE_PTR.addr(), 123); + assert_eq!(EMPTY_SLICE_PTR.len(), 456); +} diff --git a/library/core/tests/slice.rs b/library/core/tests/slice.rs index c5743eda3e80..ffe8ffcc7f2f 100644 --- a/library/core/tests/slice.rs +++ b/library/core/tests/slice.rs @@ -2678,3 +2678,16 @@ fn test_get_many_mut_duplicate() { let mut v = vec![1, 2, 3, 4, 5]; assert!(v.get_many_mut([1, 3, 3, 4]).is_err()); } + +#[test] +fn test_slice_from_raw_parts_in_const() { + static FANCY: i32 = 4; + static FANCY_SLICE: &[i32] = unsafe { std::slice::from_raw_parts(&FANCY, 1) }; + assert_eq!(FANCY_SLICE.as_ptr(), std::ptr::addr_of!(FANCY)); + assert_eq!(FANCY_SLICE.len(), 1); + + const EMPTY_SLICE: &[i32] = + unsafe { std::slice::from_raw_parts(std::ptr::without_provenance(123456), 0) }; + assert_eq!(EMPTY_SLICE.as_ptr().addr(), 123456); + assert_eq!(EMPTY_SLICE.len(), 0); +} diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index c8db028b651f..1ceff2e506cc 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -26,7 +26,6 @@ #![feature(staged_api)] #![feature(allow_internal_unstable)] #![feature(decl_macro)] -#![feature(generic_nonzero)] #![feature(maybe_uninit_write_slice)] #![feature(negative_impls)] #![feature(new_uninit)] diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 1293abddaf36..409ead0e2843 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -214,7 +214,7 @@ pub struct Permissions(fs_imp::FilePermissions); /// A structure representing a type of file with accessors for each file type. /// It is returned by [`Metadata::file_type`] method. #[stable(feature = "file_type", since = "1.1.0")] -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Hash)] #[cfg_attr(not(test), rustc_diagnostic_item = "FileType")] pub struct FileType(fs_imp::FileType); @@ -1410,15 +1410,20 @@ impl Metadata { #[stable(feature = "std_debug", since = "1.16.0")] impl fmt::Debug for Metadata { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Metadata") - .field("file_type", &self.file_type()) - .field("is_dir", &self.is_dir()) - .field("is_file", &self.is_file()) - .field("permissions", &self.permissions()) - .field("modified", &self.modified()) - .field("accessed", &self.accessed()) - .field("created", &self.created()) - .finish_non_exhaustive() + let mut debug = f.debug_struct("Metadata"); + debug.field("file_type", &self.file_type()); + debug.field("permissions", &self.permissions()); + debug.field("len", &self.len()); + if let Ok(modified) = self.modified() { + debug.field("modified", &modified); + } + if let Ok(accessed) = self.accessed() { + debug.field("accessed", &accessed); + } + if let Ok(created) = self.created() { + debug.field("created", &created); + } + debug.finish_non_exhaustive() } } @@ -1684,6 +1689,17 @@ impl FileType { } } +#[stable(feature = "std_debug", since = "1.16.0")] +impl fmt::Debug for FileType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("FileType") + .field("is_file", &self.is_file()) + .field("is_dir", &self.is_dir()) + .field("is_symlink", &self.is_symlink()) + .finish_non_exhaustive() + } +} + impl AsInner for FileType { #[inline] fn as_inner(&self) -> &fs_imp::FileType { diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 241a443fab96..aa908f0499f4 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -335,7 +335,6 @@ #![feature(float_minimum_maximum)] #![feature(float_next_up_down)] #![feature(fmt_internals)] -#![feature(generic_nonzero)] #![feature(hasher_prefixfree_extras)] #![feature(hashmap_internals)] #![feature(hint_assert_unchecked)] diff --git a/library/std/src/num.rs b/library/std/src/num.rs index 1343fdfd1dfd..fbe68f7e3038 100644 --- a/library/std/src/num.rs +++ b/library/std/src/num.rs @@ -23,11 +23,12 @@ pub use core::num::{FpCategory, ParseFloatError, ParseIntError, TryFromIntError} )] pub use core::num::ZeroablePrimitive; -#[unstable(feature = "generic_nonzero", issue = "120257")] +#[stable(feature = "generic_nonzero", since = "CURRENT_RUSTC_VERSION")] pub use core::num::NonZero; #[stable(feature = "signed_nonzero", since = "1.34.0")] pub use core::num::{NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize}; + #[stable(feature = "nonzero", since = "1.28.0")] pub use core::num::{NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize}; diff --git a/library/std/src/process.rs b/library/std/src/process.rs index 69cc61b30efe..4a73a9be88bc 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -1865,7 +1865,8 @@ impl ExitStatusError { /// # Examples /// /// ``` - /// #![feature(exit_status_error, generic_nonzero)] + /// #![feature(exit_status_error)] + /// /// # if cfg!(unix) { /// use std::num::NonZero; /// use std::process::Command; diff --git a/library/std/src/sys/pal/unix/fd.rs b/library/std/src/sys/pal/unix/fd.rs index 48e83b04ef46..203c7180001f 100644 --- a/library/std/src/sys/pal/unix/fd.rs +++ b/library/std/src/sys/pal/unix/fd.rs @@ -45,13 +45,9 @@ const READ_LIMIT: usize = libc::ssize_t::MAX as usize; #[cfg(any( target_os = "dragonfly", target_os = "freebsd", - target_os = "ios", - target_os = "tvos", - target_os = "macos", target_os = "netbsd", target_os = "openbsd", - target_os = "watchos", - target_os = "visionos", + target_vendor = "apple", ))] const fn max_iov() -> usize { libc::IOV_MAX as usize @@ -72,17 +68,13 @@ const fn max_iov() -> usize { target_os = "dragonfly", target_os = "emscripten", target_os = "freebsd", - target_os = "ios", - target_os = "tvos", target_os = "linux", - target_os = "macos", target_os = "netbsd", target_os = "nto", target_os = "openbsd", target_os = "horizon", target_os = "vita", - target_os = "watchos", - target_os = "visionos", + target_vendor = "apple", )))] const fn max_iov() -> usize { 16 // The minimum value required by POSIX. @@ -201,13 +193,10 @@ impl FileDesc { target_os = "fuchsia", target_os = "hurd", target_os = "illumos", - target_os = "ios", - target_os = "tvos", target_os = "linux", - target_os = "macos", target_os = "netbsd", target_os = "openbsd", - target_os = "watchos", + target_vendor = "apple", )))] pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result { io::default_read_vectored(|b| self.read_at(b, offset), bufs) @@ -241,15 +230,7 @@ impl FileDesc { Ok(ret as usize) } - // We support old MacOS and iOS versions that do not have `preadv`. There is - // no `syscall` possible in these platform. - #[cfg(any( - all(target_os = "android", target_pointer_width = "32"), - target_os = "ios", // ios 14.0 - target_os = "tvos", // tvos 14.0 - target_os = "macos", // macos 11.0 - target_os = "watchos", // watchos 7.0 - ))] + #[cfg(all(target_os = "android", target_pointer_width = "32"))] pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result { super::weak::weak!(fn preadv64(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize); @@ -269,6 +250,35 @@ impl FileDesc { } } + // We support old MacOS, iOS, watchOS, tvOS and visionOS. `preadv` was added in the following + // Apple OS versions: + // ios 14.0 + // tvos 14.0 + // macos 11.0 + // watchos 7.0 + // + // These versions may be newer than the minimum supported versions of OS's we support so we must + // use "weak" linking. + #[cfg(target_vendor = "apple")] + pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result { + super::weak::weak!(fn preadv(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize); + + match preadv.get() { + Some(preadv) => { + let ret = cvt(unsafe { + preadv( + self.as_raw_fd(), + bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec, + cmp::min(bufs.len(), max_iov()) as libc::c_int, + offset as _, + ) + })?; + Ok(ret as usize) + } + None => io::default_read_vectored(|b| self.read_at(b, offset), bufs), + } + } + pub fn write(&self, buf: &[u8]) -> io::Result { let ret = cvt(unsafe { libc::write( @@ -360,13 +370,10 @@ impl FileDesc { target_os = "fuchsia", target_os = "hurd", target_os = "illumos", - target_os = "ios", - target_os = "tvos", target_os = "linux", - target_os = "macos", target_os = "netbsd", target_os = "openbsd", - target_os = "watchos", + target_vendor = "apple", )))] pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result { io::default_write_vectored(|b| self.write_at(b, offset), bufs) @@ -400,15 +407,7 @@ impl FileDesc { Ok(ret as usize) } - // We support old MacOS and iOS versions that do not have `pwritev`. There is - // no `syscall` possible in these platform. - #[cfg(any( - all(target_os = "android", target_pointer_width = "32"), - target_os = "ios", // ios 14.0 - target_os = "tvos", // tvos 14.0 - target_os = "macos", // macos 11.0 - target_os = "watchos", // watchos 7.0 - ))] + #[cfg(all(target_os = "android", target_pointer_width = "32"))] pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result { super::weak::weak!(fn pwritev64(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize); @@ -428,6 +427,35 @@ impl FileDesc { } } + // We support old MacOS, iOS, watchOS, tvOS and visionOS. `pwritev` was added in the following + // Apple OS versions: + // ios 14.0 + // tvos 14.0 + // macos 11.0 + // watchos 7.0 + // + // These versions may be newer than the minimum supported versions of OS's we support so we must + // use "weak" linking. + #[cfg(target_vendor = "apple")] + pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result { + super::weak::weak!(fn pwritev(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize); + + match pwritev.get() { + Some(pwritev) => { + let ret = cvt(unsafe { + pwritev( + self.as_raw_fd(), + bufs.as_ptr() as *const libc::iovec, + cmp::min(bufs.len(), max_iov()) as libc::c_int, + offset as _, + ) + })?; + Ok(ret as usize) + } + None => io::default_write_vectored(|b| self.write_at(b, offset), bufs), + } + } + #[cfg(not(any( target_env = "newlib", target_os = "solaris", diff --git a/library/std/src/sys/pal/unix/weak.rs b/library/std/src/sys/pal/unix/weak.rs index 48cc8633e93d..4765a286e6b9 100644 --- a/library/std/src/sys/pal/unix/weak.rs +++ b/library/std/src/sys/pal/unix/weak.rs @@ -28,7 +28,7 @@ use crate::ptr; use crate::sync::atomic::{self, AtomicPtr, Ordering}; // We can use true weak linkage on ELF targets. -#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "tvos")))] +#[cfg(all(unix, not(target_vendor = "apple")))] pub(crate) macro weak { (fn $name:ident($($t:ty),*) -> $ret:ty) => ( let ref $name: ExternWeak $ret> = { @@ -43,7 +43,7 @@ pub(crate) macro weak { } // On non-ELF targets, use the dlsym approximation of weak linkage. -#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos"))] +#[cfg(target_vendor = "apple")] pub(crate) use self::dlsym as weak; pub(crate) struct ExternWeak { diff --git a/library/std/src/sys/thread_local/static_local.rs b/library/std/src/sys/thread_local/static_local.rs index 206e62bb5e2c..162c3fbd97a0 100644 --- a/library/std/src/sys/thread_local/static_local.rs +++ b/library/std/src/sys/thread_local/static_local.rs @@ -11,8 +11,6 @@ pub macro thread_local_inner { (@key $t:ty, const $init:expr) => {{ #[inline] // see comments below #[deny(unsafe_op_in_unsafe_fn)] - // FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_refs` lint - #[allow(static_mut_refs)] unsafe fn __getit( _init: $crate::option::Option<&mut $crate::option::Option<$t>>, ) -> $crate::option::Option<&'static $t> { @@ -25,7 +23,8 @@ pub macro thread_local_inner { // FIXME(#84224) this should come after the `target_thread_local` // block. static mut VAL: $t = INIT_EXPR; - unsafe { $crate::option::Option::Some(&VAL) } + // SAFETY: we only ever create shared references, so there's no mutable aliasing. + unsafe { $crate::option::Option::Some(&*$crate::ptr::addr_of!(VAL)) } } unsafe { diff --git a/library/stdarch b/library/stdarch index 7df81ba8c3e2..c0257c1660e7 160000 --- a/library/stdarch +++ b/library/stdarch @@ -1 +1 @@ -Subproject commit 7df81ba8c3e2d02c2ace0c5a6f4f32d800c09e56 +Subproject commit c0257c1660e78c80ad1b9136fcc5555b14da5b4c diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs index f3c22061d25d..f3a26e259383 100644 --- a/library/test/src/lib.rs +++ b/library/test/src/lib.rs @@ -17,7 +17,6 @@ #![unstable(feature = "test", issue = "50297")] #![doc(test(attr(deny(warnings))))] #![doc(rust_logo)] -#![feature(generic_nonzero)] #![feature(rustdoc_internals)] #![feature(internal_output_capture)] #![feature(staged_api)] diff --git a/src/bootstrap/mk/Makefile.in b/src/bootstrap/mk/Makefile.in index d6e60d52d632..3cfd02407944 100644 --- a/src/bootstrap/mk/Makefile.in +++ b/src/bootstrap/mk/Makefile.in @@ -76,10 +76,11 @@ check-aux: $(BOOTSTRAP) miri --stage 2 library/std \ --doc -- \ --skip fs:: --skip net:: --skip process:: --skip sys::pal:: - # Also test some very target-specific modules on other targets. + # Also test some very target-specific modules on other targets + # (making sure to cover an i686 target as well). $(Q)MIRIFLAGS="-Zmiri-disable-isolation" BOOTSTRAP_SKIP_TARGET_SANITY=1 \ $(BOOTSTRAP) miri --stage 2 library/std \ - --target aarch64-apple-darwin,i686-pc-windows-gnu \ + --target aarch64-apple-darwin,i686-pc-windows-msvc \ --no-doc -- \ time:: sync:: thread:: env:: dist: diff --git a/src/bootstrap/src/bin/main.rs b/src/bootstrap/src/bin/main.rs index 4cb67b7aa624..44fb1911fc6d 100644 --- a/src/bootstrap/src/bin/main.rs +++ b/src/bootstrap/src/bin/main.rs @@ -37,6 +37,7 @@ fn main() { build_lock = fd_lock::RwLock::new(t!(fs::OpenOptions::new() .write(true) + .truncate(true) .create(true) .open(&lock_path))); _build_lock_guard = match build_lock.try_write() { @@ -143,8 +144,8 @@ fn check_version(config: &Config) -> Option { // then use the one from the config.toml. This way we never show the same warnings // more than once. if let Ok(t) = fs::read_to_string(&warned_id_path) { - let last_warned_id = - usize::from_str(&t).expect(&format!("{} is corrupted.", warned_id_path.display())); + let last_warned_id = usize::from_str(&t) + .unwrap_or_else(|_| panic!("{} is corrupted.", warned_id_path.display())); // We only use the last_warned_id if it exists in `CONFIG_CHANGE_HISTORY`. // Otherwise, we may retrieve all the changes if it's not the highest value. diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index 927d72e8ccb4..8235d4634b75 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -394,7 +394,7 @@ macro_rules! tool_check_step { impl Step for $name { type Output = (); const ONLY_HOSTS: bool = true; - // don't ever check out-of-tree tools by default, they'll fail when toolstate is broken + /// don't ever check out-of-tree tools by default, they'll fail when toolstate is broken const DEFAULT: bool = matches!($source_type, SourceType::InTree) $( && $default )?; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { diff --git a/src/bootstrap/src/core/build_steps/clean.rs b/src/bootstrap/src/core/build_steps/clean.rs index 9e103a350e65..5bcaeed7faa1 100644 --- a/src/bootstrap/src/core/build_steps/clean.rs +++ b/src/bootstrap/src/core/build_steps/clean.rs @@ -181,39 +181,33 @@ fn rm_rf(path: &Path) { } Ok(metadata) => { if metadata.file_type().is_file() || metadata.file_type().is_symlink() { - do_op(path, "remove file", |p| { - fs::remove_file(p).or_else(|e| { - // Work around the fact that we cannot - // delete an executable while it runs on Windows. - #[cfg(windows)] + do_op(path, "remove file", |p| match fs::remove_file(p) { + #[cfg(windows)] + Err(e) if e.kind() == std::io::ErrorKind::PermissionDenied && p.file_name().and_then(std::ffi::OsStr::to_str) - == Some("bootstrap.exe") - { - eprintln!("WARNING: failed to delete '{}'.", p.display()); - return Ok(()); - } - Err(e) - }) + == Some("bootstrap.exe") => + { + eprintln!("WARNING: failed to delete '{}'.", p.display()); + Ok(()) + } + r => r, }); + return; } for file in t!(fs::read_dir(path)) { rm_rf(&t!(file).path()); } - do_op(path, "remove dir", |p| { - fs::remove_dir(p).or_else(|e| { - // Check for dir not empty on Windows - // FIXME: Once `ErrorKind::DirectoryNotEmpty` is stabilized, - // match on `e.kind()` instead. - #[cfg(windows)] - if e.raw_os_error() == Some(145) { - return Ok(()); - } - Err(e) - }) + do_op(path, "remove dir", |p| match fs::remove_dir(p) { + // Check for dir not empty on Windows + // FIXME: Once `ErrorKind::DirectoryNotEmpty` is stabilized, + // match on `e.kind()` instead. + #[cfg(windows)] + Err(e) if e.raw_os_error() == Some(145) => Ok(()), + r => r, }); } }; @@ -228,14 +222,14 @@ where // On windows we can't remove a readonly file, and git will often clone files as readonly. // As a result, we have some special logic to remove readonly files on windows. // This is also the reason that we can't use things like fs::remove_dir_all(). - Err(ref e) if cfg!(windows) && e.kind() == ErrorKind::PermissionDenied => { + #[cfg(windows)] + Err(ref e) if e.kind() == ErrorKind::PermissionDenied => { let m = t!(path.symlink_metadata()); let mut p = m.permissions(); p.set_readonly(false); t!(fs::set_permissions(path, p)); f(path).unwrap_or_else(|e| { // Delete symlinked directories on Windows - #[cfg(windows)] if m.file_type().is_symlink() && path.is_dir() && fs::remove_dir(path).is_ok() { return; } diff --git a/src/bootstrap/src/core/build_steps/clippy.rs b/src/bootstrap/src/core/build_steps/clippy.rs index 33323ec1e5de..01b5e99116ff 100644 --- a/src/bootstrap/src/core/build_steps/clippy.rs +++ b/src/bootstrap/src/core/build_steps/clippy.rs @@ -24,7 +24,7 @@ use super::compile::std_cargo; use super::tool::prepare_tool_cargo; use super::tool::SourceType; -// Disable the most spammy clippy lints +/// Disable the most spammy clippy lints const IGNORED_RULES_FOR_STD_AND_RUSTC: &[&str] = &[ "many_single_char_names", // there are a lot in stdarch "collapsible_if", diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 50149d370d5e..c20653b5dfa6 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -835,13 +835,13 @@ impl Rustc { } impl Step for Rustc { - // We return the stage of the "actual" compiler (not the uplifted one). - // - // By "actual" we refer to the uplifting logic where we may not compile the requested stage; - // instead, we uplift it from the previous stages. Which can lead to bootstrap failures in - // specific situations where we request stage X from other steps. However we may end up - // uplifting it from stage Y, causing the other stage to fail when attempting to link with - // stage X which was never actually built. + /// We return the stage of the "actual" compiler (not the uplifted one). + /// + /// By "actual" we refer to the uplifting logic where we may not compile the requested stage; + /// instead, we uplift it from the previous stages. Which can lead to bootstrap failures in + /// specific situations where we request stage X from other steps. However we may end up + /// uplifting it from stage Y, causing the other stage to fail when attempting to link with + /// stage X which was never actually built. type Output = u32; const ONLY_HOSTS: bool = true; const DEFAULT: bool = false; @@ -1139,7 +1139,7 @@ pub fn rustc_cargo_env( // busting caches (e.g. like #71152). if builder.config.llvm_enabled(target) { let building_is_expensive = - crate::core::build_steps::llvm::prebuilt_llvm_config(builder, target).is_err(); + crate::core::build_steps::llvm::prebuilt_llvm_config(builder, target).should_build(); // `top_stage == stage` might be false for `check --stage 1`, if we are building the stage 1 compiler let can_skip_build = builder.kind == Kind::Check && builder.top_stage == stage; let should_skip_build = building_is_expensive && can_skip_build; @@ -1282,11 +1282,7 @@ fn is_codegen_cfg_needed(path: &TaskPath, run: &RunConfig<'_>) -> bool { if path.path.to_str().unwrap().contains(CODEGEN_BACKEND_PREFIX) { let mut needs_codegen_backend_config = true; for backend in run.builder.config.codegen_backends(run.target) { - if path - .path - .to_str() - .unwrap() - .ends_with(&(CODEGEN_BACKEND_PREFIX.to_owned() + &backend)) + if path.path.to_str().unwrap().ends_with(&(CODEGEN_BACKEND_PREFIX.to_owned() + backend)) { needs_codegen_backend_config = false; } @@ -1306,7 +1302,7 @@ fn is_codegen_cfg_needed(path: &TaskPath, run: &RunConfig<'_>) -> bool { impl Step for CodegenBackend { type Output = (); const ONLY_HOSTS: bool = true; - // Only the backends specified in the `codegen-backends` entry of `config.toml` are built. + /// Only the backends specified in the `codegen-backends` entry of `config.toml` are built. const DEFAULT: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -1846,7 +1842,7 @@ impl Step for Assemble { extra_features: vec![], }); let tool_exe = exe("llvm-bitcode-linker", target_compiler.host); - builder.copy_link(&src_path, &libdir_bin.join(&tool_exe)); + builder.copy_link(&src_path, &libdir_bin.join(tool_exe)); } // Ensure that `libLLVM.so` ends up in the newly build compiler directory, diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 78176665929b..0eca20901b76 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -107,7 +107,7 @@ impl Step for JsonDocs { builder.top_stage, host, builder, - DocumentationFormat::JSON, + DocumentationFormat::Json, )); let dest = "share/doc/rust/json"; @@ -1131,7 +1131,7 @@ impl Step for Rls { let rls = builder.ensure(tool::Rls { compiler, target, extra_features: Vec::new() }); let mut tarball = Tarball::new(builder, "rls", &target.triple); - tarball.set_overlay(OverlayKind::RLS); + tarball.set_overlay(OverlayKind::Rls); tarball.is_preview(true); tarball.add_file(rls, "bin", 0o755); tarball.add_legal_and_readme_to("share/doc/rls"); @@ -2059,7 +2059,7 @@ fn install_llvm_file( if install_symlink { // For download-ci-llvm, also install the symlink, to match what LLVM does. Using a // symlink is fine here, as this is not a rustup component. - builder.copy_link(&source, &full_dest); + builder.copy_link(source, &full_dest); } else { // Otherwise, replace the symlink with an equivalent linker script. This is used when // projects like miri link against librustc_driver.so. We don't use a symlink, as @@ -2076,7 +2076,7 @@ fn install_llvm_file( } } } else { - builder.install(&source, destination, 0o644); + builder.install(source, destination, 0o644); } } @@ -2121,7 +2121,7 @@ fn maybe_install_llvm( builder.install(&llvm_dylib_path, dst_libdir, 0o644); } !builder.config.dry_run() - } else if let Ok(llvm::LlvmResult { llvm_config, .. }) = + } else if let llvm::LlvmBuildStatus::AlreadyBuilt(llvm::LlvmResult { llvm_config, .. }) = llvm::prebuilt_llvm_config(builder, target) { let mut cmd = Command::new(llvm_config); @@ -2202,7 +2202,7 @@ impl Step for LlvmTools { builder.ensure(crate::core::build_steps::llvm::Llvm { target }); let mut tarball = Tarball::new(builder, "llvm-tools", &target.triple); - tarball.set_overlay(OverlayKind::LLVM); + tarball.set_overlay(OverlayKind::Llvm); tarball.is_preview(true); if builder.config.llvm_tools_enabled { @@ -2272,9 +2272,9 @@ impl Step for LlvmBitcodeLinker { } } -// Tarball intended for internal consumption to ease rustc/std development. -// -// Should not be considered stable by end users. +/// Tarball intended for internal consumption to ease rustc/std development. +/// +/// Should not be considered stable by end users. #[derive(Clone, Debug, Eq, Hash, PartialEq)] pub struct RustDev { pub target: TargetSelection, @@ -2305,7 +2305,7 @@ impl Step for RustDev { } let mut tarball = Tarball::new(builder, "rust-dev", &target.triple); - tarball.set_overlay(OverlayKind::LLVM); + tarball.set_overlay(OverlayKind::Llvm); // LLVM requires a shared object symlink to exist on some platforms. tarball.permit_symlinks(true); @@ -2356,9 +2356,9 @@ impl Step for RustDev { } } -// Tarball intended for internal consumption to ease rustc/std development. -// -// Should not be considered stable by end users. +/// Tarball intended for internal consumption to ease rustc/std development. +/// +/// Should not be considered stable by end users. #[derive(Clone, Debug, Eq, Hash, PartialEq)] pub struct Bootstrap { pub target: TargetSelection, diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index a22cbeacf016..38c48bd9570d 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -506,7 +506,7 @@ impl Step for SharedAssets { run.never() } - // Generate shared resources used by other pieces of documentation. + /// Generate shared resources used by other pieces of documentation. fn run(self, builder: &Builder<'_>) -> Self::Output { let out = builder.doc_out(self.target); @@ -567,9 +567,9 @@ impl Step for Std { stage: run.builder.top_stage, target: run.target, format: if run.builder.config.cmd.json() { - DocumentationFormat::JSON + DocumentationFormat::Json } else { - DocumentationFormat::HTML + DocumentationFormat::Html }, crates: run.make_run_crates(Alias::Library), }); @@ -583,13 +583,13 @@ impl Step for Std { let stage = self.stage; let target = self.target; let out = match self.format { - DocumentationFormat::HTML => builder.doc_out(target), - DocumentationFormat::JSON => builder.json_doc_out(target), + DocumentationFormat::Html => builder.doc_out(target), + DocumentationFormat::Json => builder.json_doc_out(target), }; t!(fs::create_dir_all(&out)); - if self.format == DocumentationFormat::HTML { + if self.format == DocumentationFormat::Html { builder.ensure(SharedAssets { target: self.target }); } @@ -600,10 +600,10 @@ impl Step for Std { .into_string() .expect("non-utf8 paths are unsupported"); let mut extra_args = match self.format { - DocumentationFormat::HTML => { + DocumentationFormat::Html => { vec!["--markdown-css", "rust.css", "--markdown-no-toc", "--index-page", &index_page] } - DocumentationFormat::JSON => vec!["--output-format", "json"], + DocumentationFormat::Json => vec!["--output-format", "json"], }; if !builder.config.docs_minification { @@ -613,7 +613,7 @@ impl Step for Std { doc_std(builder, self.format, stage, target, &out, &extra_args, &self.crates); // Don't open if the format is json - if let DocumentationFormat::JSON = self.format { + if let DocumentationFormat::Json = self.format { return; } @@ -646,15 +646,15 @@ const STD_PUBLIC_CRATES: [&str; 5] = ["core", "alloc", "std", "proc_macro", "tes #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] pub enum DocumentationFormat { - HTML, - JSON, + Html, + Json, } impl DocumentationFormat { fn as_str(&self) -> &str { match self { - DocumentationFormat::HTML => "HTML", - DocumentationFormat::JSON => "JSON", + DocumentationFormat::Html => "HTML", + DocumentationFormat::Json => "JSON", } } } @@ -678,7 +678,7 @@ fn doc_std( let compiler = builder.compiler(stage, builder.config.build); - let target_doc_dir_name = if format == DocumentationFormat::JSON { "json-doc" } else { "doc" }; + let target_doc_dir_name = if format == DocumentationFormat::Json { "json-doc" } else { "doc" }; let target_dir = builder.stage_out(compiler, Mode::Std).join(target.triple).join(target_doc_dir_name); diff --git a/src/bootstrap/src/core/build_steps/format.rs b/src/bootstrap/src/core/build_steps/format.rs index fc9f9789bd68..9fc65a0a73a1 100644 --- a/src/bootstrap/src/core/build_steps/format.rs +++ b/src/bootstrap/src/core/build_steps/format.rs @@ -225,12 +225,12 @@ pub fn format(build: &Builder<'_>, check: bool, paths: &[PathBuf]) { Some(first) => { let find_shortcut_candidates = |p: &PathBuf| { let mut candidates = Vec::new(); - for candidate in WalkBuilder::new(src.clone()).max_depth(Some(3)).build() { - if let Ok(entry) = candidate { - if let Some(dir_name) = p.file_name() { - if entry.path().is_dir() && entry.file_name() == dir_name { - candidates.push(entry.into_path()); - } + for entry in + WalkBuilder::new(src.clone()).max_depth(Some(3)).build().map_while(Result::ok) + { + if let Some(dir_name) = p.file_name() { + if entry.path().is_dir() && entry.file_name() == dir_name { + candidates.push(entry.into_path()); } } } diff --git a/src/bootstrap/src/core/build_steps/install.rs b/src/bootstrap/src/core/build_steps/install.rs index 767c0f693649..6a75f35c93a6 100644 --- a/src/bootstrap/src/core/build_steps/install.rs +++ b/src/bootstrap/src/core/build_steps/install.rs @@ -20,8 +20,8 @@ const SHELL: &str = "bash"; #[cfg(not(target_os = "illumos"))] const SHELL: &str = "sh"; -// We have to run a few shell scripts, which choke quite a bit on both `\` -// characters and on `C:\` paths, so normalize both of them away. +/// We have to run a few shell scripts, which choke quite a bit on both `\` +/// characters and on `C:\` paths, so normalize both of them away. fn sanitize_sh(path: &Path) -> String { let path = path.to_str().unwrap().replace('\\', "/"); return change_drive(unc_to_lfs(&path)).unwrap_or(path); @@ -130,7 +130,7 @@ fn prepare_dir(destdir_env: &Option, mut path: PathBuf) -> String { // https://www.gnu.org/prep/standards/html_node/DESTDIR.html if let Some(destdir) = destdir_env { let without_destdir = path.clone(); - path = destdir.clone(); + path.clone_from(destdir); // Custom .join() which ignores disk roots. for part in without_destdir.components() { if let Component::Normal(s) = part { diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index 7cb15fe5590c..d4473e240399 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -42,14 +42,28 @@ pub struct Meta { root: String, } -// Linker flags to pass to LLVM's CMake invocation. +pub enum LlvmBuildStatus { + AlreadyBuilt(LlvmResult), + ShouldBuild(Meta), +} + +impl LlvmBuildStatus { + pub fn should_build(&self) -> bool { + match self { + LlvmBuildStatus::AlreadyBuilt(_) => false, + LlvmBuildStatus::ShouldBuild(_) => true, + } + } +} + +/// Linker flags to pass to LLVM's CMake invocation. #[derive(Debug, Clone, Default)] struct LdFlags { - // CMAKE_EXE_LINKER_FLAGS + /// CMAKE_EXE_LINKER_FLAGS exe: OsString, - // CMAKE_SHARED_LINKER_FLAGS + /// CMAKE_SHARED_LINKER_FLAGS shared: OsString, - // CMAKE_MODULE_LINKER_FLAGS + /// CMAKE_MODULE_LINKER_FLAGS module: OsString, } @@ -72,10 +86,7 @@ impl LdFlags { /// /// This will return the llvm-config if it can get it (but it will not build it /// if not). -pub fn prebuilt_llvm_config( - builder: &Builder<'_>, - target: TargetSelection, -) -> Result { +pub fn prebuilt_llvm_config(builder: &Builder<'_>, target: TargetSelection) -> LlvmBuildStatus { // If we have llvm submodule initialized already, sync it. builder.update_existing_submodule(&Path::new("src").join("llvm-project")); @@ -93,7 +104,7 @@ pub fn prebuilt_llvm_config( llvm_cmake_dir.push("lib"); llvm_cmake_dir.push("cmake"); llvm_cmake_dir.push("llvm"); - return Ok(LlvmResult { llvm_config, llvm_cmake_dir }); + return LlvmBuildStatus::AlreadyBuilt(LlvmResult { llvm_config, llvm_cmake_dir }); } } @@ -131,10 +142,10 @@ pub fn prebuilt_llvm_config( stamp.path.display() )); } - return Ok(res); + return LlvmBuildStatus::AlreadyBuilt(res); } - Err(Meta { stamp, res, out_dir, root: root.into() }) + LlvmBuildStatus::ShouldBuild(Meta { stamp, res, out_dir, root: root.into() }) } /// This retrieves the LLVM sha we *want* to use, according to git history. @@ -282,8 +293,8 @@ impl Step for Llvm { // If LLVM has already been built or been downloaded through download-ci-llvm, we avoid building it again. let Meta { stamp, res, out_dir, root } = match prebuilt_llvm_config(builder, target) { - Ok(p) => return p, - Err(m) => m, + LlvmBuildStatus::AlreadyBuilt(p) => return p, + LlvmBuildStatus::ShouldBuild(m) => m, }; if builder.llvm_link_shared() && target.is_windows() { diff --git a/src/bootstrap/src/core/build_steps/setup.rs b/src/bootstrap/src/core/build_steps/setup.rs index 7bc68b5aec11..c0683cdda1e5 100644 --- a/src/bootstrap/src/core/build_steps/setup.rs +++ b/src/bootstrap/src/core/build_steps/setup.rs @@ -93,10 +93,9 @@ impl FromStr for Profile { Ok(Profile::Tools) } "none" => Ok(Profile::None), - "llvm" | "codegen" => Err(format!( - "the \"llvm\" and \"codegen\" profiles have been removed,\ + "llvm" | "codegen" => Err("the \"llvm\" and \"codegen\" profiles have been removed,\ use \"compiler\" instead which has the same functionality" - )), + .to_string()), _ => Err(format!("unknown profile: '{s}'")), } } @@ -474,10 +473,10 @@ impl Step for Hook { // install a git hook to automatically run tidy, if they want fn install_git_hook_maybe(config: &Config) -> io::Result<()> { - let git = t!(config.git().args(["rev-parse", "--git-common-dir"]).output().map(|output| { + let git = config.git().args(["rev-parse", "--git-common-dir"]).output().map(|output| { assert!(output.status.success(), "failed to run `git`"); PathBuf::from(t!(String::from_utf8(output.stdout)).trim()) - })); + })?; let hooks_dir = git.join("hooks"); let dst = hooks_dir.join("pre-push"); if dst.exists() { diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 1980980b6d05..ca10a128b981 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -923,7 +923,7 @@ impl Step for RustdocJSStd { builder.top_stage, self.target, builder, - DocumentationFormat::HTML, + DocumentationFormat::Html, )); let _guard = builder.msg( Kind::Test, @@ -1389,10 +1389,9 @@ impl Step for RunMakeSupport { builder.run(&mut cargo); let lib_name = "librun_make_support.rlib"; - let lib = builder.tools_dir(self.compiler).join(&lib_name); + let lib = builder.tools_dir(self.compiler).join(lib_name); - let cargo_out = - builder.cargo_out(self.compiler, Mode::ToolStd, self.target).join(&lib_name); + let cargo_out = builder.cargo_out(self.compiler, Mode::ToolStd, self.target).join(lib_name); builder.copy_link(&cargo_out, &lib); lib } @@ -1434,8 +1433,8 @@ host_test!(RustdocJson { path: "tests/rustdoc-json", mode: "rustdoc-json", suite host_test!(Pretty { path: "tests/pretty", mode: "pretty", suite: "pretty" }); -// Special-handling is needed for `run-make`, so don't use `default_test` for defining `RunMake` -// tests. +/// Special-handling is needed for `run-make`, so don't use `default_test` for defining `RunMake` +/// tests. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct RunMake { pub compiler: Compiler, @@ -1528,10 +1527,10 @@ impl Coverage { impl Step for Coverage { type Output = (); - // We rely on the individual CoverageMap/CoverageRun steps to run themselves. + /// We rely on the individual CoverageMap/CoverageRun steps to run themselves. const DEFAULT: bool = false; - // When manually invoked, try to run as much as possible. - // Compiletest will automatically skip the "coverage-run" tests if necessary. + /// When manually invoked, try to run as much as possible. + /// Compiletest will automatically skip the "coverage-run" tests if necessary. const ONLY_HOSTS: bool = false; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -2483,6 +2482,7 @@ impl Step for CrateLibrustc { /// Given a `cargo test` subcommand, add the appropriate flags and run it. /// /// Returns whether the test succeeded. +#[allow(clippy::too_many_arguments)] // FIXME: reduce the number of args and remove this. fn run_cargo_test<'a>( cargo: impl Into, libtest_args: &[&str], diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index 45b1d5a05f35..994f0bef0dc7 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -135,6 +135,7 @@ impl Step for ToolBuild { } } +#[allow(clippy::too_many_arguments)] // FIXME: reduce the number of args and remove this. pub fn prepare_tool_cargo( builder: &Builder<'_>, compiler: Compiler, @@ -545,7 +546,7 @@ impl Step for Cargo { } fn run(self, builder: &Builder<'_>) -> PathBuf { - let cargo_bin_path = builder.ensure(ToolBuild { + builder.ensure(ToolBuild { compiler: self.compiler, target: self.target, tool: "cargo", @@ -554,8 +555,7 @@ impl Step for Cargo { source_type: SourceType::Submodule, extra_features: Vec::new(), allow_features: "", - }); - cargo_bin_path + }) } } @@ -573,7 +573,7 @@ impl Step for LldWrapper { } fn run(self, builder: &Builder<'_>) -> PathBuf { - let src_exe = builder.ensure(ToolBuild { + builder.ensure(ToolBuild { compiler: self.compiler, target: self.target, tool: "lld-wrapper", @@ -582,9 +582,7 @@ impl Step for LldWrapper { source_type: SourceType::InTree, extra_features: Vec::new(), allow_features: "", - }); - - src_exe + }) } } diff --git a/src/bootstrap/src/core/build_steps/toolstate.rs b/src/bootstrap/src/core/build_steps/toolstate.rs index deb782cad0ce..f88c1b3ee822 100644 --- a/src/bootstrap/src/core/build_steps/toolstate.rs +++ b/src/bootstrap/src/core/build_steps/toolstate.rs @@ -245,8 +245,12 @@ impl Builder<'_> { // Ensure the parent directory always exists t!(std::fs::create_dir_all(parent)); } - let mut file = - t!(fs::OpenOptions::new().create(true).write(true).read(true).open(path)); + let mut file = t!(fs::OpenOptions::new() + .create(true) + .truncate(false) + .write(true) + .read(true) + .open(path)); serde_json::from_reader(&mut file).unwrap_or_default() } else { @@ -278,8 +282,12 @@ impl Builder<'_> { // Ensure the parent directory always exists t!(std::fs::create_dir_all(parent)); } - let mut file = - t!(fs::OpenOptions::new().create(true).read(true).write(true).open(path)); + let mut file = t!(fs::OpenOptions::new() + .create(true) + .truncate(false) + .read(true) + .write(true) + .open(path)); let mut current_toolstates: HashMap, ToolState> = serde_json::from_reader(&mut file).unwrap_or_default(); diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs index 27ab6eac03a5..adef9ebd0e30 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder.rs @@ -132,8 +132,7 @@ impl RunConfig<'_> { Alias::Compiler => self.builder.in_tree_crates("rustc-main", Some(self.target)), }; - let crate_names = crates.into_iter().map(|krate| krate.name.to_string()).collect(); - crate_names + crates.into_iter().map(|krate| krate.name.to_string()).collect() } } @@ -323,16 +322,13 @@ const PATH_REMAP: &[(&str, &[&str])] = &[ fn remap_paths(paths: &mut Vec<&Path>) { let mut remove = vec![]; let mut add = vec![]; - for (i, path) in paths - .iter() - .enumerate() - .filter_map(|(i, path)| if let Some(s) = path.to_str() { Some((i, s)) } else { None }) + for (i, path) in paths.iter().enumerate().filter_map(|(i, path)| path.to_str().map(|s| (i, s))) { for &(search, replace) in PATH_REMAP { // Remove leading and trailing slashes so `tests/` and `tests` are equivalent if path.trim_matches(std::path::is_separator) == search { remove.push(i); - add.extend(replace.into_iter().map(Path::new)); + add.extend(replace.iter().map(Path::new)); break; } } @@ -1318,7 +1314,7 @@ impl<'a> Builder<'a> { } else if let Some(subcmd) = cmd.strip_prefix("miri") { // Command must be "miri-X". let subcmd = subcmd - .strip_prefix("-") + .strip_prefix('-') .unwrap_or_else(|| panic!("expected `miri-$subcommand`, but got {}", cmd)); cargo = self.cargo_miri_cmd(compiler); cargo.arg("miri").arg(subcmd); @@ -1438,7 +1434,7 @@ impl<'a> Builder<'a> { // rustc_llvm. But if LLVM is stale, that'll be a tiny amount // of work comparatively, and we'd likely need to rebuild it anyway, // so that's okay. - if crate::core::build_steps::llvm::prebuilt_llvm_config(self, target).is_err() { + if crate::core::build_steps::llvm::prebuilt_llvm_config(self, target).should_build() { cargo.env("RUST_CHECK", "1"); } } @@ -1540,7 +1536,7 @@ impl<'a> Builder<'a> { // `rustflags` without `cargo` making it required. rustflags.arg("-Zunstable-options"); for (restricted_mode, name, values) in EXTRA_CHECK_CFGS { - if *restricted_mode == None || *restricted_mode == Some(mode) { + if restricted_mode.is_none() || *restricted_mode == Some(mode) { rustflags.arg(&check_cfg_arg(name, *values)); } } @@ -2216,22 +2212,18 @@ impl<'a> Builder<'a> { let file = File::open(src.join(".gitmodules")).unwrap(); let mut submodules_paths = vec![]; - for line in BufReader::new(file).lines() { - if let Ok(line) = line { - let line = line.trim(); - - if line.starts_with("path") { - let actual_path = - line.split(' ').last().expect("Couldn't get value of path"); - submodules_paths.push(actual_path.to_owned()); - } + for line in BufReader::new(file).lines().map_while(Result::ok) { + let line = line.trim(); + if line.starts_with("path") { + let actual_path = line.split(' ').last().expect("Couldn't get value of path"); + submodules_paths.push(actual_path.to_owned()); } } submodules_paths }; - &SUBMODULES_PATHS.get_or_init(|| init_submodules_paths(&self.src)) + SUBMODULES_PATHS.get_or_init(|| init_submodules_paths(&self.src)) } /// Ensure that a given step is built *only if it's supposed to be built by default*, returning diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 178df633cec6..caec46366dde 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -75,7 +75,7 @@ macro_rules! doc_std { $stage, TargetSelection::from_user(stringify!($target)), &builder, - DocumentationFormat::HTML, + DocumentationFormat::Html, ) }}; } diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 2bb899a064f3..2acce6273598 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -1217,10 +1217,7 @@ impl Config { .and_then(|table: toml::Value| TomlConfig::deserialize(table)) .unwrap_or_else(|err| { if let Ok(Some(changes)) = toml::from_str(&contents) - .and_then(|table: toml::Value| ChangeIdWrapper::deserialize(table)) - .and_then(|change_id| { - Ok(change_id.inner.map(|id| crate::find_recent_config_change_ids(id))) - }) + .and_then(|table: toml::Value| ChangeIdWrapper::deserialize(table)).map(|change_id| change_id.inner.map(crate::find_recent_config_change_ids)) { if !changes.is_empty() { println!( @@ -1799,17 +1796,17 @@ impl Config { if let Some(v) = link_shared { config.llvm_link_shared.set(Some(v)); } - config.llvm_targets = targets.clone(); - config.llvm_experimental_targets = experimental_targets.clone(); + config.llvm_targets.clone_from(&targets); + config.llvm_experimental_targets.clone_from(&experimental_targets); config.llvm_link_jobs = link_jobs; - config.llvm_version_suffix = version_suffix.clone(); - config.llvm_clang_cl = clang_cl.clone(); + config.llvm_version_suffix.clone_from(&version_suffix); + config.llvm_clang_cl.clone_from(&clang_cl); - config.llvm_cflags = cflags.clone(); - config.llvm_cxxflags = cxxflags.clone(); - config.llvm_ldflags = ldflags.clone(); + config.llvm_cflags.clone_from(&cflags); + config.llvm_cxxflags.clone_from(&cxxflags); + config.llvm_ldflags.clone_from(&ldflags); set(&mut config.llvm_use_libcxx, use_libcxx); - config.llvm_use_linker = use_linker.clone(); + config.llvm_use_linker.clone_from(&use_linker); config.llvm_allow_old_toolchain = allow_old_toolchain.unwrap_or(false); config.llvm_polly = polly.unwrap_or(false); config.llvm_clang = clang.unwrap_or(false); diff --git a/src/bootstrap/src/core/config/mod.rs b/src/bootstrap/src/core/config/mod.rs index 99412848abbb..23556e8bc5d3 100644 --- a/src/bootstrap/src/core/config/mod.rs +++ b/src/bootstrap/src/core/config/mod.rs @@ -1,4 +1,5 @@ -pub(crate) mod config; +#[allow(clippy::module_inception)] +mod config; pub(crate) mod flags; #[cfg(test)] mod tests; diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 0b6d987f87de..663a2d8e17cb 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -76,6 +76,7 @@ const LLD_FILE_NAMES: &[&str] = &["ld.lld", "ld64.lld", "lld-link", "wasm-ld"]; /// Extra --check-cfg to add when building /// (Mode restriction, config name, config values (if any)) +#[allow(clippy::type_complexity)] // It's fine for hard-coded list and type is explained above. const EXTRA_CHECK_CFGS: &[(Option, &str, Option<&[&'static str]>)] = &[ (None, "bootstrap", None), (Some(Mode::Rustc), "parallel_compiler", None), @@ -1625,10 +1626,7 @@ impl Build { /// Returns `true` if unstable features should be enabled for the compiler /// we're building. fn unstable_features(&self) -> bool { - match &self.config.channel[..] { - "stable" | "beta" => false, - "nightly" | _ => true, - } + !matches!(&self.config.channel[..], "stable" | "beta") } /// Returns a Vec of all the dependencies of the given root crate, diff --git a/src/bootstrap/src/utils/cc_detect.rs b/src/bootstrap/src/utils/cc_detect.rs index 7e59b7f6f57c..540b8671333a 100644 --- a/src/bootstrap/src/utils/cc_detect.rs +++ b/src/bootstrap/src/utils/cc_detect.rs @@ -41,9 +41,7 @@ fn cc2ar(cc: &Path, target: TargetSelection) -> Option { Some(PathBuf::from(ar)) } else if target.is_msvc() { None - } else if target.contains("musl") { - Some(PathBuf::from("ar")) - } else if target.contains("openbsd") { + } else if target.contains("musl") || target.contains("openbsd") { Some(PathBuf::from("ar")) } else if target.contains("vxworks") { Some(PathBuf::from("wr-ar")) diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs index a40ee1890018..928c9aa4dff4 100644 --- a/src/bootstrap/src/utils/helpers.rs +++ b/src/bootstrap/src/utils/helpers.rs @@ -549,7 +549,12 @@ pub fn hex_encode(input: T) -> String where T: AsRef<[u8]>, { - input.as_ref().iter().map(|x| format!("{:02x}", x)).collect() + use std::fmt::Write; + + input.as_ref().iter().fold(String::with_capacity(input.as_ref().len() * 2), |mut acc, &byte| { + write!(&mut acc, "{:02x}", byte).expect("Failed to write byte to the hex String."); + acc + }) } /// Create a `--check-cfg` argument invocation for a given name diff --git a/src/bootstrap/src/utils/job.rs b/src/bootstrap/src/utils/job.rs index c5c718a892f7..d7d20e57bd13 100644 --- a/src/bootstrap/src/utils/job.rs +++ b/src/bootstrap/src/utils/job.rs @@ -11,37 +11,35 @@ pub unsafe fn setup(build: &mut crate::Build) { } } +/// Job management on Windows for bootstrapping +/// +/// Most of the time when you're running a build system (e.g., make) you expect +/// Ctrl-C or abnormal termination to actually terminate the entire tree of +/// process in play, not just the one at the top. This currently works "by +/// default" on Unix platforms because Ctrl-C actually sends a signal to the +/// *process group* rather than the parent process, so everything will get torn +/// down. On Windows, however, this does not happen and Ctrl-C just kills the +/// parent process. +/// +/// To achieve the same semantics on Windows we use Job Objects to ensure that +/// all processes die at the same time. Job objects have a mode of operation +/// where when all handles to the object are closed it causes all child +/// processes associated with the object to be terminated immediately. +/// Conveniently whenever a process in the job object spawns a new process the +/// child will be associated with the job object as well. This means if we add +/// ourselves to the job object we create then everything will get torn down! +/// +/// Unfortunately most of the time the build system is actually called from a +/// python wrapper (which manages things like building the build system) so this +/// all doesn't quite cut it so far. To go the last mile we duplicate the job +/// object handle into our parent process (a python process probably) and then +/// close our own handle. This means that the only handle to the job object +/// resides in the parent python process, so when python dies the whole build +/// system dies (as one would probably expect!). +/// +/// Note that this is a Windows specific module as none of this logic is required on Unix. #[cfg(windows)] mod for_windows { - //! Job management on Windows for bootstrapping - //! - //! Most of the time when you're running a build system (e.g., make) you expect - //! Ctrl-C or abnormal termination to actually terminate the entire tree of - //! process in play, not just the one at the top. This currently works "by - //! default" on Unix platforms because Ctrl-C actually sends a signal to the - //! *process group* rather than the parent process, so everything will get torn - //! down. On Windows, however, this does not happen and Ctrl-C just kills the - //! parent process. - //! - //! To achieve the same semantics on Windows we use Job Objects to ensure that - //! all processes die at the same time. Job objects have a mode of operation - //! where when all handles to the object are closed it causes all child - //! processes associated with the object to be terminated immediately. - //! Conveniently whenever a process in the job object spawns a new process the - //! child will be associated with the job object as well. This means if we add - //! ourselves to the job object we create then everything will get torn down! - //! - //! Unfortunately most of the time the build system is actually called from a - //! python wrapper (which manages things like building the build system) so this - //! all doesn't quite cut it so far. To go the last mile we duplicate the job - //! object handle into our parent process (a python process probably) and then - //! close our own handle. This means that the only handle to the job object - //! resides in the parent python process, so when python dies the whole build - //! system dies (as one would probably expect!). - //! - //! Note that this module has a #[cfg(windows)] above it as none of this logic - //! is required on Unix. - use crate::Build; use std::env; use std::ffi::c_void; diff --git a/src/bootstrap/src/utils/render_tests.rs b/src/bootstrap/src/utils/render_tests.rs index 16e0c2ac1851..462b76d03bd2 100644 --- a/src/bootstrap/src/utils/render_tests.rs +++ b/src/bootstrap/src/utils/render_tests.rs @@ -239,7 +239,7 @@ impl<'a> Renderer<'a> { suite.filtered_out, time = match suite.exec_time { Some(t) => format!("; finished in {:.2?}", Duration::from_secs_f64(t)), - None => format!(""), + None => String::new(), } ); } diff --git a/src/bootstrap/src/utils/tarball.rs b/src/bootstrap/src/utils/tarball.rs index cc86e3bab0c0..2a950e669b92 100644 --- a/src/bootstrap/src/utils/tarball.rs +++ b/src/bootstrap/src/utils/tarball.rs @@ -18,13 +18,13 @@ use crate::utils::helpers::t; #[derive(Copy, Clone)] pub(crate) enum OverlayKind { Rust, - LLVM, + Llvm, Cargo, Clippy, Miri, Rustfmt, RustDemangler, - RLS, + Rls, RustAnalyzer, RustcCodegenCranelift, LlvmBitcodeLinker, @@ -34,7 +34,7 @@ impl OverlayKind { fn legal_and_readme(&self) -> &[&str] { match self { OverlayKind::Rust => &["COPYRIGHT", "LICENSE-APACHE", "LICENSE-MIT", "README.md"], - OverlayKind::LLVM => { + OverlayKind::Llvm => { &["src/llvm-project/llvm/LICENSE.TXT", "src/llvm-project/llvm/README.txt"] } OverlayKind::Cargo => &[ @@ -61,7 +61,7 @@ impl OverlayKind { OverlayKind::RustDemangler => { &["src/tools/rust-demangler/README.md", "LICENSE-APACHE", "LICENSE-MIT"] } - OverlayKind::RLS => &["src/tools/rls/README.md", "LICENSE-APACHE", "LICENSE-MIT"], + OverlayKind::Rls => &["src/tools/rls/README.md", "LICENSE-APACHE", "LICENSE-MIT"], OverlayKind::RustAnalyzer => &[ "src/tools/rust-analyzer/README.md", "src/tools/rust-analyzer/LICENSE-APACHE", @@ -84,7 +84,7 @@ impl OverlayKind { fn version(&self, builder: &Builder<'_>) -> String { match self { OverlayKind::Rust => builder.rust_version(), - OverlayKind::LLVM => builder.rust_version(), + OverlayKind::Llvm => builder.rust_version(), OverlayKind::RustDemangler => builder.release_num("rust-demangler"), OverlayKind::Cargo => { builder.cargo_info.version(builder, &builder.release_num("cargo")) @@ -96,7 +96,7 @@ impl OverlayKind { OverlayKind::Rustfmt => { builder.rustfmt_info.version(builder, &builder.release_num("rustfmt")) } - OverlayKind::RLS => builder.release(&builder.release_num("rls")), + OverlayKind::Rls => builder.release(&builder.release_num("rls")), OverlayKind::RustAnalyzer => builder .rust_analyzer_info .version(builder, &builder.release_num("rust-analyzer/crates/rust-analyzer")), @@ -357,7 +357,7 @@ impl<'a> Tarball<'a> { &self.builder.config.dist_compression_profile }; - cmd.args(&["--compression-profile", compression_profile]); + cmd.args(["--compression-profile", compression_profile]); self.builder.run(&mut cmd); // Ensure there are no symbolic links in the tarball. In particular, diff --git a/src/ci/docker/host-x86_64/dist-various-1/Dockerfile b/src/ci/docker/host-x86_64/dist-various-1/Dockerfile index 5c459e5cd180..00552db4b014 100644 --- a/src/ci/docker/host-x86_64/dist-various-1/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-various-1/Dockerfile @@ -55,6 +55,9 @@ RUN ./install-riscv64-none-elf.sh COPY host-x86_64/dist-various-1/install-riscv32-none-elf.sh /build RUN ./install-riscv32-none-elf.sh +COPY host-x86_64/dist-various-1/install-llvm-mingw.sh /build +RUN ./install-llvm-mingw.sh + # Suppress some warnings in the openwrt toolchains we downloaded ENV STAGING_DIR=/tmp @@ -111,6 +114,9 @@ ENV TARGETS=$TARGETS,armv7r-none-eabi ENV TARGETS=$TARGETS,armv7r-none-eabihf ENV TARGETS=$TARGETS,thumbv7neon-unknown-linux-gnueabihf ENV TARGETS=$TARGETS,armv7a-none-eabi +ENV TARGETS=$TARGETS,aarch64-pc-windows-gnullvm +ENV TARGETS=$TARGETS,i686-pc-windows-gnullvm +ENV TARGETS=$TARGETS,x86_64-pc-windows-gnullvm ENV CFLAGS_armv5te_unknown_linux_musleabi="-march=armv5te -marm -mfloat-abi=soft" \ CFLAGS_arm_unknown_linux_musleabi="-march=armv6 -marm" \ @@ -142,7 +148,10 @@ ENV CFLAGS_armv5te_unknown_linux_musleabi="-march=armv5te -marm -mfloat-abi=soft CC_riscv64imac_unknown_none_elf=riscv64-unknown-elf-gcc \ CFLAGS_riscv64imac_unknown_none_elf=-march=rv64imac -mabi=lp64 \ CC_riscv64gc_unknown_none_elf=riscv64-unknown-elf-gcc \ - CFLAGS_riscv64gc_unknown_none_elf=-march=rv64gc -mabi=lp64 + CFLAGS_riscv64gc_unknown_none_elf=-march=rv64gc -mabi=lp64 \ + CC_aarch64_pc_windows_gnullvm=aarch64-w64-mingw32-clang \ + CC_i686_pc_windows_gnullvm=i686-w64-mingw32-clang \ + CC_x86_64_pc_windows_gnullvm=x86_64-w64-mingw32-clang ENV RUST_CONFIGURE_ARGS \ --musl-root-armv5te=/musl-armv5te \ diff --git a/src/ci/docker/host-x86_64/dist-various-1/install-llvm-mingw.sh b/src/ci/docker/host-x86_64/dist-various-1/install-llvm-mingw.sh new file mode 100755 index 000000000000..95471895fe77 --- /dev/null +++ b/src/ci/docker/host-x86_64/dist-various-1/install-llvm-mingw.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +set -ex + +release_date=20240404 +archive=llvm-mingw-${release_date}-ucrt-ubuntu-20.04-x86_64.tar.xz +curl -L https://github.com/mstorsjo/llvm-mingw/releases/download/${release_date}/${archive} | \ +tar --extract --lzma --strip 1 --directory /usr/local diff --git a/src/ci/docker/host-x86_64/mingw-check/Dockerfile b/src/ci/docker/host-x86_64/mingw-check/Dockerfile index ae8dfadec738..0eff7ca5962b 100644 --- a/src/ci/docker/host-x86_64/mingw-check/Dockerfile +++ b/src/ci/docker/host-x86_64/mingw-check/Dockerfile @@ -21,10 +21,12 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ mingw-w64 \ && rm -rf /var/lib/apt/lists/* -RUN curl -sL https://nodejs.org/dist/v16.9.0/node-v16.9.0-linux-x64.tar.xz | tar -xJ -ENV PATH="/node-v16.9.0-linux-x64/bin:${PATH}" ENV RUST_CONFIGURE_ARGS="--set rust.validate-mir-opts=3" +COPY scripts/nodejs.sh /scripts/ +RUN sh /scripts/nodejs.sh /node +ENV PATH="/node/bin:${PATH}" + # Install es-check # Pin its version to prevent unrelated CI failures due to future es-check versions. RUN npm install es-check@6.1.1 eslint@8.6.0 -g @@ -46,6 +48,7 @@ ENV SCRIPT python3 ../x.py --stage 2 test src/tools/expand-yaml-anchors && \ # We also skip the x86_64-unknown-linux-gnu target as it is well-tested by other jobs. python3 ../x.py check --stage 0 --set build.optimized-compiler-builtins=false core alloc std --target=aarch64-unknown-linux-gnu,i686-pc-windows-msvc,i686-unknown-linux-gnu,x86_64-apple-darwin,x86_64-pc-windows-gnu,x86_64-pc-windows-msvc && \ python3 ../x.py check --target=i686-pc-windows-gnu --host=i686-pc-windows-gnu && \ + python3 ../x.py clippy bootstrap -Dwarnings && \ python3 ../x.py clippy compiler library -Aclippy::all -Dclippy::correctness && \ python3 ../x.py build --stage 0 src/tools/build-manifest && \ python3 ../x.py test --stage 0 src/tools/compiletest && \ diff --git a/src/ci/docker/host-x86_64/test-various/Dockerfile b/src/ci/docker/host-x86_64/test-various/Dockerfile index 8875e226fd86..f874b2ed475f 100644 --- a/src/ci/docker/host-x86_64/test-various/Dockerfile +++ b/src/ci/docker/host-x86_64/test-various/Dockerfile @@ -28,8 +28,9 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ qemu-system-x86 \ && rm -rf /var/lib/apt/lists/* -RUN curl -sL https://nodejs.org/dist/v18.12.0/node-v18.12.0-linux-x64.tar.xz | \ - tar -xJ +COPY scripts/nodejs.sh /scripts/ +RUN sh /scripts/nodejs.sh /node +ENV PATH="/node/bin:${PATH}" WORKDIR /build/ COPY scripts/musl-toolchain.sh /build/ @@ -45,7 +46,6 @@ ENV WASI_SDK_PATH=/wasi-sdk-22.0 ENV RUST_CONFIGURE_ARGS \ --musl-root-x86_64=/usr/local/x86_64-linux-musl \ - --set build.nodejs=/node-v18.12.0-linux-x64/bin/node \ --set rust.lld # Some run-make tests have assertions about code size, and enabling debug diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile index a3e8f6176a3b..6a09ab3065ff 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile @@ -72,9 +72,9 @@ ENV GCC_EXEC_PREFIX="/usr/lib/gcc/" COPY host-x86_64/x86_64-gnu-tools/checktools.sh /tmp/ -RUN curl -sL https://nodejs.org/dist/v14.20.0/node-v14.20.0-linux-x64.tar.xz | tar -xJ -ENV NODE_FOLDER=/node-v14.20.0-linux-x64/bin -ENV PATH="$NODE_FOLDER:${PATH}" +COPY scripts/nodejs.sh /scripts/ +RUN sh /scripts/nodejs.sh /node +ENV PATH="/node/bin:${PATH}" COPY host-x86_64/x86_64-gnu-tools/browser-ui-test.version /tmp/ diff --git a/src/ci/docker/scripts/nodejs.sh b/src/ci/docker/scripts/nodejs.sh new file mode 100644 index 000000000000..ae28da0bdc7f --- /dev/null +++ b/src/ci/docker/scripts/nodejs.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +set -ex + +NODEJS_VERSION=v20.12.2 +INSTALL_PATH=${1:-/node} + +url="https://nodejs.org/dist/${NODEJS_VERSION}/node-${NODEJS_VERSION}-linux-x64.tar.xz" +curl -sL "$url" | tar -xJ +mv node-${NODEJS_VERSION}-linux-x64 "${INSTALL_PATH}" diff --git a/src/doc/book b/src/doc/book index 3131aa4642c6..d207d894cc5e 160000 --- a/src/doc/book +++ b/src/doc/book @@ -1 +1 @@ -Subproject commit 3131aa4642c627a24f523c82566b94a7d920f68c +Subproject commit d207d894cc5e1d496ab99beeacd1a420e5d4d238 diff --git a/src/doc/edition-guide b/src/doc/edition-guide index eb3eb80e106d..0c68e90acaae 160000 --- a/src/doc/edition-guide +++ b/src/doc/edition-guide @@ -1 +1 @@ -Subproject commit eb3eb80e106d03250c1fb7c5666b1c8c59672862 +Subproject commit 0c68e90acaae5a611f8f5098a3c2980de9845ab2 diff --git a/src/doc/reference b/src/doc/reference index 55694913b130..5854fcc28655 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit 55694913b1301cc809f9bf4a1ad1b3d6920efbd9 +Subproject commit 5854fcc286557ad3ab34d325073d11d8118096b6 diff --git a/src/doc/rustc-dev-guide b/src/doc/rustc-dev-guide index b77a34bd4639..07425fed36b0 160000 --- a/src/doc/rustc-dev-guide +++ b/src/doc/rustc-dev-guide @@ -1 +1 @@ -Subproject commit b77a34bd46399687b4ce6a17198e9f316c988794 +Subproject commit 07425fed36b00e60341c5e29e28d37d40cbd4451 diff --git a/src/doc/rustc/src/codegen-options/index.md b/src/doc/rustc/src/codegen-options/index.md index 35bf2c27ff1b..673ff12b5ceb 100644 --- a/src/doc/rustc/src/codegen-options/index.md +++ b/src/doc/rustc/src/codegen-options/index.md @@ -568,22 +568,23 @@ data from binaries during linking. Supported values for this option are: -- `none` - debuginfo and symbols (if they exist) are copied to the produced - binary or separate files depending on the target (e.g. `.pdb` files in case - of MSVC). +- `none` - debuginfo and symbols are not modified. - `debuginfo` - debuginfo sections and debuginfo symbols from the symbol table - section are stripped at link time and are not copied to the produced binary - or separate files. This should leave backtraces mostly-intact but may make - using a debugger like gdb or lldb ineffectual. -- `symbols` - same as `debuginfo`, but the rest of the symbol table section is stripped as well, - depending on platform support. On platforms which depend on this symbol table for backtraces, - profiling, and similar, this can affect them so negatively as to make the trace incomprehensible. - Programs which may be combined with others, such as CLI pipelines and developer tooling, - or even anything which wants crash-reporting, should usually avoid `-Cstrip=symbols`. + section are stripped at link time and are not copied to the produced binary. + This should leave backtraces mostly-intact but may make using a debugger like + gdb or lldb ineffectual. Prior to 1.79, this unintentionally disabled the + generation of `*.pdb` files on MSVC, resulting in the absence of symbols. +- `symbols` - same as `debuginfo`, but the rest of the symbol table section is + stripped as well, depending on platform support. On platforms which depend on + this symbol table for backtraces, profiling, and similar, this can affect + them so negatively as to make the trace incomprehensible. Programs which may + be combined with others, such as CLI pipelines and developer tooling, or even + anything which wants crash-reporting, should usually avoid `-Cstrip=symbols`. -Note that, at any level, removing debuginfo only necessarily impacts "friendly" introspection. -`-Cstrip` cannot be relied on as a meaningful security or obfuscation measure, as disassemblers -and decompilers can extract considerable information even in the absence of symbols. +Note that, at any level, removing debuginfo only necessarily impacts "friendly" +introspection. `-Cstrip` cannot be relied on as a meaningful security or +obfuscation measure, as disassemblers and decompilers can extract considerable +information even in the absence of symbols. ## symbol-mangling-version diff --git a/src/doc/rustc/src/instrument-coverage.md b/src/doc/rustc/src/instrument-coverage.md index 185a3ba5dbd4..bbd81e7437c6 100644 --- a/src/doc/rustc/src/instrument-coverage.md +++ b/src/doc/rustc/src/instrument-coverage.md @@ -351,8 +351,8 @@ $ llvm-cov report \ This unstable option provides finer control over some aspects of coverage instrumentation. Pass one or more of the following values, separated by commas. -- `branch` or `no-branch` - - Enables or disables branch coverage instrumentation. +- Either `no-branch`, `branch` or `mcdc` + - `branch` enables branch coverage instrumentation and `mcdc` further enables modified condition/decision coverage instrumentation. `no-branch` disables branch coverage instrumentation, which is same as do not pass `branch` or `mcdc`. ## Other references diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index f7836f18cf9d..2578b71a158b 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -138,6 +138,7 @@ target | std | notes `aarch64-fuchsia` | ✓ | Alias for `aarch64-unknown-fuchsia` [`aarch64-unknown-fuchsia`](platform-support/fuchsia.md) | ✓ | ARM64 Fuchsia [`aarch64-linux-android`](platform-support/android.md) | ✓ | ARM64 Android +[`aarch64-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | ARM64 MinGW (Windows 10+), LLVM ABI [`aarch64-unknown-linux-ohos`](platform-support/openharmony.md) | ✓ | ARM64 OpenHarmony `aarch64-unknown-none-softfloat` | * | Bare ARM64, softfloat `aarch64-unknown-none` | * | Bare ARM64, hardfloat @@ -161,6 +162,7 @@ target | std | notes `i586-unknown-linux-gnu` | ✓ | 32-bit Linux w/o SSE (kernel 3.2, glibc 2.17) [^x86_32-floats-x87] `i586-unknown-linux-musl` | ✓ | 32-bit Linux w/o SSE, musl 1.2.3 [^x86_32-floats-x87] [`i686-linux-android`](platform-support/android.md) | ✓ | 32-bit x86 Android [^x86_32-floats-return-ABI] +[`i686-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | 32-bit x86 MinGW (Windows 10+), LLVM ABI [^x86_32-floats-return-ABI] `i686-unknown-freebsd` | ✓ | 32-bit FreeBSD [^x86_32-floats-return-ABI] `i686-unknown-linux-musl` | ✓ | 32-bit Linux with musl 1.2.3 [^x86_32-floats-return-ABI] [`i686-unknown-uefi`](platform-support/unknown-uefi.md) | ? | 32-bit UEFI @@ -196,6 +198,7 @@ target | std | notes [`x86_64-unknown-fuchsia`](platform-support/fuchsia.md) | ✓ | 64-bit x86 Fuchsia [`x86_64-linux-android`](platform-support/android.md) | ✓ | 64-bit x86 Android `x86_64-pc-solaris` | ✓ | 64-bit Solaris 11, illumos +[`x86_64-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | 64-bit x86 MinGW (Windows 10+), LLVM ABI `x86_64-unknown-linux-gnux32` | ✓ | 64-bit Linux (x32 ABI) (kernel 4.15, glibc 2.27) [`x86_64-unknown-linux-ohos`](platform-support/openharmony.md) | ✓ | x86_64 OpenHarmony [`x86_64-unknown-none`](platform-support/x86_64-unknown-none.md) | * | Freestanding/bare-metal x86_64, softfloat @@ -247,7 +250,6 @@ target | std | host | notes [`aarch64-apple-visionos-sim`](platform-support/apple-visionos.md) | ✓ | | ARM64 Apple visionOS Simulator [`aarch64-kmc-solid_asp3`](platform-support/kmc-solid.md) | ✓ | | ARM64 SOLID with TOPPERS/ASP3 [`aarch64-nintendo-switch-freestanding`](platform-support/aarch64-nintendo-switch-freestanding.md) | * | | ARM64 Nintendo Switch, Horizon -[`aarch64-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | ✓ | [`aarch64-unknown-teeos`](platform-support/aarch64-unknown-teeos.md) | ? | | ARM64 TEEOS | [`aarch64-unknown-nto-qnx710`](platform-support/nto-qnx.md) | ✓ | | ARM64 QNX Neutrino 7.1 RTOS | `aarch64-unknown-freebsd` | ✓ | ✓ | ARM64 FreeBSD @@ -294,7 +296,6 @@ target | std | host | notes [`i586-pc-nto-qnx700`](platform-support/nto-qnx.md) | * | | 32-bit x86 QNX Neutrino 7.0 RTOS [^x86_32-floats-return-ABI] [`i586-unknown-netbsd`](platform-support/netbsd.md) | ✓ | | 32-bit x86, restricted to Pentium `i686-apple-darwin` | ✓ | ✓ | 32-bit macOS (10.12+, Sierra+) [^x86_32-floats-return-ABI] -[`i686-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | ✓ | [^x86_32-floats-return-ABI] `i686-unknown-haiku` | ✓ | ✓ | 32-bit Haiku [^x86_32-floats-return-ABI] [`i686-unknown-hurd-gnu`](platform-support/hurd.md) | ✓ | ✓ | 32-bit GNU/Hurd [^x86_32-floats-return-ABI] [`i686-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | NetBSD/i386 with SSE2 [^x86_32-floats-return-ABI] @@ -370,7 +371,6 @@ target | std | host | notes [`x86_64-apple-tvos`](platform-support/apple-tvos.md) | ? | | x86 64-bit tvOS [`x86_64-apple-watchos-sim`](platform-support/apple-watchos.md) | ✓ | | x86 64-bit Apple WatchOS simulator [`x86_64-pc-nto-qnx710`](platform-support/nto-qnx.md) | ✓ | | x86 64-bit QNX Neutrino 7.1 RTOS | -[`x86_64-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | ✓ | [`x86_64-unikraft-linux-musl`](platform-support/unikraft-linux-musl.md) | ✓ | | 64-bit Unikraft with musl 1.2.3 `x86_64-unknown-dragonfly` | ✓ | ✓ | 64-bit DragonFlyBSD `x86_64-unknown-haiku` | ✓ | ✓ | 64-bit Haiku diff --git a/src/doc/rustc/src/platform-support/pc-windows-gnullvm.md b/src/doc/rustc/src/platform-support/pc-windows-gnullvm.md index a6246fa3bd80..ed55bcf4f35e 100644 --- a/src/doc/rustc/src/platform-support/pc-windows-gnullvm.md +++ b/src/doc/rustc/src/platform-support/pc-windows-gnullvm.md @@ -1,6 +1,6 @@ # \*-pc-windows-gnullvm -**Tier: 3** +**Tier: 2 (without host tools)** Windows targets similar to `*-pc-windows-gnu` but using UCRT as the runtime and various LLVM tools/libraries instead of GCC/Binutils. @@ -12,38 +12,52 @@ Target triples available so far: ## Target maintainers - [@mati865](https://github.com/mati865) +- [@thomcc](https://github.com/thomcc) ## Requirements -The easiest way to obtain these targets is cross-compilation but native build from `x86_64-pc-windows-gnu` is possible with few hacks which I don't recommend. +The easiest way to obtain these targets is cross-compilation, but native build from `x86_64-pc-windows-gnu` is possible with few hacks which I don't recommend. Std support is expected to be on pair with `*-pc-windows-gnu`. Binaries for this target should be at least on pair with `*-pc-windows-gnu` in terms of requirements and functionality. Those targets follow Windows calling convention for `extern "C"`. -Like with any other Windows target created binaries are in PE format. +Like with any other Windows target, created binaries are in PE format. ## Building the target -For cross-compilation I recommend using [llvm-mingw](https://github.com/mstorsjo/llvm-mingw) toolchain, one change that seems necessary beside configuring cross compilers is disabling experimental `m86k` target. Otherwise LLVM build fails with `multiple definition ...` errors. -Native bootstrapping builds require rather fragile hacks until host artifacts are available so I won't describe them here. +These targets can be easily cross-compiled +using [llvm-mingw](https://github.com/mstorsjo/llvm-mingw) toolchain or [MSYS2 CLANG*](https://www.msys2.org/docs/environments/) environments. +Just fill `[target.*]` sections for both build and resulting compiler and set installation prefix in `config.toml`. +Then run `./x.py install`. +In my case I had ran `./x.py install --host x86_64-pc-windows-gnullvm --target x86_64-pc-windows-gnullvm` inside MSYS2 MINGW64 shell +so `x86_64-pc-windows-gnu` was my build toolchain. + +Native bootstrapping is doable in two ways: +- cross-compile gnullvm host toolchain and use it as build toolchain for the next build, +- copy libunwind libraries and rename them to mimic libgcc like here: https://github.com/msys2/MINGW-packages/blob/68e640756df2df6df6afa60f025e3f936e7b977c/mingw-w64-rust/PKGBUILD#L108-L109, stage0 compiler will be mostly broken but good enough to build the next stage. + +The second option might stop working anytime, so it's not recommended. ## Building Rust programs -Rust does not yet ship pre-compiled artifacts for this target. To compile for -this target, you will either need to build Rust with the target enabled (see -"Building the target" above), or build your own copy of `core` by using -`build-std` or similar. +Rust does ship a pre-compiled std library for those targets. +That means one can easily cross-compile for those targets from other hosts if C proper toolchain is installed. + +Alternatively full toolchain can be built as described in the previous section. ## Testing Created binaries work fine on Windows or Wine using native hardware. Testing AArch64 on x86_64 is problematic though and requires spending some time with QEMU. -Once these targets bootstrap themselves on native hardware they should pass Rust testsuite. +Most of x86_64 testsuite does pass when cross-compiling, +with exception for `rustdoc` and `ui-fulldeps` that fail with and error regarding a missing library, +they do pass in native builds though. +The only failing test is std's `process::tests::test_proc_thread_attributes` for unknown reason. ## Cross-compilation toolchains and C code -Compatible C code can be built with Clang's `aarch64-pc-windows-gnu`, `i686-pc-windows-gnullvm` and `x86_64-pc-windows-gnu` targets as long as LLVM based C toolchains are used. +Compatible C code can be built with Clang's `aarch64-pc-windows-gnu`, `i686-pc-windows-gnullvm` and `x86_64-pc-windows-gnu` targets as long as LLVM-based C toolchains are used. Those include: - [llvm-mingw](https://github.com/mstorsjo/llvm-mingw) - [MSYS2 with CLANG* environment](https://www.msys2.org/docs/environments) diff --git a/src/doc/unstable-book/src/compiler-flags/coverage-options.md b/src/doc/unstable-book/src/compiler-flags/coverage-options.md index 450573cc6c74..5e192d9aca13 100644 --- a/src/doc/unstable-book/src/compiler-flags/coverage-options.md +++ b/src/doc/unstable-book/src/compiler-flags/coverage-options.md @@ -5,4 +5,4 @@ This option controls details of the coverage instrumentation performed by Multiple options can be passed, separated by commas. Valid options are: -- `branch` or `no-branch`: Enables or disables branch coverage instrumentation. +- `no-branch`, `branch` or `mcdc`: `branch` enables branch coverage instrumentation and `mcdc` further enables modified condition/decision coverage instrumentation. `no-branch` disables branch coverage instrumentation as well as mcdc instrumentation, which is same as do not pass `branch` or `mcdc`. diff --git a/src/tools/build-manifest/src/main.rs b/src/tools/build-manifest/src/main.rs index 392a5a119676..f3972346bb5b 100644 --- a/src/tools/build-manifest/src/main.rs +++ b/src/tools/build-manifest/src/main.rs @@ -56,6 +56,7 @@ static TARGETS: &[&str] = &[ "aarch64-apple-ios-sim", "aarch64-unknown-fuchsia", "aarch64-linux-android", + "aarch64-pc-windows-gnullvm", "aarch64-pc-windows-msvc", "aarch64-unknown-hermit", "aarch64-unknown-linux-gnu", @@ -96,6 +97,7 @@ static TARGETS: &[&str] = &[ "i686-apple-darwin", "i686-linux-android", "i686-pc-windows-gnu", + "i686-pc-windows-gnullvm", "i686-pc-windows-msvc", "i686-unknown-freebsd", "i686-unknown-linux-gnu", @@ -157,6 +159,7 @@ static TARGETS: &[&str] = &[ "x86_64-unknown-fuchsia", "x86_64-linux-android", "x86_64-pc-windows-gnu", + "x86_64-pc-windows-gnullvm", "x86_64-pc-windows-msvc", "x86_64-pc-solaris", "x86_64-unikraft-linux-musl", @@ -464,7 +467,8 @@ impl Builder { | PkgType::LlvmTools | PkgType::RustAnalysis | PkgType::JsonDocs - | PkgType::RustcCodegenCranelift => { + | PkgType::RustcCodegenCranelift + | PkgType::LlvmBitcodeLinker => { extensions.push(host_component(pkg)); } PkgType::RustcDev | PkgType::RustcDocs => { diff --git a/src/tools/build-manifest/src/versions.rs b/src/tools/build-manifest/src/versions.rs index e4cdf965eb60..233a2670ed22 100644 --- a/src/tools/build-manifest/src/versions.rs +++ b/src/tools/build-manifest/src/versions.rs @@ -58,6 +58,7 @@ pkg_type! { Miri = "miri"; preview = true, JsonDocs = "rust-docs-json"; preview = true, RustcCodegenCranelift = "rustc-codegen-cranelift"; preview = true, + LlvmBitcodeLinker = "llvm-bitcode-linker"; preview = true, } impl PkgType { @@ -94,6 +95,7 @@ impl PkgType { PkgType::ReproducibleArtifacts => true, PkgType::RustMingw => true, PkgType::RustAnalysis => true, + PkgType::LlvmBitcodeLinker => true, } } @@ -121,6 +123,7 @@ impl PkgType { Rustfmt => HOSTS, RustAnalysis => TARGETS, LlvmTools => TARGETS, + LlvmBitcodeLinker => HOSTS, } } diff --git a/src/tools/compiletest/src/errors.rs b/src/tools/compiletest/src/errors.rs index 140c3aa0a64e..3231f9fd3a4b 100644 --- a/src/tools/compiletest/src/errors.rs +++ b/src/tools/compiletest/src/errors.rs @@ -118,7 +118,7 @@ fn parse_expected( // //[rev1]~ // //[rev1,rev2]~^^ static RE: Lazy = - Lazy::new(|| Regex::new(r"//(?:\[(?P[\w,]+)])?~(?P\||\^*)").unwrap()); + Lazy::new(|| Regex::new(r"//(?:\[(?P[\w\-,]+)])?~(?P\||\^*)").unwrap()); let captures = RE.captures(line)?; @@ -178,3 +178,6 @@ fn parse_expected( ); Some((which, Error { line_num, kind, msg })) } + +#[cfg(test)] +mod tests; diff --git a/src/tools/compiletest/src/errors/tests.rs b/src/tools/compiletest/src/errors/tests.rs new file mode 100644 index 000000000000..dfb001d8e397 --- /dev/null +++ b/src/tools/compiletest/src/errors/tests.rs @@ -0,0 +1,12 @@ +use super::*; + +#[test] +fn test_parse_expected_matching() { + // Ensure that we correctly extract expected revisions + let d1 = "//[rev1,rev2]~^ ERROR foo"; + let d2 = "//[rev1,rev2-foo]~^ ERROR foo"; + assert!(parse_expected(None, 1, d1, Some("rev1")).is_some()); + assert!(parse_expected(None, 1, d1, Some("rev2")).is_some()); + assert!(parse_expected(None, 1, d2, Some("rev1")).is_some()); + assert!(parse_expected(None, 1, d2, Some("rev2-foo")).is_some()); +} diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index f5d7ce1c5f9a..8aafbb3e3992 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -244,7 +244,7 @@ mod directives { pub const STDERR_PER_BITWIDTH: &'static str = "stderr-per-bitwidth"; pub const INCREMENTAL: &'static str = "incremental"; pub const KNOWN_BUG: &'static str = "known-bug"; - pub const MIR_UNIT_TEST: &'static str = "unit-test"; + pub const TEST_MIR_PASS: &'static str = "test-mir-pass"; pub const REMAP_SRC_BASE: &'static str = "remap-src-base"; pub const COMPARE_OUTPUT_LINES_BY_SUBSET: &'static str = "compare-output-lines-by-subset"; pub const LLVM_COV_FLAGS: &'static str = "llvm-cov-flags"; @@ -549,7 +549,7 @@ impl TestProps { config.set_name_value_directive( ln, - MIR_UNIT_TEST, + TEST_MIR_PASS, &mut self.mir_unit_test, |s| s.trim().to_string(), ); @@ -922,7 +922,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "should-fail", "should-ice", "stderr-per-bitwidth", - "unit-test", + "test-mir-pass", "unset-exec-env", "unset-rustc-env", // tidy-alphabetical-end diff --git a/src/tools/lint-docs/src/lib.rs b/src/tools/lint-docs/src/lib.rs index 2566124a0375..aca780e33f0e 100644 --- a/src/tools/lint-docs/src/lib.rs +++ b/src/tools/lint-docs/src/lib.rs @@ -7,6 +7,43 @@ use walkdir::WalkDir; mod groups; +/// List of lints which have been renamed. +/// +/// These will get redirects in the output to the new name. The +/// format is `(level, [(old_name, new_name), ...])`. +/// +/// Note: This hard-coded list is a temporary hack. The intent is in the +/// future to have `rustc` expose this information in some way (like a `-Z` +/// flag spitting out JSON). Also, this does not yet support changing the +/// level of the lint, which will be more difficult to support, since rustc +/// currently does not track that historical information. +static RENAMES: &[(Level, &[(&str, &str)])] = &[ + ( + Level::Allow, + &[ + ("single-use-lifetime", "single-use-lifetimes"), + ("elided-lifetime-in-path", "elided-lifetimes-in-paths"), + ("async-idents", "keyword-idents"), + ("disjoint-capture-migration", "rust-2021-incompatible-closure-captures"), + ("or-patterns-back-compat", "rust-2021-incompatible-or-patterns"), + ], + ), + ( + Level::Warn, + &[ + ("bare-trait-object", "bare-trait-objects"), + ("unstable-name-collision", "unstable-name-collisions"), + ("unused-doc-comment", "unused-doc-comments"), + ("redundant-semicolon", "redundant-semicolons"), + ("overlapping-patterns", "overlapping-range-endpoints"), + ("non-fmt-panic", "non-fmt-panics"), + ("unused-tuple-struct-fields", "dead-code"), + ("static-mut-ref", "static-mut-refs"), + ], + ), + (Level::Deny, &[("exceeding-bitshifts", "arithmetic-overflow")]), +]; + pub struct LintExtractor<'a> { /// Path to the `src` directory, where it will scan for `.rs` files to /// find lint declarations. @@ -126,6 +163,7 @@ impl<'a> LintExtractor<'a> { ) })?; } + add_renamed_lints(&mut lints); self.save_lints_markdown(&lints)?; self.generate_group_docs(&lints)?; Ok(()) @@ -482,6 +520,7 @@ impl<'a> LintExtractor<'a> { } result.push('\n'); } + add_rename_redirect(level, &mut result); let out_path = self.out_path.join("listing").join(level.doc_filename()); // Delete the output because rustbuild uses hard links in its copies. let _ = fs::remove_file(&out_path); @@ -491,6 +530,56 @@ impl<'a> LintExtractor<'a> { } } +/// Adds `Lint`s that have been renamed. +fn add_renamed_lints(lints: &mut Vec) { + for (level, names) in RENAMES { + for (from, to) in *names { + lints.push(Lint { + name: from.to_string(), + doc: vec![format!("The lint `{from}` has been renamed to [`{to}`](#{to}).")], + level: *level, + path: PathBuf::new(), + lineno: 0, + }); + } + } +} + +// This uses DOMContentLoaded instead of running immediately because for some +// reason on Firefox (124 of this writing) doesn't update the `target` CSS +// selector if only the hash changes. +static RENAME_START: &str = " + +"; + +/// Adds the javascript redirection code to the given markdown output. +fn add_rename_redirect(level: Level, output: &mut String) { + for (rename_level, names) in RENAMES { + if *rename_level == level { + let filename = level.doc_filename().replace(".md", ".html"); + output.push_str(RENAME_START); + for (from, to) in *names { + write!(output, " \"#{from}\": \"{filename}#{to}\",\n").unwrap(); + } + output.push_str(RENAME_END); + } + } +} + /// Extracts the lint name (removing the visibility modifier, and checking validity). fn lint_name(line: &str) -> Result { // Skip over any potential `pub` visibility. diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index db2cd01ce0bf..0070d1f3ebc5 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -1,4 +1,3 @@ -#![feature(generic_nonzero)] #![feature(rustc_private, stmt_expr_attributes)] #![allow( clippy::manual_range_contains, diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs index d44d04e9bf83..0c0ac4c6036d 100644 --- a/src/tools/miri/src/diagnostics.rs +++ b/src/tools/miri/src/diagnostics.rs @@ -291,7 +291,7 @@ pub fn report_error<'tcx, 'mir>( ValidationErrorKind::PointerAsInt { .. } | ValidationErrorKind::PartialPointer ) => { - ecx.handle_ice(); // print interpreter backtrace + ecx.handle_ice(); // print interpreter backtrace (this is outside the eval `catch_unwind`) bug!( "This validation error should be impossible in Miri: {}", format_interp_error(ecx.tcx.dcx(), e) @@ -308,7 +308,7 @@ pub fn report_error<'tcx, 'mir>( InvalidProgramInfo::AlreadyReported(_) | InvalidProgramInfo::Layout(..), ) => "post-monomorphization error", _ => { - ecx.handle_ice(); // print interpreter backtrace + ecx.handle_ice(); // print interpreter backtrace (this is outside the eval `catch_unwind`) bug!( "This error should be impossible in Miri: {}", format_interp_error(ecx.tcx.dcx(), e) diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index 2e19c9ff7135..e1c0da9118d3 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -2,7 +2,6 @@ #![feature(cell_update)] #![feature(const_option)] #![feature(float_gamma)] -#![feature(generic_nonzero)] #![feature(map_try_insert)] #![feature(never_type)] #![feature(try_blocks)] diff --git a/src/tools/miri/tests/panic/mir-validation.rs b/src/tools/miri/tests/panic/mir-validation.rs index fe618f6c8194..f1d0ccc7d037 100644 --- a/src/tools/miri/tests/panic/mir-validation.rs +++ b/src/tools/miri/tests/panic/mir-validation.rs @@ -1,9 +1,13 @@ //! Ensure that the MIR validator runs on Miri's input. -//@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "" -//@normalize-stderr-test: "\n +at [^\n]+" -> "" -//@normalize-stderr-test: "\n +\[\.\.\. omitted [0-9]+ frames? \.\.\.\]" -> "" +//@rustc-env:RUSTC_ICE=0 +//@normalize-stderr-test: "\n +[0-9]+:.+" -> "" +//@normalize-stderr-test: "\n +at .+" -> "" +//@normalize-stderr-test: "\n +\[\.\.\. omitted [0-9]+ frames? \.\.\.\].*" -> "" //@normalize-stderr-test: "\n[ =]*note:.*" -> "" //@normalize-stderr-test: "DefId\([^()]*\)" -> "DefId" +// Somehow on rustc Windows CI, the "Miri caused an ICE" message is not shown +// and we don't even get a regular panic; rustc aborts with a different exit code instead. +//@ignore-host-windows #![feature(custom_mir, core_intrinsics)] use core::intrinsics::mir::*; diff --git a/src/tools/miri/tests/pass/async-drop.rs b/src/tools/miri/tests/pass/async-drop.rs new file mode 100644 index 000000000000..f16206f3db62 --- /dev/null +++ b/src/tools/miri/tests/pass/async-drop.rs @@ -0,0 +1,191 @@ +//@revisions: stack tree +//@compile-flags: -Zmiri-strict-provenance +//@[tree]compile-flags: -Zmiri-tree-borrows +#![feature(async_drop, impl_trait_in_assoc_type, noop_waker, async_closure)] +#![allow(incomplete_features, dead_code)] + +// FIXME(zetanumbers): consider AsyncDestruct::async_drop cleanup tests +use core::future::{async_drop_in_place, AsyncDrop, Future}; +use core::hint::black_box; +use core::mem::{self, ManuallyDrop}; +use core::pin::{pin, Pin}; +use core::task::{Context, Poll, Waker}; + +async fn test_async_drop(x: T) { + let mut x = mem::MaybeUninit::new(x); + let dtor = pin!(unsafe { async_drop_in_place(x.as_mut_ptr()) }); + test_idempotency(dtor).await; +} + +fn test_idempotency(mut x: Pin<&mut T>) -> impl Future + '_ +where + T: Future, +{ + core::future::poll_fn(move |cx| { + assert_eq!(x.as_mut().poll(cx), Poll::Ready(())); + assert_eq!(x.as_mut().poll(cx), Poll::Ready(())); + Poll::Ready(()) + }) +} + +fn main() { + let waker = Waker::noop(); + let mut cx = Context::from_waker(&waker); + + let i = 13; + let fut = pin!(async { + test_async_drop(Int(0)).await; + test_async_drop(AsyncInt(0)).await; + test_async_drop([AsyncInt(1), AsyncInt(2)]).await; + test_async_drop((AsyncInt(3), AsyncInt(4))).await; + test_async_drop(5).await; + let j = 42; + test_async_drop(&i).await; + test_async_drop(&j).await; + test_async_drop(AsyncStruct { b: AsyncInt(8), a: AsyncInt(7), i: 6 }).await; + test_async_drop(ManuallyDrop::new(AsyncInt(9))).await; + + let foo = AsyncInt(10); + test_async_drop(AsyncReference { foo: &foo }).await; + + let foo = AsyncInt(11); + test_async_drop(|| { + black_box(foo); + let foo = AsyncInt(10); + foo + }) + .await; + + test_async_drop(AsyncEnum::A(AsyncInt(12))).await; + test_async_drop(AsyncEnum::B(SyncInt(13))).await; + + test_async_drop(SyncInt(14)).await; + test_async_drop(SyncThenAsync { i: 15, a: AsyncInt(16), b: SyncInt(17), c: AsyncInt(18) }) + .await; + + let async_drop_fut = pin!(core::future::async_drop(AsyncInt(19))); + test_idempotency(async_drop_fut).await; + + let foo = AsyncInt(20); + test_async_drop(async || { + black_box(foo); + let foo = AsyncInt(19); + // Await point there, but this is async closure so it's fine + black_box(core::future::ready(())).await; + foo + }) + .await; + + test_async_drop(AsyncUnion { signed: 21 }).await; + }); + let res = fut.poll(&mut cx); + assert_eq!(res, Poll::Ready(())); +} + +struct AsyncInt(i32); + +impl AsyncDrop for AsyncInt { + type Dropper<'a> = impl Future; + + fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> { + async move { + println!("AsyncInt::Dropper::poll: {}", self.0); + } + } +} + +struct SyncInt(i32); + +impl Drop for SyncInt { + fn drop(&mut self) { + println!("SyncInt::drop: {}", self.0); + } +} + +struct SyncThenAsync { + i: i32, + a: AsyncInt, + b: SyncInt, + c: AsyncInt, +} + +impl Drop for SyncThenAsync { + fn drop(&mut self) { + println!("SyncThenAsync::drop: {}", self.i); + } +} + +struct AsyncReference<'a> { + foo: &'a AsyncInt, +} + +impl AsyncDrop for AsyncReference<'_> { + type Dropper<'a> = impl Future where Self: 'a; + + fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> { + async move { + println!("AsyncReference::Dropper::poll: {}", self.foo.0); + } + } +} + +struct Int(i32); + +struct AsyncStruct { + i: i32, + a: AsyncInt, + b: AsyncInt, +} + +impl AsyncDrop for AsyncStruct { + type Dropper<'a> = impl Future; + + fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> { + async move { + println!("AsyncStruct::Dropper::poll: {}", self.i); + } + } +} + +enum AsyncEnum { + A(AsyncInt), + B(SyncInt), +} + +impl AsyncDrop for AsyncEnum { + type Dropper<'a> = impl Future; + + fn async_drop(mut self: Pin<&mut Self>) -> Self::Dropper<'_> { + async move { + let new_self = match &*self { + AsyncEnum::A(foo) => { + println!("AsyncEnum(A)::Dropper::poll: {}", foo.0); + AsyncEnum::B(SyncInt(foo.0)) + } + AsyncEnum::B(foo) => { + println!("AsyncEnum(B)::Dropper::poll: {}", foo.0); + AsyncEnum::A(AsyncInt(foo.0)) + } + }; + mem::forget(mem::replace(&mut *self, new_self)); + } + } +} + +// FIXME(zetanumbers): Disallow types with `AsyncDrop` in unions +union AsyncUnion { + signed: i32, + unsigned: u32, +} + +impl AsyncDrop for AsyncUnion { + type Dropper<'a> = impl Future; + + fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> { + async move { + println!("AsyncUnion::Dropper::poll: {}, {}", unsafe { self.signed }, unsafe { + self.unsigned + }); + } + } +} diff --git a/src/tools/miri/tests/pass/async-drop.stack.stdout b/src/tools/miri/tests/pass/async-drop.stack.stdout new file mode 100644 index 000000000000..9cae4331caf9 --- /dev/null +++ b/src/tools/miri/tests/pass/async-drop.stack.stdout @@ -0,0 +1,22 @@ +AsyncInt::Dropper::poll: 0 +AsyncInt::Dropper::poll: 1 +AsyncInt::Dropper::poll: 2 +AsyncInt::Dropper::poll: 3 +AsyncInt::Dropper::poll: 4 +AsyncStruct::Dropper::poll: 6 +AsyncInt::Dropper::poll: 7 +AsyncInt::Dropper::poll: 8 +AsyncReference::Dropper::poll: 10 +AsyncInt::Dropper::poll: 11 +AsyncEnum(A)::Dropper::poll: 12 +SyncInt::drop: 12 +AsyncEnum(B)::Dropper::poll: 13 +AsyncInt::Dropper::poll: 13 +SyncInt::drop: 14 +SyncThenAsync::drop: 15 +AsyncInt::Dropper::poll: 16 +SyncInt::drop: 17 +AsyncInt::Dropper::poll: 18 +AsyncInt::Dropper::poll: 19 +AsyncInt::Dropper::poll: 20 +AsyncUnion::Dropper::poll: 21, 21 diff --git a/src/tools/miri/tests/pass/async-drop.tree.stdout b/src/tools/miri/tests/pass/async-drop.tree.stdout new file mode 100644 index 000000000000..9cae4331caf9 --- /dev/null +++ b/src/tools/miri/tests/pass/async-drop.tree.stdout @@ -0,0 +1,22 @@ +AsyncInt::Dropper::poll: 0 +AsyncInt::Dropper::poll: 1 +AsyncInt::Dropper::poll: 2 +AsyncInt::Dropper::poll: 3 +AsyncInt::Dropper::poll: 4 +AsyncStruct::Dropper::poll: 6 +AsyncInt::Dropper::poll: 7 +AsyncInt::Dropper::poll: 8 +AsyncReference::Dropper::poll: 10 +AsyncInt::Dropper::poll: 11 +AsyncEnum(A)::Dropper::poll: 12 +SyncInt::drop: 12 +AsyncEnum(B)::Dropper::poll: 13 +AsyncInt::Dropper::poll: 13 +SyncInt::drop: 14 +SyncThenAsync::drop: 15 +AsyncInt::Dropper::poll: 16 +SyncInt::drop: 17 +AsyncInt::Dropper::poll: 18 +AsyncInt::Dropper::poll: 19 +AsyncInt::Dropper::poll: 20 +AsyncUnion::Dropper::poll: 21, 21 diff --git a/src/tools/rust-analyzer/.github/ISSUE_TEMPLATE/bug_report.md b/src/tools/rust-analyzer/.github/ISSUE_TEMPLATE/bug_report.md index 97c1b64494d5..0d99d06bcdde 100644 --- a/src/tools/rust-analyzer/.github/ISSUE_TEMPLATE/bug_report.md +++ b/src/tools/rust-analyzer/.github/ISSUE_TEMPLATE/bug_report.md @@ -22,6 +22,8 @@ Otherwise please try to provide information which will help us to fix the issue **rustc version**: (eg. output of `rustc -V`) +**editor or extension**: (eg. VSCode, Vim, Emacs, etc. For VSCode users, specify your extension version; for users of other editors, provide the distribution if applicable) + **relevant settings**: (eg. client settings, or environment variables like `CARGO`, `RUSTC`, `RUSTUP_HOME` or `CARGO_HOME`) **repository link (if public, optional)**: (eg. [rust-analyzer](https://github.com/rust-lang/rust-analyzer)) diff --git a/src/tools/rust-analyzer/.github/workflows/ci.yaml b/src/tools/rust-analyzer/.github/workflows/ci.yaml index 08ad10c29718..a10345a7060a 100644 --- a/src/tools/rust-analyzer/.github/workflows/ci.yaml +++ b/src/tools/rust-analyzer/.github/workflows/ci.yaml @@ -91,7 +91,7 @@ jobs: run: cargo build --quiet ${{ env.USE_SYSROOT_ABI }} - name: Test - if: matrix.os == 'ubuntu-latest' || github.event_name == 'push' + if: matrix.os == 'ubuntu-latest' || matrix.os == 'windows-latest' || github.event_name == 'push' run: cargo test ${{ env.USE_SYSROOT_ABI }} -- --nocapture --quiet - name: Switch to stable toolchain diff --git a/src/tools/rust-analyzer/.github/workflows/metrics.yaml b/src/tools/rust-analyzer/.github/workflows/metrics.yaml index de61b2389ae2..b6cd4a795a8d 100644 --- a/src/tools/rust-analyzer/.github/workflows/metrics.yaml +++ b/src/tools/rust-analyzer/.github/workflows/metrics.yaml @@ -58,7 +58,7 @@ jobs: key: ${{ runner.os }}-target-${{ github.sha }} - name: Upload build metrics - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: build-${{ github.sha }} path: target/build.json @@ -95,7 +95,7 @@ jobs: run: cargo xtask metrics "${{ matrix.names }}" - name: Upload metrics - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ${{ matrix.names }}-${{ github.sha }} path: target/${{ matrix.names }}.json @@ -109,32 +109,32 @@ jobs: uses: actions/checkout@v4 - name: Download build metrics - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: build-${{ github.sha }} - name: Download self metrics - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: self-${{ github.sha }} - name: Download ripgrep-13.0.0 metrics - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: ripgrep-13.0.0-${{ github.sha }} - name: Download webrender-2022 metrics - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: webrender-2022-${{ github.sha }} - name: Download diesel-1.4.8 metrics - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: diesel-1.4.8-${{ github.sha }} - name: Download hyper-0.14.18 metrics - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: hyper-0.14.18-${{ github.sha }} diff --git a/src/tools/rust-analyzer/CONTRIBUTING.md b/src/tools/rust-analyzer/CONTRIBUTING.md new file mode 100644 index 000000000000..da65b034be3c --- /dev/null +++ b/src/tools/rust-analyzer/CONTRIBUTING.md @@ -0,0 +1,30 @@ +# Contributing to rust-analyzer + +Thank you for your interest in contributing to rust-analyzer! There are many ways to contribute +and we appreciate all of them. + +To get a quick overview of the crates and structure of the project take a look at the +[./docs/dev](./docs/dev) folder. + +If you have any questions please ask them in the [rust-analyzer zulip stream]( +https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Frust-analyzer) or if unsure where +to start out when working on a concrete issue drop a comment on the related issue for mentoring +instructions (general discussions are recommended to happen on zulip though). + +## Fixing a bug or improving a feature + +Generally it's fine to just work on these kinds of things and put a pull-request out for it. If there +is an issue accompanying it make sure to link it in the pull request description so it can be closed +afterwards or linked for context. + +If you want to find something to fix or work on keep a look out for the `C-bug` and `C-enhancement` +labels. + +## Implementing a new feature + +It's advised to first open an issue for any kind of new feature so the team can tell upfront whether +the feature is desirable or not before any implementation work happens. We want to minimize the +possibility of someone putting a lot of work into a feature that is then going to waste as we deem +it out of scope (be it due to generally not fitting in with rust-analyzer, or just not having the +maintenance capacity). If there already is a feature issue open but it is not clear whether it is +considered accepted feel free to just drop a comment and ask! diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index c7cf4479b330..a6e460134f21 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -161,9 +161,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chalk-derive" -version = "0.96.0" +version = "0.97.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5676cea088c32290fe65c82895be9d06dd21e0fa49bb97ca840529e9417ab71a" +checksum = "92a0aedc4ac2adc5c0b7dc9ec38c5c816284ad28da6d4ecd01873b9683f54972" dependencies = [ "proc-macro2", "quote", @@ -173,20 +173,19 @@ dependencies = [ [[package]] name = "chalk-ir" -version = "0.96.0" +version = "0.97.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff550c2cdd63ff74394214dce03d06386928a641c0f08837535f04af573a966d" +checksum = "db18493569b190f7266a04901e520fc3a5c00564475154287906f8a27302c119" dependencies = [ "bitflags 2.4.2", "chalk-derive", - "lazy_static", ] [[package]] name = "chalk-recursive" -version = "0.96.0" +version = "0.97.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c4559e5c9b200240453b07d893f9c3c74413b53b0d33cbe272c68b0b77aa1c3" +checksum = "ae4ba8ce5bd2e1b59f1f79495bc8704db09a8285e51cc5ddf01d9baee1bf447d" dependencies = [ "chalk-derive", "chalk-ir", @@ -197,9 +196,9 @@ dependencies = [ [[package]] name = "chalk-solve" -version = "0.96.0" +version = "0.97.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0882e68ce9eb5a0a2413806538494d19df6ee520ab17d1faf489e952f32e98b8" +checksum = "b2ec1b3b7f7b1ec38f099ef39c2bc3ea29335be1b8316d114baff46d96d131e9" dependencies = [ "chalk-derive", "chalk-ir", @@ -552,6 +551,7 @@ dependencies = [ "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "limit", "mbe", + "parser", "rustc-hash", "smallvec", "span", @@ -695,6 +695,7 @@ version = "0.0.0" dependencies = [ "arrayvec", "base-db", + "bitflags 2.4.2", "cov-mark", "crossbeam-channel", "either", @@ -781,6 +782,7 @@ checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" dependencies = [ "equivalent", "hashbrown", + "serde", ] [[package]] @@ -1594,6 +1596,7 @@ dependencies = [ "ide", "ide-db", "ide-ssr", + "indexmap", "itertools", "load-cargo", "lsp-server 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1622,6 +1625,7 @@ dependencies = [ "test-fixture", "test-utils", "tikv-jemallocator", + "toml", "toolchain", "tracing", "tracing-subscriber", @@ -1775,6 +1779,15 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_spanned" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +dependencies = [ + "serde", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -1822,6 +1835,7 @@ dependencies = [ "salsa", "stdx", "syntax", + "text-size", "vfs", ] @@ -2025,6 +2039,40 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +[[package]] +name = "toml" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "toolchain" version = "0.0.0" @@ -2401,6 +2449,15 @@ version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" +[[package]] +name = "winnow" +version = "0.5.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8434aeec7b290e8da5c3f0d628cb0eac6cabcb31d14bb74f779a08109a5914d6" +dependencies = [ + "memchr", +] + [[package]] name = "write-json" version = "0.1.4" diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index d9343d2b9632..f7e3ae51dfd0 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -22,6 +22,7 @@ smol_str.opt-level = 3 text-size.opt-level = 3 # This speeds up `cargo xtask dist`. miniz_oxide.opt-level = 3 +salsa.opt-level = 3 [profile.release] incremental = true @@ -106,10 +107,10 @@ arrayvec = "0.7.4" bitflags = "2.4.1" cargo_metadata = "0.18.1" camino = "1.1.6" -chalk-solve = { version = "0.96.0", default-features = false } -chalk-ir = "0.96.0" -chalk-recursive = { version = "0.96.0", default-features = false } -chalk-derive = "0.96.0" +chalk-solve = { version = "0.97.0", default-features = false } +chalk-ir = "0.97.0" +chalk-recursive = { version = "0.97.0", default-features = false } +chalk-derive = "0.97.0" command-group = "2.0.1" crossbeam-channel = "0.5.8" dissimilar = "1.0.7" @@ -188,6 +189,8 @@ enum_variant_names = "allow" new_ret_no_self = "allow" # Has a bunch of false positives useless_asref = "allow" +# Has false positives +assigning_clones = "allow" ## Following lints should be tackled at some point too_many_arguments = "allow" diff --git a/src/tools/rust-analyzer/README.md b/src/tools/rust-analyzer/README.md index 8c3f6f8468bf..552f71f15184 100644 --- a/src/tools/rust-analyzer/README.md +++ b/src/tools/rust-analyzer/README.md @@ -13,8 +13,9 @@ https://rust-analyzer.github.io/manual.html#installation ## Documentation -If you want to **contribute** to rust-analyzer or are just curious about how -things work under the hood, check the [./docs/dev](./docs/dev) folder. +If you want to **contribute** to rust-analyzer check out the [CONTRIBUTING.md](./CONTRIBUTING.md) or +if you are just curious about how things work under the hood, check the [./docs/dev](./docs/dev) +folder. If you want to **use** rust-analyzer's language server with your editor of choice, check [the manual](https://rust-analyzer.github.io/manual.html) folder. 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 27eb05cd4dc5..240af7925ccc 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/input.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/input.rs @@ -19,6 +19,10 @@ 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>>; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct SourceRootId(pub u32); + /// 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 /// Rust crate. Source roots *might* be nested: in this case, a file belongs to @@ -26,9 +30,6 @@ pub type ProcMacroPaths = FxHashMap, AbsPathBuf) /// source root, and the analyzer does not know the root path of the source root at /// all. So, a file from one source root can't refer to a file in another source /// root by path. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct SourceRootId(pub u32); - #[derive(Clone, Debug, PartialEq, Eq)] pub struct SourceRoot { /// Sysroot or crates.io library. @@ -285,20 +286,39 @@ pub struct CrateData { /// For purposes of analysis, crates are anonymous (only names in /// `Dependency` matters), this name should only be used for UI. pub display_name: Option, - pub cfg_options: CfgOptions, + pub cfg_options: Arc, /// The cfg options that could be used by the crate - pub potential_cfg_options: Option, + pub potential_cfg_options: Option>, pub env: Env, pub dependencies: Vec, pub origin: CrateOrigin, pub is_proc_macro: bool, } -#[derive(Default, Debug, Clone, PartialEq, Eq)] +#[derive(Default, Clone, PartialEq, Eq)] pub struct Env { entries: FxHashMap, } +impl fmt::Debug for Env { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + struct EnvDebug<'s>(Vec<(&'s String, &'s String)>); + + impl fmt::Debug for EnvDebug<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_map().entries(self.0.iter().copied()).finish() + } + } + f.debug_struct("Env") + .field("entries", &{ + let mut entries: Vec<_> = self.entries.iter().collect(); + entries.sort(); + EnvDebug(entries) + }) + .finish() + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Dependency { pub crate_id: CrateId, @@ -328,12 +348,13 @@ impl CrateGraph { edition: Edition, display_name: Option, version: Option, - cfg_options: CfgOptions, - potential_cfg_options: Option, - env: Env, + cfg_options: Arc, + potential_cfg_options: Option>, + mut env: Env, is_proc_macro: bool, origin: CrateOrigin, ) -> CrateId { + env.entries.shrink_to_fit(); let data = CrateData { root_file_id, edition, @@ -650,16 +671,24 @@ impl FromIterator<(String, String)> for Env { } impl Env { - pub fn set(&mut self, env: &str, value: String) { - self.entries.insert(env.to_owned(), value); + pub fn set(&mut self, env: &str, value: impl Into) { + self.entries.insert(env.to_owned(), value.into()); } pub fn get(&self, env: &str) -> Option { self.entries.get(env).cloned() } - pub fn iter(&self) -> impl Iterator { - self.entries.iter().map(|(k, v)| (k.as_str(), v.as_str())) + pub fn extend_from_other(&mut self, other: &Env) { + self.entries.extend(other.entries.iter().map(|(x, y)| (x.to_owned(), y.to_owned()))); + } +} + +impl From for Vec<(String, String)> { + fn from(env: Env) -> Vec<(String, String)> { + let mut entries: Vec<_> = env.entries.into_iter().collect(); + entries.sort(); + entries } } 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 785ff9ceffad..2b64a07a5a95 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/lib.rs @@ -45,7 +45,7 @@ pub trait Upcast { pub const DEFAULT_FILE_TEXT_LRU_CAP: usize = 16; pub const DEFAULT_PARSE_LRU_CAP: usize = 128; -pub const DEFAULT_BORROWCK_LRU_CAP: usize = 1024; +pub const DEFAULT_BORROWCK_LRU_CAP: usize = 2024; pub trait FileLoader { /// Text of the file. @@ -83,7 +83,8 @@ fn toolchain_channel(db: &dyn SourceDatabase, krate: CrateId) -> Option Parse { let _p = tracing::span!(tracing::Level::INFO, "parse_query", ?file_id).entered(); let text = db.file_text(file_id); - SourceFile::parse(&text) + // FIXME: Edition based parsing + SourceFile::parse(&text, span::Edition::CURRENT) } /// We don't want to give HIR knowledge of source roots, hence we extract these diff --git a/src/tools/rust-analyzer/crates/cfg/src/lib.rs b/src/tools/rust-analyzer/crates/cfg/src/lib.rs index 454d6fc5384b..9a365889e6a4 100644 --- a/src/tools/rust-analyzer/crates/cfg/src/lib.rs +++ b/src/tools/rust-analyzer/crates/cfg/src/lib.rs @@ -58,13 +58,6 @@ impl CfgOptions { self.enabled.insert(CfgAtom::KeyValue { key, value }); } - pub fn difference<'a>( - &'a self, - other: &'a CfgOptions, - ) -> impl Iterator + 'a { - self.enabled.difference(&other.enabled) - } - pub fn apply_diff(&mut self, diff: CfgDiff) { for atom in diff.enable { self.enabled.insert(atom); diff --git a/src/tools/rust-analyzer/crates/cfg/src/tests.rs b/src/tools/rust-analyzer/crates/cfg/src/tests.rs index 62fb429a63fa..a1ae15fcddad 100644 --- a/src/tools/rust-analyzer/crates/cfg/src/tests.rs +++ b/src/tools/rust-analyzer/crates/cfg/src/tests.rs @@ -1,12 +1,12 @@ use arbitrary::{Arbitrary, Unstructured}; use expect_test::{expect, Expect}; use mbe::{syntax_node_to_token_tree, DummyTestSpanMap, DUMMY}; -use syntax::{ast, AstNode}; +use syntax::{ast, AstNode, Edition}; use crate::{CfgAtom, CfgExpr, CfgOptions, DnfExpr}; fn assert_parse_result(input: &str, expected: CfgExpr) { - let source_file = ast::SourceFile::parse(input).ok().unwrap(); + let source_file = ast::SourceFile::parse(input, Edition::CURRENT).ok().unwrap(); let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); let tt = syntax_node_to_token_tree(tt.syntax(), DummyTestSpanMap, DUMMY); let cfg = CfgExpr::parse(&tt); @@ -14,7 +14,7 @@ fn assert_parse_result(input: &str, expected: CfgExpr) { } fn check_dnf(input: &str, expect: Expect) { - let source_file = ast::SourceFile::parse(input).ok().unwrap(); + let source_file = ast::SourceFile::parse(input, Edition::CURRENT).ok().unwrap(); let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); let tt = syntax_node_to_token_tree(tt.syntax(), DummyTestSpanMap, DUMMY); let cfg = CfgExpr::parse(&tt); @@ -23,7 +23,7 @@ fn check_dnf(input: &str, expect: Expect) { } fn check_why_inactive(input: &str, opts: &CfgOptions, expect: Expect) { - let source_file = ast::SourceFile::parse(input).ok().unwrap(); + let source_file = ast::SourceFile::parse(input, Edition::CURRENT).ok().unwrap(); let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); let tt = syntax_node_to_token_tree(tt.syntax(), DummyTestSpanMap, DUMMY); let cfg = CfgExpr::parse(&tt); @@ -34,7 +34,7 @@ fn check_why_inactive(input: &str, opts: &CfgOptions, expect: Expect) { #[track_caller] fn check_enable_hints(input: &str, opts: &CfgOptions, expected_hints: &[&str]) { - let source_file = ast::SourceFile::parse(input).ok().unwrap(); + let source_file = ast::SourceFile::parse(input, Edition::CURRENT).ok().unwrap(); let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); let tt = syntax_node_to_token_tree(tt.syntax(), DummyTestSpanMap, DUMMY); let cfg = CfgExpr::parse(&tt); diff --git a/src/tools/rust-analyzer/crates/flycheck/src/command.rs b/src/tools/rust-analyzer/crates/flycheck/src/command.rs index 091146a0010a..8ba7018316a1 100644 --- a/src/tools/rust-analyzer/crates/flycheck/src/command.rs +++ b/src/tools/rust-analyzer/crates/flycheck/src/command.rs @@ -4,12 +4,13 @@ use std::{ ffi::OsString, fmt, io, + marker::PhantomData, path::PathBuf, process::{ChildStderr, ChildStdout, Command, Stdio}, }; use command_group::{CommandGroup, GroupChild}; -use crossbeam_channel::{unbounded, Receiver, Sender}; +use crossbeam_channel::Sender; use stdx::process::streaming_output; /// Cargo output is structured as a one JSON per line. This trait abstracts parsing one line of @@ -99,10 +100,10 @@ pub(crate) struct CommandHandle { /// a read syscall dropping and therefore terminating the process is our best option. child: JodGroupChild, thread: stdx::thread::JoinHandle>, - pub(crate) receiver: Receiver, program: OsString, arguments: Vec, current_dir: Option, + _phantom: PhantomData, } impl fmt::Debug for CommandHandle { @@ -116,7 +117,7 @@ impl fmt::Debug for CommandHandle { } impl CommandHandle { - pub(crate) fn spawn(mut command: Command) -> std::io::Result { + pub(crate) fn spawn(mut command: Command, sender: Sender) -> std::io::Result { command.stdout(Stdio::piped()).stderr(Stdio::piped()).stdin(Stdio::null()); let mut child = command.group_spawn().map(JodGroupChild)?; @@ -127,13 +128,12 @@ impl CommandHandle { let stdout = child.0.inner().stdout.take().unwrap(); let stderr = child.0.inner().stderr.take().unwrap(); - let (sender, receiver) = unbounded(); let actor = CargoActor::::new(sender, stdout, stderr); let thread = stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker) .name("CommandHandle".to_owned()) .spawn(move || actor.run()) .expect("failed to spawn thread"); - Ok(CommandHandle { program, arguments, current_dir, child, thread, receiver }) + Ok(CommandHandle { program, arguments, current_dir, child, thread, _phantom: PhantomData }) } pub(crate) fn cancel(mut self) { diff --git a/src/tools/rust-analyzer/crates/flycheck/src/lib.rs b/src/tools/rust-analyzer/crates/flycheck/src/lib.rs index 4ee86954acd9..5dfaaf774209 100644 --- a/src/tools/rust-analyzer/crates/flycheck/src/lib.rs +++ b/src/tools/rust-analyzer/crates/flycheck/src/lib.rs @@ -41,19 +41,50 @@ pub enum InvocationLocation { Workspace, } +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct CargoOptions { + pub target_triples: Vec, + pub all_targets: bool, + pub no_default_features: bool, + pub all_features: bool, + pub features: Vec, + pub extra_args: Vec, + pub extra_env: FxHashMap, + pub target_dir: Option, +} + +impl CargoOptions { + fn apply_on_command(&self, cmd: &mut Command) { + for target in &self.target_triples { + cmd.args(["--target", target.as_str()]); + } + if self.all_targets { + cmd.arg("--all-targets"); + } + if self.all_features { + cmd.arg("--all-features"); + } else { + if self.no_default_features { + cmd.arg("--no-default-features"); + } + if !self.features.is_empty() { + cmd.arg("--features"); + cmd.arg(self.features.join(" ")); + } + } + if let Some(target_dir) = &self.target_dir { + cmd.arg("--target-dir").arg(target_dir); + } + cmd.envs(&self.extra_env); + } +} + #[derive(Clone, Debug, PartialEq, Eq)] pub enum FlycheckConfig { CargoCommand { command: String, - target_triples: Vec, - all_targets: bool, - no_default_features: bool, - all_features: bool, - features: Vec, - extra_args: Vec, - extra_env: FxHashMap, + options: CargoOptions, ansi_color_output: bool, - target_dir: Option, }, CustomCommand { command: String, @@ -184,6 +215,8 @@ struct FlycheckActor { /// have to wrap sub-processes output handling in a thread and pass messages /// back over a channel. command_handle: Option>, + /// The receiver side of the channel mentioned above. + command_receiver: Option>, } enum Event { @@ -209,6 +242,7 @@ impl FlycheckActor { sysroot_root, root: workspace_root, command_handle: None, + command_receiver: None, } } @@ -217,14 +251,13 @@ impl FlycheckActor { } fn next_event(&self, inbox: &Receiver) -> Option { - let check_chan = self.command_handle.as_ref().map(|cargo| &cargo.receiver); if let Ok(msg) = inbox.try_recv() { // give restarts a preference so check outputs don't block a restart or stop return Some(Event::RequestStateChange(msg)); } select! { recv(inbox) -> msg => msg.ok().map(Event::RequestStateChange), - recv(check_chan.unwrap_or(&never())) -> msg => Some(Event::CheckEvent(msg.ok())), + recv(self.command_receiver.as_ref().unwrap_or(&never())) -> msg => Some(Event::CheckEvent(msg.ok())), } } @@ -253,10 +286,12 @@ impl FlycheckActor { let formatted_command = format!("{:?}", command); tracing::debug!(?command, "will restart flycheck"); - match CommandHandle::spawn(command) { + let (sender, receiver) = unbounded(); + match CommandHandle::spawn(command, sender) { Ok(command_handle) => { - tracing::debug!(command = formatted_command, "did restart flycheck"); + tracing::debug!(command = formatted_command, "did restart flycheck"); self.command_handle = Some(command_handle); + self.command_receiver = Some(receiver); self.report_progress(Progress::DidStart); } Err(error) => { @@ -272,13 +307,15 @@ impl FlycheckActor { // Watcher finished let command_handle = self.command_handle.take().unwrap(); + self.command_receiver.take(); let formatted_handle = format!("{:?}", command_handle); let res = command_handle.join(); - if res.is_err() { + if let Err(error) = &res { tracing::error!( - "Flycheck failed to run the following command: {}", - formatted_handle + "Flycheck failed to run the following command: {}, error={}", + formatted_handle, + error ); } self.report_progress(Progress::DidFinish(res)); @@ -332,18 +369,7 @@ impl FlycheckActor { saved_file: Option<&AbsPath>, ) -> Option { let (mut cmd, args) = match &self.config { - FlycheckConfig::CargoCommand { - command, - target_triples, - no_default_features, - all_targets, - all_features, - extra_args, - features, - extra_env, - ansi_color_output, - target_dir, - } => { + FlycheckConfig::CargoCommand { command, options, ansi_color_output } => { let mut cmd = Command::new(Tool::Cargo.path()); if let Some(sysroot_root) = &self.sysroot_root { cmd.env("RUSTUP_TOOLCHAIN", AsRef::::as_ref(sysroot_root)); @@ -365,28 +391,8 @@ impl FlycheckActor { cmd.arg("--manifest-path"); cmd.arg(self.root.join("Cargo.toml")); - for target in target_triples { - cmd.args(["--target", target.as_str()]); - } - if *all_targets { - cmd.arg("--all-targets"); - } - if *all_features { - cmd.arg("--all-features"); - } else { - if *no_default_features { - cmd.arg("--no-default-features"); - } - if !features.is_empty() { - cmd.arg("--features"); - cmd.arg(features.join(" ")); - } - } - if let Some(target_dir) = target_dir { - cmd.arg("--target-dir").arg(target_dir); - } - cmd.envs(extra_env); - (cmd, extra_args.clone()) + options.apply_on_command(&mut cmd); + (cmd, options.extra_args.clone()) } FlycheckConfig::CustomCommand { command, diff --git a/src/tools/rust-analyzer/crates/flycheck/src/test_runner.rs b/src/tools/rust-analyzer/crates/flycheck/src/test_runner.rs index 9f761c9ead18..c136dd13664a 100644 --- a/src/tools/rust-analyzer/crates/flycheck/src/test_runner.rs +++ b/src/tools/rust-analyzer/crates/flycheck/src/test_runner.rs @@ -3,11 +3,15 @@ use std::process::Command; -use crossbeam_channel::Receiver; +use crossbeam_channel::Sender; +use paths::AbsPath; use serde::Deserialize; use toolchain::Tool; -use crate::command::{CommandHandle, ParseFromLine}; +use crate::{ + command::{CommandHandle, ParseFromLine}, + CargoOptions, +}; #[derive(Debug, Deserialize)] #[serde(tag = "event", rename_all = "camelCase")] @@ -51,30 +55,34 @@ impl ParseFromLine for CargoTestMessage { #[derive(Debug)] pub struct CargoTestHandle { - handle: CommandHandle, + _handle: CommandHandle, } // Example of a cargo test command: // cargo test --workspace --no-fail-fast -- module::func -Z unstable-options --format=json impl CargoTestHandle { - pub fn new(path: Option<&str>) -> std::io::Result { + pub fn new( + path: Option<&str>, + options: CargoOptions, + root: &AbsPath, + sender: Sender, + ) -> std::io::Result { let mut cmd = Command::new(Tool::Cargo.path()); cmd.env("RUSTC_BOOTSTRAP", "1"); cmd.arg("test"); cmd.arg("--workspace"); // --no-fail-fast is needed to ensure that all requested tests will run cmd.arg("--no-fail-fast"); + cmd.arg("--manifest-path"); + cmd.arg(root.join("Cargo.toml")); + options.apply_on_command(&mut cmd); cmd.arg("--"); if let Some(path) = path { cmd.arg(path); } cmd.args(["-Z", "unstable-options"]); cmd.arg("--format=json"); - Ok(Self { handle: CommandHandle::spawn(cmd)? }) - } - - pub fn receiver(&self) -> &Receiver { - &self.handle.receiver + Ok(Self { _handle: CommandHandle::spawn(cmd, sender)? }) } } diff --git a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml index 523ff6fc404e..41c59ea0d93f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml @@ -54,7 +54,7 @@ test-utils.workspace = true test-fixture.workspace = true [features] -in-rust-tree = [] +in-rust-tree = ["hir-expand/in-rust-tree"] [lints] workspace = true 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 fa7730f302ec..d9eeffd7983b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs @@ -5,7 +5,7 @@ pub mod builtin; #[cfg(test)] mod tests; -use std::{hash::Hash, ops, slice::Iter as SliceIter}; +use std::{borrow::Cow, hash::Hash, ops, slice::Iter as SliceIter}; use base_db::CrateId; use cfg::{CfgExpr, CfgOptions}; @@ -141,6 +141,10 @@ impl Attrs { } } + pub fn cfgs(&self) -> impl Iterator + '_ { + self.by_key("cfg").tt_values().map(CfgExpr::parse) + } + pub(crate) fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> bool { match self.cfg() { None => true, @@ -569,6 +573,10 @@ impl<'attr> AttrQuery<'attr> { self.attrs().find_map(|attr| attr.string_value()) } + pub fn string_value_unescape(self) -> Option> { + self.attrs().find_map(|attr| attr.string_value_unescape()) + } + pub fn exists(self) -> bool { self.attrs().next().is_some() } 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 index 1a63e96bfa92..9b68797fbf70 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attr/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attr/tests.rs @@ -11,7 +11,7 @@ use syntax::{ast, AstNode, TextRange}; use crate::attr::{DocAtom, DocExpr}; fn assert_parse_result(input: &str, expected: DocExpr) { - let source_file = ast::SourceFile::parse(input).ok().unwrap(); + let source_file = ast::SourceFile::parse(input, span::Edition::CURRENT).ok().unwrap(); let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); let map = SpanMap::RealSpanMap(Arc::new(RealSpanMap::absolute(FileId::from_raw(0)))); let tt = syntax_node_to_token_tree( 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 da790f11516d..e3d750d33cad 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/data.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/data.rs @@ -510,6 +510,7 @@ pub struct ConstData { pub type_ref: Interned, pub visibility: RawVisibility, pub rustc_allow_incoherent_impl: bool, + pub has_body: bool, } impl ConstData { @@ -533,6 +534,7 @@ impl ConstData { type_ref: konst.type_ref.clone(), visibility, rustc_allow_incoherent_impl, + has_body: konst.has_body, }) } } @@ -737,7 +739,7 @@ impl<'a> AssocItemCollector<'a> { &AstIdWithPath::new(file_id, ast_id, Clone::clone(path)), ctxt, expand_to, - self.expander.module.krate(), + self.expander.krate(), resolver, ) { Ok(Some(call_id)) => { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs index a7461b78af1c..0fe73418e51f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs @@ -26,7 +26,7 @@ use crate::{ tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree}, type_ref::TypeRef, visibility::RawVisibility, - EnumId, EnumVariantId, LocalFieldId, LocalModuleId, Lookup, StructId, UnionId, + EnumId, EnumVariantId, LocalFieldId, LocalModuleId, Lookup, StructId, UnionId, VariantId, }; /// Note that we use `StructData` for unions as well! @@ -191,8 +191,6 @@ impl StructData { let krate = loc.container.krate; 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()[krate].cfg_options.clone(); - let attrs = item_tree.attrs(db, krate, ModItem::from(loc.id.value).into()); let mut flags = StructFlags::NO_FLAGS; @@ -219,7 +217,7 @@ impl StructData { loc.id.file_id(), loc.container.local_id, &item_tree, - &cfg_options, + &db.crate_graph()[krate].cfg_options, &strukt.fields, None, ); @@ -248,8 +246,6 @@ impl StructData { let krate = loc.container.krate; 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()[krate].cfg_options.clone(); - let attrs = item_tree.attrs(db, krate, ModItem::from(loc.id.value).into()); let mut flags = StructFlags::NO_FLAGS; if attrs.by_key("rustc_has_incoherent_inherent_impls").exists() { @@ -266,7 +262,7 @@ impl StructData { loc.id.file_id(), loc.container.local_id, &item_tree, - &cfg_options, + &db.crate_graph()[krate].cfg_options, &union.fields, None, ); @@ -338,7 +334,6 @@ impl EnumVariantData { let container = loc.parent.lookup(db).container; let krate = container.krate; let item_tree = loc.id.item_tree(db); - let cfg_options = db.crate_graph()[krate].cfg_options.clone(); let variant = &item_tree[loc.id.value]; let (var_data, diagnostics) = lower_fields( @@ -347,7 +342,7 @@ impl EnumVariantData { loc.id.file_id(), container.local_id, &item_tree, - &cfg_options, + &db.crate_graph()[krate].cfg_options, &variant.fields, Some(item_tree[loc.parent.lookup(db).id.value].visibility), ); @@ -383,6 +378,15 @@ impl VariantData { VariantData::Unit => StructKind::Unit, } } + + #[allow(clippy::self_named_constructors)] + pub(crate) fn variant_data(db: &dyn DefDatabase, id: VariantId) -> Arc { + match id { + VariantId::StructId(it) => db.struct_data(it).variant_data.clone(), + VariantId::EnumVariantId(it) => db.enum_variant_data(it).variant_data.clone(), + VariantId::UnionId(it) => db.union_data(it).variant_data.clone(), + } + } } #[derive(Debug, Copy, Clone, PartialEq, Eq)] 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 30d52d87f192..55ecabdc38e2 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/db.rs @@ -12,7 +12,7 @@ use crate::{ attr::{Attrs, AttrsWithOwner}, body::{scope::ExprScopes, Body, BodySourceMap}, data::{ - adt::{EnumData, EnumVariantData, StructData}, + adt::{EnumData, EnumVariantData, StructData, VariantData}, ConstData, ExternCrateDeclData, FunctionData, ImplData, Macro2Data, MacroRulesData, ProcMacroData, StaticData, TraitAliasData, TraitData, TypeAliasData, }, @@ -127,6 +127,9 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast (Arc, DefDiagnostics); + #[salsa::transparent] + #[salsa::invoke(VariantData::variant_data)] + fn variant_data(&self, id: VariantId) -> Arc; #[salsa::transparent] #[salsa::invoke(ImplData::impl_data_query)] fn impl_data(&self, e: ImplId) -> Arc; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expander.rs b/src/tools/rust-analyzer/crates/hir-def/src/expander.rs index b0872fcdc0e6..73ce942c5807 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expander.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expander.rs @@ -11,6 +11,7 @@ use hir_expand::{ }; use limit::Limit; use syntax::{ast, Parse}; +use triomphe::Arc; use crate::{ attr::Attrs, db::DefDatabase, lower::LowerCtx, path::Path, AsMacroCall, MacroId, ModuleId, @@ -19,9 +20,8 @@ use crate::{ #[derive(Debug)] pub struct Expander { - cfg_options: CfgOptions, + cfg_options: Arc, span_map: OnceCell, - krate: CrateId, current_file_id: HirFileId, pub(crate) module: ModuleId, /// `recursion_depth == usize::MAX` indicates that the recursion limit has been reached. @@ -45,10 +45,13 @@ impl Expander { recursion_limit, cfg_options: db.crate_graph()[module.krate].cfg_options.clone(), span_map: OnceCell::new(), - krate: module.krate, } } + pub fn krate(&self) -> CrateId { + self.module.krate + } + pub fn enter_expand( &mut self, db: &dyn DefDatabase, @@ -112,7 +115,7 @@ impl Expander { pub(crate) fn parse_attrs(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> Attrs { Attrs::filter( db, - self.krate, + self.krate(), RawAttrs::new( db.upcast(), owner, 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 0cd4a5db8c34..bf728a710793 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 @@ -30,6 +30,8 @@ pub fn find_path( find_path_inner(FindPathCtx { db, prefixed: None, prefer_no_std, prefer_prelude }, item, from) } +/// Find a path that can be used to refer to a certain item. This can depend on +/// *from where* you're referring to the item, hence the `from` parameter. pub fn find_path_prefixed( db: &dyn DefDatabase, item: ItemInNs, @@ -255,7 +257,7 @@ fn find_in_scope( item: ItemInNs, ) -> Option { def_map.with_ancestor_maps(db, from.local_id, &mut |def_map, local_id| { - def_map[local_id].scope.name_of(item).map(|(name, _, _)| name.clone()) + def_map[local_id].scope.names_of(item, |name, _, _| Some(name.clone())) }) } @@ -608,7 +610,8 @@ mod tests { ) { let (db, pos) = TestDB::with_position(ra_fixture); let module = db.module_at_position(pos); - let parsed_path_file = syntax::SourceFile::parse(&format!("use {path};")); + let parsed_path_file = + syntax::SourceFile::parse(&format!("use {path};"), span::Edition::CURRENT); let ast_path = parsed_path_file.syntax_node().descendants().find_map(syntax::ast::Path::cast).unwrap(); let mod_path = ModPath::from_src(&db, ast_path, &mut |range| { 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 4638b3771973..acc60e1d9e4e 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/generics.rs @@ -3,13 +3,15 @@ //! generic parameters. See also the `Generics` type and the `generics_of` query //! in rustc. +use std::ops; + use either::Either; use hir_expand::{ name::{AsName, Name}, ExpandResult, }; use intern::Interned; -use la_arena::{Arena, Idx}; +use la_arena::Arena; use once_cell::unsync::Lazy; use stdx::impl_from; use syntax::ast::{self, HasGenericParams, HasName, HasTypeBounds}; @@ -23,12 +25,14 @@ use crate::{ nameres::{DefMap, MacroSubNs}, type_ref::{ConstRef, LifetimeRef, TypeBound, TypeRef}, AdtId, ConstParamId, GenericDefId, HasModule, ItemTreeLoc, LifetimeParamId, - LocalTypeOrConstParamId, Lookup, TypeOrConstParamId, TypeParamId, + LocalLifetimeParamId, LocalTypeOrConstParamId, Lookup, TypeOrConstParamId, TypeParamId, }; /// Data about a generic type parameter (to a function, struct, impl, ...). #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub struct TypeParamData { + /// [`None`] only if the type ref is an [`TypeRef::ImplTrait`]. FIXME: Might be better to just + /// make it always be a value, giving impl trait a special name. pub name: Option, pub default: Option>, pub provenance: TypeParamProvenance, @@ -156,6 +160,20 @@ pub struct GenericParams { pub where_predicates: Box<[WherePredicate]>, } +impl ops::Index for GenericParams { + type Output = TypeOrConstParamData; + fn index(&self, index: LocalTypeOrConstParamId) -> &TypeOrConstParamData { + &self.type_or_consts[index] + } +} + +impl ops::Index for GenericParams { + type Output = LifetimeParamData; + fn index(&self, index: LocalLifetimeParamId) -> &LifetimeParamData { + &self.lifetimes[index] + } +} + /// A single predicate from a where clause, i.e. `where Type: Trait`. Combined /// where clauses like `where T: Foo + Bar` are turned into multiple of these. /// It might still result in multiple actual predicates though, because of @@ -197,7 +215,7 @@ impl GenericParamsCollector { lower_ctx: &LowerCtx<'_>, node: &dyn HasGenericParams, add_param_attrs: impl FnMut( - Either, Idx>, + Either, ast::GenericParam, ), ) { @@ -225,7 +243,7 @@ impl GenericParamsCollector { lower_ctx: &LowerCtx<'_>, params: ast::GenericParamList, mut add_param_attrs: impl FnMut( - Either, Idx>, + Either, ast::GenericParam, ), ) { @@ -414,16 +432,16 @@ impl GenericParams { } /// Iterator of type_or_consts field - pub fn iter( + pub fn iter_type_or_consts( &self, - ) -> impl DoubleEndedIterator, &TypeOrConstParamData)> { + ) -> impl DoubleEndedIterator { self.type_or_consts.iter() } /// Iterator of lifetimes field pub fn iter_lt( &self, - ) -> impl DoubleEndedIterator, &LifetimeParamData)> { + ) -> impl DoubleEndedIterator { self.lifetimes.iter() } 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 2b059d1f8dcd..a60b9f9f3ab7 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 @@ -277,13 +277,43 @@ impl ItemScope { ItemInNs::Types(def) => self.types.iter().find_map(|(name, &(other_def, vis, i))| { (other_def == def).then_some((name, vis, i.is_none())) }), - ItemInNs::Values(def) => self.values.iter().find_map(|(name, &(other_def, vis, i))| { (other_def == def).then_some((name, vis, i.is_none())) }), } } + /// XXX: this is O(N) rather than O(1), try to not introduce new usages. + pub(crate) fn names_of( + &self, + item: ItemInNs, + mut cb: impl FnMut(&Name, Visibility, bool) -> Option, + ) -> Option { + match item { + ItemInNs::Macros(def) => self + .macros + .iter() + .filter_map(|(name, &(other_def, vis, i))| { + (other_def == def).then_some((name, vis, i.is_none())) + }) + .find_map(|(a, b, c)| cb(a, b, c)), + ItemInNs::Types(def) => self + .types + .iter() + .filter_map(|(name, &(other_def, vis, i))| { + (other_def == def).then_some((name, vis, i.is_none())) + }) + .find_map(|(a, b, c)| cb(a, b, c)), + ItemInNs::Values(def) => self + .values + .iter() + .filter_map(|(name, &(other_def, vis, i))| { + (other_def == def).then_some((name, vis, i.is_none())) + }) + .find_map(|(a, b, c)| cb(a, b, c)), + } + } + pub(crate) fn traits(&self) -> impl Iterator + '_ { self.types .values() 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 585e93ce21e1..610480736ccd 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 @@ -716,6 +716,7 @@ pub struct Const { pub visibility: RawVisibilityId, pub type_ref: Interned, pub ast_id: FileAstId, + pub has_body: bool, } #[derive(Debug, Clone, Eq, PartialEq)] 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 f02163cbe44f..4b5ef56d782e 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 @@ -446,7 +446,7 @@ impl<'a> Ctx<'a> { let type_ref = self.lower_type_ref_opt(konst.ty()); let visibility = self.lower_visibility(konst); let ast_id = self.source_ast_id_map.ast_id(konst); - let res = Const { name, visibility, type_ref, ast_id }; + let res = Const { name, visibility, type_ref, ast_id, has_body: konst.body().is_some() }; id(self.data().consts.alloc(res)) } 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 0c84057950bd..cef2a3fb8663 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 @@ -357,7 +357,7 @@ impl Printer<'_> { wln!(self, "}}"); } ModItem::Const(it) => { - let Const { name, visibility, type_ref, ast_id } = &self.tree[it]; + let Const { name, visibility, type_ref, ast_id, has_body: _ } = &self.tree[it]; self.print_ast_id(ast_id.erase()); self.print_visibility(*visibility); w!(self, "const "); 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 46898ce542d6..88d4572196cd 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -422,6 +422,10 @@ impl ModuleId { } } + pub fn crate_def_map(self, db: &dyn DefDatabase) -> Arc { + db.crate_def_map(self.krate) + } + pub fn krate(self) -> CrateId { self.krate } @@ -438,6 +442,8 @@ impl ModuleId { }) } + /// Returns the module containing `self`, either the parent `mod`, or the module (or block) containing + /// the block, if `self` corresponds to a block expression. pub fn containing_module(self, db: &dyn DefDatabase) -> Option { self.def_map(db).containing_module(self.local_id) } @@ -929,6 +935,18 @@ impl GenericDefId { GenericDefId::EnumVariantId(_) => (FileId::BOGUS.into(), None), } } + + pub fn assoc_trait_container(self, db: &dyn DefDatabase) -> Option { + match match self { + GenericDefId::FunctionId(f) => f.lookup(db).container, + GenericDefId::TypeAliasId(t) => t.lookup(db).container, + GenericDefId::ConstId(c) => c.lookup(db).container, + _ => return None, + } { + ItemContainerId::TraitId(trait_) => Some(trait_), + _ => None, + } + } } impl From for GenericDefId { 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 89c1b446081c..163211fea526 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 @@ -610,6 +610,10 @@ struct Foo { field1: i32, #[cfg(never)] field2: (), + #[cfg(feature = "never")] + field3: (), + #[cfg(not(feature = "never"))] + field4: (), } #[derive(Default)] enum Bar { @@ -618,12 +622,16 @@ enum Bar { Bar, } "#, - expect![[r#" + expect![[r##" #[derive(Default)] struct Foo { field1: i32, #[cfg(never)] field2: (), + #[cfg(feature = "never")] + field3: (), + #[cfg(not(feature = "never"))] + field4: (), } #[derive(Default)] enum Bar { @@ -635,7 +643,7 @@ enum Bar { impl < > $crate::default::Default for Foo< > where { fn default() -> Self { Foo { - field1: $crate::default::Default::default(), + field1: $crate::default::Default::default(), field4: $crate::default::Default::default(), } } } @@ -643,6 +651,6 @@ impl < > $crate::default::Default for Bar< > where { fn default() -> Self { Bar::Bar } -}"#]], +}"##]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs index 23b10cfd8e6c..8904aca9f284 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs @@ -316,8 +316,11 @@ impl ProcMacroExpander for IdentityWhenValidProcMacroExpander { _: Span, _: Span, ) -> Result { - let (parse, _) = - ::mbe::token_tree_to_syntax_node(subtree, ::mbe::TopEntryPoint::MacroItems); + let (parse, _) = ::mbe::token_tree_to_syntax_node( + subtree, + ::mbe::TopEntryPoint::MacroItems, + span::Edition::CURRENT, + ); if parse.errors().is_empty() { Ok(subtree.clone()) } else { 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 ae8f028e488d..0a6cd0fe9ed5 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 @@ -534,8 +534,7 @@ impl DefCollector<'_> { Edition::Edition2015 => name![rust_2015], Edition::Edition2018 => name![rust_2018], Edition::Edition2021 => name![rust_2021], - // FIXME: update this when rust_2024 exists - Edition::Edition2024 => name![rust_2021], + Edition::Edition2024 => name![rust_2024], }; let path_kind = match self.def_map.data.edition { @@ -1918,7 +1917,7 @@ impl ModCollector<'_, '_> { } fn collect_module(&mut self, module_id: FileItemTreeId, attrs: &Attrs) { - let path_attr = attrs.by_key("path").string_value(); + let path_attr = attrs.by_key("path").string_value_unescape(); let is_macro_use = attrs.by_key("macro_use").exists(); let module = &self.item_tree[module_id]; match &module.kind { @@ -1932,7 +1931,8 @@ impl ModCollector<'_, '_> { module_id, ); - let Some(mod_dir) = self.mod_dir.descend_into_definition(&module.name, path_attr) + let Some(mod_dir) = + self.mod_dir.descend_into_definition(&module.name, path_attr.as_deref()) else { return; }; @@ -1953,8 +1953,12 @@ impl ModCollector<'_, '_> { ModKind::Outline => { let ast_id = AstId::new(self.file_id(), module.ast_id); let db = self.def_collector.db; - match self.mod_dir.resolve_declaration(db, self.file_id(), &module.name, path_attr) - { + match self.mod_dir.resolve_declaration( + db, + self.file_id(), + &module.name, + path_attr.as_deref(), + ) { Ok((file_id, is_mod_rs, mod_dir)) => { let item_tree = db.file_item_tree(file_id.into()); let krate = self.def_collector.def_map.krate; diff --git a/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml b/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml index 4f3080801565..ca05618aecd9 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml @@ -32,9 +32,13 @@ tt.workspace = true mbe.workspace = true limit.workspace = true span.workspace = true +parser.workspace = true [dev-dependencies] expect-test = "1.4.0" +[features] +in-rust-tree = ["syntax/in-rust-tree"] + [lints] workspace = true 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 f1540498f266..f8bf88d83cd9 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}; +use std::{borrow::Cow, fmt, ops}; use base_db::CrateId; use cfg::CfgExpr; @@ -8,6 +8,7 @@ use intern::Interned; use mbe::{syntax_node_to_token_tree, DelimiterKind, Punct}; use smallvec::{smallvec, SmallVec}; use span::{Span, SyntaxContextId}; +use syntax::unescape; use syntax::{ast, format_smolstr, match_ast, AstNode, AstToken, SmolStr, SyntaxNode}; use triomphe::ThinArc; @@ -54,8 +55,7 @@ impl RawAttrs { Attr { id, input: Some(Interned::new(AttrInput::Literal(tt::Literal { - // FIXME: Escape quotes from comment content - text: SmolStr::new(format_smolstr!("\"{doc}\"",)), + text: SmolStr::new(format_smolstr!("\"{}\"", Self::escape_chars(doc))), span, }))), path: Interned::new(ModPath::from(crate::name!(doc))), @@ -74,6 +74,10 @@ impl RawAttrs { RawAttrs { entries } } + fn escape_chars(s: &str) -> String { + s.replace('\\', r#"\\"#).replace('"', r#"\""#) + } + pub fn from_attrs_owner( db: &dyn ExpandDatabase, owner: InFile<&dyn ast::HasAttrs>, @@ -297,6 +301,18 @@ impl Attr { } } + pub fn string_value_unescape(&self) -> Option> { + match self.input.as_deref()? { + AttrInput::Literal(it) => match it.text.strip_prefix('r') { + Some(it) => { + it.trim_matches('#').strip_prefix('"')?.strip_suffix('"').map(Cow::Borrowed) + } + None => it.text.strip_prefix('"')?.strip_suffix('"').and_then(unescape), + }, + _ => None, + } + } + /// #[path(ident)] pub fn single_ident_value(&self) -> Option<&tt::Ident> { match self.input.as_deref()? { @@ -346,6 +362,33 @@ impl Attr { } } +fn unescape(s: &str) -> Option> { + let mut buf = String::new(); + let mut prev_end = 0; + let mut has_error = false; + unescape::unescape_unicode(s, unescape::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(s.len()); + buf.push_str(&s[..prev_end]); + buf.push(c); + } + (Err(_), _) => has_error = true, + }); + + match (has_error, buf.capacity() == 0) { + (true, _) => None, + (false, false) => Some(Cow::Owned(buf)), + (false, true) => Some(Cow::Borrowed(s)), + } +} + pub fn collect_attrs( owner: &dyn ast::HasAttrs, ) -> impl Iterator)> { 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 528038a9ccfa..94681b42a9b1 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 @@ -204,7 +204,11 @@ struct BasicAdtInfo { } fn parse_adt(tt: &tt::Subtree, call_site: Span) -> Result { - let (parsed, tm) = &mbe::token_tree_to_syntax_node(tt, mbe::TopEntryPoint::MacroItems); + let (parsed, tm) = &mbe::token_tree_to_syntax_node( + tt, + mbe::TopEntryPoint::MacroItems, + parser::Edition::CURRENT, + ); let macro_items = ast::MacroItems::cast(parsed.syntax_node()) .ok_or_else(|| ExpandError::other("invalid item definition"))?; let item = macro_items.items().next().ok_or_else(|| ExpandError::other("no item found"))?; 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 fd3e4e7a4dba..4d6fe6db396e 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 @@ -219,7 +219,7 @@ fn assert_expand( span: Span, ) -> ExpandResult { let call_site_span = span_with_call_site_ctxt(db, span, id); - let args = parse_exprs_with_sep(tt, ',', call_site_span); + let args = parse_exprs_with_sep(tt, ',', call_site_span, Edition::CURRENT); let dollar_crate = dollar_crate(span); let expanded = match &*args { [cond, panic_args @ ..] => { diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs b/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs index db3558a84e9e..9dd44262ba95 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs @@ -1,57 +1,59 @@ //! Processes out #[cfg] and #[cfg_attr] attributes from the input for the derive macro use std::iter::Peekable; +use base_db::CrateId; use cfg::{CfgAtom, CfgExpr}; use rustc_hash::FxHashSet; use syntax::{ ast::{self, Attr, HasAttrs, Meta, VariantList}, - AstNode, NodeOrToken, SyntaxElement, SyntaxNode, T, + AstNode, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, T, }; use tracing::{debug, warn}; use tt::SmolStr; use crate::{db::ExpandDatabase, proc_macro::ProcMacroKind, MacroCallLoc, MacroDefKind}; -fn check_cfg_attr(attr: &Attr, loc: &MacroCallLoc, db: &dyn ExpandDatabase) -> Option { +fn check_cfg(db: &dyn ExpandDatabase, attr: &Attr, krate: CrateId) -> Option { if !attr.simple_name().as_deref().map(|v| v == "cfg")? { return None; } - debug!("Evaluating cfg {}", attr); let cfg = parse_from_attr_meta(attr.meta()?)?; - debug!("Checking cfg {:?}", cfg); - let enabled = db.crate_graph()[loc.krate].cfg_options.check(&cfg) != Some(false); + let enabled = db.crate_graph()[krate].cfg_options.check(&cfg) != Some(false); Some(enabled) } -fn check_cfg_attr_attr(attr: &Attr, loc: &MacroCallLoc, db: &dyn ExpandDatabase) -> Option { +fn check_cfg_attr(db: &dyn ExpandDatabase, attr: &Attr, krate: CrateId) -> Option { if !attr.simple_name().as_deref().map(|v| v == "cfg_attr")? { return None; } - debug!("Evaluating cfg_attr {}", attr); let cfg_expr = parse_from_attr_meta(attr.meta()?)?; - debug!("Checking cfg_attr {:?}", cfg_expr); - let enabled = db.crate_graph()[loc.krate].cfg_options.check(&cfg_expr) != Some(false); + let enabled = db.crate_graph()[krate].cfg_options.check(&cfg_expr) != Some(false); Some(enabled) } fn process_has_attrs_with_possible_comma( - items: impl Iterator, - loc: &MacroCallLoc, db: &dyn ExpandDatabase, + items: impl Iterator, + krate: CrateId, remove: &mut FxHashSet, ) -> Option<()> { for item in items { let field_attrs = item.attrs(); 'attrs: for attr in field_attrs { - if check_cfg_attr(&attr, loc, db).map(|enabled| !enabled).unwrap_or_default() { - debug!("censoring type {:?}", item.syntax()); - remove.insert(item.syntax().clone().into()); - // We need to remove the , as well - remove_possible_comma(&item, remove); - break 'attrs; + if let Some(enabled) = check_cfg(db, &attr, krate) { + if enabled { + debug!("censoring {:?}", attr.syntax()); + remove.insert(attr.syntax().clone().into()); + } else { + debug!("censoring {:?}", item.syntax()); + remove.insert(item.syntax().clone().into()); + // We need to remove the , as well + remove_possible_comma(&item, remove); + break 'attrs; + } } - if let Some(enabled) = check_cfg_attr_attr(&attr, loc, db) { + if let Some(enabled) = check_cfg_attr(db, &attr, krate) { if enabled { debug!("Removing cfg_attr tokens {:?}", attr); let meta = attr.meta()?; @@ -60,13 +62,13 @@ fn process_has_attrs_with_possible_comma( } else { debug!("censoring type cfg_attr {:?}", item.syntax()); remove.insert(attr.syntax().clone().into()); - continue; } } } } Some(()) } + #[derive(Debug, PartialEq, Eq, Clone, Copy)] enum CfgExprStage { /// Stripping the CFGExpr part of the attribute @@ -78,6 +80,7 @@ enum CfgExprStage { // Related Issue: https://github.com/rust-lang/rust-analyzer/issues/10110 EverythingElse, } + /// This function creates its own set of tokens to remove. To help prevent malformed syntax as input. fn remove_tokens_within_cfg_attr(meta: Meta) -> Option> { let mut remove: FxHashSet = FxHashSet::default(); @@ -131,23 +134,28 @@ fn remove_possible_comma(item: &impl AstNode, res: &mut FxHashSet } } fn process_enum( - variants: VariantList, - loc: &MacroCallLoc, db: &dyn ExpandDatabase, + variants: VariantList, + krate: CrateId, remove: &mut FxHashSet, ) -> Option<()> { 'variant: for variant in variants.variants() { for attr in variant.attrs() { - if check_cfg_attr(&attr, loc, db).map(|enabled| !enabled).unwrap_or_default() { - // Rustc does not strip the attribute if it is enabled. So we will leave it - debug!("censoring type {:?}", variant.syntax()); - remove.insert(variant.syntax().clone().into()); - // We need to remove the , as well - remove_possible_comma(&variant, remove); - continue 'variant; - }; + if let Some(enabled) = check_cfg(db, &attr, krate) { + if enabled { + debug!("censoring {:?}", attr.syntax()); + remove.insert(attr.syntax().clone().into()); + } else { + // Rustc does not strip the attribute if it is enabled. So we will leave it + debug!("censoring type {:?}", variant.syntax()); + remove.insert(variant.syntax().clone().into()); + // We need to remove the , as well + remove_possible_comma(&variant, remove); + continue 'variant; + } + } - if let Some(enabled) = check_cfg_attr_attr(&attr, loc, db) { + if let Some(enabled) = check_cfg_attr(db, &attr, krate) { if enabled { debug!("Removing cfg_attr tokens {:?}", attr); let meta = attr.meta()?; @@ -156,17 +164,16 @@ fn process_enum( } else { debug!("censoring type cfg_attr {:?}", variant.syntax()); remove.insert(attr.syntax().clone().into()); - continue; } } } if let Some(fields) = variant.field_list() { match fields { ast::FieldList::RecordFieldList(fields) => { - process_has_attrs_with_possible_comma(fields.fields(), loc, db, remove)?; + process_has_attrs_with_possible_comma(db, fields.fields(), krate, remove)?; } ast::FieldList::TupleFieldList(fields) => { - process_has_attrs_with_possible_comma(fields.fields(), loc, db, remove)?; + process_has_attrs_with_possible_comma(db, fields.fields(), krate, remove)?; } } } @@ -175,9 +182,9 @@ fn process_enum( } pub(crate) fn process_cfg_attrs( + db: &dyn ExpandDatabase, node: &SyntaxNode, loc: &MacroCallLoc, - db: &dyn ExpandDatabase, ) -> Option> { // FIXME: #[cfg_eval] is not implemented. But it is not stable yet let is_derive = match loc.def.kind { @@ -193,36 +200,35 @@ pub(crate) fn process_cfg_attrs( let item = ast::Item::cast(node.clone())?; for attr in item.attrs() { - if let Some(enabled) = check_cfg_attr_attr(&attr, loc, db) { + if let Some(enabled) = check_cfg_attr(db, &attr, loc.krate) { if enabled { debug!("Removing cfg_attr tokens {:?}", attr); let meta = attr.meta()?; let removes_from_cfg_attr = remove_tokens_within_cfg_attr(meta)?; remove.extend(removes_from_cfg_attr); } else { - debug!("censoring type cfg_attr {:?}", item.syntax()); + debug!("Removing type cfg_attr {:?}", item.syntax()); remove.insert(attr.syntax().clone().into()); - continue; } } } match item { ast::Item::Struct(it) => match it.field_list()? { ast::FieldList::RecordFieldList(fields) => { - process_has_attrs_with_possible_comma(fields.fields(), loc, db, &mut remove)?; + process_has_attrs_with_possible_comma(db, fields.fields(), loc.krate, &mut remove)?; } ast::FieldList::TupleFieldList(fields) => { - process_has_attrs_with_possible_comma(fields.fields(), loc, db, &mut remove)?; + process_has_attrs_with_possible_comma(db, fields.fields(), loc.krate, &mut remove)?; } }, ast::Item::Enum(it) => { - process_enum(it.variant_list()?, loc, db, &mut remove)?; + process_enum(db, it.variant_list()?, loc.krate, &mut remove)?; } ast::Item::Union(it) => { process_has_attrs_with_possible_comma( - it.record_field_list()?.fields(), - loc, db, + it.record_field_list()?.fields(), + loc.krate, &mut remove, )?; } @@ -234,10 +240,22 @@ pub(crate) fn process_cfg_attrs( /// Parses a `cfg` attribute from the meta fn parse_from_attr_meta(meta: Meta) -> Option { let tt = meta.token_tree()?; - let mut iter = tt.token_trees_and_tokens().skip(1).peekable(); + let mut iter = tt + .token_trees_and_tokens() + .filter(is_not_whitespace) + .skip(1) + .take_while(is_not_closing_paren) + .peekable(); next_cfg_expr_from_syntax(&mut iter) } +fn is_not_closing_paren(element: &NodeOrToken) -> bool { + !matches!(element, NodeOrToken::Token(token) if (token.kind() == syntax::T![')'])) +} +fn is_not_whitespace(element: &NodeOrToken) -> bool { + !matches!(element, NodeOrToken::Token(token) if (token.kind() == SyntaxKind::WHITESPACE)) +} + fn next_cfg_expr_from_syntax(iter: &mut Peekable) -> Option where I: Iterator>, @@ -256,14 +274,13 @@ where let Some(NodeOrToken::Node(tree)) = iter.next() else { return Some(CfgExpr::Invalid); }; - let mut tree_iter = tree.token_trees_and_tokens().skip(1).peekable(); - while tree_iter - .peek() - .filter( - |element| matches!(element, NodeOrToken::Token(token) if (token.kind() != syntax::T![')'])), - ) - .is_some() - { + let mut tree_iter = tree + .token_trees_and_tokens() + .filter(is_not_whitespace) + .skip(1) + .take_while(is_not_closing_paren) + .peekable(); + while tree_iter.peek().is_some() { let pred = next_cfg_expr_from_syntax(&mut tree_iter); if let Some(pred) = pred { preds.push(pred); @@ -310,7 +327,7 @@ mod tests { use crate::cfg_process::parse_from_attr_meta; fn check_dnf_from_syntax(input: &str, expect: Expect) { - let parse = SourceFile::parse(input); + let parse = SourceFile::parse(input, span::Edition::CURRENT); let node = match parse.tree().syntax().descendants().find_map(Attr::cast) { Some(it) => it, None => { 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 5461c1c49a32..d7233a8923fe 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs @@ -3,7 +3,7 @@ use base_db::{salsa, CrateId, FileId, SourceDatabase}; use either::Either; use limit::Limit; -use mbe::syntax_node_to_token_tree; +use mbe::{syntax_node_to_token_tree, MatchedArmIndex}; use rustc_hash::FxHashSet; use span::{AstIdMap, Span, SyntaxContextData, SyntaxContextId}; use syntax::{ast, AstNode, Parse, SyntaxElement, SyntaxError, SyntaxNode, SyntaxToken, T}; @@ -175,7 +175,7 @@ pub fn expand_speculative( }; let censor_cfg = - cfg_process::process_cfg_attrs(speculative_args, &loc, db).unwrap_or_default(); + cfg_process::process_cfg_attrs(db, speculative_args, &loc).unwrap_or_default(); let mut fixups = fixup::fixup_syntax(span_map, speculative_args, span); fixups.append.retain(|it, _| match it { syntax::NodeOrToken::Token(_) => true, @@ -225,43 +225,45 @@ pub fn expand_speculative( // Do the actual expansion, we need to directly expand the proc macro due to the attribute args // Otherwise the expand query will fetch the non speculative attribute args and pass those instead. - let mut speculative_expansion = match loc.def.kind { - MacroDefKind::ProcMacro(expander, _, ast) => { - let span = db.proc_macro_span(ast); - tt.delimiter = tt::Delimiter::invisible_spanned(span); - expander.expand( - db, - loc.def.krate, - loc.krate, - &tt, - attr_arg.as_ref(), - span_with_def_site_ctxt(db, span, actual_macro_call), - span_with_call_site_ctxt(db, span, actual_macro_call), - span_with_mixed_site_ctxt(db, span, actual_macro_call), - ) - } - MacroDefKind::BuiltInAttr(BuiltinAttrExpander::Derive, _) => { - pseudo_derive_attr_expansion(&tt, attr_arg.as_ref()?, span) - } - MacroDefKind::Declarative(it) => { - db.decl_macro_expander(loc.krate, it).expand_unhygienic(db, tt, loc.def.krate, span) - } - MacroDefKind::BuiltIn(it, _) => { - it.expand(db, actual_macro_call, &tt, span).map_err(Into::into) - } - MacroDefKind::BuiltInDerive(it, ..) => { - it.expand(db, actual_macro_call, &tt, span).map_err(Into::into) - } - MacroDefKind::BuiltInEager(it, _) => { - it.expand(db, actual_macro_call, &tt, span).map_err(Into::into) - } - MacroDefKind::BuiltInAttr(it, _) => it.expand(db, actual_macro_call, &tt, span), - }; + let mut speculative_expansion = + match loc.def.kind { + MacroDefKind::ProcMacro(expander, _, ast) => { + let span = db.proc_macro_span(ast); + tt.delimiter = tt::Delimiter::invisible_spanned(span); + expander.expand( + db, + loc.def.krate, + loc.krate, + &tt, + attr_arg.as_ref(), + span_with_def_site_ctxt(db, span, actual_macro_call), + span_with_call_site_ctxt(db, span, actual_macro_call), + span_with_mixed_site_ctxt(db, span, actual_macro_call), + ) + } + MacroDefKind::BuiltInAttr(BuiltinAttrExpander::Derive, _) => { + pseudo_derive_attr_expansion(&tt, attr_arg.as_ref()?, span) + } + MacroDefKind::Declarative(it) => db + .decl_macro_expander(loc.krate, it) + .expand_unhygienic(db, tt, loc.def.krate, span, loc.def.edition), + MacroDefKind::BuiltIn(it, _) => { + it.expand(db, actual_macro_call, &tt, span).map_err(Into::into) + } + MacroDefKind::BuiltInDerive(it, ..) => { + it.expand(db, actual_macro_call, &tt, span).map_err(Into::into) + } + MacroDefKind::BuiltInEager(it, _) => { + it.expand(db, actual_macro_call, &tt, span).map_err(Into::into) + } + MacroDefKind::BuiltInAttr(it, _) => it.expand(db, actual_macro_call, &tt, span), + }; let expand_to = loc.expand_to(); fixup::reverse_fixups(&mut speculative_expansion.value, &undo_info); - let (node, rev_tmap) = token_tree_to_syntax_node(&speculative_expansion.value, expand_to); + let (node, rev_tmap) = + token_tree_to_syntax_node(&speculative_expansion.value, expand_to, loc.def.edition); let syntax_node = node.syntax_node(); let token = rev_tmap @@ -309,16 +311,20 @@ fn parse_macro_expansion( ) -> ExpandResult<(Parse, Arc)> { let _p = tracing::span!(tracing::Level::INFO, "parse_macro_expansion").entered(); let loc = db.lookup_intern_macro_call(macro_file.macro_call_id); + let edition = loc.def.edition; let expand_to = loc.expand_to(); - let mbe::ValueResult { value: tt, err } = macro_expand(db, macro_file.macro_call_id, loc); + let mbe::ValueResult { value: (tt, matched_arm), err } = + macro_expand(db, macro_file.macro_call_id, loc); - let (parse, rev_token_map) = token_tree_to_syntax_node( + let (parse, mut rev_token_map) = token_tree_to_syntax_node( match &tt { CowArc::Arc(it) => it, CowArc::Owned(it) => it, }, expand_to, + edition, ); + rev_token_map.matched_arm = matched_arm; ExpandResult { value: (parse, Arc::new(rev_token_map)), err } } @@ -462,7 +468,7 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult { let (mut tt, undo_info) = { let syntax = item_node.syntax(); - let censor_cfg = cfg_process::process_cfg_attrs(syntax, &loc, db).unwrap_or_default(); + let censor_cfg = cfg_process::process_cfg_attrs(db, syntax, &loc).unwrap_or_default(); let mut fixups = fixup::fixup_syntax(map.as_ref(), syntax, span); fixups.append.retain(|it, _| match it { syntax::NodeOrToken::Token(_) => true, @@ -540,11 +546,13 @@ fn macro_expand( db: &dyn ExpandDatabase, macro_call_id: MacroCallId, loc: MacroCallLoc, -) -> ExpandResult> { +) -> ExpandResult<(CowArc, MatchedArmIndex)> { let _p = tracing::span!(tracing::Level::INFO, "macro_expand").entered(); - let (ExpandResult { value: tt, err }, span) = match loc.def.kind { - MacroDefKind::ProcMacro(..) => return db.expand_proc_macro(macro_call_id).map(CowArc::Arc), + let (ExpandResult { value: (tt, matched_arm), err }, span) = match loc.def.kind { + MacroDefKind::ProcMacro(..) => { + return db.expand_proc_macro(macro_call_id).map(CowArc::Arc).zip_val(None) + } _ => { let (macro_arg, undo_info, span) = db.macro_arg_considering_derives(macro_call_id, &loc.kind); @@ -556,10 +564,10 @@ fn macro_expand( .decl_macro_expander(loc.def.krate, id) .expand(db, arg.clone(), macro_call_id, span), MacroDefKind::BuiltIn(it, _) => { - it.expand(db, macro_call_id, arg, span).map_err(Into::into) + it.expand(db, macro_call_id, arg, span).map_err(Into::into).zip_val(None) } MacroDefKind::BuiltInDerive(it, _) => { - it.expand(db, macro_call_id, arg, span).map_err(Into::into) + it.expand(db, macro_call_id, arg, span).map_err(Into::into).zip_val(None) } MacroDefKind::BuiltInEager(it, _) => { // This might look a bit odd, but we do not expand the inputs to eager macros here. @@ -570,7 +578,8 @@ fn macro_expand( // As such we just return the input subtree here. let eager = match &loc.kind { MacroCallKind::FnLike { eager: None, .. } => { - return ExpandResult::ok(CowArc::Arc(macro_arg.clone())); + return ExpandResult::ok(CowArc::Arc(macro_arg.clone())) + .zip_val(None); } MacroCallKind::FnLike { eager: Some(eager), .. } => Some(&**eager), _ => None, @@ -582,12 +591,12 @@ fn macro_expand( // FIXME: We should report both errors! res.err = error.clone().or(res.err); } - res + res.zip_val(None) } MacroDefKind::BuiltInAttr(it, _) => { let mut res = it.expand(db, macro_call_id, arg, span); fixup::reverse_fixups(&mut res.value, &undo_info); - res + res.zip_val(None) } _ => unreachable!(), }; @@ -599,16 +608,18 @@ fn macro_expand( if !loc.def.is_include() { // Set a hard limit for the expanded tt if let Err(value) = check_tt_count(&tt) { - return value.map(|()| { - CowArc::Owned(tt::Subtree { - delimiter: tt::Delimiter::invisible_spanned(span), - token_trees: Box::new([]), + return value + .map(|()| { + CowArc::Owned(tt::Subtree { + delimiter: tt::Delimiter::invisible_spanned(span), + token_trees: Box::new([]), + }) }) - }); + .zip_val(matched_arm); } } - ExpandResult { value: CowArc::Owned(tt), err } + ExpandResult { value: (CowArc::Owned(tt), matched_arm), err } } fn proc_macro_span(db: &dyn ExpandDatabase, ast: AstId) -> Span { @@ -668,6 +679,7 @@ fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult (Parse, ExpansionSpanMap) { let entry_point = match expand_to { ExpandTo::Statements => mbe::TopEntryPoint::MacroStmts, @@ -676,7 +688,7 @@ fn token_tree_to_syntax_node( ExpandTo::Type => mbe::TopEntryPoint::Type, ExpandTo::Expr => mbe::TopEntryPoint::Expr, }; - mbe::token_tree_to_syntax_node(tt, entry_point) + mbe::token_tree_to_syntax_node(tt, entry_point, edition) } fn check_tt_count(tt: &tt::Subtree) -> Result<(), ExpandResult<()>> { diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs index 9a0b218e6d1d..66465ce60057 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs @@ -2,7 +2,8 @@ use std::sync::OnceLock; use base_db::{CrateId, VersionReq}; -use span::{MacroCallId, Span, SyntaxContextId}; +use span::{Edition, MacroCallId, Span, SyntaxContextId}; +use stdx::TupleExt; use syntax::{ast, AstNode}; use triomphe::Arc; @@ -30,7 +31,7 @@ impl DeclarativeMacroExpander { tt: tt::Subtree, call_id: MacroCallId, span: Span, - ) -> ExpandResult { + ) -> ExpandResult<(tt::Subtree, Option)> { let loc = db.lookup_intern_macro_call(call_id); let toolchain = db.toolchain(loc.def.krate); let new_meta_vars = toolchain.as_ref().map_or(false, |version| { @@ -46,7 +47,7 @@ impl DeclarativeMacroExpander { }); match self.mac.err() { Some(_) => ExpandResult::new( - tt::Subtree::empty(tt::DelimSpan { open: span, close: span }), + (tt::Subtree::empty(tt::DelimSpan { open: span, close: span }), None), ExpandError::MacroDefinition, ), None => self @@ -56,6 +57,7 @@ impl DeclarativeMacroExpander { |s| s.ctx = apply_mark(db, s.ctx, call_id, self.transparency), new_meta_vars, span, + loc.def.edition, ) .map_err(Into::into), } @@ -67,6 +69,7 @@ impl DeclarativeMacroExpander { tt: tt::Subtree, krate: CrateId, call_site: Span, + def_site_edition: Edition, ) -> ExpandResult { let toolchain = db.toolchain(krate); let new_meta_vars = toolchain.as_ref().map_or(false, |version| { @@ -85,7 +88,11 @@ impl DeclarativeMacroExpander { tt::Subtree::empty(tt::DelimSpan { open: call_site, close: call_site }), ExpandError::MacroDefinition, ), - None => self.mac.expand(&tt, |_| (), new_meta_vars, call_site).map_err(Into::into), + None => self + .mac + .expand(&tt, |_| (), new_meta_vars, call_site, def_site_edition) + .map(TupleExt::head) + .map_err(Into::into), } } 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 959595afb572..711acfeb3d85 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs @@ -396,7 +396,7 @@ mod tests { #[track_caller] fn check(ra_fixture: &str, mut expect: Expect) { - let parsed = syntax::SourceFile::parse(ra_fixture); + let parsed = syntax::SourceFile::parse(ra_fixture, span::Edition::CURRENT); let span_map = SpanMap::RealSpanMap(Arc::new(RealSpanMap::absolute(FileId::from_raw(0)))); let fixups = super::fixup_syntax( span_map.as_ref(), @@ -417,7 +417,11 @@ mod tests { expect.assert_eq(&actual); // the fixed-up tree should be syntactically valid - let (parse, _) = mbe::token_tree_to_syntax_node(&tt, ::mbe::TopEntryPoint::MacroItems); + let (parse, _) = mbe::token_tree_to_syntax_node( + &tt, + ::mbe::TopEntryPoint::MacroItems, + parser::Edition::CURRENT, + ); assert!( parse.errors().is_empty(), "parse has syntax errors. parse tree:\n{:#?}", 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 db8bbeccef8f..338bd25ede31 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs @@ -3,7 +3,7 @@ //! Specifically, it implements a concept of `MacroFile` -- a file whose syntax //! tree originates not from the text of some `FileId`, but from some macro //! expansion. - +#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] #![warn(rust_2018_idioms, unused_lifetimes)] pub mod attrs; 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 0b69799e6bff..8f74bffc2b94 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs @@ -303,6 +303,7 @@ pub mod known { rust_2015, rust_2018, rust_2021, + rust_2024, v1, new_display, new_debug, diff --git a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml index bf473740166c..a83ee9824e23 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml @@ -61,7 +61,7 @@ test-utils.workspace = true test-fixture.workspace = true [features] -in-rust-tree = [] +in-rust-tree = ["hir-expand/in-rust-tree"] [lints] workspace = true 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 cb118a368486..41acd3555eb9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs @@ -74,6 +74,10 @@ impl TyBuilder { (self.data, subst) } + pub fn build_into_subst(self) -> Substitution { + self.build_internal().1 + } + pub fn push(mut self, arg: impl CastTo) -> Self { assert!(self.remaining() > 0); let arg = arg.cast(Interner); @@ -291,7 +295,6 @@ impl TyBuilder { ) -> Self { // Note that we're building ADT, so we never have parent generic parameters. let defaults = db.generic_defaults(self.data.into()); - let dummy_ty = TyKind::Error.intern(Interner).cast(Interner); for default_ty in defaults.iter().skip(self.vec.len()) { // NOTE(skip_binders): we only check if the arg type is error type. if let Some(x) = default_ty.skip_binders().ty(Interner) { @@ -301,13 +304,16 @@ impl TyBuilder { } } // Each default can only depend on the previous parameters. - // FIXME: we don't handle const generics here. let subst_so_far = Substitution::from_iter( Interner, self.vec .iter() .cloned() - .chain(iter::repeat(dummy_ty.clone())) + .chain(self.param_kinds[self.vec.len()..].iter().map(|it| match it { + ParamKind::Type => TyKind::Error.intern(Interner).cast(Interner), + ParamKind::Lifetime => error_lifetime().cast(Interner), + ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()), + })) .take(self.param_kinds.len()), ); self.vec.push(default_ty.clone().substitute(Interner, &subst_so_far).cast(Interner)); 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 d1aebeff261c..0bf01b0bc6ae 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,6 +1,8 @@ //! Various extensions traits for Chalk types. -use chalk_ir::{cast::Cast, FloatTy, IntTy, Mutability, Scalar, TyVariableKind, UintTy}; +use chalk_ir::{ + cast::Cast, FloatTy, IntTy, Mutability, Scalar, TyVariableKind, TypeOutlives, UintTy, +}; use hir_def::{ builtin_type::{BuiltinFloat, BuiltinInt, BuiltinType, BuiltinUint}, generics::TypeOrConstParamData, @@ -312,7 +314,7 @@ impl TyExt for Ty { .generic_predicates(id.parent) .iter() .map(|pred| pred.clone().substitute(Interner, &substs)) - .filter(|wc| match &wc.skip_binders() { + .filter(|wc| match wc.skip_binders() { WhereClause::Implemented(tr) => { &tr.self_type_parameter(Interner) == self } @@ -320,6 +322,9 @@ impl TyExt for Ty { alias: AliasTy::Projection(proj), ty: _, }) => &proj.self_type_parameter(db) == self, + WhereClause::TypeOutlives(TypeOutlives { ty, lifetime: _ }) => { + ty == self + } _ => false, }) .collect::>(); 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 705609ba68de..f09277a92e66 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -77,30 +77,32 @@ pub(crate) fn path_to_const( resolver: &Resolver, path: &Path, mode: ParamLoweringMode, - args_lazy: impl FnOnce() -> Generics, + args: impl FnOnce() -> Option, debruijn: DebruijnIndex, expected_ty: Ty, ) -> Option { match resolver.resolve_path_in_value_ns_fully(db.upcast(), path) { Some(ValueNs::GenericParam(p)) => { let ty = db.const_param_ty(p); - let args = args_lazy(); let value = match mode { ParamLoweringMode::Placeholder => { ConstValue::Placeholder(to_placeholder_idx(db, p.into())) } - ParamLoweringMode::Variable => match args.param_idx(p.into()) { - Some(it) => ConstValue::BoundVar(BoundVar::new(debruijn, it)), - None => { - never!( - "Generic list doesn't contain this param: {:?}, {:?}, {:?}", - args, - path, - p - ); - return None; + ParamLoweringMode::Variable => { + let args = args(); + match args.as_ref().and_then(|args| args.type_or_const_param_idx(p.into())) { + Some(it) => ConstValue::BoundVar(BoundVar::new(debruijn, it)), + None => { + never!( + "Generic list doesn't contain this param: {:?}, {:?}, {:?}", + args, + path, + p + ); + return None; + } } - }, + } }; Some(ConstData { ty, value }.intern(Interner)) } @@ -285,7 +287,6 @@ pub(crate) fn eval_to_const( expr: ExprId, mode: ParamLoweringMode, ctx: &mut InferenceContext<'_>, - args: impl FnOnce() -> Generics, debruijn: DebruijnIndex, ) -> Const { let db = ctx.db; @@ -304,7 +305,9 @@ pub(crate) fn eval_to_const( } if let Expr::Path(p) = &ctx.body.exprs[expr] { let resolver = &ctx.resolver; - if let Some(c) = path_to_const(db, resolver, p, mode, args, debruijn, infer[expr].clone()) { + if let Some(c) = + path_to_const(db, resolver, p, mode, || ctx.generics(), debruijn, infer[expr].clone()) + { return c; } } 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 38eb3371e3ce..ecbb1d4c60e5 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 @@ -43,7 +43,7 @@ mod allow { } pub fn incorrect_case(db: &dyn HirDatabase, owner: ModuleDefId) -> Vec { - let _p = tracing::span!(tracing::Level::INFO, "validate_module_item").entered(); + let _p = tracing::span!(tracing::Level::INFO, "incorrect_case").entered(); let mut validator = DeclValidator::new(db); validator.validate_item(owner); validator.sink 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 20b0da441daa..a5a42c52af04 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 @@ -11,6 +11,7 @@ use hir_def::{ItemContainerId, Lookup}; use hir_expand::name; use itertools::Itertools; use rustc_hash::FxHashSet; +use rustc_pattern_analysis::constructor::Constructor; use syntax::{ast, AstNode}; use tracing::debug; use triomphe::Arc; @@ -190,45 +191,45 @@ impl ExprValidator { let pattern_arena = Arena::new(); let mut m_arms = Vec::with_capacity(arms.len()); let mut has_lowering_errors = false; + // Note: Skipping the entire diagnostic rather than just not including a faulty match arm is + // preferred to avoid the chance of false positives. for arm in arms { - if let Some(pat_ty) = self.infer.type_of_pat.get(arm.pat) { - // We only include patterns whose type matches the type - // of the scrutinee expression. If we had an InvalidMatchArmPattern - // diagnostic or similar we could raise that in an else - // block here. - // - // When comparing the types, we also have to consider that rustc - // will automatically de-reference the scrutinee expression type if - // necessary. - // - // FIXME we should use the type checker for this. - if (pat_ty == scrut_ty - || scrut_ty - .as_reference() - .map(|(match_expr_ty, ..)| match_expr_ty == pat_ty) - .unwrap_or(false)) - && types_of_subpatterns_do_match(arm.pat, &self.body, &self.infer) - { - // If we had a NotUsefulMatchArm diagnostic, we could - // check the usefulness of each pattern as we added it - // to the matrix here. - let pat = self.lower_pattern(&cx, arm.pat, db, &mut has_lowering_errors); - let m_arm = pat_analysis::MatchArm { - pat: pattern_arena.alloc(pat), - has_guard: arm.guard.is_some(), - arm_data: (), - }; - m_arms.push(m_arm); - if !has_lowering_errors { - continue; - } + let Some(pat_ty) = self.infer.type_of_pat.get(arm.pat) else { + return; + }; + + // We only include patterns whose type matches the type + // of the scrutinee expression. If we had an InvalidMatchArmPattern + // diagnostic or similar we could raise that in an else + // block here. + // + // When comparing the types, we also have to consider that rustc + // will automatically de-reference the scrutinee expression type if + // necessary. + // + // FIXME we should use the type checker for this. + if (pat_ty == scrut_ty + || scrut_ty + .as_reference() + .map(|(match_expr_ty, ..)| match_expr_ty == pat_ty) + .unwrap_or(false)) + && types_of_subpatterns_do_match(arm.pat, &self.body, &self.infer) + { + // If we had a NotUsefulMatchArm diagnostic, we could + // check the usefulness of each pattern as we added it + // to the matrix here. + let pat = self.lower_pattern(&cx, arm.pat, db, &mut has_lowering_errors); + let m_arm = pat_analysis::MatchArm { + pat: pattern_arena.alloc(pat), + has_guard: arm.guard.is_some(), + arm_data: (), + }; + m_arms.push(m_arm); + if !has_lowering_errors { + continue; } } - - // If we can't resolve the type of a pattern, or the pattern type doesn't - // fit the match expression, we skip this diagnostic. Skipping the entire - // diagnostic rather than just not including this match arm is preferred - // to avoid the chance of false positives. + // If the pattern type doesn't fit the match expression, we skip this diagnostic. cov_mark::hit!(validate_match_bailed_out); return; } @@ -266,15 +267,17 @@ impl ExprValidator { let mut have_errors = false; let deconstructed_pat = self.lower_pattern(&cx, pat, db, &mut have_errors); + + // optimization, wildcard trivially hold + if have_errors || matches!(deconstructed_pat.ctor(), Constructor::Wildcard) { + continue; + } + let match_arm = rustc_pattern_analysis::MatchArm { pat: pattern_arena.alloc(deconstructed_pat), has_guard: false, arm_data: (), }; - if have_errors { - continue; - } - let report = match cx.compute_match_usefulness(&[match_arm], ty.clone()) { Ok(v) => v, Err(e) => { @@ -531,8 +534,16 @@ fn types_of_subpatterns_do_match(pat: PatId, body: &Body, infer: &InferenceResul fn walk(pat: PatId, body: &Body, infer: &InferenceResult, has_type_mismatches: &mut bool) { match infer.type_mismatch_for_pat(pat) { Some(_) => *has_type_mismatches = true, + None if *has_type_mismatches => (), None => { - body[pat].walk_child_pats(|subpat| walk(subpat, body, infer, has_type_mismatches)) + let pat = &body[pat]; + if let Pat::ConstBlock(expr) | Pat::Lit(expr) = *pat { + *has_type_mismatches |= infer.type_mismatch_for_expr(expr).is_some(); + if *has_type_mismatches { + return; + } + } + pat.walk_child_pats(|subpat| walk(subpat, body, infer, has_type_mismatches)) } } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs index f45beb4c92bf..c171dbc1700a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs @@ -1,9 +1,9 @@ //! Interface with `rustc_pattern_analysis`. use std::fmt; -use tracing::debug; -use hir_def::{DefWithBodyId, EnumVariantId, HasModule, LocalFieldId, ModuleId, VariantId}; +use hir_def::{DefWithBodyId, EnumId, EnumVariantId, HasModule, LocalFieldId, ModuleId, VariantId}; +use once_cell::unsync::Lazy; use rustc_hash::FxHashMap; use rustc_pattern_analysis::{ constructor::{Constructor, ConstructorSet, VariantVisibility}, @@ -36,6 +36,24 @@ pub(crate) type WitnessPat<'p> = rustc_pattern_analysis::pat::WitnessPat Self { + // Find the index of this variant in the list of variants. + use hir_def::Lookup; + let i = target_evid.lookup(db.upcast()).index as usize; + EnumVariantContiguousIndex(i) + } + + fn to_enum_variant_id(self, db: &dyn HirDatabase, eid: EnumId) -> EnumVariantId { + db.enum_data(eid).variants[self.0].0 + } +} + #[derive(Clone)] pub(crate) struct MatchCheckCtx<'p> { module: ModuleId, @@ -73,25 +91,27 @@ impl<'p> MatchCheckCtx<'p> { } fn is_uninhabited(&self, ty: &Ty) -> bool { - is_ty_uninhabited_from(ty, self.module, self.db) + is_ty_uninhabited_from(self.db, ty, self.module) } - /// Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`. - fn is_foreign_non_exhaustive_enum(&self, ty: &Ty) -> bool { - match ty.as_adt() { - Some((adt @ hir_def::AdtId::EnumId(_), _)) => { - let has_non_exhaustive_attr = - self.db.attrs(adt.into()).by_key("non_exhaustive").exists(); - let is_local = adt.module(self.db.upcast()).krate() == self.module.krate(); - has_non_exhaustive_attr && !is_local - } - _ => false, - } + /// Returns whether the given ADT is from another crate declared `#[non_exhaustive]`. + fn is_foreign_non_exhaustive(&self, adt: hir_def::AdtId) -> bool { + let is_local = adt.krate(self.db.upcast()) == self.module.krate(); + !is_local && self.db.attrs(adt.into()).by_key("non_exhaustive").exists() } - fn variant_id_for_adt(ctor: &Constructor, adt: hir_def::AdtId) -> Option { + fn variant_id_for_adt( + db: &'p dyn HirDatabase, + ctor: &Constructor, + adt: hir_def::AdtId, + ) -> Option { match ctor { - &Variant(id) => Some(id.into()), + Variant(id) => { + let hir_def::AdtId::EnumId(eid) = adt else { + panic!("bad constructor {ctor:?} for adt {adt:?}") + }; + Some(id.to_enum_variant_id(db, eid).into()) + } Struct | UnionField => match adt { hir_def::AdtId::EnumId(_) => None, hir_def::AdtId::StructId(id) => Some(id.into()), @@ -175,19 +195,24 @@ impl<'p> MatchCheckCtx<'p> { ctor = Struct; arity = 1; } - &TyKind::Adt(adt, _) => { + &TyKind::Adt(AdtId(adt), _) => { ctor = match pat.kind.as_ref() { - PatKind::Leaf { .. } if matches!(adt.0, hir_def::AdtId::UnionId(_)) => { + PatKind::Leaf { .. } if matches!(adt, hir_def::AdtId::UnionId(_)) => { UnionField } PatKind::Leaf { .. } => Struct, - PatKind::Variant { enum_variant, .. } => Variant(*enum_variant), + PatKind::Variant { enum_variant, .. } => { + Variant(EnumVariantContiguousIndex::from_enum_variant_id( + self.db, + *enum_variant, + )) + } _ => { never!(); Wildcard } }; - let variant = Self::variant_id_for_adt(&ctor, adt.0).unwrap(); + let variant = Self::variant_id_for_adt(self.db, &ctor, adt).unwrap(); arity = variant.variant_data(self.db.upcast()).fields().len(); } _ => { @@ -239,7 +264,7 @@ impl<'p> MatchCheckCtx<'p> { PatKind::Deref { subpattern: subpatterns.next().unwrap() } } TyKind::Adt(adt, substs) => { - let variant = Self::variant_id_for_adt(pat.ctor(), adt.0).unwrap(); + let variant = Self::variant_id_for_adt(self.db, pat.ctor(), adt.0).unwrap(); let subpatterns = self .list_variant_fields(pat.ty(), variant) .zip(subpatterns) @@ -277,7 +302,7 @@ impl<'p> MatchCheckCtx<'p> { impl<'p> PatCx for MatchCheckCtx<'p> { type Error = (); type Ty = Ty; - type VariantIdx = EnumVariantId; + type VariantIdx = EnumVariantContiguousIndex; type StrLit = Void; type ArmData = (); type PatData = PatData<'p>; @@ -303,7 +328,7 @@ impl<'p> PatCx for MatchCheckCtx<'p> { // patterns. If we're here we can assume this is a box pattern. 1 } else { - let variant = Self::variant_id_for_adt(ctor, adt).unwrap(); + let variant = Self::variant_id_for_adt(self.db, ctor, adt).unwrap(); variant.variant_data(self.db.upcast()).fields().len() } } @@ -343,25 +368,22 @@ impl<'p> PatCx for MatchCheckCtx<'p> { let subst_ty = substs.at(Interner, 0).assert_ty_ref(Interner).clone(); single(subst_ty) } else { - let variant = Self::variant_id_for_adt(ctor, adt).unwrap(); - let (adt, _) = ty.as_adt().unwrap(); + let variant = Self::variant_id_for_adt(self.db, ctor, adt).unwrap(); - let adt_is_local = - variant.module(self.db.upcast()).krate() == self.module.krate(); // Whether we must not match the fields of this variant exhaustively. - let is_non_exhaustive = - self.db.attrs(variant.into()).by_key("non_exhaustive").exists() - && !adt_is_local; - let visibilities = self.db.field_visibilities(variant); + let is_non_exhaustive = Lazy::new(|| self.is_foreign_non_exhaustive(adt)); + let visibilities = Lazy::new(|| self.db.field_visibilities(variant)); self.list_variant_fields(ty, variant) .map(move |(fid, ty)| { - let is_visible = matches!(adt, hir_def::AdtId::EnumId(..)) - || visibilities[fid] - .is_visible_from(self.db.upcast(), self.module); + let is_visible = || { + matches!(adt, hir_def::AdtId::EnumId(..)) + || visibilities[fid] + .is_visible_from(self.db.upcast(), self.module) + }; let is_uninhabited = self.is_uninhabited(&ty); let private_uninhabited = - is_uninhabited && (!is_visible || is_non_exhaustive); + is_uninhabited && (!is_visible() || *is_non_exhaustive); (ty, PrivateUninhabitedField(private_uninhabited)) }) .collect() @@ -413,23 +435,26 @@ impl<'p> PatCx for MatchCheckCtx<'p> { TyKind::Scalar(Scalar::Char) => unhandled(), TyKind::Scalar(Scalar::Int(..) | Scalar::Uint(..)) => unhandled(), TyKind::Array(..) | TyKind::Slice(..) => unhandled(), - TyKind::Adt(AdtId(hir_def::AdtId::EnumId(enum_id)), subst) => { - let enum_data = cx.db.enum_data(*enum_id); - let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive_enum(ty); + &TyKind::Adt(AdtId(adt @ hir_def::AdtId::EnumId(enum_id)), ref subst) => { + let enum_data = cx.db.enum_data(enum_id); + let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive(adt); if enum_data.variants.is_empty() && !is_declared_nonexhaustive { ConstructorSet::NoConstructors } else { - let mut variants = FxHashMap::default(); - for &(variant, _) in enum_data.variants.iter() { + let mut variants = FxHashMap::with_capacity_and_hasher( + enum_data.variants.len(), + Default::default(), + ); + for (i, &(variant, _)) in enum_data.variants.iter().enumerate() { let is_uninhabited = - is_enum_variant_uninhabited_from(variant, subst, cx.module, cx.db); + is_enum_variant_uninhabited_from(cx.db, variant, subst, cx.module); let visibility = if is_uninhabited { VariantVisibility::Empty } else { VariantVisibility::Visible }; - variants.insert(variant, visibility); + variants.insert(EnumVariantContiguousIndex(i), visibility); } ConstructorSet::Variants { @@ -453,10 +478,10 @@ impl<'p> PatCx for MatchCheckCtx<'p> { f: &mut fmt::Formatter<'_>, pat: &rustc_pattern_analysis::pat::DeconstructedPat, ) -> fmt::Result { - let variant = - pat.ty().as_adt().and_then(|(adt, _)| Self::variant_id_for_adt(pat.ctor(), adt)); - let db = pat.data().db; + let variant = + pat.ty().as_adt().and_then(|(adt, _)| Self::variant_id_for_adt(db, pat.ctor(), adt)); + if let Some(variant) = variant { match variant { VariantId::EnumVariantId(v) => { @@ -474,7 +499,7 @@ impl<'p> PatCx for MatchCheckCtx<'p> { } fn bug(&self, fmt: fmt::Arguments<'_>) { - debug!("{}", fmt) + never!("{}", fmt) } fn complexity_exceeded(&self) -> Result<(), Self::Error> { 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 cbca0e801d4d..081b4d83a86f 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 @@ -4,7 +4,7 @@ use hir_def::{ body::Body, hir::{Expr, ExprId, UnaryOp}, - resolver::{resolver_for_expr, ResolveValueResult, ValueNs}, + resolver::{resolver_for_expr, ResolveValueResult, Resolver, ValueNs}, DefWithBodyId, }; @@ -13,9 +13,9 @@ use crate::{ }; pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> Vec { - let infer = db.infer(def); - let mut res = Vec::new(); + let _p = tracing::span!(tracing::Level::INFO, "missing_unsafe").entered(); + let mut res = Vec::new(); let is_unsafe = match def { DefWithBodyId::FunctionId(it) => db.function_data(it).has_unsafe_kw(), DefWithBodyId::StaticId(_) @@ -28,6 +28,7 @@ pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> Vec { } let body = db.body(def); + let infer = db.infer(def); unsafe_expressions(db, &infer, def, &body, body.body_expr, &mut |expr| { if !expr.inside_unsafe_block { res.push(expr.expr); @@ -51,14 +52,24 @@ pub fn unsafe_expressions( current: ExprId, unsafe_expr_cb: &mut dyn FnMut(UnsafeExpr), ) { - walk_unsafe(db, infer, def, body, current, false, unsafe_expr_cb) + walk_unsafe( + db, + infer, + body, + &mut resolver_for_expr(db.upcast(), def, current), + def, + current, + false, + unsafe_expr_cb, + ) } fn walk_unsafe( db: &dyn HirDatabase, infer: &InferenceResult, - def: DefWithBodyId, body: &Body, + resolver: &mut Resolver, + def: DefWithBodyId, current: ExprId, inside_unsafe_block: bool, unsafe_expr_cb: &mut dyn FnMut(UnsafeExpr), @@ -73,13 +84,14 @@ fn walk_unsafe( } } Expr::Path(path) => { - let resolver = resolver_for_expr(db.upcast(), def, current); + let g = resolver.update_to_inner_scope(db.upcast(), def, current); let value_or_partial = resolver.resolve_path_in_value_ns(db.upcast(), path); if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id), _)) = value_or_partial { if db.static_data(id).mutable { unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block }); } } + resolver.reset_to_guard(g); } Expr::MethodCall { .. } => { if infer @@ -97,13 +109,13 @@ fn walk_unsafe( } Expr::Unsafe { .. } => { return expr.walk_child_exprs(|child| { - walk_unsafe(db, infer, def, body, child, true, unsafe_expr_cb); + walk_unsafe(db, infer, body, resolver, def, child, true, unsafe_expr_cb); }); } _ => {} } expr.walk_child_exprs(|child| { - walk_unsafe(db, infer, def, body, child, inside_unsafe_block, unsafe_expr_cb); + walk_unsafe(db, infer, body, resolver, def, child, inside_unsafe_block, unsafe_expr_cb); }); } 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 8740ae6797cb..a357e8503515 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -9,6 +9,7 @@ use std::{ use base_db::CrateId; use chalk_ir::{BoundVar, Safety, TyKind}; +use either::Either; use hir_def::{ data::adt::VariantData, db::DefDatabase, @@ -27,7 +28,7 @@ use intern::{Internable, Interned}; use itertools::Itertools; use la_arena::ArenaMap; use smallvec::SmallVec; -use stdx::never; +use stdx::{never, IsNoneOr}; use triomphe::Arc; use crate::{ @@ -40,10 +41,11 @@ use crate::{ mir::pad16, primitive, to_assoc_type_id, utils::{self, detect_variant_from_bytes, generics, ClosureSubst}, - AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, Const, ConstScalar, ConstValue, - DomainGoal, FnAbi, GenericArg, ImplTraitId, Interner, Lifetime, LifetimeData, LifetimeOutlives, - MemoryMap, Mutability, OpaqueTy, ProjectionTy, ProjectionTyExt, QuantifiedWhereClause, Scalar, - Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyExt, WhereClause, + AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, ConcreteConst, Const, + ConstScalar, ConstValue, DomainGoal, FnAbi, GenericArg, ImplTraitId, Interner, Lifetime, + LifetimeData, LifetimeOutlives, MemoryMap, Mutability, OpaqueTy, ProjectionTy, ProjectionTyExt, + QuantifiedWhereClause, Scalar, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, + TyExt, WhereClause, }; pub trait HirWrite: fmt::Write { @@ -58,11 +60,18 @@ impl HirWrite for String {} impl HirWrite for fmt::Formatter<'_> {} pub struct HirFormatter<'a> { + /// The database handle pub db: &'a dyn HirDatabase, + /// The sink to write into fmt: &'a mut dyn HirWrite, + /// A buffer to intercept writes with, this allows us to track the overall size of the formatted output. buf: String, + /// The current size of the formatted output. curr_size: usize, - pub(crate) max_size: Option, + /// Size from which we should truncate the output. + max_size: Option, + /// When rendering something that has a concept of "children" (like fields in a struct), this limits + /// how many should be rendered. pub entity_limit: Option, omit_verbose_types: bool, closure_style: ClosureStyle, @@ -302,7 +311,6 @@ impl DisplayTarget { #[derive(Debug)] pub enum DisplaySourceCodeError { PathNotFound, - UnknownType, Coroutine, OpaqueType, } @@ -414,12 +422,7 @@ impl HirDisplay for ProjectionTy { let proj_params_count = self.substitution.len(Interner) - trait_ref.substitution.len(Interner); let proj_params = &self.substitution.as_slice(Interner)[..proj_params_count]; - if !proj_params.is_empty() { - write!(f, "<")?; - f.write_joined(proj_params, ", ")?; - write!(f, ">")?; - } - Ok(()) + hir_fmt_generics(f, proj_params, None) } } @@ -452,7 +455,7 @@ impl HirDisplay for Const { ConstValue::Placeholder(idx) => { 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]; + let param_data = &generics.params[id.local_id]; write!(f, "{}", param_data.name().unwrap().display(f.db.upcast()))?; Ok(()) } @@ -460,7 +463,11 @@ impl HirDisplay for Const { 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()))?; + hir_fmt_generics( + f, + parameters.as_slice(Interner), + c.generic_def(f.db.upcast()), + )?; Ok(()) } ConstScalar::Unknown => f.write_char('_'), @@ -936,36 +943,31 @@ impl HirDisplay for Ty { } }; f.end_location_link(); + if parameters.len(Interner) > 0 { let generics = generics(db.upcast(), def.into()); - let ( - parent_params, - self_param, - type_params, - const_params, - _impl_trait_params, - lifetime_params, - ) = generics.provenance_split(); - let total_len = - parent_params + self_param + type_params + const_params + lifetime_params; + let (parent_len, self_, type_, const_, impl_, lifetime) = + generics.provenance_split(); + let parameters = parameters.as_slice(Interner); // We print all params except implicit impl Trait params. Still a bit weird; should we leave out parent and self? - if total_len > 0 { + if parameters.len() - impl_ > 0 { // `parameters` are in the order of fn's params (including impl traits), fn's lifetimes // parent's params (those from enclosing impl or trait, if any). - let parameters = parameters.as_slice(Interner); - let fn_params_len = self_param + type_params + const_params; - // This will give slice till last type or const - let fn_params = parameters.get(..fn_params_len); - let fn_lt_params = - parameters.get(fn_params_len..(fn_params_len + lifetime_params)); - let parent_params = parameters.get(parameters.len() - parent_params..); - let params = parent_params - .into_iter() - .chain(fn_lt_params) - .chain(fn_params) - .flatten(); + let (fn_params, other) = + parameters.split_at(self_ + type_ + const_ + lifetime); + let (_impl, parent_params) = other.split_at(impl_); + debug_assert_eq!(parent_params.len(), parent_len); + + let parent_params = + generic_args_sans_defaults(f, Some(def.into()), parent_params); + let fn_params = generic_args_sans_defaults(f, Some(def.into()), fn_params); + write!(f, "<")?; - f.write_joined(params, ", ")?; + hir_fmt_generic_arguments(f, parent_params)?; + if !parent_params.is_empty() && !fn_params.is_empty() { + write!(f, ", ")?; + } + hir_fmt_generic_arguments(f, fn_params)?; write!(f, ">")?; } } @@ -1009,7 +1011,7 @@ impl HirDisplay for Ty { let generic_def = self.as_generic_def(db); - hir_fmt_generics(f, parameters, generic_def)?; + hir_fmt_generics(f, parameters.as_slice(Interner), generic_def)?; } TyKind::AssociatedType(assoc_type_id, parameters) => { let type_alias = from_assoc_type_id(*assoc_type_id); @@ -1032,20 +1034,15 @@ impl HirDisplay for Ty { f.end_location_link(); // Note that the generic args for the associated type come before those for the // trait (including the self type). - // FIXME: reconsider the generic args order upon formatting? - if parameters.len(Interner) > 0 { - write!(f, "<")?; - f.write_joined(parameters.as_slice(Interner), ", ")?; - write!(f, ">")?; - } + hir_fmt_generics(f, parameters.as_slice(Interner), None) } else { let projection_ty = ProjectionTy { associated_ty_id: to_assoc_type_id(type_alias), substitution: parameters.clone(), }; - projection_ty.hir_fmt(f)?; - } + projection_ty.hir_fmt(f) + }?; } TyKind::Foreign(type_alias) => { let alias = from_foreign_def_id(*type_alias); @@ -1072,6 +1069,7 @@ impl HirDisplay for Ty { write_bounds_like_dyn_trait_with_prefix( f, "impl", + Either::Left(self), bounds.skip_binders(), SizedByDefault::Sized { anchor: krate }, )?; @@ -1087,6 +1085,7 @@ impl HirDisplay for Ty { write_bounds_like_dyn_trait_with_prefix( f, "impl", + Either::Left(self), bounds.skip_binders(), SizedByDefault::Sized { anchor: krate }, )?; @@ -1137,7 +1136,7 @@ impl HirDisplay for Ty { } ClosureStyle::ClosureWithSubst => { write!(f, "{{closure#{:?}}}", id.0.as_u32())?; - return hir_fmt_generics(f, substs, None); + return hir_fmt_generics(f, substs.as_slice(Interner), None); } _ => (), } @@ -1173,7 +1172,7 @@ impl HirDisplay for Ty { TyKind::Placeholder(idx) => { let id = from_placeholder_idx(db, *idx); let generics = generics(db.upcast(), id.parent); - let param_data = &generics.params.type_or_consts[id.local_id]; + let param_data = &generics.params[id.local_id]; match param_data { TypeOrConstParamData::TypeParamData(p) => match p.provenance { TypeParamProvenance::TypeParamList | TypeParamProvenance::TraitSelf => { @@ -1189,21 +1188,24 @@ impl HirDisplay for Ty { .generic_predicates(id.parent) .iter() .map(|pred| pred.clone().substitute(Interner, &substs)) - .filter(|wc| match &wc.skip_binders() { + .filter(|wc| match wc.skip_binders() { WhereClause::Implemented(tr) => { - &tr.self_type_parameter(Interner) == self + tr.self_type_parameter(Interner) == *self } WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(proj), ty: _, - }) => &proj.self_type_parameter(db) == self, - _ => false, + }) => proj.self_type_parameter(db) == *self, + WhereClause::AliasEq(_) => false, + WhereClause::TypeOutlives(to) => to.ty == *self, + WhereClause::LifetimeOutlives(_) => false, }) .collect::>(); let krate = id.parent.module(db.upcast()).krate(); write_bounds_like_dyn_trait_with_prefix( f, "impl", + Either::Left(self), &bounds, SizedByDefault::Sized { anchor: krate }, )?; @@ -1229,6 +1231,7 @@ impl HirDisplay for Ty { write_bounds_like_dyn_trait_with_prefix( f, "dyn", + Either::Left(self), &bounds, SizedByDefault::NotSized, )?; @@ -1252,6 +1255,7 @@ impl HirDisplay for Ty { write_bounds_like_dyn_trait_with_prefix( f, "impl", + Either::Left(self), bounds.skip_binders(), SizedByDefault::Sized { anchor: krate }, )?; @@ -1266,6 +1270,7 @@ impl HirDisplay for Ty { write_bounds_like_dyn_trait_with_prefix( f, "impl", + Either::Left(self), bounds.skip_binders(), SizedByDefault::Sized { anchor: krate }, )?; @@ -1277,11 +1282,10 @@ impl HirDisplay for Ty { } TyKind::Error => { if f.display_target.is_source_code() { - return Err(HirDisplayError::DisplaySourceCodeError( - DisplaySourceCodeError::UnknownType, - )); + f.write_char('_')?; + } else { + write!(f, "{{unknown}}")?; } - write!(f, "{{unknown}}")?; } TyKind::InferenceVar(..) => write!(f, "_")?, TyKind::Coroutine(_, subst) => { @@ -1318,93 +1322,92 @@ impl HirDisplay for Ty { fn hir_fmt_generics( f: &mut HirFormatter<'_>, - parameters: &Substitution, + parameters: &[GenericArg], generic_def: Option, ) -> Result<(), HirDisplayError> { - let db = f.db; - if parameters.len(Interner) > 0 { - use std::cmp::Ordering; - let param_compare = - |a: &GenericArg, b: &GenericArg| match (a.data(Interner), b.data(Interner)) { - (crate::GenericArgData::Lifetime(_), crate::GenericArgData::Lifetime(_)) => { - Ordering::Equal - } - (crate::GenericArgData::Lifetime(_), _) => Ordering::Less, - (_, crate::GenericArgData::Lifetime(_)) => Ordering::Less, - (_, _) => Ordering::Equal, - }; - 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(|it| it.kind(Interner)) - == Some(&TyKind::Error) - { - return true; - } - if let Some(ConstValue::Concrete(c)) = - parameter.constant(Interner).map(|it| &it.data(Interner).value) - { - if c.interned == ConstScalar::Unknown { - return true; - } - } - if parameter.lifetime(Interner).map(|it| it.data(Interner)) - == Some(&crate::LifetimeData::Static) - { - return true; - } - let default_parameter = match default_parameters.get(i) { - Some(it) => it, - 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) - }; - //FIXME: Should handle the ordering of lifetimes when creating substitutions - let mut parameters_to_write = parameters_to_write.to_vec(); - parameters_to_write.sort_by(param_compare); - if !parameters_to_write.is_empty() { - write!(f, "<")?; - let mut first = true; - 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)?; - } - } + if parameters.is_empty() { + return Ok(()); + } - write!(f, ">")?; + let parameters_to_write = generic_args_sans_defaults(f, generic_def, parameters); + if !parameters_to_write.is_empty() { + write!(f, "<")?; + hir_fmt_generic_arguments(f, parameters_to_write)?; + write!(f, ">")?; + } + + Ok(()) +} + +fn generic_args_sans_defaults<'ga>( + f: &mut HirFormatter<'_>, + generic_def: Option, + parameters: &'ga [GenericArg], +) -> &'ga [GenericArg] { + if f.display_target.is_source_code() || f.omit_verbose_types() { + match generic_def + .map(|generic_def_id| f.db.generic_defaults(generic_def_id)) + .filter(|it| !it.is_empty()) + { + None => parameters, + Some(default_parameters) => { + let should_show = |arg: &GenericArg, i: usize| { + let is_err = |arg: &GenericArg| match arg.data(Interner) { + chalk_ir::GenericArgData::Lifetime(it) => { + *it.data(Interner) == LifetimeData::Error + } + chalk_ir::GenericArgData::Ty(it) => *it.kind(Interner) == TyKind::Error, + chalk_ir::GenericArgData::Const(it) => matches!( + it.data(Interner).value, + ConstValue::Concrete(ConcreteConst { + interned: ConstScalar::Unknown, + .. + }) + ), + }; + // if the arg is error like, render it to inform the user + if is_err(arg) { + return true; + } + // otherwise, if the arg is equal to the param default, hide it (unless the + // default is an error which can happen for the trait Self type) + default_parameters.get(i).is_none_or(|default_parameter| { + // !is_err(default_parameter.skip_binders()) + // && + arg != &default_parameter.clone().substitute(Interner, ¶meters) + }) + }; + let mut default_from = 0; + for (i, parameter) in parameters.iter().enumerate() { + if should_show(parameter, i) { + default_from = i + 1; + } + } + ¶meters[0..default_from] + } } + } else { + parameters + } +} + +fn hir_fmt_generic_arguments( + f: &mut HirFormatter<'_>, + parameters: &[GenericArg], +) -> Result<(), HirDisplayError> { + let mut first = true; + let lifetime_offset = parameters.iter().position(|arg| arg.lifetime(Interner).is_some()); + + let (ty_or_const, lifetimes) = match lifetime_offset { + Some(offset) => parameters.split_at(offset), + None => (parameters, &[][..]), + }; + for generic_arg in lifetimes.iter().chain(ty_or_const) { + if !first { + write!(f, ", ")?; + } + first = false; + generic_arg.hir_fmt(f)?; } Ok(()) } @@ -1468,6 +1471,7 @@ impl SizedByDefault { pub fn write_bounds_like_dyn_trait_with_prefix( f: &mut HirFormatter<'_>, prefix: &str, + this: Either<&Ty, &Lifetime>, predicates: &[QuantifiedWhereClause], default_sized: SizedByDefault, ) -> Result<(), HirDisplayError> { @@ -1476,7 +1480,7 @@ pub fn write_bounds_like_dyn_trait_with_prefix( || predicates.is_empty() && matches!(default_sized, SizedByDefault::Sized { .. }) { write!(f, " ")?; - write_bounds_like_dyn_trait(f, predicates, default_sized) + write_bounds_like_dyn_trait(f, this, predicates, default_sized) } else { Ok(()) } @@ -1484,6 +1488,7 @@ pub fn write_bounds_like_dyn_trait_with_prefix( fn write_bounds_like_dyn_trait( f: &mut HirFormatter<'_>, + this: Either<&Ty, &Lifetime>, predicates: &[QuantifiedWhereClause], default_sized: SizedByDefault, ) -> Result<(), HirDisplayError> { @@ -1524,23 +1529,54 @@ fn write_bounds_like_dyn_trait( f.start_location_link(trait_.into()); 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 { + if is_fn_trait { + if let [_self, params @ ..] = trait_ref.substitution.as_slice(Interner) { if let Some(args) = params.first().and_then(|it| it.assert_ty_ref(Interner).as_tuple()) { write!(f, "(")?; - f.write_joined(args.as_slice(Interner), ", ")?; + hir_fmt_generic_arguments(f, args.as_slice(Interner))?; write!(f, ")")?; } - } else if !params.is_empty() { - write!(f, "<")?; - f.write_joined(params, ", ")?; - // there might be assoc type bindings, so we leave the angle brackets open - angle_open = true; + } + } else { + let params = generic_args_sans_defaults( + f, + Some(trait_.into()), + trait_ref.substitution.as_slice(Interner), + ); + if let [_self, params @ ..] = params { + if !params.is_empty() { + write!(f, "<")?; + hir_fmt_generic_arguments(f, params)?; + // there might be assoc type bindings, so we leave the angle brackets open + angle_open = true; + } } } } + WhereClause::TypeOutlives(to) if Either::Left(&to.ty) == this => { + if !is_fn_trait && angle_open { + write!(f, ">")?; + angle_open = false; + } + if !first { + write!(f, " + ")?; + } + to.lifetime.hir_fmt(f)?; + } + WhereClause::TypeOutlives(_) => {} + WhereClause::LifetimeOutlives(lo) if Either::Right(&lo.a) == this => { + if !is_fn_trait && angle_open { + write!(f, ">")?; + angle_open = false; + } + if !first { + write!(f, " + ")?; + } + lo.b.hir_fmt(f)?; + } + WhereClause::LifetimeOutlives(_) => {} WhereClause::AliasEq(alias_eq) if is_fn_trait => { is_fn_trait = false; if !alias_eq.ty.is_unit() { @@ -1567,9 +1603,9 @@ fn write_bounds_like_dyn_trait( let proj_arg_count = generics(f.db.upcast(), assoc_ty_id.into()).len_self(); if proj_arg_count > 0 { write!(f, "<")?; - f.write_joined( + hir_fmt_generic_arguments( + f, &proj.substitution.as_slice(Interner)[..proj_arg_count], - ", ", )?; write!(f, ">")?; } @@ -1577,10 +1613,6 @@ fn write_bounds_like_dyn_trait( } ty.hir_fmt(f)?; } - - // FIXME implement these - WhereClause::LifetimeOutlives(_) => {} - WhereClause::TypeOutlives(_) => {} } first = false; } @@ -1630,12 +1662,7 @@ fn fmt_trait_ref( f.start_location_link(trait_.into()); write!(f, "{}", f.db.trait_data(trait_).name.display(f.db.upcast()))?; f.end_location_link(); - if tr.substitution.len(Interner) > 1 { - write!(f, "<")?; - f.write_joined(&tr.substitution.as_slice(Interner)[1..], ", ")?; - write!(f, ">")?; - } - Ok(()) + hir_fmt_generics(f, &tr.substitution.as_slice(Interner)[1..], None) } impl HirDisplay for TraitRef { @@ -1690,16 +1717,18 @@ impl HirDisplay for Lifetime { impl HirDisplay for LifetimeData { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { match self { - LifetimeData::BoundVar(idx) => idx.hir_fmt(f), - LifetimeData::InferenceVar(_) => write!(f, "_"), LifetimeData::Placeholder(idx) => { 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]; + let param_data = &generics.params[id.local_id]; write!(f, "{}", param_data.name.display(f.db.upcast()))?; Ok(()) } + _ if f.display_target.is_source_code() => write!(f, "'_"), + LifetimeData::BoundVar(idx) => idx.hir_fmt(f), + LifetimeData::InferenceVar(_) => write!(f, "_"), LifetimeData::Static => write!(f, "'static"), + LifetimeData::Error => write!(f, "'{{error}}"), LifetimeData::Erased => Ok(()), LifetimeData::Phantom(_, _) => Ok(()), } 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 be3b50e1411d..281386e1364b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -55,12 +55,12 @@ use triomphe::Arc; use crate::{ db::HirDatabase, - fold_tys, + error_lifetime, fold_tys, infer::{coerce::CoerceMany, unify::InferenceTable}, lower::ImplTraitLoweringMode, - static_lifetime, to_assoc_type_id, + to_assoc_type_id, traits::FnTrait, - utils::{InTypeConstIdMetadata, UnevaluatedConstEvaluatorFolder}, + utils::{Generics, InTypeConstIdMetadata, UnevaluatedConstEvaluatorFolder}, AliasEq, AliasTy, Binders, ClosureId, Const, DomainGoal, GenericArg, Goal, ImplTraitId, ImplTraitIdx, InEnvironment, Interner, Lifetime, OpaqueTyId, ProjectionTy, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, @@ -326,7 +326,7 @@ pub struct Adjustment { impl Adjustment { pub fn borrow(m: Mutability, ty: Ty) -> Self { - let ty = TyKind::Ref(m, static_lifetime(), ty).intern(Interner); + let ty = TyKind::Ref(m, error_lifetime(), ty).intern(Interner); Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(m)), target: ty } } } @@ -429,7 +429,10 @@ pub struct InferenceResult { /// Type of the result of `.into_iter()` on the for. `ExprId` is the one of the whole for loop. pub type_of_for_iterator: FxHashMap, type_mismatches: FxHashMap, + /// Whether there are any type-mismatching errors in the result. + pub(crate) has_errors: bool, /// Interned common types to return references to. + // FIXME: Move this into `InferenceContext` standard_types: InternedStandardTypes, /// Stores the types which were implicitly dereferenced in pattern binding modes. pub pat_adjustments: FxHashMap>, @@ -628,6 +631,10 @@ impl<'a> InferenceContext<'a> { } } + pub(crate) fn generics(&self) -> Option { + Some(crate::utils::generics(self.db.upcast(), self.resolver.generic_def()?)) + } + // FIXME: This function should be private in module. It is currently only used in the consteval, since we need // `InferenceResult` in the middle of inference. See the fixme comment in `consteval::eval_to_const`. If you // used this function for another workaround, mention it here. If you really need this function and believe that @@ -654,6 +661,7 @@ impl<'a> InferenceContext<'a> { type_of_rpit, type_of_for_iterator, type_mismatches, + has_errors, standard_types: _, pat_adjustments, binding_modes: _, @@ -695,6 +703,9 @@ impl<'a> InferenceContext<'a> { for ty in type_of_for_iterator.values_mut() { *ty = table.resolve_completely(ty.clone()); } + + *has_errors = !type_mismatches.is_empty(); + type_mismatches.retain(|_, mismatch| { mismatch.expected = table.resolve_completely(mismatch.expected.clone()); mismatch.actual = table.resolve_completely(mismatch.actual.clone()); @@ -1646,9 +1657,11 @@ impl std::ops::BitOrAssign for Diverges { *self = *self | other; } } -/// A zipper that checks for unequal `{unknown}` occurrences in the two types. Used to filter out -/// mismatch diagnostics that only differ in `{unknown}`. These mismatches are usually not helpful. -/// As the cause is usually an underlying name resolution problem. + +/// A zipper that checks for unequal occurrences of `{unknown}` and unresolved projections +/// in the two types. Used to filter out mismatch diagnostics that only differ in +/// `{unknown}` and unresolved projections. These mismatches are usually not helpful. +/// As the cause is usually an underlying name resolution problem struct UnknownMismatch<'db>(&'db dyn HirDatabase); impl chalk_ir::zip::Zipper for UnknownMismatch<'_> { fn zip_tys(&mut self, variance: Variance, a: &Ty, b: &Ty) -> chalk_ir::Fallible<()> { @@ -1721,7 +1734,12 @@ impl chalk_ir::zip::Zipper for UnknownMismatch<'_> { zip_substs(self, None, &fn_ptr_a.substitution.0, &fn_ptr_b.substitution.0)? } (TyKind::Error, TyKind::Error) => (), - (TyKind::Error, _) | (_, TyKind::Error) => return Err(chalk_ir::NoSolution), + (TyKind::Error, _) + | (_, TyKind::Error) + | (TyKind::Alias(AliasTy::Projection(_)) | TyKind::AssociatedType(_, _), _) + | (_, TyKind::Alias(AliasTy::Projection(_)) | TyKind::AssociatedType(_, _)) => { + return Err(chalk_ir::NoSolution) + } _ => (), } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs index f8c03ee2886b..060b5f36f292 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs @@ -19,10 +19,6 @@ impl CastCheck { let expr_ty = table.resolve_ty_shallow(&self.expr_ty); let cast_ty = table.resolve_ty_shallow(&self.cast_ty); - if expr_ty.contains_unknown() || cast_ty.contains_unknown() { - return; - } - if table.coerce(&expr_ty, &cast_ty).is_ok() { return; } 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 32845ac2e36a..a25498eff333 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 @@ -22,11 +22,11 @@ use stdx::never; use crate::{ db::{HirDatabase, InternedClosure}, - from_chalk_trait_id, from_placeholder_idx, make_binders, + error_lifetime, from_chalk_trait_id, from_placeholder_idx, make_binders, mir::{BorrowKind, MirSpan, MutBorrowKind, ProjectionElem}, - static_lifetime, to_chalk_trait_id, + to_chalk_trait_id, traits::FnTrait, - utils::{self, elaborate_clause_supertraits, generics, Generics}, + utils::{self, elaborate_clause_supertraits, Generics}, Adjust, Adjustment, AliasEq, AliasTy, Binders, BindingMode, ChalkTraitId, ClosureId, DynTy, DynTyExt, FnAbi, FnPointer, FnSig, Interner, OpaqueTy, ProjectionTyExt, Substitution, Ty, TyExt, WhereClause, @@ -324,21 +324,17 @@ impl CapturedItemWithoutTy { BorrowKind::Mut { .. } => Mutability::Mut, _ => Mutability::Not, }; - TyKind::Ref(m, static_lifetime(), ty).intern(Interner) + TyKind::Ref(m, error_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), + ty: replace_placeholder_with_binder(ctx, ty), }; - fn replace_placeholder_with_binder( - db: &dyn HirDatabase, - owner: DefWithBodyId, - ty: Ty, - ) -> Binders { + fn replace_placeholder_with_binder(ctx: &mut InferenceContext<'_>, ty: Ty) -> Binders { struct Filler<'a> { db: &'a dyn HirDatabase, generics: Generics, @@ -361,7 +357,7 @@ impl CapturedItemWithoutTy { outer_binder: DebruijnIndex, ) -> Result, Self::Error> { let x = from_placeholder_idx(self.db, idx); - let Some(idx) = self.generics.param_idx(x) else { + let Some(idx) = self.generics.type_or_const_param_idx(x) else { return Err(()); }; Ok(BoundVar::new(outer_binder, idx).to_const(Interner, ty)) @@ -373,18 +369,18 @@ impl CapturedItemWithoutTy { outer_binder: DebruijnIndex, ) -> std::result::Result { let x = from_placeholder_idx(self.db, idx); - let Some(idx) = self.generics.param_idx(x) else { + let Some(idx) = self.generics.type_or_const_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 { + let Some(generics) = ctx.generics() else { return Binders::empty(Interner, ty); }; - let filler = &mut Filler { db, generics: generics(db.upcast(), generic_def) }; + let filler = &mut Filler { db: ctx.db, generics }; let result = ty.clone().try_fold_with(filler, DebruijnIndex::INNERMOST).unwrap_or(ty); - make_binders(db, &filler.generics, result) + make_binders(ctx.db, &filler.generics, result) } } } 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 cfbbc9dd6c04..72928851f123 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 @@ -18,11 +18,11 @@ use triomphe::Arc; use crate::{ autoderef::{Autoderef, AutoderefKind}, db::HirDatabase, + error_lifetime, infer::{ Adjust, Adjustment, AutoBorrow, InferOk, InferenceContext, OverloadedDeref, PointerCast, TypeError, TypeMismatch, }, - static_lifetime, utils::ClosureSubst, Canonical, DomainGoal, FnAbi, FnPointer, FnSig, Guidance, InEnvironment, Interner, Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, @@ -427,7 +427,7 @@ impl InferenceTable<'_> { // compare those. Note that this means we use the target // mutability [1], since it may be that we are coercing // from `&mut T` to `&U`. - let lt = static_lifetime(); // FIXME: handle lifetimes correctly, see rustc + let lt = error_lifetime(); // FIXME: handle lifetimes correctly, see rustc let derefd_from_ty = TyKind::Ref(to_mt, lt, referent_ty).intern(Interner); match autoderef.table.try_unify(&derefd_from_ty, to_ty) { Ok(result) => { @@ -621,7 +621,7 @@ impl InferenceTable<'_> { (TyKind::Ref(from_mt, _, from_inner), &TyKind::Ref(to_mt, _, _)) => { coerce_mutabilities(*from_mt, to_mt)?; - let lt = static_lifetime(); + let lt = error_lifetime(); Some(( Adjustment { kind: Adjust::Deref(None), target: from_inner.clone() }, Adjustment { 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 35d59679355a..d011a62e77a3 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 @@ -23,6 +23,7 @@ use crate::{ autoderef::{builtin_deref, deref_by_trait, Autoderef}, consteval, db::{InternedClosure, InternedCoroutine}, + error_lifetime, infer::{ coerce::{CoerceMany, CoercionCause}, find_continuable, @@ -630,7 +631,7 @@ impl InferenceContext<'_> { let inner_ty = self.infer_expr_inner(*expr, &expectation); match rawness { Rawness::RawPtr => TyKind::Raw(mutability, inner_ty), - Rawness::Ref => TyKind::Ref(mutability, static_lifetime(), inner_ty), + Rawness::Ref => TyKind::Ref(mutability, error_lifetime(), inner_ty), } .intern(Interner) } @@ -1039,18 +1040,12 @@ impl InferenceContext<'_> { ( elem_ty, - if let Some(g_def) = self.owner.as_generic_def_id() { - let generics = generics(self.db.upcast(), g_def); - consteval::eval_to_const( - repeat, - ParamLoweringMode::Placeholder, - self, - || generics, - DebruijnIndex::INNERMOST, - ) - } else { - consteval::usize_const(self.db, None, krate) - }, + consteval::eval_to_const( + repeat, + ParamLoweringMode::Placeholder, + self, + DebruijnIndex::INNERMOST, + ), ) } }; @@ -1851,7 +1846,7 @@ impl InferenceContext<'_> { ty, c, ParamLoweringMode::Placeholder, - || generics(this.db.upcast(), this.resolver.generic_def().unwrap()), + || this.generics(), DebruijnIndex::INNERMOST, ) }, 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 09a4d998ee13..1b354935a5bf 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 @@ -2,21 +2,22 @@ use std::iter::repeat_with; -use chalk_ir::Mutability; use hir_def::{ body::Body, hir::{Binding, BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, Literal, Pat, PatId}, path::Path, }; use hir_expand::name::Name; +use stdx::TupleExt; use crate::{ consteval::{try_const_usize, usize_const}, + error_lifetime, infer::{BindingMode, Expectation, InferenceContext, TypeMismatch}, lower::lower_to_chalk_mutability, primitive::UintTy, - static_lifetime, InferenceDiagnostic, Interner, Scalar, Substitution, Ty, TyBuilder, TyExt, - TyKind, + static_lifetime, InferenceDiagnostic, Interner, Mutability, Scalar, Substitution, Ty, + TyBuilder, TyExt, TyKind, }; /// Used to generalize patterns and assignee expressions. @@ -89,9 +90,6 @@ impl InferenceContext<'_> { self.unify(&ty, expected); - let substs = - ty.as_adt().map(|(_, s)| s.clone()).unwrap_or_else(|| Substitution::empty(Interner)); - match def { _ if subs.is_empty() => {} Some(def) => { @@ -108,8 +106,10 @@ impl InferenceContext<'_> { let pre_iter = pre.iter().enumerate(); let post_iter = (post_idx_offset..).zip(post.iter()); + let substs = ty.as_adt().map(TupleExt::tail); + for (i, &subpat) in pre_iter.chain(post_iter) { - let field_def = { + let expected_ty = { match variant_data.field(&Name::new_tuple_field(i)) { Some(local_id) => { if !visibilities[local_id] @@ -117,17 +117,17 @@ impl InferenceContext<'_> { { // FIXME(DIAGNOSE): private tuple field } - Some(local_id) + let f = field_types[local_id].clone(); + let expected_ty = match substs { + Some(substs) => f.substitute(Interner, substs), + None => f.substitute(Interner, &Substitution::empty(Interner)), + }; + self.normalize_associated_types_in(expected_ty) } - None => None, + None => self.err_ty(), } }; - let expected_ty = field_def.map_or(self.err_ty(), |f| { - field_types[f].clone().substitute(Interner, &substs) - }); - let expected_ty = self.normalize_associated_types_in(expected_ty); - T::infer(self, subpat, &expected_ty, default_bm); } } @@ -149,7 +149,7 @@ impl InferenceContext<'_> { expected: &Ty, default_bm: T::BindingMode, id: T, - subs: impl Iterator + ExactSizeIterator, + subs: impl ExactSizeIterator, ) -> Ty { let (ty, def) = self.resolve_variant(path, false); if let Some(variant) = def { @@ -158,9 +158,6 @@ impl InferenceContext<'_> { self.unify(&ty, expected); - let substs = - ty.as_adt().map(|(_, s)| s.clone()).unwrap_or_else(|| Substitution::empty(Interner)); - match def { _ if subs.len() == 0 => {} Some(def) => { @@ -168,8 +165,10 @@ impl InferenceContext<'_> { let variant_data = def.variant_data(self.db.upcast()); let visibilities = self.db.field_visibilities(def); + let substs = ty.as_adt().map(TupleExt::tail); + for (name, inner) in subs { - let field_def = { + let expected_ty = { match variant_data.field(&name) { Some(local_id) => { if !visibilities[local_id] @@ -180,23 +179,23 @@ impl InferenceContext<'_> { private: true, }); } - Some(local_id) + let f = field_types[local_id].clone(); + let expected_ty = match substs { + Some(substs) => f.substitute(Interner, substs), + None => f.substitute(Interner, &Substitution::empty(Interner)), + }; + self.normalize_associated_types_in(expected_ty) } None => { self.push_diagnostic(InferenceDiagnostic::NoSuchField { field: inner.into(), private: false, }); - None + self.err_ty() } } }; - let expected_ty = field_def.map_or(self.err_ty(), |f| { - field_types[f].clone().substitute(Interner, &substs) - }); - let expected_ty = self.normalize_associated_types_in(expected_ty); - T::infer(self, inner, &expected_ty, default_bm); } } @@ -396,14 +395,14 @@ impl InferenceContext<'_> { None => { let inner_ty = self.table.new_type_var(); let ref_ty = - TyKind::Ref(mutability, static_lifetime(), inner_ty.clone()).intern(Interner); + TyKind::Ref(mutability, error_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(inner_pat, &expectation, default_bm); - TyKind::Ref(mutability, static_lifetime(), subty).intern(Interner) + TyKind::Ref(mutability, error_lifetime(), subty).intern(Interner) } fn infer_bind_pat( @@ -430,7 +429,7 @@ impl InferenceContext<'_> { let bound_ty = match mode { BindingMode::Ref(mutability) => { - TyKind::Ref(mutability, static_lifetime(), inner_ty.clone()).intern(Interner) + TyKind::Ref(mutability, error_lifetime(), inner_ty.clone()).intern(Interner) } BindingMode::Move => inner_ty.clone(), }; 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 afb89fe1e5b1..b68fefc5150b 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 @@ -16,8 +16,8 @@ use triomphe::Arc; use super::{InferOk, InferResult, InferenceContext, TypeError}; use crate::{ - consteval::unknown_const, db::HirDatabase, fold_generic_args, fold_tys_and_consts, - static_lifetime, to_chalk_trait_id, traits::FnTrait, AliasEq, AliasTy, BoundVar, Canonical, + consteval::unknown_const, db::HirDatabase, error_lifetime, fold_generic_args, + fold_tys_and_consts, to_chalk_trait_id, traits::FnTrait, AliasEq, AliasTy, BoundVar, Canonical, Const, ConstValue, DebruijnIndex, DomainGoal, GenericArg, GenericArgData, Goal, GoalData, Guidance, InEnvironment, InferenceVar, Interner, Lifetime, OpaqueTyId, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, @@ -43,40 +43,21 @@ impl InferenceContext<'_> { let obligations = pending_obligations .iter() .filter_map(|obligation| match obligation.value.value.goal.data(Interner) { - GoalData::DomainGoal(DomainGoal::Holds( - clause @ WhereClause::AliasEq(AliasEq { - alias: AliasTy::Projection(projection), - .. - }), - )) => { - let projection_self = projection.self_type_parameter(self.db); - let uncanonical = chalk_ir::Substitute::apply( - &obligation.free_vars, - projection_self, - Interner, - ); - if matches!( - self.resolve_ty_shallow(&uncanonical).kind(Interner), - TyKind::InferenceVar(iv, TyVariableKind::General) if *iv == root, - ) { - Some(chalk_ir::Substitute::apply( - &obligation.free_vars, - clause.clone(), - Interner, - )) - } else { - None - } - } - GoalData::DomainGoal(DomainGoal::Holds( - clause @ WhereClause::Implemented(trait_ref), - )) => { - let trait_ref_self = trait_ref.self_type_parameter(Interner); - let uncanonical = chalk_ir::Substitute::apply( - &obligation.free_vars, - trait_ref_self, - Interner, - ); + GoalData::DomainGoal(DomainGoal::Holds(clause)) => { + let ty = match clause { + WhereClause::AliasEq(AliasEq { + alias: AliasTy::Projection(projection), + .. + }) => projection.self_type_parameter(self.db), + WhereClause::Implemented(trait_ref) => { + trait_ref.self_type_parameter(Interner) + } + WhereClause::TypeOutlives(to) => to.ty.clone(), + _ => return None, + }; + + let uncanonical = + chalk_ir::Substitute::apply(&obligation.free_vars, ty, Interner); if matches!( self.resolve_ty_shallow(&uncanonical).kind(Interner), TyKind::InferenceVar(iv, TyVariableKind::General) if *iv == root, @@ -121,8 +102,9 @@ impl> Canonicalized { VariableKind::Ty(TyVariableKind::General) => ctx.new_type_var().cast(Interner), VariableKind::Ty(TyVariableKind::Integer) => ctx.new_integer_var().cast(Interner), VariableKind::Ty(TyVariableKind::Float) => ctx.new_float_var().cast(Interner), - // Chalk can sometimes return new lifetime variables. We just use the static lifetime everywhere - VariableKind::Lifetime => static_lifetime().cast(Interner), + // Chalk can sometimes return new lifetime variables. We just replace them by errors + // for now. + VariableKind::Lifetime => error_lifetime().cast(Interner), VariableKind::Const(ty) => ctx.new_const_var(ty.clone()).cast(Interner), }), ); @@ -1020,11 +1002,11 @@ mod resolve { _var: InferenceVar, _outer_binder: DebruijnIndex, ) -> Lifetime { - // fall back all lifetimes to 'static -- currently we don't deal + // fall back all lifetimes to 'error -- currently we don't deal // with any lifetimes, but we can sometimes get some lifetime // variables through Chalk's unification, and this at least makes // sure we don't leak them outside of inference - crate::static_lifetime() + crate::error_lifetime() } } } 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 532b650e8ff2..7546369d8d47 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs @@ -5,42 +5,36 @@ use chalk_ir::{ visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}, DebruijnIndex, }; -use hir_def::{ - attr::Attrs, data::adt::VariantData, visibility::Visibility, AdtId, EnumVariantId, HasModule, - ModuleId, VariantId, -}; +use hir_def::{visibility::Visibility, AdtId, EnumVariantId, HasModule, ModuleId, VariantId}; use rustc_hash::FxHashSet; use crate::{ consteval::try_const_usize, db::HirDatabase, Binders, Interner, Substitution, Ty, TyKind, }; +// FIXME: Turn this into a query, it can be quite slow /// 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 { +pub(crate) fn is_ty_uninhabited_from(db: &dyn HirDatabase, ty: &Ty, target_mod: ModuleId) -> bool { + let _p = tracing::span!(tracing::Level::INFO, "is_ty_uninhabited_from", ?ty).entered(); 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 } +// FIXME: Turn this into a query, it can be quite slow /// Checks whether a variant is visibly uninhabited from a particular module. pub(crate) fn is_enum_variant_uninhabited_from( + db: &dyn HirDatabase, variant: EnumVariantId, subst: &Substitution, target_mod: ModuleId, - db: &dyn HirDatabase, ) -> bool { - let is_local = variant.module(db.upcast()).krate() == target_mod.krate(); + let _p = tracing::span!(tracing::Level::INFO, "is_enum_variant_uninhabited_from").entered(); let mut uninhabited_from = UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: FxHashSet::default() }; - let inhabitedness = uninhabited_from.visit_variant( - variant.into(), - &db.enum_variant_data(variant).variant_data, - subst, - &db.attrs(variant.into()), - is_local, - ); + let inhabitedness = uninhabited_from.visit_variant(variant.into(), subst); inhabitedness == BREAK_VISIBLY_UNINHABITED } @@ -98,34 +92,18 @@ impl TypeVisitor for UninhabitedFrom<'_> { impl UninhabitedFrom<'_> { fn visit_adt(&mut self, adt: AdtId, subst: &Substitution) -> ControlFlow { - let attrs = self.db.attrs(adt.into()); - let adt_non_exhaustive = attrs.by_key("non_exhaustive").exists(); - let is_local = adt.module(self.db.upcast()).krate() == self.target_mod.krate(); - if adt_non_exhaustive && !is_local { - return CONTINUE_OPAQUELY_INHABITED; - } - // An ADT is uninhabited iff all its variants uninhabited. match adt { // rustc: For now, `union`s are never considered uninhabited. AdtId::UnionId(_) => CONTINUE_OPAQUELY_INHABITED, - AdtId::StructId(s) => { - let struct_data = self.db.struct_data(s); - self.visit_variant(s.into(), &struct_data.variant_data, subst, &attrs, is_local) - } + AdtId::StructId(s) => self.visit_variant(s.into(), subst), AdtId::EnumId(e) => { let enum_data = self.db.enum_data(e); for &(variant, _) in enum_data.variants.iter() { - let variant_inhabitedness = self.visit_variant( - variant.into(), - &self.db.enum_variant_data(variant).variant_data, - subst, - &self.db.attrs(variant.into()), - is_local, - ); + let variant_inhabitedness = self.visit_variant(variant.into(), subst); match variant_inhabitedness { - Break(VisiblyUninhabited) => continue, + Break(VisiblyUninhabited) => (), Continue(()) => return CONTINUE_OPAQUELY_INHABITED, } } @@ -137,34 +115,36 @@ impl UninhabitedFrom<'_> { fn visit_variant( &mut self, variant: VariantId, - variant_data: &VariantData, subst: &Substitution, - attrs: &Attrs, - is_local: bool, ) -> ControlFlow { - let non_exhaustive_field_list = attrs.by_key("non_exhaustive").exists(); - if non_exhaustive_field_list && !is_local { + let is_local = variant.krate(self.db.upcast()) == self.target_mod.krate(); + if !is_local && self.db.attrs(variant.into()).by_key("non_exhaustive").exists() { + return CONTINUE_OPAQUELY_INHABITED; + } + + let variant_data = self.db.variant_data(variant); + let fields = variant_data.fields(); + if fields.is_empty() { return CONTINUE_OPAQUELY_INHABITED; } let is_enum = matches!(variant, VariantId::EnumVariantId(..)); let field_tys = self.db.field_types(variant); - let field_vis = self.db.field_visibilities(variant); + let field_vis = if is_enum { None } else { Some(self.db.field_visibilities(variant)) }; - for (fid, _) in variant_data.fields().iter() { - self.visit_field(field_vis[fid], &field_tys[fid], subst, is_enum)?; + for (fid, _) in fields.iter() { + self.visit_field(field_vis.as_ref().map(|it| it[fid]), &field_tys[fid], subst)?; } CONTINUE_OPAQUELY_INHABITED } fn visit_field( &mut self, - vis: Visibility, + vis: Option, ty: &Binders, subst: &Substitution, - is_enum: bool, ) -> ControlFlow { - if is_enum || vis.is_visible_from(self.db.upcast(), self.target_mod) { + if vis.map_or(true, |it| it.is_visible_from(self.db.upcast(), self.target_mod)) { let ty = ty.clone().substitute(Interner, subst); ty.visit_with(self, DebruijnIndex::INNERMOST) } else { 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 ba64f5c8d7ea..1727cec9893b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -56,7 +56,6 @@ use base_db::salsa::impl_intern_value_trivial; use chalk_ir::{ fold::{Shift, TypeFoldable}, interner::HasInterner, - visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}, NoSolution, }; use either::Either; @@ -98,7 +97,9 @@ pub use traits::TraitEnvironment; pub use utils::{all_super_traits, is_fn_unsafe_to_call}; pub use chalk_ir::{ - cast::Cast, AdtId, BoundVar, DebruijnIndex, Mutability, Safety, Scalar, TyVariableKind, + cast::Cast, + visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}, + AdtId, BoundVar, DebruijnIndex, Mutability, Safety, Scalar, TyVariableKind, }; pub type ForeignDefId = chalk_ir::ForeignDefId; @@ -288,7 +289,7 @@ impl Hash for ConstScalar { /// Return an index of a parameter in the generic type parameter list by it's id. pub fn param_idx(db: &dyn HirDatabase, id: TypeOrConstParamId) -> Option { - generics(db.upcast(), id.parent).param_idx(id) + generics(db.upcast(), id.parent).type_or_const_param_idx(id) } pub(crate) fn wrap_empty_binders(value: T) -> Binders @@ -603,14 +604,14 @@ pub enum ImplTraitId { } impl_intern_value_trivial!(ImplTraitId); -#[derive(Clone, PartialEq, Eq, Debug, Hash)] +#[derive(PartialEq, Eq, Debug, Hash)] pub struct ImplTraits { pub(crate) impl_traits: Arena, } has_interner!(ImplTraits); -#[derive(Clone, PartialEq, Eq, Debug, Hash)] +#[derive(PartialEq, Eq, Debug, Hash)] pub struct ImplTrait { pub(crate) bounds: Binders>, } @@ -622,7 +623,7 @@ pub fn static_lifetime() -> Lifetime { } pub fn error_lifetime() -> Lifetime { - static_lifetime() + LifetimeData::Error.intern(Interner) } pub(crate) fn fold_free_vars + TypeFoldable>( @@ -861,7 +862,7 @@ where if cfg!(debug_assertions) { Err(NoSolution) } else { - Ok(static_lifetime()) + Ok(error_lifetime()) } } @@ -873,7 +874,7 @@ where if cfg!(debug_assertions) { Err(NoSolution) } else { - Ok(static_lifetime()) + Ok(error_lifetime()) } } } 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 25ccc84c13c8..4d0516ead67c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -15,7 +15,10 @@ use base_db::{ CrateId, }; use chalk_ir::{ - cast::Cast, fold::Shift, fold::TypeFoldable, interner::HasInterner, Mutability, Safety, + cast::Cast, + fold::{Shift, TypeFoldable}, + interner::HasInterner, + Mutability, Safety, TypeOutlives, }; use either::Either; @@ -59,14 +62,14 @@ use crate::{ mapping::{from_chalk_trait_id, lt_to_placeholder_idx, ToChalk}, static_lifetime, to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx, utils::{ - all_super_trait_refs, associated_type_by_name_including_super_traits, generics, Generics, - InTypeConstIdMetadata, + self, all_super_trait_refs, associated_type_by_name_including_super_traits, generics, + Generics, InTypeConstIdMetadata, }, AliasEq, AliasTy, Binders, BoundVar, CallableSig, Const, ConstScalar, DebruijnIndex, DynTy, FnAbi, FnPointer, FnSig, FnSubst, ImplTrait, ImplTraitId, ImplTraits, Interner, Lifetime, - LifetimeData, ParamKind, PolyFnSig, ProjectionTy, QuantifiedWhereClause, - QuantifiedWhereClauses, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, - TyKind, WhereClause, + LifetimeData, LifetimeOutlives, ParamKind, PolyFnSig, ProgramClause, ProjectionTy, + QuantifiedWhereClause, QuantifiedWhereClauses, Substitution, TraitEnvironment, TraitRef, + TraitRefExt, Ty, TyBuilder, TyKind, WhereClause, }; #[derive(Debug)] @@ -242,13 +245,8 @@ impl<'a> TyLoweringContext<'a> { ) } - fn generics(&self) -> Generics { - generics( - self.db.upcast(), - self.resolver - .generic_def() - .expect("there should be generics if there's a generic param"), - ) + fn generics(&self) -> Option { + Some(generics(self.db.upcast(), self.resolver.generic_def()?)) } pub fn lower_ty_ext(&self, type_ref: &TypeRef) -> (Ty, Option) { @@ -282,7 +280,7 @@ impl<'a> TyLoweringContext<'a> { let inner_ty = self.lower_ty(inner); // FIXME: It should infer the eldided lifetimes instead of stubbing with static let lifetime = - lifetime.as_ref().map_or_else(static_lifetime, |lr| self.lower_lifetime(lr)); + lifetime.as_ref().map_or_else(error_lifetime, |lr| self.lower_lifetime(lr)); TyKind::Ref(lower_to_chalk_mutability(*mutability), lifetime, inner_ty) .intern(Interner) } @@ -318,7 +316,7 @@ impl<'a> TyLoweringContext<'a> { // place even if we encounter more opaque types while // lowering the bounds let idx = opaque_type_data.borrow_mut().alloc(ImplTrait { - bounds: crate::make_single_type_binders(Vec::new()), + bounds: crate::make_single_type_binders(Vec::default()), }); // We don't want to lower the bounds inside the binders // we're currently in, because they don't end up inside @@ -349,8 +347,7 @@ impl<'a> TyLoweringContext<'a> { let idx = counter.get(); // FIXME we're probably doing something wrong here counter.set(idx + count_impl_traits(type_ref) as u16); - if let Some(def) = self.resolver.generic_def() { - let generics = generics(self.db.upcast(), def); + if let Some(generics) = self.generics() { let param = generics .iter() .filter(|(_, data)| { @@ -385,8 +382,7 @@ impl<'a> TyLoweringContext<'a> { const_params, _impl_trait_params, _lifetime_params, - ) = if let Some(def) = self.resolver.generic_def() { - let generics = generics(self.db.upcast(), def); + ) = if let Some(generics) = self.generics() { generics.provenance_split() } else { (0, 0, 0, 0, 0, 0) @@ -574,44 +570,40 @@ impl<'a> TyLoweringContext<'a> { // FIXME(trait_alias): Implement trait alias. return (TyKind::Error.intern(Interner), None); } - TypeNs::GenericParam(param_id) => { - let generics = generics( - self.db.upcast(), - self.resolver.generic_def().expect("generics in scope"), - ); - match self.type_param_mode { - ParamLoweringMode::Placeholder => { - TyKind::Placeholder(to_placeholder_idx(self.db, param_id.into())) - } - ParamLoweringMode::Variable => { - let idx = match generics.param_idx(param_id.into()) { - None => { - never!("no matching generics"); - return (TyKind::Error.intern(Interner), None); - } - Some(idx) => idx, - }; - - TyKind::BoundVar(BoundVar::new(self.in_binders, idx)) - } + TypeNs::GenericParam(param_id) => match self.type_param_mode { + ParamLoweringMode::Placeholder => { + TyKind::Placeholder(to_placeholder_idx(self.db, param_id.into())) + } + ParamLoweringMode::Variable => { + let idx = match self + .generics() + .expect("generics in scope") + .type_or_const_param_idx(param_id.into()) + { + None => { + never!("no matching generics"); + return (TyKind::Error.intern(Interner), None); + } + Some(idx) => idx, + }; + + TyKind::BoundVar(BoundVar::new(self.in_binders, idx)) } - .intern(Interner) } + .intern(Interner), TypeNs::SelfType(impl_id) => { - let def = - self.resolver.generic_def().expect("impl should have generic param scope"); - let generics = generics(self.db.upcast(), def); + let generics = self.generics().expect("impl should have generic param scope"); match self.type_param_mode { ParamLoweringMode::Placeholder => { // `def` can be either impl itself or item within, and we need impl itself // now. - let generics = generics.parent_generics().unwrap_or(&generics); + let generics = generics.parent_or_self(); let subst = generics.placeholder_subst(self.db); self.db.impl_self_ty(impl_id).substitute(Interner, &subst) } ParamLoweringMode::Variable => { - let starting_from = match def { + let starting_from = match generics.def() { GenericDefId::ImplId(_) => 0, // `def` is an item within impl. We need to substitute `BoundVar`s but // remember that they are for parent (i.e. impl) generic params so they @@ -679,12 +671,12 @@ impl<'a> TyLoweringContext<'a> { } fn select_associated_type(&self, res: Option, segment: PathSegment<'_>) -> Ty { - let Some((def, res)) = self.resolver.generic_def().zip(res) else { + let Some((generics, res)) = self.generics().zip(res) else { return TyKind::Error.intern(Interner); }; let ty = named_associated_type_shorthand_candidates( self.db, - def, + generics.def(), res, Some(segment.name.clone()), move |name, t, associated_ty| { @@ -696,7 +688,6 @@ impl<'a> TyLoweringContext<'a> { let parent_subst = match self.type_param_mode { ParamLoweringMode::Placeholder => { // if we're lowering to placeholders, we have to put them in now. - let generics = generics(self.db.upcast(), def); let s = generics.placeholder_subst(self.db); s.apply(parent_subst, Interner) } @@ -718,7 +709,7 @@ impl<'a> TyLoweringContext<'a> { None, ); - let len_self = generics(self.db.upcast(), associated_ty.into()).len_self(); + let len_self = utils::generics(self.db.upcast(), associated_ty.into()).len_self(); let substs = Substitution::from_iter( Interner, @@ -1016,40 +1007,43 @@ impl<'a> TyLoweringContext<'a> { self.substs_from_path_segment(segment, Some(resolved.into()), false, explicit_self_ty) } - pub(crate) fn lower_where_predicate( - &self, - where_predicate: &WherePredicate, + pub(crate) fn lower_where_predicate<'b>( + &'b self, + where_predicate: &'b WherePredicate, ignore_bindings: bool, - ) -> impl Iterator { + ) -> impl Iterator + 'b { match where_predicate { WherePredicate::ForLifetime { target, bound, .. } | WherePredicate::TypeBound { target, bound } => { let self_ty = match target { WherePredicateTypeTarget::TypeRef(type_ref) => self.lower_ty(type_ref), - WherePredicateTypeTarget::TypeOrConstParam(param_id) => { - let generic_def = self.resolver.generic_def().expect("generics in scope"); - let generics = generics(self.db.upcast(), generic_def); - let param_id = hir_def::TypeOrConstParamId { - parent: generic_def, - local_id: *param_id, - }; - let placeholder = to_placeholder_idx(self.db, param_id); + &WherePredicateTypeTarget::TypeOrConstParam(local_id) => { + let def = self.resolver.generic_def().expect("generics in scope"); + let param_id = hir_def::TypeOrConstParamId { parent: def, local_id }; match self.type_param_mode { - ParamLoweringMode::Placeholder => TyKind::Placeholder(placeholder), + ParamLoweringMode::Placeholder => { + TyKind::Placeholder(to_placeholder_idx(self.db, param_id)) + } ParamLoweringMode::Variable => { - let idx = generics.param_idx(param_id).expect("matching generics"); + let idx = generics(self.db.upcast(), def) + .type_or_const_param_idx(param_id) + .expect("matching generics"); TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, idx)) } } .intern(Interner) } }; - self.lower_type_bound(bound, self_ty, ignore_bindings) - .collect::>() - .into_iter() + Either::Left(self.lower_type_bound(bound, self_ty, ignore_bindings)) } - WherePredicate::Lifetime { .. } => vec![].into_iter(), + WherePredicate::Lifetime { bound, target } => Either::Right(iter::once( + crate::wrap_empty_binders(WhereClause::LifetimeOutlives(LifetimeOutlives { + a: self.lower_lifetime(bound), + b: self.lower_lifetime(target), + })), + )), } + .into_iter() } pub(crate) fn lower_type_bound( @@ -1058,11 +1052,11 @@ impl<'a> TyLoweringContext<'a> { self_ty: Ty, ignore_bindings: bool, ) -> impl Iterator + 'a { - let mut bindings = None; - let trait_ref = match bound.as_ref() { + let mut trait_ref = None; + let clause = match bound.as_ref() { TypeBound::Path(path, TraitBoundModifier::None) => { - bindings = self.lower_trait_ref_from_path(path, Some(self_ty)); - bindings + trait_ref = self.lower_trait_ref_from_path(path, Some(self_ty)); + trait_ref .clone() .filter(|tr| { // ignore `T: Drop` or `T: Destruct` bounds. @@ -1098,14 +1092,20 @@ impl<'a> TyLoweringContext<'a> { } TypeBound::ForLifetime(_, path) => { // FIXME Don't silently drop the hrtb lifetimes here - bindings = self.lower_trait_ref_from_path(path, Some(self_ty)); - bindings.clone().map(WhereClause::Implemented).map(crate::wrap_empty_binders) + trait_ref = self.lower_trait_ref_from_path(path, Some(self_ty)); + trait_ref.clone().map(WhereClause::Implemented).map(crate::wrap_empty_binders) + } + TypeBound::Lifetime(l) => { + let lifetime = self.lower_lifetime(l); + Some(crate::wrap_empty_binders(WhereClause::TypeOutlives(TypeOutlives { + ty: self_ty, + lifetime, + }))) } - TypeBound::Lifetime(_) => None, TypeBound::Error => None, }; - trait_ref.into_iter().chain( - bindings + clause.into_iter().chain( + trait_ref .into_iter() .filter(move |_| !ignore_bindings) .flat_map(move |tr| self.assoc_type_bindings_from_type_bound(bound, tr)), @@ -1203,8 +1203,8 @@ impl<'a> TyLoweringContext<'a> { }); if let Some(target_param_idx) = target_param_idx { let mut counter = 0; - for (idx, data) in self.generics().params.type_or_consts.iter() - { + let generics = self.generics().expect("generics in scope"); + for (idx, data) in generics.params.type_or_consts.iter() { // Count the number of `impl Trait` things that appear before // the target of our `bound`. // Our counter within `impl_trait_mode` should be that number @@ -1264,10 +1264,19 @@ impl<'a> TyLoweringContext<'a> { // bounds in the input. // INVARIANT: If this function returns `DynTy`, there should be at least one trait bound. // These invariants are utilized by `TyExt::dyn_trait()` and chalk. + let mut lifetime = None; let bounds = self.with_shifted_in(DebruijnIndex::ONE, |ctx| { let mut bounds: Vec<_> = bounds .iter() .flat_map(|b| ctx.lower_type_bound(b, self_ty.clone(), false)) + .filter(|b| match b.skip_binders() { + WhereClause::Implemented(_) | WhereClause::AliasEq(_) => true, + WhereClause::LifetimeOutlives(_) => false, + WhereClause::TypeOutlives(t) => { + lifetime = Some(t.lifetime.clone()); + false + } + }) .collect(); let mut multiple_regular_traits = false; @@ -1305,7 +1314,7 @@ impl<'a> TyLoweringContext<'a> { _ => unreachable!(), } } - // We don't produce `WhereClause::{TypeOutlives, LifetimeOutlives}` yet. + // `WhereClause::{TypeOutlives, LifetimeOutlives}` have been filtered out _ => unreachable!(), } }); @@ -1325,7 +1334,21 @@ impl<'a> TyLoweringContext<'a> { if let Some(bounds) = bounds { let bounds = crate::make_single_type_binders(bounds); - TyKind::Dyn(DynTy { bounds, lifetime: static_lifetime() }).intern(Interner) + TyKind::Dyn(DynTy { + bounds, + lifetime: match lifetime { + Some(it) => match it.bound_var(Interner) { + Some(bound_var) => LifetimeData::BoundVar(BoundVar::new( + DebruijnIndex::INNERMOST, + bound_var.index, + )) + .intern(Interner), + None => it, + }, + None => static_lifetime(), + }, + }) + .intern(Interner) } else { // FIXME: report error // (additional non-auto traits, associated type rebound, or no resolved trait) @@ -1355,8 +1378,8 @@ impl<'a> TyLoweringContext<'a> { crate::wrap_empty_binders(clause) }); predicates.extend(sized_clause); - predicates.shrink_to_fit(); } + predicates.shrink_to_fit(); predicates }); ImplTrait { bounds: crate::make_single_type_binders(predicates) } @@ -1371,10 +1394,7 @@ impl<'a> TyLoweringContext<'a> { LifetimeData::Placeholder(lt_to_placeholder_idx(self.db, id)) } ParamLoweringMode::Variable => { - let generics = generics( - self.db.upcast(), - self.resolver.generic_def().expect("generics in scope"), - ); + let generics = self.generics().expect("generics in scope"); let idx = match generics.lifetime_idx(id) { None => return error_lifetime(), Some(idx) => idx, @@ -1485,7 +1505,7 @@ fn named_associated_type_shorthand_candidates( // Handle `Self::Type` referring to own associated type in trait definitions if let GenericDefId::TraitId(trait_id) = param_id.parent() { let trait_generics = generics(db.upcast(), trait_id.into()); - if trait_generics.params.type_or_consts[param_id.local_id()].is_trait_self() { + if trait_generics.params[param_id.local_id()].is_trait_self() { let def_generics = generics(db.upcast(), def); let starting_idx = match def { GenericDefId::TraitId(_) => 0, @@ -1604,10 +1624,14 @@ pub(crate) fn generic_predicates_for_param_query( let subst = generics.bound_vars_subst(db, DebruijnIndex::INNERMOST); let explicitly_unsized_tys = ctx.unsized_types.into_inner(); - let implicitly_sized_predicates = + if let Some(implicitly_sized_predicates) = implicitly_sized_clauses(db, param_id.parent, &explicitly_unsized_tys, &subst, &resolver) - .map(|p| make_binders(db, &generics, crate::wrap_empty_binders(p))); - predicates.extend(implicitly_sized_predicates); + { + predicates.extend( + implicitly_sized_predicates + .map(|p| make_binders(db, &generics, crate::wrap_empty_binders(p))), + ); + } predicates.into() } @@ -1657,18 +1681,7 @@ pub(crate) fn trait_environment_query( } } - let container: Option = match def { - // FIXME: is there a function for this? - GenericDefId::FunctionId(f) => Some(f.lookup(db.upcast()).container), - GenericDefId::AdtId(_) => None, - GenericDefId::TraitId(_) => None, - GenericDefId::TraitAliasId(_) => None, - GenericDefId::TypeAliasId(t) => Some(t.lookup(db.upcast()).container), - GenericDefId::ImplId(_) => None, - GenericDefId::EnumVariantId(_) => None, - GenericDefId::ConstId(c) => Some(c.lookup(db.upcast()).container), - }; - if let Some(ItemContainerId::TraitId(trait_id)) = container { + if let Some(trait_id) = def.assoc_trait_container(db.upcast()) { // add `Self: Trait` to the environment in trait // function default implementations (and speculative code // inside consts or type aliases) @@ -1676,24 +1689,23 @@ pub(crate) fn trait_environment_query( let substs = TyBuilder::placeholder_subst(db, trait_id); let trait_ref = TraitRef { trait_id: to_chalk_trait_id(trait_id), substitution: substs }; let pred = WhereClause::Implemented(trait_ref); - let program_clause: chalk_ir::ProgramClause = pred.cast(Interner); - clauses.push(program_clause.into_from_env_clause(Interner)); + clauses.push(pred.cast::(Interner).into_from_env_clause(Interner)); } let subst = generics(db.upcast(), def).placeholder_subst(db); let explicitly_unsized_tys = ctx.unsized_types.into_inner(); - let implicitly_sized_clauses = - implicitly_sized_clauses(db, def, &explicitly_unsized_tys, &subst, &resolver).map(|pred| { - let program_clause: chalk_ir::ProgramClause = pred.cast(Interner); - program_clause.into_from_env_clause(Interner) - }); - clauses.extend(implicitly_sized_clauses); - - let krate = def.module(db.upcast()).krate(); + if let Some(implicitly_sized_clauses) = + implicitly_sized_clauses(db, def, &explicitly_unsized_tys, &subst, &resolver) + { + clauses.extend( + implicitly_sized_clauses + .map(|pred| pred.cast::(Interner).into_from_env_clause(Interner)), + ); + } let env = chalk_ir::Environment::new(Interner).add_clauses(Interner, clauses); - TraitEnvironment::new(krate, None, traits_in_scope.into_boxed_slice(), env) + TraitEnvironment::new(resolver.krate(), None, traits_in_scope.into_boxed_slice(), env) } /// Resolve the where clause(s) of an item with generics. @@ -1721,10 +1733,14 @@ pub(crate) fn generic_predicates_query( let subst = generics.bound_vars_subst(db, DebruijnIndex::INNERMOST); let explicitly_unsized_tys = ctx.unsized_types.into_inner(); - let implicitly_sized_predicates = + if let Some(implicitly_sized_predicates) = implicitly_sized_clauses(db, def, &explicitly_unsized_tys, &subst, &resolver) - .map(|p| make_binders(db, &generics, crate::wrap_empty_binders(p))); - predicates.extend(implicitly_sized_predicates); + { + predicates.extend( + implicitly_sized_predicates + .map(|p| make_binders(db, &generics, crate::wrap_empty_binders(p))), + ); + } predicates.into() } @@ -1736,24 +1752,24 @@ fn implicitly_sized_clauses<'a>( explicitly_unsized_tys: &'a FxHashSet, substitution: &'a Substitution, resolver: &Resolver, -) -> impl Iterator + 'a { +) -> Option + 'a> { let is_trait_def = matches!(def, GenericDefId::TraitId(..)); let generic_args = &substitution.as_slice(Interner)[is_trait_def as usize..]; let sized_trait = db .lang_item(resolver.krate(), LangItem::Sized) .and_then(|lang_item| lang_item.as_trait().map(to_chalk_trait_id)); - sized_trait.into_iter().flat_map(move |sized_trait| { - let implicitly_sized_tys = generic_args + sized_trait.map(move |sized_trait| { + generic_args .iter() .filter_map(|generic_arg| generic_arg.ty(Interner)) - .filter(move |&self_ty| !explicitly_unsized_tys.contains(self_ty)); - implicitly_sized_tys.map(move |self_ty| { - WhereClause::Implemented(TraitRef { - trait_id: sized_trait, - substitution: Substitution::from1(Interner, self_ty.clone()), + .filter(move |&self_ty| !explicitly_unsized_tys.contains(self_ty)) + .map(move |self_ty| { + WhereClause::Implemented(TraitRef { + trait_id: sized_trait, + substitution: Substitution::from1(Interner, self_ty.clone()), + }) }) - }) }) } @@ -1796,8 +1812,7 @@ pub(crate) fn generic_defaults_query( make_binders(db, &generic_params, val) } GenericParamDataRef::LifetimeParamData(_) => { - // using static because it requires defaults - make_binders(db, &generic_params, static_lifetime().cast(Interner)) + make_binders(db, &generic_params, error_lifetime().cast(Interner)) } } })); @@ -1817,7 +1832,7 @@ pub(crate) fn generic_defaults_recover( let val = match id { GenericParamId::TypeParamId(_) => TyKind::Error.intern(Interner).cast(Interner), GenericParamId::ConstParamId(id) => unknown_const_as_generic(db.const_param_ty(id)), - GenericParamId::LifetimeParamId(_) => static_lifetime().cast(Interner), + GenericParamId::LifetimeParamId(_) => error_lifetime().cast(Interner), }; crate::make_binders(db, &generic_params, val) })); @@ -2232,7 +2247,7 @@ pub(crate) fn const_or_path_to_chalk( expected_ty: Ty, value: &ConstRef, mode: ParamLoweringMode, - args: impl FnOnce() -> Generics, + args: impl FnOnce() -> Option, debruijn: DebruijnIndex, ) -> Const { match value { @@ -2251,7 +2266,7 @@ pub(crate) fn const_or_path_to_chalk( .unwrap_or_else(|| unknown_const(expected_ty)) } &ConstRef::Complex(it) => { - let crate_data = &db.crate_graph()[owner.module(db.upcast()).krate()]; + let crate_data = &db.crate_graph()[resolver.krate()]; if crate_data.env.get("__ra_is_test_fixture").is_none() && crate_data.origin.is_local() { // FIXME: current `InTypeConstId` is very unstable, so we only use it in non local crate 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 73b07df56f7e..cd723494713b 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 @@ -22,10 +22,10 @@ use triomphe::Arc; use crate::{ autoderef::{self, AutoderefKind}, db::HirDatabase, - from_chalk_trait_id, from_foreign_def_id, + error_lifetime, from_chalk_trait_id, from_foreign_def_id, infer::{unify::InferenceTable, Adjust, Adjustment, OverloadedDeref, PointerCast}, primitive::{FloatTy, IntTy, UintTy}, - static_lifetime, to_chalk_trait_id, + to_chalk_trait_id, utils::all_super_traits, AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, DynTyExt, ForeignDefId, Goal, Guidance, InEnvironment, Interner, Scalar, Solution, Substitution, TraitEnvironment, TraitRef, @@ -1035,7 +1035,7 @@ fn iterate_method_candidates_with_autoref( iterate_method_candidates_by_receiver(receiver_ty.clone(), maybe_reborrowed)?; let refed = Canonical { - value: TyKind::Ref(Mutability::Not, static_lifetime(), receiver_ty.value.clone()) + value: TyKind::Ref(Mutability::Not, error_lifetime(), receiver_ty.value.clone()) .intern(Interner), binders: receiver_ty.binders.clone(), }; @@ -1043,7 +1043,7 @@ fn iterate_method_candidates_with_autoref( iterate_method_candidates_by_receiver(refed, first_adjustment.with_autoref(Mutability::Not))?; let ref_muted = Canonical { - value: TyKind::Ref(Mutability::Mut, static_lifetime(), receiver_ty.value.clone()) + value: TyKind::Ref(Mutability::Mut, error_lifetime(), receiver_ty.value.clone()) .intern(Interner), binders: receiver_ty.binders, }; @@ -1369,6 +1369,7 @@ pub(crate) fn resolve_indexing_op( None } +// FIXME: Replace this with a `Try` impl once stable macro_rules! check_that { ($cond:expr) => { if !$cond { @@ -1377,6 +1378,7 @@ macro_rules! check_that { }; } +#[derive(Debug)] enum IsValidCandidate { Yes, No, 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 index d4d669182f2e..fee3dd3ada85 100644 --- 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 @@ -9,11 +9,14 @@ use hir_def::{ resolver::HasResolver, }; -use crate::mir::eval::{ - name, pad16, static_lifetime, Address, AdtId, Arc, BuiltinType, Evaluator, FunctionId, - HasModule, HirDisplay, Interned, InternedClosure, Interner, Interval, IntervalAndTy, - IntervalOrOwned, ItemContainerId, LangItem, Layout, Locals, Lookup, MirEvalError, MirSpan, - Mutability, Result, Substitution, Ty, TyBuilder, TyExt, +use crate::{ + error_lifetime, + mir::eval::{ + name, pad16, Address, AdtId, Arc, BuiltinType, Evaluator, FunctionId, HasModule, + HirDisplay, Interned, InternedClosure, Interner, Interval, IntervalAndTy, IntervalOrOwned, + ItemContainerId, LangItem, Layout, Locals, Lookup, MirEvalError, MirSpan, Mutability, + Result, Substitution, Ty, TyBuilder, TyExt, + }, }; mod simd; @@ -247,7 +250,7 @@ impl Evaluator<'_> { let tmp = self.heap_allocate(self.ptr_size(), self.ptr_size())?; let arg = IntervalAndTy { interval: Interval { addr: tmp, size: self.ptr_size() }, - ty: TyKind::Ref(Mutability::Not, static_lifetime(), ty.clone()).intern(Interner), + ty: TyKind::Ref(Mutability::Not, error_lifetime(), ty.clone()).intern(Interner), }; let offset = layout.fields.offset(i).bytes_usize(); self.write_memory(tmp, &addr.offset(offset).to_bytes())?; 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 7e582c03efcd..151f65cfbb8e 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 @@ -27,6 +27,7 @@ use crate::{ consteval::ConstEvalError, db::{HirDatabase, InternedClosure}, display::HirDisplay, + error_lifetime, infer::{CaptureKind, CapturedItem, TypeMismatch}, inhabitedness::is_ty_uninhabited_from, layout::LayoutError, @@ -90,7 +91,7 @@ pub enum MirLowerError { UnresolvedField, UnsizedTemporary(Ty), MissingFunctionDefinition(DefWithBodyId, ExprId), - TypeMismatch(TypeMismatch), + TypeMismatch(Option), /// This should never happen. Type mismatch should catch everything. TypeError(&'static str), NotSupported(String), @@ -170,14 +171,15 @@ impl MirLowerError { body.pretty_print_expr(db.upcast(), *owner, *it) )?; } - MirLowerError::TypeMismatch(e) => { - writeln!( + MirLowerError::TypeMismatch(e) => match e { + Some(e) => writeln!( f, "Type mismatch: Expected {}, found {}", e.expected.display(db), e.actual.display(db), - )?; - } + )?, + None => writeln!(f, "Type mismatch: types mismatch with {{unknown}}",)?, + }, MirLowerError::GenericArgNotProvided(id, subst) => { let parent = id.parent; let param = &db.generic_params(parent).type_or_consts[id.local_id]; @@ -493,9 +495,11 @@ impl<'ctx> MirLowerCtx<'ctx> { ty, value: chalk_ir::ConstValue::BoundVar(BoundVar::new( DebruijnIndex::INNERMOST, - gen.param_idx(p.into()).ok_or(MirLowerError::TypeError( - "fail to lower const generic param", - ))?, + gen.type_or_const_param_idx(p.into()).ok_or( + MirLowerError::TypeError( + "fail to lower const generic param", + ), + )?, )), } .intern(Interner), @@ -1702,7 +1706,7 @@ impl<'ctx> MirLowerCtx<'ctx> { } fn is_uninhabited(&self, expr_id: ExprId) -> bool { - is_ty_uninhabited_from(&self.infer[expr_id], self.owner.module(self.db.upcast()), self.db) + is_ty_uninhabited_from(self.db, &self.infer[expr_id], self.owner.module(self.db.upcast())) } /// This function push `StorageLive` statement for the binding, and applies changes to add `StorageDead` and @@ -2032,10 +2036,12 @@ pub fn mir_body_for_closure_query( 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), + FnTrait::FnMut => { + TyKind::Ref(Mutability::Mut, error_lifetime(), infer[expr].clone()).intern(Interner) + } + FnTrait::Fn => { + TyKind::Ref(Mutability::Not, error_lifetime(), infer[expr].clone()).intern(Interner) + } }, }); ctx.result.param_locals.push(closure_local); @@ -2152,8 +2158,10 @@ pub fn lower_to_mir( // need to take this input explicitly. root_expr: ExprId, ) -> Result { - if let Some((_, it)) = infer.type_mismatches().next() { - return Err(MirLowerError::TypeMismatch(it.clone())); + if infer.has_errors { + return Err(MirLowerError::TypeMismatch( + infer.type_mismatches().next().map(|(_, it)| it.clone()), + )); } let mut ctx = MirLowerCtx::new(db, owner, body, infer); // 0 is return local 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 be81915bb407..4ad00909e412 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 @@ -290,7 +290,7 @@ impl MirLowerCtx<'_> { Some((_, _, mutability)) => mutability, None => Mutability::Not, }; - let result_ref = TyKind::Ref(mutability, static_lifetime(), result_ty).intern(Interner); + let result_ref = TyKind::Ref(mutability, error_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( @@ -333,8 +333,8 @@ impl MirLowerCtx<'_> { BorrowKind::Mut { kind: MutBorrowKind::Default }, ) }; - 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 ty_ref = TyKind::Ref(chalk_mut, error_lifetime(), source_ty.clone()).intern(Interner); + let target_ty_ref = TyKind::Ref(chalk_mut, error_lifetime(), target_ty).intern(Interner); let ref_place: Place = self.temp(ty_ref, current, span)?.into(); self.push_assignment(current, ref_place, Rvalue::Ref(borrow_kind, place), span); let deref_trait = self 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 index d6557c3a8160..a384c9306eee 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs @@ -101,7 +101,7 @@ impl FallibleTypeFolder for Filler<'_> { _outer_binder: DebruijnIndex, ) -> std::result::Result, Self::Error> { let it = from_placeholder_idx(self.db, idx); - let Some(idx) = self.generics.as_ref().and_then(|g| g.param_idx(it)) else { + let Some(idx) = self.generics.as_ref().and_then(|g| g.type_or_const_param_idx(it)) else { not_supported!("missing idx in generics"); }; Ok(self @@ -119,7 +119,7 @@ impl FallibleTypeFolder for Filler<'_> { _outer_binder: DebruijnIndex, ) -> std::result::Result { let it = from_placeholder_idx(self.db, idx); - let Some(idx) = self.generics.as_ref().and_then(|g| g.param_idx(it)) else { + let Some(idx) = self.generics.as_ref().and_then(|g| g.type_or_const_param_idx(it)) else { not_supported!("missing idx in generics"); }; Ok(self diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/diagnostics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/diagnostics.rs index 80f92eaf4355..119de7f050e7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/diagnostics.rs @@ -136,3 +136,20 @@ impl Trait for () { "#, ); } + +#[test] +fn no_mismatches_with_unresolved_projections() { + check_no_mismatches( + r#" +// `Thing` is `{unknown}` +fn create() -> Option<(i32, Thing)> { + Some((69420, Thing)) +} + +fn consume() -> Option<()> { + let (number, thing) = create()?; + Some(()) +} +"#, + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs index 506926749960..e8369caa7714 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs @@ -85,7 +85,7 @@ fn render_dyn_for_ty() { trait Foo<'a> {} fn foo(foo: &dyn for<'a> Foo<'a>) {} - // ^^^ &dyn Foo<'static> + // ^^^ &dyn Foo<'_> "#, ); } 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 80d5a0ae001b..4355881d7299 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 @@ -1109,7 +1109,7 @@ fn var_args() { #[lang = "va_list"] pub struct VaListImpl<'f>; fn my_fn(foo: ...) {} - //^^^ VaListImpl<'static> + //^^^ VaListImpl<'{error}> "#, ); } 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 8565b60210ba..c2d2047e6f99 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 @@ -896,13 +896,13 @@ fn flush(&self) { "#, expect![[r#" 123..127 'self': &Mutex - 150..152 '{}': MutexGuard<'static, T> + 150..152 '{}': MutexGuard<'{error}, 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<'static, BufWriter> + 278..286 'w.lock()': MutexGuard<'{error}, BufWriter> "#]], ); } 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 f39404593e5d..a9d28ebfef57 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 @@ -3092,7 +3092,7 @@ fn main() { 389..394 'boxed': Box> 389..406 'boxed....nner()': &i32 416..421 'good1': &i32 - 424..438 'Foo::get_inner': fn get_inner(&Box>) -> &i32 + 424..438 'Foo::get_inner': fn get_inner(&Box>) -> &i32 424..446 'Foo::g...boxed)': &i32 439..445 '&boxed': &Box> 440..445 'boxed': Box> @@ -3100,7 +3100,7 @@ fn main() { 464..469 'boxed': Box> 464..480 'boxed....self()': &Foo 490..495 'good2': &Foo - 498..511 'Foo::get_self': fn get_self(&Box>) -> &Foo + 498..511 'Foo::get_self': fn get_self(&Box>) -> &Foo 498..519 'Foo::g...boxed)': &Foo 512..518 '&boxed': &Box> 513..518 'boxed': Box> @@ -3659,7 +3659,7 @@ fn main() { let are = "are"; let count = 10; builtin#format_args("hello {count:02} {} friends, we {are:?} {0}{last}", "fancy", last = "!"); - // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type: Arguments<'static> + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type: Arguments<'{error}> } "#, ); 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 759af18c98bf..dfcd322a39c7 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 @@ -1602,7 +1602,7 @@ fn weird_bounds() { r#" //- minicore: sized trait Trait {} -fn test( +fn test<'lifetime>( a: impl Trait + 'lifetime, b: impl 'lifetime, c: impl (Trait), @@ -1612,13 +1612,13 @@ fn test( ) {} "#, expect![[r#" - 28..29 'a': impl Trait - 59..60 'b': impl Sized - 82..83 'c': impl Trait - 103..104 'd': impl Sized - 128..129 'e': impl ?Sized - 148..149 'f': impl Trait + ?Sized - 173..175 '{}': () + 39..40 'a': impl Trait + 'lifetime + 70..71 'b': impl 'lifetime + 93..94 'c': impl Trait + 114..115 'd': impl 'lifetime + 139..140 'e': impl ?Sized + 159..160 'f': impl Trait + ?Sized + 184..186 '{}': () "#]], ); } 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 afd4d1f271df..42c7a8403283 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs @@ -262,7 +262,7 @@ impl<'a> ClosureSubst<'a> { } } -#[derive(Debug)] +#[derive(Clone, Debug)] pub(crate) struct Generics { def: GenericDefId, pub(crate) params: Interned, @@ -274,6 +274,10 @@ impl Generics { self.iter().map(|(id, _)| id) } + pub(crate) fn def(&self) -> GenericDefId { + self.def + } + /// Iterator over types and const params of self, then parent. pub(crate) fn iter<'a>( &'a self, @@ -304,7 +308,11 @@ impl Generics { }; let lt_iter = self.params.iter_lt().map(from_lt_id(self)); - self.params.iter().map(from_toc_id(self)).chain(lt_iter).chain(self.iter_parent()) + self.params + .iter_type_or_consts() + .map(from_toc_id(self)) + .chain(lt_iter) + .chain(self.iter_parent()) } /// Iterate over types and const params without parent params. @@ -336,16 +344,19 @@ impl Generics { } }; - self.params.iter().map(from_toc_id(self)).chain(self.params.iter_lt().map(from_lt_id(self))) + self.params + .iter_type_or_consts() + .map(from_toc_id(self)) + .chain(self.params.iter_lt().map(from_lt_id(self))) } /// Iterator over types and const params of parent. - #[allow(clippy::needless_lifetimes)] - pub(crate) fn iter_parent<'a>( - &'a self, - ) -> impl DoubleEndedIterator)> + 'a { + pub(crate) fn iter_parent( + &self, + ) -> impl DoubleEndedIterator)> + '_ { self.parent_generics().into_iter().flat_map(|it| { - let from_toc_id = move |(local_id, p): (_, &'a TypeOrConstParamData)| { + let from_toc_id = move |(local_id, p)| { + let p: &_ = p; let id = TypeOrConstParamId { parent: it.def, local_id }; match p { TypeOrConstParamData::TypeParamData(p) => ( @@ -359,14 +370,14 @@ impl Generics { } }; - let from_lt_id = move |(local_id, p): (_, &'a LifetimeParamData)| { + let from_lt_id = move |(local_id, p): (_, _)| { ( GenericParamId::LifetimeParamId(LifetimeParamId { parent: it.def, local_id }), GenericParamDataRef::LifetimeParamData(p), ) }; let lt_iter = it.params.iter_lt().map(from_lt_id); - it.params.iter().map(from_toc_id).chain(lt_iter) + it.params.iter_type_or_consts().map(from_toc_id).chain(lt_iter) }) } @@ -383,7 +394,7 @@ impl Generics { } /// Returns number of generic parameter excluding those from parent - fn len_params(&self) -> usize { + fn len_type_and_const_params(&self) -> usize { self.params.type_or_consts.len() } @@ -394,7 +405,7 @@ impl Generics { let mut impl_trait_params = 0; let mut const_params = 0; let mut lifetime_params = 0; - self.params.iter().for_each(|(_, data)| match data { + self.params.iter_type_or_consts().for_each(|(_, data)| match data { TypeOrConstParamData::TypeParamData(p) => match p.provenance { TypeParamProvenance::TypeParamList => type_params += 1, TypeParamProvenance::TraitSelf => self_params += 1, @@ -409,18 +420,23 @@ impl Generics { (parent_len, self_params, type_params, const_params, impl_trait_params, lifetime_params) } - pub(crate) fn param_idx(&self, param: TypeOrConstParamId) -> Option { - Some(self.find_param(param)?.0) + pub(crate) fn type_or_const_param_idx(&self, param: TypeOrConstParamId) -> Option { + Some(self.find_type_or_const_param(param)?.0) } - fn find_param(&self, param: TypeOrConstParamId) -> Option<(usize, &TypeOrConstParamData)> { + fn find_type_or_const_param( + &self, + param: TypeOrConstParamId, + ) -> Option<(usize, &TypeOrConstParamData)> { if param.parent == self.def { - let (idx, (_local_id, data)) = - self.params.iter().enumerate().find(|(_, (idx, _))| *idx == param.local_id)?; - Some((idx, data)) + let idx = param.local_id.into_raw().into_u32() as usize; + if idx >= self.params.type_or_consts.len() { + return None; + } + Some((idx, &self.params.type_or_consts[param.local_id])) } else { self.parent_generics() - .and_then(|g| g.find_param(param)) + .and_then(|g| g.find_type_or_const_param(param)) // Remember that parent parameters come after parameters for self. .map(|(idx, data)| (self.len_self() + idx, data)) } @@ -432,13 +448,14 @@ impl Generics { fn find_lifetime(&self, lifetime: LifetimeParamId) -> Option<(usize, &LifetimeParamData)> { if lifetime.parent == self.def { - let (idx, (_local_id, data)) = self - .params - .iter_lt() - .enumerate() - .find(|(_, (idx, _))| *idx == lifetime.local_id)?; - - Some((self.len_params() + idx, data)) + let idx = lifetime.local_id.into_raw().into_u32() as usize; + if idx >= self.params.lifetimes.len() { + return None; + } + Some(( + self.len_type_and_const_params() + idx, + &self.params.lifetimes[lifetime.local_id], + )) } else { self.parent_generics() .and_then(|g| g.find_lifetime(lifetime)) @@ -450,6 +467,10 @@ impl Generics { self.parent_generics.as_deref() } + pub(crate) fn parent_or_self(&self) -> &Generics { + self.parent_generics.as_deref().unwrap_or(self) + } + /// Returns a Substitution that replaces each parameter by a bound variable. pub(crate) fn bound_vars_subst( &self, diff --git a/src/tools/rust-analyzer/crates/hir/Cargo.toml b/src/tools/rust-analyzer/crates/hir/Cargo.toml index 190722075a20..6d7ecd1e50b6 100644 --- a/src/tools/rust-analyzer/crates/hir/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir/Cargo.toml @@ -33,7 +33,7 @@ tt.workspace = true span.workspace = true [features] -in-rust-tree = [] +in-rust-tree = ["hir-expand/in-rust-tree"] [lints] workspace = true diff --git a/src/tools/rust-analyzer/crates/hir/src/db.rs b/src/tools/rust-analyzer/crates/hir/src/db.rs index 1d74f9a4bb22..cb5f5b06aefb 100644 --- a/src/tools/rust-analyzer/crates/hir/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir/src/db.rs @@ -4,24 +4,35 @@ //! //! But we need this for at least LRU caching at the query level. pub use hir_def::db::{ - AttrsQuery, BlockDefMapQuery, BodyQuery, BodyWithSourceMapQuery, ConstDataQuery, - ConstVisibilityQuery, CrateLangItemsQuery, CrateSupportsNoStdQuery, DefDatabase, - DefDatabaseStorage, EnumDataQuery, EnumVariantDataWithDiagnosticsQuery, ExprScopesQuery, - ExternCrateDeclDataQuery, FieldVisibilitiesQuery, FieldsAttrsQuery, FieldsAttrsSourceMapQuery, - FileItemTreeQuery, FunctionDataQuery, FunctionVisibilityQuery, GenericParamsQuery, - ImplDataWithDiagnosticsQuery, ImportMapQuery, InternAnonymousConstQuery, InternBlockQuery, - InternConstQuery, InternDatabase, InternDatabaseStorage, InternEnumQuery, - InternExternBlockQuery, InternExternCrateQuery, InternFunctionQuery, InternImplQuery, - InternInTypeConstQuery, InternMacro2Query, InternMacroRulesQuery, InternProcMacroQuery, - InternStaticQuery, InternStructQuery, InternTraitAliasQuery, InternTraitQuery, - InternTypeAliasQuery, InternUnionQuery, InternUseQuery, LangItemQuery, Macro2DataQuery, - MacroRulesDataQuery, ProcMacroDataQuery, StaticDataQuery, StructDataWithDiagnosticsQuery, - TraitAliasDataQuery, TraitDataWithDiagnosticsQuery, TypeAliasDataQuery, - UnionDataWithDiagnosticsQuery, + AttrsQuery, BlockDefMapQuery, BlockItemTreeQuery, BodyQuery, BodyWithSourceMapQuery, + ConstDataQuery, ConstVisibilityQuery, CrateDefMapQuery, CrateLangItemsQuery, + CrateNotableTraitsQuery, CrateSupportsNoStdQuery, DefDatabase, DefDatabaseStorage, + EnumDataQuery, EnumVariantDataWithDiagnosticsQuery, ExprScopesQuery, ExternCrateDeclDataQuery, + FieldVisibilitiesQuery, FieldsAttrsQuery, FieldsAttrsSourceMapQuery, FileItemTreeQuery, + FunctionDataQuery, FunctionVisibilityQuery, GenericParamsQuery, ImplDataWithDiagnosticsQuery, + ImportMapQuery, InternAnonymousConstQuery, InternBlockQuery, InternConstQuery, InternDatabase, + InternDatabaseStorage, InternEnumQuery, InternExternBlockQuery, InternExternCrateQuery, + InternFunctionQuery, InternImplQuery, InternInTypeConstQuery, InternMacro2Query, + InternMacroRulesQuery, InternProcMacroQuery, InternStaticQuery, InternStructQuery, + InternTraitAliasQuery, InternTraitQuery, InternTypeAliasQuery, InternUnionQuery, + InternUseQuery, LangItemQuery, Macro2DataQuery, MacroRulesDataQuery, ProcMacroDataQuery, + StaticDataQuery, StructDataWithDiagnosticsQuery, TraitAliasDataQuery, + TraitDataWithDiagnosticsQuery, TypeAliasDataQuery, UnionDataWithDiagnosticsQuery, }; pub use hir_expand::db::{ AstIdMapQuery, DeclMacroExpanderQuery, ExpandDatabase, ExpandDatabaseStorage, ExpandProcMacroQuery, InternMacroCallQuery, InternSyntaxContextQuery, MacroArgQuery, ParseMacroExpansionErrorQuery, ParseMacroExpansionQuery, ProcMacrosQuery, RealSpanMapQuery, }; -pub use hir_ty::db::*; +pub use hir_ty::db::{ + AdtDatumQuery, AdtVarianceQuery, AssociatedTyDataQuery, AssociatedTyValueQuery, BorrowckQuery, + CallableItemSignatureQuery, ConstEvalDiscriminantQuery, ConstEvalQuery, ConstEvalStaticQuery, + ConstParamTyQuery, FieldTypesQuery, FnDefDatumQuery, FnDefVarianceQuery, GenericDefaultsQuery, + GenericPredicatesForParamQuery, GenericPredicatesQuery, HirDatabase, HirDatabaseStorage, + ImplDatumQuery, ImplSelfTyQuery, ImplTraitQuery, IncoherentInherentImplCratesQuery, + InherentImplsInBlockQuery, InherentImplsInCrateQuery, InternCallableDefQuery, + InternClosureQuery, InternCoroutineQuery, InternImplTraitIdQuery, InternLifetimeParamIdQuery, + InternTypeOrConstParamIdQuery, LayoutOfAdtQuery, MirBodyQuery, ProgramClausesForChalkEnvQuery, + ReturnTypeImplTraitsQuery, TargetDataLayoutQuery, TraitDatumQuery, TraitEnvironmentQuery, + TraitImplsInBlockQuery, TraitImplsInCrateQuery, TraitImplsInDepsQuery, TyQuery, ValueTyQuery, +}; diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs index 23c6b078b969..84f03d111f27 100644 --- a/src/tools/rust-analyzer/crates/hir/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir/src/display.rs @@ -1,4 +1,5 @@ //! HirDisplay implementations for various hir types. +use either::Either; use hir_def::{ data::adt::{StructKind, VariantData}, generics::{ @@ -13,7 +14,7 @@ use hir_ty::{ write_bounds_like_dyn_trait_with_prefix, write_visibility, HirDisplay, HirDisplayError, HirFormatter, SizedByDefault, }, - Interner, TraitRefExt, WhereClause, + AliasEq, AliasTy, Interner, ProjectionTyExt, TraitRefExt, TyKind, WhereClause, }; use crate::{ @@ -363,16 +364,52 @@ impl HirDisplay for TypeOrConstParam { impl HirDisplay for TypeParam { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { - write!(f, "{}", self.name(f.db).display(f.db.upcast()))?; + let params = f.db.generic_params(self.id.parent()); + let param_data = ¶ms.type_or_consts[self.id.local_id()]; + let substs = TyBuilder::placeholder_subst(f.db, self.id.parent()); + let krate = self.id.parent().krate(f.db).id; + let ty = + TyKind::Placeholder(hir_ty::to_placeholder_idx(f.db, self.id.into())).intern(Interner); + let predicates = f.db.generic_predicates(self.id.parent()); + let predicates = predicates + .iter() + .cloned() + .map(|pred| pred.substitute(Interner, &substs)) + .filter(|wc| match wc.skip_binders() { + WhereClause::Implemented(tr) => tr.self_type_parameter(Interner) == ty, + WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(proj), ty: _ }) => { + proj.self_type_parameter(f.db) == ty + } + WhereClause::AliasEq(_) => false, + WhereClause::TypeOutlives(to) => to.ty == ty, + WhereClause::LifetimeOutlives(_) => false, + }) + .collect::>(); + + match param_data { + TypeOrConstParamData::TypeParamData(p) => match p.provenance { + TypeParamProvenance::TypeParamList | TypeParamProvenance::TraitSelf => { + write!(f, "{}", p.name.clone().unwrap().display(f.db.upcast()))? + } + TypeParamProvenance::ArgumentImplTrait => { + return write_bounds_like_dyn_trait_with_prefix( + f, + "impl", + Either::Left(&ty), + &predicates, + SizedByDefault::Sized { anchor: krate }, + ); + } + }, + TypeOrConstParamData::ConstParamData(p) => { + write!(f, "{}", p.name.display(f.db.upcast()))?; + } + } + if f.omit_verbose_types() { return Ok(()); } - let bounds = f.db.generic_predicates_for_param(self.id.parent(), self.id.into(), None); - let substs = TyBuilder::placeholder_subst(f.db, self.id.parent()); - let predicates: Vec<_> = - bounds.iter().cloned().map(|b| b.substitute(Interner, &substs)).collect(); - let krate = self.id.parent().krate(f.db).id; let sized_trait = f.db.lang_item(krate, LangItem::Sized).and_then(|lang_item| lang_item.as_trait()); let has_only_sized_bound = predicates.iter().all(move |pred| match pred.skip_binders() { @@ -382,7 +419,16 @@ impl HirDisplay for TypeParam { let has_only_not_sized_bound = predicates.is_empty(); if !has_only_sized_bound || has_only_not_sized_bound { let default_sized = SizedByDefault::Sized { anchor: krate }; - write_bounds_like_dyn_trait_with_prefix(f, ":", &predicates, default_sized)?; + write_bounds_like_dyn_trait_with_prefix( + f, + ":", + Either::Left( + &hir_ty::TyKind::Placeholder(hir_ty::to_placeholder_idx(f.db, self.id.into())) + .intern(Interner), + ), + &predicates, + default_sized, + )?; } Ok(()) } diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 106056c2fc3a..bcd94a611a90 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -239,7 +239,7 @@ impl Crate { db: &dyn DefDatabase, query: import_map::Query, ) -> impl Iterator> { - let _p = tracing::span!(tracing::Level::INFO, "query_external_importables"); + let _p = tracing::span!(tracing::Level::INFO, "query_external_importables").entered(); import_map::search_dependencies(db, self.into(), &query).into_iter().map(|item| { match ItemInNs::from(item) { ItemInNs::Types(mod_id) | ItemInNs::Values(mod_id) => Either::Left(mod_id), @@ -260,11 +260,11 @@ impl Crate { doc_url.map(|s| s.trim_matches('"').trim_end_matches('/').to_owned() + "/") } - pub fn cfg(&self, db: &dyn HirDatabase) -> CfgOptions { + pub fn cfg(&self, db: &dyn HirDatabase) -> Arc { db.crate_graph()[self.id].cfg_options.clone() } - pub fn potential_cfg(&self, db: &dyn HirDatabase) -> CfgOptions { + pub fn potential_cfg(&self, db: &dyn HirDatabase) -> Arc { let data = &db.crate_graph()[self.id]; data.potential_cfg_options.clone().unwrap_or_else(|| data.cfg_options.clone()) } @@ -548,8 +548,8 @@ impl Module { acc: &mut Vec, style_lints: bool, ) { - let name = self.name(db); - let _p = tracing::span!(tracing::Level::INFO, "Module::diagnostics", ?name); + let _p = tracing::span!(tracing::Level::INFO, "Module::diagnostics", name = ?self.name(db)) + .entered(); let def_map = self.id.def_map(db.upcast()); for diag in def_map.diagnostics() { if diag.in_module != self.id.local_id { @@ -653,7 +653,7 @@ impl Module { GenericParamId::LifetimeParamId(LifetimeParamId { parent, local_id }) }); let type_params = generic_params - .iter() + .iter_type_or_consts() .filter(|(_, it)| it.type_param().is_some()) .map(|(local_id, _)| { GenericParamId::TypeParamId(TypeParamId::from_unchecked( @@ -684,7 +684,7 @@ impl Module { let items = &db.trait_data(trait_.into()).items; let required_items = items.iter().filter(|&(_, assoc)| match *assoc { AssocItemId::FunctionId(it) => !db.function_data(it).has_body(), - AssocItemId::ConstId(id) => Const::from(id).value(db).is_none(), + AssocItemId::ConstId(id) => !db.const_data(id).has_body, AssocItemId::TypeAliasId(it) => db.type_alias_data(it).type_ref.is_none(), }); impl_assoc_items_scratch.extend(db.impl_data(impl_def.id).items.iter().filter_map( @@ -1418,16 +1418,14 @@ impl Adt { } pub fn layout(self, db: &dyn HirDatabase) -> Result { - if !db.generic_params(self.into()).is_empty() { - return Err(LayoutError::HasPlaceholder); - } - let krate = self.krate(db).id; db.layout_of_adt( self.into(), - Substitution::empty(Interner), + TyBuilder::adt(db, self.into()) + .fill_with_defaults(db, || TyKind::Error.intern(Interner)) + .build_into_subst(), db.trait_environment(self.into()), ) - .map(|layout| Layout(layout, db.target_data_layout(krate).unwrap())) + .map(|layout| Layout(layout, db.target_data_layout(self.krate(db).id).unwrap())) } /// Turns this ADT into a type. Any type parameters of the ADT will be @@ -1630,7 +1628,6 @@ impl DefWithBody { acc: &mut Vec, style_lints: bool, ) { - db.unwind_if_cancelled(); let krate = self.module(db).id.krate(); let (body, source_map) = db.body_with_source_map(self.into()); @@ -1678,6 +1675,7 @@ impl DefWithBody { for d in &infer.diagnostics { acc.extend(AnyDiagnostic::inference_diagnostic(db, self.into(), d, &source_map)); } + 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), @@ -1763,7 +1761,9 @@ impl DefWithBody { need_mut = &mir::MutabilityReason::Not; } let local = Local { parent: self.into(), binding_id }; - match (need_mut, local.is_mut(db)) { + let is_mut = body[binding_id].mode == BindingAnnotation::Mutable; + + match (need_mut, is_mut) { (mir::MutabilityReason::Unused, _) => { let should_ignore = matches!(body[binding_id].name.as_str(), Some(it) if it.starts_with('_')); if !should_ignore { @@ -2007,12 +2007,15 @@ impl Function { /// is this a `fn main` or a function with an `export_name` of `main`? pub fn is_main(self, db: &dyn HirDatabase) -> bool { - if !self.module(db).is_crate_root() { - return false; - } let data = db.function_data(self.id); + data.attrs.export_name() == Some("main") + || self.module(db).is_crate_root() && data.name.to_smol_str() == "main" + } - data.name.to_smol_str() == "main" || data.attrs.export_name() == Some("main") + /// Is this a function with an `export_name` of `main`? + pub fn exported_main(self, db: &dyn HirDatabase) -> bool { + let data = db.function_data(self.id); + data.attrs.export_name() == Some("main") } /// Does this function have the ignore attribute? @@ -3909,7 +3912,7 @@ impl Type { inner.derived( TyKind::Ref( if m.is_mut() { hir_ty::Mutability::Mut } else { hir_ty::Mutability::Not }, - hir_ty::static_lifetime(), + hir_ty::error_lifetime(), inner.ty.clone(), ) .intern(Interner), @@ -4492,7 +4495,7 @@ impl Type { name: Option<&Name>, mut callback: impl FnMut(Function) -> Option, ) -> Option { - let _p = tracing::span!(tracing::Level::INFO, "iterate_method_candidates"); + let _p = tracing::span!(tracing::Level::INFO, "iterate_method_candidates").entered(); let mut slot = None; self.iterate_method_candidates_dyn( @@ -4580,7 +4583,7 @@ impl Type { name: Option<&Name>, mut callback: impl FnMut(AssocItem) -> Option, ) -> Option { - let _p = tracing::span!(tracing::Level::INFO, "iterate_path_candidates"); + let _p = tracing::span!(tracing::Level::INFO, "iterate_path_candidates").entered(); let mut slot = None; self.iterate_path_candidates_dyn( db, @@ -4647,7 +4650,7 @@ impl Type { &'a self, db: &'a dyn HirDatabase, ) -> impl Iterator + 'a { - let _p = tracing::span!(tracing::Level::INFO, "applicable_inherent_traits"); + let _p = tracing::span!(tracing::Level::INFO, "applicable_inherent_traits").entered(); self.autoderef_(db) .filter_map(|ty| ty.dyn_trait()) .flat_map(move |dyn_trait_id| hir_ty::all_super_traits(db.upcast(), dyn_trait_id)) @@ -4655,7 +4658,7 @@ impl Type { } pub fn env_traits<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator + 'a { - let _p = tracing::span!(tracing::Level::INFO, "env_traits"); + let _p = tracing::span!(tracing::Level::INFO, "env_traits").entered(); self.autoderef_(db) .filter(|ty| matches!(ty.kind(Interner), TyKind::Placeholder(_))) .flat_map(|ty| { @@ -4709,10 +4712,12 @@ impl Type { if let WhereClause::Implemented(trait_ref) = pred.skip_binders() { cb(type_.clone()); // skip the self type. it's likely the type we just got the bounds from - for ty in - trait_ref.substitution.iter(Interner).skip(1).filter_map(|a| a.ty(Interner)) - { - walk_type(db, &type_.derived(ty.clone()), cb); + if let [self_ty, params @ ..] = trait_ref.substitution.as_slice(Interner) { + for ty in + params.iter().filter(|&ty| ty != self_ty).filter_map(|a| a.ty(Interner)) + { + walk_type(db, &type_.derived(ty.clone()), cb); + } } } } diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 9796009cb45c..e792e159acf0 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -722,7 +722,7 @@ impl<'db> SemanticsImpl<'db> { mut token: SyntaxToken, f: &mut dyn FnMut(InFile) -> ControlFlow<()>, ) { - let _p = tracing::span!(tracing::Level::INFO, "descend_into_macros"); + let _p = tracing::span!(tracing::Level::INFO, "descend_into_macros").entered(); let (sa, span, file_id) = match token.parent().and_then(|parent| self.analyze_no_infer(&parent)) { Some(sa) => match sa.file_id.file_id() { @@ -1246,6 +1246,17 @@ impl<'db> SemanticsImpl<'db> { .map_or(false, |m| matches!(m.id, MacroId::ProcMacroId(..))) } + pub fn resolve_macro_call_arm(&self, macro_call: &ast::MacroCall) -> Option { + let sa = self.analyze(macro_call.syntax())?; + self.db + .parse_macro_expansion( + sa.expand(self.db, self.wrap_node_infile(macro_call.clone()).as_ref())?, + ) + .value + .1 + .matched_arm + } + pub fn is_unsafe_macro_call(&self, macro_call: &ast::MacroCall) -> bool { let sa = match self.analyze(macro_call.syntax()) { Some(it) => it, @@ -1359,7 +1370,7 @@ impl<'db> SemanticsImpl<'db> { offset: Option, infer_body: bool, ) -> Option { - let _p = tracing::span!(tracing::Level::INFO, "Semantics::analyze_impl"); + let _p = tracing::span!(tracing::Level::INFO, "Semantics::analyze_impl").entered(); let node = self.find_file(node); let container = self.with_ctx(|ctx| ctx.find_container(node))?; 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 d4d6f0b243fd..434e4b5a0cf1 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 @@ -118,7 +118,7 @@ pub(super) struct SourceToDefCtx<'a, 'b> { impl SourceToDefCtx<'_, '_> { pub(super) fn file_to_def(&self, file: FileId) -> SmallVec<[ModuleId; 1]> { - let _p = tracing::span!(tracing::Level::INFO, "SourceBinder::file_to_module_def"); + let _p = tracing::span!(tracing::Level::INFO, "SourceBinder::file_to_module_def").entered(); let mut mods = SmallVec::new(); for &crate_id in self.db.relevant_crates(file).iter() { // FIXME: inner items @@ -133,7 +133,7 @@ impl SourceToDefCtx<'_, '_> { } pub(super) fn module_to_def(&mut self, src: InFile) -> Option { - let _p = tracing::span!(tracing::Level::INFO, "module_to_def"); + let _p = tracing::span!(tracing::Level::INFO, "module_to_def").entered(); let parent_declaration = src .syntax() .ancestors_with_macros_skip_attr_item(self.db.upcast()) @@ -158,7 +158,7 @@ impl SourceToDefCtx<'_, '_> { } pub(super) fn source_file_to_def(&self, src: InFile) -> Option { - let _p = tracing::span!(tracing::Level::INFO, "source_file_to_def"); + let _p = tracing::span!(tracing::Level::INFO, "source_file_to_def").entered(); let file_id = src.file_id.original_file(self.db.upcast()); self.file_to_def(file_id).first().copied() } 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 c1b95bb1e253..24a32086f3da 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 @@ -105,7 +105,7 @@ fn add_missing_impl_members_inner( assist_id: &'static str, label: &'static str, ) -> Option<()> { - let _p = tracing::span!(tracing::Level::INFO, "add_missing_impl_members_inner"); + let _p = tracing::span!(tracing::Level::INFO, "add_missing_impl_members_inner").entered(); let impl_def = ctx.find_node_at_offset::()?; let impl_ = ctx.sema.to_def(&impl_def)?; 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 62696d1a9a80..b90bccb48ede 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 @@ -1588,4 +1588,82 @@ mod bar { "#, ); } + + #[test] + fn local_inline_import_has_alias() { + // FIXME + check_assist_not_applicable( + auto_import, + r#" +struct S(T); +use S as IoResult; + +mod foo { + pub fn bar() -> S$0<()> {} +} +"#, + ); + } + + #[test] + fn alias_local() { + // FIXME + check_assist_not_applicable( + auto_import, + r#" +struct S(T); +use S as IoResult; + +mod foo { + pub fn bar() -> IoResult$0<()> {} +} +"#, + ); + } + + #[test] + fn preserve_raw_identifiers_strict() { + check_assist( + auto_import, + r" + r#as$0 + + pub mod ffi_mod { + pub fn r#as() {}; + } + ", + r" + use ffi_mod::r#as; + + r#as + + pub mod ffi_mod { + pub fn r#as() {}; + } + ", + ); + } + + #[test] + fn preserve_raw_identifiers_reserved() { + check_assist( + auto_import, + r" + r#abstract$0 + + pub mod ffi_mod { + pub fn r#abstract() {}; + } + ", + r" + use ffi_mod::r#abstract; + + r#abstract + + pub mod ffi_mod { + pub fn r#abstract() {}; + } + ", + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs new file mode 100644 index 000000000000..5459bd334c87 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs @@ -0,0 +1,250 @@ +use ide_db::{famous_defs::FamousDefs, traits::resolve_target_trait}; +use itertools::Itertools; +use syntax::{ + ast::{self, make, AstNode, HasName}, + ted, +}; + +use crate::{AssistContext, AssistId, AssistKind, Assists}; + +// Assist: convert_from_to_tryfrom +// +// Converts a From impl to a TryFrom impl, wrapping returns in `Ok`. +// +// ``` +// # //- minicore: from +// impl $0From for Thing { +// fn from(val: usize) -> Self { +// Thing { +// b: val.to_string(), +// a: val +// } +// } +// } +// ``` +// -> +// ``` +// impl TryFrom for Thing { +// type Error = ${0:()}; +// +// fn try_from(val: usize) -> Result { +// Ok(Thing { +// b: val.to_string(), +// a: val +// }) +// } +// } +// ``` +pub(crate) fn convert_from_to_tryfrom(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + let impl_ = ctx.find_node_at_offset::()?; + let trait_ty = impl_.trait_()?; + + let module = ctx.sema.scope(impl_.syntax())?.module(); + + let from_type = match &trait_ty { + ast::Type::PathType(path) => { + path.path()?.segment()?.generic_arg_list()?.generic_args().next()? + } + _ => return None, + }; + + let associated_items = impl_.assoc_item_list()?; + let from_fn = associated_items.assoc_items().find_map(|item| { + if let ast::AssocItem::Fn(f) = item { + if f.name()?.text() == "from" { + return Some(f); + } + }; + None + })?; + + let from_fn_name = from_fn.name()?; + let from_fn_return_type = from_fn.ret_type()?.ty()?; + + let return_exprs = from_fn.body()?.syntax().descendants().filter_map(ast::ReturnExpr::cast); + let tail_expr = from_fn.body()?.tail_expr()?; + + if resolve_target_trait(&ctx.sema, &impl_)? + != FamousDefs(&ctx.sema, module.krate()).core_convert_From()? + { + return None; + } + + acc.add( + AssistId("convert_from_to_tryfrom", AssistKind::RefactorRewrite), + "Convert From to TryFrom", + impl_.syntax().text_range(), + |builder| { + let trait_ty = builder.make_mut(trait_ty); + let from_fn_return_type = builder.make_mut(from_fn_return_type); + let from_fn_name = builder.make_mut(from_fn_name); + let tail_expr = builder.make_mut(tail_expr); + let return_exprs = return_exprs.map(|r| builder.make_mut(r)).collect_vec(); + let associated_items = builder.make_mut(associated_items).clone(); + + ted::replace( + trait_ty.syntax(), + make::ty(&format!("TryFrom<{from_type}>")).syntax().clone_for_update(), + ); + ted::replace( + from_fn_return_type.syntax(), + make::ty("Result").syntax().clone_for_update(), + ); + ted::replace(from_fn_name.syntax(), make::name("try_from").syntax().clone_for_update()); + ted::replace( + tail_expr.syntax(), + wrap_ok(tail_expr.clone()).syntax().clone_for_update(), + ); + + for r in return_exprs { + let t = r.expr().unwrap_or_else(make::expr_unit); + ted::replace(t.syntax(), wrap_ok(t.clone()).syntax().clone_for_update()); + } + + let error_type = ast::AssocItem::TypeAlias(make::ty_alias( + "Error", + None, + None, + None, + Some((make::ty_unit(), None)), + )) + .clone_for_update(); + + if let Some(cap) = ctx.config.snippet_cap { + if let ast::AssocItem::TypeAlias(type_alias) = &error_type { + if let Some(ty) = type_alias.ty() { + builder.add_placeholder_snippet(cap, ty); + } + } + } + + associated_items.add_item_at_start(error_type); + }, + ) +} + +fn wrap_ok(expr: ast::Expr) -> ast::Expr { + make::expr_call( + make::expr_path(make::ext::ident_path("Ok")), + make::arg_list(std::iter::once(expr)), + ) +} + +#[cfg(test)] +mod tests { + use super::*; + + use crate::tests::{check_assist, check_assist_not_applicable}; + + #[test] + fn converts_from_to_tryfrom() { + check_assist( + convert_from_to_tryfrom, + r#" +//- minicore: from +struct Foo(String); + +impl $0From for Foo { + fn from(val: String) -> Self { + if val == "bar" { + return Foo(val); + } + Self(val) + } +} + "#, + r#" +struct Foo(String); + +impl TryFrom for Foo { + type Error = ${0:()}; + + fn try_from(val: String) -> Result { + if val == "bar" { + return Ok(Foo(val)); + } + Ok(Self(val)) + } +} + "#, + ); + } + + #[test] + fn converts_from_to_tryfrom_nested_type() { + check_assist( + convert_from_to_tryfrom, + r#" +//- minicore: from +struct Foo(String); + +impl $0From> for Foo { + fn from(val: Option) -> Self { + match val { + Some(val) => Foo(val), + None => Foo("".to_string()) + } + } +} + "#, + r#" +struct Foo(String); + +impl TryFrom> for Foo { + type Error = ${0:()}; + + fn try_from(val: Option) -> Result { + Ok(match val { + Some(val) => Foo(val), + None => Foo("".to_string()) + }) + } +} + "#, + ); + } + + #[test] + fn converts_from_to_tryfrom_preserves_lifetimes() { + check_assist( + convert_from_to_tryfrom, + r#" +//- minicore: from +struct Foo<'a>(&'a str); + +impl<'a> $0From<&'a str> for Foo<'a> { + fn from(val: &'a str) -> Self { + Self(val) + } +} + "#, + r#" +struct Foo<'a>(&'a str); + +impl<'a> TryFrom<&'a str> for Foo<'a> { + type Error = ${0:()}; + + fn try_from(val: &'a str) -> Result { + Ok(Self(val)) + } +} + "#, + ); + } + + #[test] + fn other_trait_not_applicable() { + check_assist_not_applicable( + convert_from_to_tryfrom, + r#" +struct Foo(String); + +impl $0TryFrom for Foo { + fn try_from(val: String) -> Result { + Ok(Self(val)) + } +} + "#, + ); + } +} 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 65ce3e822c5b..34326294d2e1 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 @@ -1149,8 +1149,14 @@ fn reference_is_exclusive( node: &dyn HasTokenAtOffset, ctx: &AssistContext<'_>, ) -> bool { + // FIXME: this quite an incorrect way to go about doing this :-) + // `FileReference` is an IDE-type --- it encapsulates data communicated to the human, + // but doesn't necessary fully reflect all the intricacies of the underlying language semantics + // The correct approach here would be to expose this entire analysis as a method on some hir + // type. Something like `body.free_variables(statement_range)`. + // we directly modify variable with set: `n = 0`, `n += 1` - if reference.category == Some(ReferenceCategory::Write) { + if reference.category.contains(ReferenceCategory::WRITE) { return true; } @@ -5617,7 +5623,7 @@ fn func(i: Struct<'_, T>) { fun_name(i); } -fn $0fun_name(i: Struct<'static, T>) { +fn $0fun_name(i: Struct<'_, T>) { foo(i); } "#, 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 ff051fa870f5..db94a21a6d2c 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 @@ -175,8 +175,7 @@ fn add_func_to_accumulator( edit.edit_file(file); let target = function_builder.target.clone(); - let function_template = function_builder.render(); - let func = function_template.to_ast(ctx.config.snippet_cap, edit); + let func = function_builder.render(ctx.config.snippet_cap, edit); if let Some(name) = adt_name { let name = make::ty_path(make::ext::ident_path(&format!("{}", name.display(ctx.db())))); @@ -205,37 +204,6 @@ fn get_adt_source( find_struct_impl(ctx, &adt_source, &[fn_name.to_owned()]).map(|impl_| (impl_, range.file_id)) } -struct FunctionTemplate { - fn_def: ast::Fn, - ret_type: Option, - should_focus_return_type: bool, - tail_expr: ast::Expr, -} - -impl FunctionTemplate { - fn to_ast(&self, cap: Option, edit: &mut SourceChangeBuilder) -> ast::Fn { - let Self { fn_def, ret_type, should_focus_return_type, tail_expr } = self; - - if let Some(cap) = cap { - if *should_focus_return_type { - // Focus the return type if there is one - match ret_type { - Some(ret_type) => { - edit.add_placeholder_snippet(cap, ret_type.clone()); - } - None => { - edit.add_placeholder_snippet(cap, tail_expr.clone()); - } - } - } else { - edit.add_placeholder_snippet(cap, tail_expr.clone()); - } - } - - fn_def.clone() - } -} - struct FunctionBuilder { target: GeneratedFunctionTarget, fn_name: ast::Name, @@ -339,7 +307,7 @@ impl FunctionBuilder { }) } - fn render(self) -> FunctionTemplate { + fn render(self, cap: Option, edit: &mut SourceChangeBuilder) -> ast::Fn { let placeholder_expr = make::ext::expr_todo(); let fn_body = make::block_expr(vec![], Some(placeholder_expr)); let visibility = match self.visibility { @@ -361,17 +329,31 @@ impl FunctionBuilder { ) .clone_for_update(); - FunctionTemplate { - ret_type: fn_def.ret_type(), - // PANIC: we guarantee we always create a function body with a tail expr - tail_expr: fn_def - .body() - .expect("generated function should have a body") - .tail_expr() - .expect("function body should have a tail expression"), - should_focus_return_type: self.should_focus_return_type, - fn_def, + let ret_type = fn_def.ret_type(); + // PANIC: we guarantee we always create a function body with a tail expr + let tail_expr = fn_def + .body() + .expect("generated function should have a body") + .tail_expr() + .expect("function body should have a tail expression"); + + if let Some(cap) = cap { + if self.should_focus_return_type { + // Focus the return type if there is one + match ret_type { + Some(ret_type) => { + edit.add_placeholder_snippet(cap, ret_type.clone()); + } + None => { + edit.add_placeholder_snippet(cap, tail_expr.clone()); + } + } + } else { + edit.add_placeholder_snippet(cap, tail_expr.clone()); + } } + + fn_def } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs index a90fe83857e9..44307ffd75bf 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs @@ -49,13 +49,13 @@ use crate::{ // // fn bar() { // { -// let word = "안녕하세요"; +// let word: &str = "안녕하세요"; // if !word.is_empty() { // print(word); // } // }; // { -// let word = "여러분"; +// let word: &str = "여러분"; // if !word.is_empty() { // print(word); // } 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 67fea772c795..7c2dc0e0c10a 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 @@ -59,10 +59,7 @@ pub(crate) fn promote_local_to_const(acc: &mut Assists, ctx: &AssistContext<'_>) 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; - } + Err(_) => return None, }; let initializer = let_stmt.initializer()?; @@ -315,13 +312,17 @@ fn foo() { #[test] fn not_applicable_unknown_ty() { - cov_mark::check!(promote_local_not_applicable_if_ty_not_inferred); - check_assist_not_applicable( + check_assist( promote_local_to_const, r" fn foo() { let x$0 = bar(); } +", + r" +fn foo() { + const $0X: _ = bar(); +} ", ); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs index d67b259d2f5f..0f0f13bbc80b 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs @@ -145,7 +145,7 @@ fn used_once_in_scope(ctx: &AssistContext<'_>, def: Definition, scopes: &Vec) -> Option<()> { let attr: ast::Attr = ctx.find_node_at_offset()?; let func = attr.syntax().parent().and_then(ast::Fn::cast)?; - let attr = test_related_attribute(&func)?; + let attr = test_related_attribute_syn(&func)?; match has_ignore_attribute(&func) { None => acc.add( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs new file mode 100644 index 000000000000..0fa46ef43a1e --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs @@ -0,0 +1,581 @@ +use ide_db::source_change::SourceChangeBuilder; +use itertools::Itertools; +use syntax::{ + algo, + ast::{self, make, AstNode}, + ted::{self, Position}, + NodeOrToken, SyntaxToken, TextRange, T, +}; + +use crate::{AssistContext, AssistId, AssistKind, Assists}; + +// Assist: wrap_unwrap_cfg_attr +// +// Wraps an attribute to a cfg_attr attribute or unwraps a cfg_attr attribute to the inner attributes. +// +// ``` +// #[derive$0(Debug)] +// struct S { +// field: i32 +// } +// ``` +// -> +// ``` +// #[cfg_attr($0, derive(Debug))] +// struct S { +// field: i32 +// } + +enum WrapUnwrapOption { + WrapDerive { derive: TextRange, attr: ast::Attr }, + WrapAttr(ast::Attr), +} + +/// Attempts to get the derive attribute from a derive attribute list +/// +/// This will collect all the tokens in the "path" within the derive attribute list +/// But a derive attribute list doesn't have paths. So we need to collect all the tokens before and after the ident +/// +/// If this functions return None just map to WrapAttr +fn attempt_get_derive(attr: ast::Attr, ident: SyntaxToken) -> WrapUnwrapOption { + let attempt_attr = || { + { + let mut derive = ident.text_range(); + // TokenTree is all the tokens between the `(` and `)`. They do not have paths. So a path `serde::Serialize` would be [Ident Colon Colon Ident] + // So lets say we have derive(Debug, serde::Serialize, Copy) ident would be on Serialize + // We need to grab all previous tokens until we find a `,` or `(` and all following tokens until we find a `,` or `)` + // We also want to consume the following comma if it exists + + let mut prev = algo::skip_trivia_token( + ident.prev_sibling_or_token()?.into_token()?, + syntax::Direction::Prev, + )?; + let mut following = algo::skip_trivia_token( + ident.next_sibling_or_token()?.into_token()?, + syntax::Direction::Next, + )?; + if (prev.kind() == T![,] || prev.kind() == T!['(']) + && (following.kind() == T![,] || following.kind() == T![')']) + { + // This would be a single ident such as Debug. As no path is present + if following.kind() == T![,] { + derive = derive.cover(following.text_range()); + } else if following.kind() == T![')'] && prev.kind() == T![,] { + derive = derive.cover(prev.text_range()); + } + + Some(WrapUnwrapOption::WrapDerive { derive, attr: attr.clone() }) + } else { + let mut consumed_comma = false; + // Collect the path + while let Some(prev_token) = algo::skip_trivia_token(prev, syntax::Direction::Prev) + { + let kind = prev_token.kind(); + if kind == T![,] { + consumed_comma = true; + derive = derive.cover(prev_token.text_range()); + break; + } else if kind == T!['('] { + break; + } else { + derive = derive.cover(prev_token.text_range()); + } + prev = prev_token.prev_sibling_or_token()?.into_token()?; + } + while let Some(next_token) = + algo::skip_trivia_token(following.clone(), syntax::Direction::Next) + { + let kind = next_token.kind(); + match kind { + T![,] if !consumed_comma => { + derive = derive.cover(next_token.text_range()); + break; + } + T![')'] | T![,] => break, + _ => derive = derive.cover(next_token.text_range()), + } + following = next_token.next_sibling_or_token()?.into_token()?; + } + Some(WrapUnwrapOption::WrapDerive { derive, attr: attr.clone() }) + } + } + }; + if ident.parent().and_then(ast::TokenTree::cast).is_none() + || !attr.simple_name().map(|v| v.eq("derive")).unwrap_or_default() + { + WrapUnwrapOption::WrapAttr(attr) + } else { + attempt_attr().unwrap_or(WrapUnwrapOption::WrapAttr(attr)) + } +} +pub(crate) fn wrap_unwrap_cfg_attr(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + let option = if ctx.has_empty_selection() { + let ident = ctx.find_token_syntax_at_offset(T![ident]); + let attr = ctx.find_node_at_offset::(); + match (attr, ident) { + (Some(attr), Some(ident)) + if attr.simple_name().map(|v| v.eq("derive")).unwrap_or_default() => + { + Some(attempt_get_derive(attr.clone(), ident)) + } + + (Some(attr), _) => Some(WrapUnwrapOption::WrapAttr(attr)), + _ => None, + } + } else { + let covering_element = ctx.covering_element(); + match covering_element { + NodeOrToken::Node(node) => ast::Attr::cast(node).map(WrapUnwrapOption::WrapAttr), + NodeOrToken::Token(ident) if ident.kind() == syntax::T![ident] => { + let attr = ident.parent_ancestors().find_map(ast::Attr::cast)?; + Some(attempt_get_derive(attr.clone(), ident)) + } + _ => None, + } + }?; + match option { + WrapUnwrapOption::WrapAttr(attr) if attr.simple_name().as_deref() == Some("cfg_attr") => { + unwrap_cfg_attr(acc, attr) + } + WrapUnwrapOption::WrapAttr(attr) => wrap_cfg_attr(acc, ctx, attr), + WrapUnwrapOption::WrapDerive { derive, attr } => wrap_derive(acc, ctx, attr, derive), + } +} + +fn wrap_derive( + acc: &mut Assists, + ctx: &AssistContext<'_>, + attr: ast::Attr, + derive_element: TextRange, +) -> Option<()> { + let range = attr.syntax().text_range(); + let token_tree = attr.token_tree()?; + let mut path_text = String::new(); + + let mut cfg_derive_tokens = Vec::new(); + let mut new_derive = Vec::new(); + + for tt in token_tree.token_trees_and_tokens() { + let NodeOrToken::Token(token) = tt else { + continue; + }; + if token.kind() == T!['('] || token.kind() == T![')'] { + continue; + } + + if derive_element.contains_range(token.text_range()) { + if token.kind() != T![,] && token.kind() != syntax::SyntaxKind::WHITESPACE { + path_text.push_str(token.text()); + cfg_derive_tokens.push(NodeOrToken::Token(token)); + } + } else { + new_derive.push(NodeOrToken::Token(token)); + } + } + let handle_source_change = |edit: &mut SourceChangeBuilder| { + let new_derive = make::attr_outer(make::meta_token_tree( + make::ext::ident_path("derive"), + make::token_tree(T!['('], new_derive), + )) + .clone_for_update(); + let meta = make::meta_token_tree( + make::ext::ident_path("cfg_attr"), + make::token_tree( + T!['('], + vec![ + NodeOrToken::Token(make::token(T![,])), + NodeOrToken::Token(make::tokens::whitespace(" ")), + NodeOrToken::Token(make::tokens::ident("derive")), + NodeOrToken::Node(make::token_tree(T!['('], cfg_derive_tokens)), + ], + ), + ); + // Remove the derive attribute + let edit_attr = edit.make_syntax_mut(attr.syntax().clone()); + + ted::replace(edit_attr, new_derive.syntax().clone()); + let cfg_attr = make::attr_outer(meta).clone_for_update(); + + ted::insert_all_raw( + Position::after(new_derive.syntax().clone()), + vec![make::tokens::whitespace("\n").into(), cfg_attr.syntax().clone().into()], + ); + if let Some(snippet_cap) = ctx.config.snippet_cap { + if let Some(first_meta) = + cfg_attr.meta().and_then(|meta| meta.token_tree()).and_then(|tt| tt.l_paren_token()) + { + edit.add_tabstop_after_token(snippet_cap, first_meta) + } + } + }; + + acc.add( + AssistId("wrap_unwrap_cfg_attr", AssistKind::Refactor), + format!("Wrap #[derive({path_text})] in `cfg_attr`",), + range, + handle_source_change, + ); + Some(()) +} +fn wrap_cfg_attr(acc: &mut Assists, ctx: &AssistContext<'_>, attr: ast::Attr) -> Option<()> { + let range = attr.syntax().text_range(); + let path = attr.path()?; + let handle_source_change = |edit: &mut SourceChangeBuilder| { + let mut raw_tokens = vec![ + NodeOrToken::Token(make::token(T![,])), + NodeOrToken::Token(make::tokens::whitespace(" ")), + ]; + path.syntax().descendants_with_tokens().for_each(|it| { + if let NodeOrToken::Token(token) = it { + raw_tokens.push(NodeOrToken::Token(token)); + } + }); + if let Some(meta) = attr.meta() { + if let (Some(eq), Some(expr)) = (meta.eq_token(), meta.expr()) { + raw_tokens.push(NodeOrToken::Token(make::tokens::whitespace(" "))); + raw_tokens.push(NodeOrToken::Token(eq.clone())); + raw_tokens.push(NodeOrToken::Token(make::tokens::whitespace(" "))); + + expr.syntax().descendants_with_tokens().for_each(|it| { + if let NodeOrToken::Token(token) = it { + raw_tokens.push(NodeOrToken::Token(token)); + } + }); + } else if let Some(tt) = meta.token_tree() { + raw_tokens.extend(tt.token_trees_and_tokens()); + } + } + let meta = make::meta_token_tree( + make::ext::ident_path("cfg_attr"), + make::token_tree(T!['('], raw_tokens), + ); + let cfg_attr = if attr.excl_token().is_some() { + make::attr_inner(meta) + } else { + make::attr_outer(meta) + } + .clone_for_update(); + let attr_syntax = edit.make_syntax_mut(attr.syntax().clone()); + ted::replace(attr_syntax, cfg_attr.syntax()); + + if let Some(snippet_cap) = ctx.config.snippet_cap { + if let Some(first_meta) = + cfg_attr.meta().and_then(|meta| meta.token_tree()).and_then(|tt| tt.l_paren_token()) + { + edit.add_tabstop_after_token(snippet_cap, first_meta) + } + } + }; + acc.add( + AssistId("wrap_unwrap_cfg_attr", AssistKind::Refactor), + "Convert to `cfg_attr`", + range, + handle_source_change, + ); + Some(()) +} +fn unwrap_cfg_attr(acc: &mut Assists, attr: ast::Attr) -> Option<()> { + let range = attr.syntax().text_range(); + let meta = attr.meta()?; + let meta_tt = meta.token_tree()?; + let mut inner_attrs = Vec::with_capacity(1); + let mut found_comma = false; + let mut iter = meta_tt.token_trees_and_tokens().skip(1).peekable(); + while let Some(tt) = iter.next() { + if let NodeOrToken::Token(token) = &tt { + if token.kind() == T![')'] { + break; + } + if token.kind() == T![,] { + found_comma = true; + continue; + } + } + if !found_comma { + continue; + } + let Some(attr_name) = tt.into_token().and_then(|token| { + if token.kind() == T![ident] { + Some(make::ext::ident_path(token.text())) + } else { + None + } + }) else { + continue; + }; + let next_tt = iter.next()?; + let meta = match next_tt { + NodeOrToken::Node(tt) => make::meta_token_tree(attr_name, tt), + NodeOrToken::Token(token) if token.kind() == T![,] || token.kind() == T![')'] => { + make::meta_path(attr_name) + } + NodeOrToken::Token(token) => { + let equals = algo::skip_trivia_token(token, syntax::Direction::Next)?; + if equals.kind() != T![=] { + return None; + } + let expr_token = + algo::skip_trivia_token(equals.next_token()?, syntax::Direction::Next) + .and_then(|it| { + if it.kind().is_literal() { + Some(make::expr_literal(it.text())) + } else { + None + } + })?; + make::meta_expr(attr_name, ast::Expr::Literal(expr_token)) + } + }; + if attr.excl_token().is_some() { + inner_attrs.push(make::attr_inner(meta)); + } else { + inner_attrs.push(make::attr_outer(meta)); + } + } + if inner_attrs.is_empty() { + return None; + } + let handle_source_change = |f: &mut SourceChangeBuilder| { + let inner_attrs = inner_attrs.iter().map(|it| it.to_string()).join("\n"); + f.replace(range, inner_attrs); + }; + acc.add( + AssistId("wrap_unwrap_cfg_attr", AssistKind::Refactor), + "Extract Inner Attributes from `cfg_attr`", + range, + handle_source_change, + ); + Some(()) +} +#[cfg(test)] +mod tests { + use crate::tests::check_assist; + + use super::*; + + #[test] + fn test_basic_to_from_cfg_attr() { + check_assist( + wrap_unwrap_cfg_attr, + r#" + #[derive$0(Debug)] + pub struct Test { + test: u32, + } + "#, + r#" + #[cfg_attr($0, derive(Debug))] + pub struct Test { + test: u32, + } + "#, + ); + check_assist( + wrap_unwrap_cfg_attr, + r#" + #[cfg_attr(debug_assertions, $0 derive(Debug))] + pub struct Test { + test: u32, + } + "#, + r#" + #[derive(Debug)] + pub struct Test { + test: u32, + } + "#, + ); + } + #[test] + fn to_from_path_attr() { + check_assist( + wrap_unwrap_cfg_attr, + r#" + pub struct Test { + #[foo$0] + test: u32, + } + "#, + r#" + pub struct Test { + #[cfg_attr($0, foo)] + test: u32, + } + "#, + ); + check_assist( + wrap_unwrap_cfg_attr, + r#" + pub struct Test { + #[cfg_attr(debug_assertions$0, foo)] + test: u32, + } + "#, + r#" + pub struct Test { + #[foo] + test: u32, + } + "#, + ); + } + #[test] + fn to_from_eq_attr() { + check_assist( + wrap_unwrap_cfg_attr, + r#" + pub struct Test { + #[foo = "bar"$0] + test: u32, + } + "#, + r#" + pub struct Test { + #[cfg_attr($0, foo = "bar")] + test: u32, + } + "#, + ); + check_assist( + wrap_unwrap_cfg_attr, + r#" + pub struct Test { + #[cfg_attr(debug_assertions$0, foo = "bar")] + test: u32, + } + "#, + r#" + pub struct Test { + #[foo = "bar"] + test: u32, + } + "#, + ); + } + #[test] + fn inner_attrs() { + check_assist( + wrap_unwrap_cfg_attr, + r#" + #![no_std$0] + "#, + r#" + #![cfg_attr($0, no_std)] + "#, + ); + check_assist( + wrap_unwrap_cfg_attr, + r#" + #![cfg_attr(not(feature = "std")$0, no_std)] + "#, + r#" + #![no_std] + "#, + ); + } + #[test] + fn test_derive_wrap() { + check_assist( + wrap_unwrap_cfg_attr, + r#" + #[derive(Debug$0, Clone, Copy)] + pub struct Test { + test: u32, + } + "#, + r#" + #[derive( Clone, Copy)] + #[cfg_attr($0, derive(Debug))] + pub struct Test { + test: u32, + } + "#, + ); + check_assist( + wrap_unwrap_cfg_attr, + r#" + #[derive(Clone, Debug$0, Copy)] + pub struct Test { + test: u32, + } + "#, + r#" + #[derive(Clone, Copy)] + #[cfg_attr($0, derive(Debug))] + pub struct Test { + test: u32, + } + "#, + ); + } + #[test] + fn test_derive_wrap_with_path() { + check_assist( + wrap_unwrap_cfg_attr, + r#" + #[derive(std::fmt::Debug$0, Clone, Copy)] + pub struct Test { + test: u32, + } + "#, + r#" + #[derive( Clone, Copy)] + #[cfg_attr($0, derive(std::fmt::Debug))] + pub struct Test { + test: u32, + } + "#, + ); + check_assist( + wrap_unwrap_cfg_attr, + r#" + #[derive(Clone, std::fmt::Debug$0, Copy)] + pub struct Test { + test: u32, + } + "#, + r#" + #[derive(Clone, Copy)] + #[cfg_attr($0, derive(std::fmt::Debug))] + pub struct Test { + test: u32, + } + "#, + ); + } + #[test] + fn test_derive_wrap_at_end() { + check_assist( + wrap_unwrap_cfg_attr, + r#" + #[derive(std::fmt::Debug, Clone, Cop$0y)] + pub struct Test { + test: u32, + } + "#, + r#" + #[derive(std::fmt::Debug, Clone)] + #[cfg_attr($0, derive(Copy))] + pub struct Test { + test: u32, + } + "#, + ); + check_assist( + wrap_unwrap_cfg_attr, + r#" + #[derive(Clone, Copy, std::fmt::D$0ebug)] + pub struct Test { + test: u32, + } + "#, + r#" + #[derive(Clone, Copy)] + #[cfg_attr($0, derive(std::fmt::Debug))] + pub struct Test { + test: u32, + } + "#, + ); + } +} 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 8f0b8f861c22..0df5e913a573 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs @@ -116,6 +116,7 @@ mod handlers { mod change_visibility; mod convert_bool_then; mod convert_comment_block; + mod convert_from_to_tryfrom; mod convert_integer_literal; mod convert_into_to_from; mod convert_iter_for_each_to_for; @@ -217,6 +218,7 @@ mod handlers { mod unwrap_result_return_type; mod unwrap_tuple; mod wrap_return_type_in_result; + mod wrap_unwrap_cfg_attr; pub(crate) fn all() -> &'static [Handler] { &[ @@ -237,6 +239,7 @@ mod handlers { convert_bool_then::convert_bool_then_to_if, convert_bool_then::convert_if_to_bool_then, convert_comment_block::convert_comment_block, + convert_from_to_tryfrom::convert_from_to_tryfrom, convert_integer_literal::convert_integer_literal, convert_into_to_from::convert_into_to_from, convert_iter_for_each_to_for::convert_iter_for_each_to_for, @@ -342,6 +345,8 @@ mod handlers { unwrap_tuple::unwrap_tuple, unqualify_method_call::unqualify_method_call, wrap_return_type_in_result::wrap_return_type_in_result, + wrap_unwrap_cfg_attr::wrap_unwrap_cfg_attr, + // These are manually sorted for better priorities. By default, // priority is determined by the size of the target range (smaller // target wins). If the ranges are equal, position in this list is 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 a66e199a75b8..937e78f8d7d2 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 @@ -390,6 +390,36 @@ fn main() { ) } +#[test] +fn doctest_convert_from_to_tryfrom() { + check_doc_test( + "convert_from_to_tryfrom", + r#####" +//- minicore: from +impl $0From for Thing { + fn from(val: usize) -> Self { + Thing { + b: val.to_string(), + a: val + } + } +} +"#####, + r#####" +impl TryFrom for Thing { + type Error = ${0:()}; + + fn try_from(val: usize) -> Result { + Ok(Thing { + b: val.to_string(), + a: val + }) + } +} +"#####, + ) +} + #[test] fn doctest_convert_if_to_bool_then() { check_doc_test( @@ -1820,13 +1850,13 @@ fn print(_: &str) {} fn bar() { { - let word = "안녕하세요"; + let word: &str = "안녕하세요"; if !word.is_empty() { print(word); } }; { - let word = "여러분"; + let word: &str = "여러분"; if !word.is_empty() { print(word); } @@ -3151,3 +3181,22 @@ fn foo() -> Result { Ok(42i32) } "#####, ) } + +#[test] +fn doctest_wrap_unwrap_cfg_attr() { + check_doc_test( + "wrap_unwrap_cfg_attr", + r#####" +#[derive$0(Debug)] +struct S { + field: i32 +} +"#####, + r#####" +#[cfg_attr($0, derive(Debug))] +struct S { + field: i32 +} +"#####, + ) +} 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 8bd5d1793313..bc0c9b79c75e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -71,7 +71,7 @@ pub fn extract_trivial_expression(block_expr: &ast::BlockExpr) -> Option Option { +pub fn test_related_attribute_syn(fn_def: &ast::Fn) -> Option { fn_def.attrs().find_map(|attr| { let path = attr.path()?; let text = path.syntax().text().to_string(); @@ -83,6 +83,19 @@ pub fn test_related_attribute(fn_def: &ast::Fn) -> Option { }) } +pub fn has_test_related_attribute(attrs: &hir::AttrsWithOwner) -> bool { + attrs.iter().any(|attr| { + let path = attr.path(); + (|| { + Some( + path.segments().first()?.as_text()?.starts_with("test") + || path.segments().last()?.as_text()?.ends_with("test"), + ) + })() + .unwrap_or_default() + }) +} + #[derive(Clone, Copy, PartialEq)] pub enum IgnoreAssocItems { DocHiddenAttrPresent, 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 361ad821f4a4..c6e243b31a79 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 @@ -9,6 +9,7 @@ use ide_db::{ ty_filter::TryEnum, SnippetCap, }; +use stdx::never; use syntax::{ ast::{self, make, AstNode, AstToken}, SyntaxKind::{BLOCK_EXPR, EXPR_STMT, FOR_EXPR, IF_EXPR, LOOP_EXPR, STMT_LIST, WHILE_EXPR}, @@ -319,7 +320,9 @@ fn build_postfix_snippet_builder<'ctx>( ) -> Option Builder + 'ctx> { let receiver_range = ctx.sema.original_range_opt(receiver.syntax())?.range; if ctx.source_range().end() < receiver_range.start() { - // This shouldn't happen, yet it does. I assume this might be due to an incorrect token mapping. + // This shouldn't happen, yet it does. I assume this might be due to an incorrect token + // mapping. + never!(); return None; } let delete_range = TextRange::new(receiver_range.start(), ctx.source_range().end()); 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 e4678089462a..2361d14aae7f 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 @@ -226,7 +226,7 @@ pub(crate) fn complete_ascribed_type( if !path_ctx.is_trivial_path() { return None; } - let x = match ascription { + let ty = match ascription { TypeAscriptionTarget::Let(pat) | TypeAscriptionTarget::FnParam(pat) => { ctx.sema.type_of_pat(pat.as_ref()?) } @@ -235,7 +235,9 @@ pub(crate) fn complete_ascribed_type( } }? .adjusted(); - let ty_string = x.display_source_code(ctx.db, ctx.module.into(), true).ok()?; - acc.add(render_type_inference(ty_string, ctx)); + if !ty.is_unknown() { + let ty_string = ty.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/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs index 995e3f482532..8b435f419c71 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, SmolStr, + AstNode, Edition, SmolStr, SyntaxKind::{self, *}, SyntaxToken, TextRange, TextSize, T, }; @@ -667,7 +667,8 @@ impl<'a> CompletionContext<'a> { let file_with_fake_ident = { let parse = db.parse(file_id); let edit = Indel::insert(offset, COMPLETION_MARKER.to_owned()); - parse.reparse(&edit).tree() + // FIXME: Edition + parse.reparse(&edit, Edition::CURRENT).tree() }; // always pick the token to the immediate left of the cursor, as that is what we are actually diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs b/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs index e667e2e01683..7d710f1e1308 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs @@ -200,7 +200,7 @@ fn validate_snippet( ) -> Option<(Box<[GreenNode]>, String, Option>)> { let mut imports = Vec::with_capacity(requires.len()); for path in requires.iter() { - let use_path = ast::SourceFile::parse(&format!("use {path};")) + let use_path = ast::SourceFile::parse(&format!("use {path};"), syntax::Edition::CURRENT) .syntax_node() .descendants() .find_map(ast::Path::cast)?; 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 3718dff56e88..64a32dee3d73 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 @@ -19,7 +19,7 @@ struct Foo<'lt, T, const C: usize> where $0 {} en Enum Enum ma makro!(…) macro_rules! makro md module - st Foo<…> Foo<'static, {unknown}, _> + st Foo<…> Foo<'{error}, {unknown}, _> st Record Record st Tuple Tuple st Unit Unit @@ -92,7 +92,7 @@ struct Foo<'lt, T, const C: usize> where for<'a> $0 {} en Enum Enum ma makro!(…) macro_rules! makro md module - st Foo<…> Foo<'static, {unknown}, _> + st Foo<…> Foo<'{error}, {unknown}, _> st Record Record st Tuple Tuple st Unit Unit 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 97709728656d..66f1bff7c1c1 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 @@ -20,8 +20,8 @@ struct Foo<'lt, T, const C: usize> { en Enum Enum ma makro!(…) macro_rules! makro md module - sp Self Foo<'static, {unknown}, _> - st Foo<…> Foo<'static, {unknown}, _> + sp Self Foo<'{error}, {unknown}, _> + st Foo<…> Foo<'{error}, {unknown}, _> st Record Record st Tuple Tuple st Unit Unit @@ -45,8 +45,8 @@ struct Foo<'lt, T, const C: usize>(f$0); en Enum Enum ma makro!(…) macro_rules! makro md module - sp Self Foo<'static, {unknown}, _> - st Foo<…> Foo<'static, {unknown}, _> + sp Self Foo<'{error}, {unknown}, _> + st Foo<…> Foo<'{error}, {unknown}, _> st Record Record st Tuple Tuple st Unit Unit diff --git a/src/tools/rust-analyzer/crates/ide-db/Cargo.toml b/src/tools/rust-analyzer/crates/ide-db/Cargo.toml index 071e1b471793..9a6826a5c426 100644 --- a/src/tools/rust-analyzer/crates/ide-db/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide-db/Cargo.toml @@ -26,6 +26,7 @@ indexmap.workspace = true memchr = "2.6.4" triomphe.workspace = true nohash-hasher.workspace = true +bitflags.workspace = true # local deps base-db.workspace = true 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 ec05f6d13d11..ce9a5f0dd29f 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 @@ -91,82 +91,101 @@ impl RootDatabase { crate::symbol_index::LocalRootsQuery crate::symbol_index::LibraryRootsQuery // HirDatabase - 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::InternCoroutineQuery - hir::db::AssociatedTyDataQuery - hir::db::TraitDatumQuery hir::db::AdtDatumQuery - hir::db::ImplDatumQuery + hir::db::AdtVarianceQuery + hir::db::AssociatedTyDataQuery + hir::db::AssociatedTyValueQuery + hir::db::BorrowckQuery + hir::db::CallableItemSignatureQuery + hir::db::ConstEvalDiscriminantQuery + hir::db::ConstEvalQuery + hir::db::ConstEvalStaticQuery + hir::db::ConstParamTyQuery + hir::db::FieldTypesQuery hir::db::FnDefDatumQuery hir::db::FnDefVarianceQuery - hir::db::AdtVarianceQuery - hir::db::AssociatedTyValueQuery + hir::db::GenericDefaultsQuery + hir::db::GenericPredicatesForParamQuery + hir::db::GenericPredicatesQuery + hir::db::ImplDatumQuery + hir::db::ImplSelfTyQuery + hir::db::ImplTraitQuery + hir::db::IncoherentInherentImplCratesQuery + hir::db::InherentImplsInBlockQuery + hir::db::InherentImplsInCrateQuery + hir::db::InternCallableDefQuery + hir::db::InternClosureQuery + hir::db::InternCoroutineQuery + hir::db::InternImplTraitIdQuery + hir::db::InternLifetimeParamIdQuery + hir::db::InternTypeOrConstParamIdQuery + hir::db::LayoutOfAdtQuery + hir::db::MirBodyQuery hir::db::ProgramClausesForChalkEnvQuery + hir::db::ReturnTypeImplTraitsQuery + hir::db::TargetDataLayoutQuery + hir::db::TraitDatumQuery + hir::db::TraitEnvironmentQuery + hir::db::TraitImplsInBlockQuery + hir::db::TraitImplsInCrateQuery + hir::db::TraitImplsInDepsQuery + hir::db::TyQuery + hir::db::ValueTyQuery // DefDatabase - hir::db::FileItemTreeQuery + hir::db::AttrsQuery hir::db::BlockDefMapQuery - hir::db::StructDataWithDiagnosticsQuery - hir::db::UnionDataWithDiagnosticsQuery + hir::db::BlockItemTreeQuery + hir::db::BodyQuery + hir::db::BodyWithSourceMapQuery + hir::db::ConstDataQuery + hir::db::ConstVisibilityQuery + hir::db::CrateDefMapQuery + hir::db::CrateLangItemsQuery + hir::db::CrateNotableTraitsQuery + hir::db::CrateSupportsNoStdQuery hir::db::EnumDataQuery hir::db::EnumVariantDataWithDiagnosticsQuery - hir::db::ImplDataWithDiagnosticsQuery - hir::db::TraitDataWithDiagnosticsQuery - hir::db::TraitAliasDataQuery - hir::db::TypeAliasDataQuery + hir::db::ExprScopesQuery + hir::db::ExternCrateDeclDataQuery + hir::db::FieldVisibilitiesQuery + hir::db::FieldsAttrsQuery + hir::db::FieldsAttrsSourceMapQuery + hir::db::FileItemTreeQuery hir::db::FunctionDataQuery - hir::db::ConstDataQuery - hir::db::StaticDataQuery + hir::db::FunctionVisibilityQuery + hir::db::GenericParamsQuery + hir::db::ImplDataWithDiagnosticsQuery + hir::db::ImportMapQuery + hir::db::InternAnonymousConstQuery + hir::db::InternBlockQuery + hir::db::InternConstQuery + hir::db::InternEnumQuery + hir::db::InternExternBlockQuery + hir::db::InternExternCrateQuery + hir::db::InternFunctionQuery + hir::db::InternImplQuery + hir::db::InternInTypeConstQuery + hir::db::InternMacro2Query + hir::db::InternMacroRulesQuery + hir::db::InternProcMacroQuery + hir::db::InternStaticQuery + hir::db::InternStructQuery + hir::db::InternTraitAliasQuery + hir::db::InternTraitQuery + hir::db::InternTypeAliasQuery + hir::db::InternUnionQuery + hir::db::InternUseQuery + hir::db::LangItemQuery hir::db::Macro2DataQuery hir::db::MacroRulesDataQuery hir::db::ProcMacroDataQuery - hir::db::BodyWithSourceMapQuery - hir::db::BodyQuery - hir::db::ExprScopesQuery - hir::db::GenericParamsQuery - hir::db::FieldsAttrsQuery - 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 - hir::db::ExternCrateDeclDataQuery - hir::db::InternAnonymousConstQuery - hir::db::InternExternCrateQuery - hir::db::InternInTypeConstQuery - hir::db::InternUseQuery + hir::db::StaticDataQuery + hir::db::StructDataWithDiagnosticsQuery + hir::db::TraitAliasDataQuery + hir::db::TraitDataWithDiagnosticsQuery + hir::db::TypeAliasDataQuery + hir::db::UnionDataWithDiagnosticsQuery // InternDatabase hir::db::InternFunctionQuery @@ -192,9 +211,10 @@ impl RootDatabase { hir::db::InternMacroCallQuery hir::db::InternSyntaxContextQuery hir::db::MacroArgQuery + hir::db::ParseMacroExpansionErrorQuery hir::db::ParseMacroExpansionQuery - hir::db::RealSpanMapQuery hir::db::ProcMacrosQuery + hir::db::RealSpanMapQuery // LineIndexDatabase crate::LineIndexQuery diff --git a/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs b/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs index 72ca354365e7..58e77b95c329 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs @@ -91,8 +91,10 @@ pub fn docs_with_rangemap( db: &dyn DefDatabase, attrs: &AttrsWithOwner, ) -> Option<(Documentation, DocsRangeMap)> { - let docs = - attrs.by_key("doc").attrs().filter_map(|attr| attr.string_value().map(|s| (s, attr.id))); + let docs = attrs + .by_key("doc") + .attrs() + .filter_map(|attr| attr.string_value_unescape().map(|s| (s, attr.id))); let indent = doc_indent(attrs); let mut buf = String::new(); let mut mapping = Vec::new(); @@ -132,7 +134,7 @@ pub fn docs_with_rangemap( } pub fn docs_from_attrs(attrs: &hir::Attrs) -> Option { - let docs = attrs.by_key("doc").attrs().filter_map(|attr| attr.string_value()); + let docs = attrs.by_key("doc").attrs().filter_map(|attr| attr.string_value_unescape()); let indent = doc_indent(attrs); let mut buf = String::new(); for doc in docs { @@ -270,10 +272,9 @@ fn doc_indent(attrs: &hir::Attrs) -> usize { attrs .by_key("doc") .attrs() - .filter_map(|attr| attr.string_value()) + .filter_map(|attr| attr.string_value()) // no need to use unescape version here .flat_map(|s| s.lines()) - .filter(|line| !line.chars().all(|c| c.is_whitespace())) - .map(|line| line.chars().take_while(|c| c.is_whitespace()).count()) + .filter_map(|line| line.chars().position(|c| !c.is_whitespace())) .min() .unwrap_or(0) } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs index bd5c464c5574..e97f1b86143d 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs @@ -176,7 +176,7 @@ pub fn insert_use(scope: &ImportScope, path: ast::Path, cfg: &InsertUseConfig) { pub fn insert_use_as_alias(scope: &ImportScope, path: ast::Path, cfg: &InsertUseConfig) { let text: &str = "use foo as _"; - let parse = syntax::SourceFile::parse(text); + let parse = syntax::SourceFile::parse(text, span::Edition::CURRENT); let node = parse .tree() .syntax() diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use/tests.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use/tests.rs index 10c285a13fbc..9d1f1cc09c63 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use/tests.rs @@ -1243,7 +1243,7 @@ fn check_with_config( .and_then(|it| ImportScope::find_insert_use_container(&it, sema)) .or_else(|| ImportScope::from(syntax)) .unwrap(); - let path = ast::SourceFile::parse(&format!("use {path};")) + let path = ast::SourceFile::parse(&format!("use {path};"), span::Edition::CURRENT) .tree() .syntax() .descendants() @@ -1292,14 +1292,14 @@ fn check_one(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { } fn check_merge_only_fail(ra_fixture0: &str, ra_fixture1: &str, mb: MergeBehavior) { - let use0 = ast::SourceFile::parse(ra_fixture0) + let use0 = ast::SourceFile::parse(ra_fixture0, span::Edition::CURRENT) .tree() .syntax() .descendants() .find_map(ast::Use::cast) .unwrap(); - let use1 = ast::SourceFile::parse(ra_fixture1) + let use1 = ast::SourceFile::parse(ra_fixture1, span::Edition::CURRENT) .tree() .syntax() .descendants() @@ -1311,7 +1311,7 @@ fn check_merge_only_fail(ra_fixture0: &str, ra_fixture1: &str, mb: MergeBehavior } fn check_guess(ra_fixture: &str, expected: ImportGranularityGuess) { - let syntax = ast::SourceFile::parse(ra_fixture).tree().syntax().clone(); + let syntax = ast::SourceFile::parse(ra_fixture, span::Edition::CURRENT).tree().syntax().clone(); let file = ImportScope::from(syntax).unwrap(); assert_eq!(super::guess_granularity_from_scope(&file), expected); } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/label.rs b/src/tools/rust-analyzer/crates/ide-db/src/label.rs index 4b6d54b5eab1..919c1273e5ab 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/label.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/label.rs @@ -1,6 +1,8 @@ //! See [`Label`] use std::fmt; +use stdx::always; + /// A type to specify UI label, like an entry in the list of assists. Enforces /// proper casing: /// @@ -30,7 +32,7 @@ impl From Option> { +fn fixes( + db: &RootDatabase, + var_name: Name, + name_range: TextRange, + diagnostic_range: FileRange, + is_in_marco: bool, +) -> Option> { if is_in_marco { return None; } + Some(vec![Assist { id: AssistId("unscore_unused_variable_name", AssistKind::QuickFix), - label: Label::new(format!("Rename unused {} to _{}", var_name, var_name)), + label: Label::new(format!( + "Rename unused {} to _{}", + var_name.display(db), + var_name.display(db) + )), group: None, target: diagnostic_range.range, source_change: Some(SourceChange::from_text_edit( diagnostic_range.file_id, - TextEdit::replace(diagnostic_range.range, format!("_{}", var_name)), + TextEdit::replace(name_range, format!("_{}", var_name.display(db))), )), trigger_signature_help: false, }]) @@ -210,6 +236,23 @@ macro_rules! my_macro { fn main() { my_macro!(); } +"#, + ); + } + #[test] + fn unused_variable_in_array_destructure() { + check_fix( + r#" +fn main() { + let arr = [1, 2, 3, 4, 5]; + let [_x, y$0 @ ..] = arr; +} +"#, + r#" +fn main() { + let arr = [1, 2, 3, 4, 5]; + let [_x, _y @ ..] = arr; +} "#, ); } 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 270cf844c65b..c3ced36a696b 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -320,13 +320,11 @@ pub fn diagnostics( let module = sema.file_to_module_def(file_id); let ctx = DiagnosticsContext { config, sema, resolve }; - if module.is_none() { - handlers::unlinked_file::unlinked_file(&ctx, &mut res, file_id); - } let mut diags = Vec::new(); - if let Some(m) = module { - m.diagnostics(db, &mut diags, config.style_lints); + match module { + Some(m) => m.diagnostics(db, &mut diags, config.style_lints), + None => handlers::unlinked_file::unlinked_file(&ctx, &mut res, file_id), } for diag in diags { @@ -409,6 +407,11 @@ pub fn diagnostics( res.push(d) } + res.retain(|d| { + !(ctx.config.disabled.contains(d.code.as_str()) + || ctx.config.disable_experimental && d.experimental) + }); + let mut diagnostics_of_range = res .iter_mut() .filter_map(|it| { @@ -421,9 +424,14 @@ pub fn diagnostics( }) .collect::>(); + if diagnostics_of_range.is_empty() { + return res; + } + let mut rustc_stack: FxHashMap> = FxHashMap::default(); let mut clippy_stack: FxHashMap> = FxHashMap::default(); + // FIXME: This becomes quite expensive for big files handle_lint_attributes( &ctx.sema, parse.syntax(), @@ -432,11 +440,7 @@ pub fn diagnostics( &mut diagnostics_of_range, ); - res.retain(|d| { - d.severity != Severity::Allow - && !ctx.config.disabled.contains(d.code.as_str()) - && !(ctx.config.disable_experimental && d.experimental) - }); + res.retain(|d| d.severity != Severity::Allow); res } @@ -476,6 +480,7 @@ fn handle_lint_attributes( clippy_stack: &mut FxHashMap>, diagnostics_of_range: &mut FxHashMap, &mut Diagnostic>, ) { + let _g = tracing::span!(tracing::Level::INFO, "handle_lint_attributes").entered(); let file_id = sema.hir_file_for(root); let preorder = root.preorder(); for ev in preorder { @@ -486,24 +491,24 @@ fn handle_lint_attributes( stack.push(severity); }); } - if let Some(x) = + if let Some(it) = diagnostics_of_range.get_mut(&InFile { file_id, value: node.clone() }) { const EMPTY_LINTS: &[&str] = &[]; - let (names, stack) = match x.code { + let (names, stack) = match it.code { DiagnosticCode::RustcLint(name) => ( - RUSTC_LINT_GROUPS_DICT.get(name).map_or(EMPTY_LINTS, |x| &**x), + RUSTC_LINT_GROUPS_DICT.get(name).map_or(EMPTY_LINTS, |it| &**it), &mut *rustc_stack, ), DiagnosticCode::Clippy(name) => ( - CLIPPY_LINT_GROUPS_DICT.get(name).map_or(EMPTY_LINTS, |x| &**x), + CLIPPY_LINT_GROUPS_DICT.get(name).map_or(EMPTY_LINTS, |it| &**it), &mut *clippy_stack, ), _ => continue, }; for &name in names { - if let Some(s) = stack.get(name).and_then(|x| x.last()) { - x.severity = *s; + if let Some(s) = stack.get(name).and_then(|it| it.last()) { + it.severity = *s; } } } @@ -571,8 +576,8 @@ fn parse_lint_attribute( if let Some(lint) = lint.as_single_name_ref() { job(rustc_stack.entry(lint.to_string()).or_default(), severity); } - if let Some(tool) = lint.qualifier().and_then(|x| x.as_single_name_ref()) { - if let Some(name_ref) = &lint.segment().and_then(|x| x.name_ref()) { + if let Some(tool) = lint.qualifier().and_then(|it| it.as_single_name_ref()) { + if let Some(name_ref) = &lint.segment().and_then(|it| it.name_ref()) { if tool.to_string() == "clippy" { job(clippy_stack.entry(name_ref.to_string()).or_default(), severity); } diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/fragments.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/fragments.rs index 4d6809efbe19..ca937a03f82d 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/fragments.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/fragments.rs @@ -27,7 +27,7 @@ pub(crate) fn expr(s: &str) -> Result { pub(crate) fn stmt(s: &str) -> Result { let template = "const _: () = { {}; };"; let input = template.replace("{}", s); - let parse = syntax::SourceFile::parse(&input); + let parse = syntax::SourceFile::parse(&input, syntax::Edition::CURRENT); if !parse.errors().is_empty() { return Err(()); } @@ -48,7 +48,7 @@ pub(crate) fn stmt(s: &str) -> Result { fn fragment(template: &str, s: &str) -> Result { let s = s.trim(); let input = template.replace("{}", s); - let parse = syntax::SourceFile::parse(&input); + let parse = syntax::SourceFile::parse(&input, syntax::Edition::CURRENT); if !parse.errors().is_empty() { return Err(()); } diff --git a/src/tools/rust-analyzer/crates/ide/src/annotations/fn_references.rs b/src/tools/rust-analyzer/crates/ide/src/annotations/fn_references.rs index a090b60413eb..2e7e230e5aca 100644 --- a/src/tools/rust-analyzer/crates/ide/src/annotations/fn_references.rs +++ b/src/tools/rust-analyzer/crates/ide/src/annotations/fn_references.rs @@ -2,7 +2,7 @@ //! We have to skip tests, so cannot reuse file_structure module. use hir::Semantics; -use ide_assists::utils::test_related_attribute; +use ide_assists::utils::test_related_attribute_syn; use ide_db::RootDatabase; use syntax::{ast, ast::HasName, AstNode, SyntaxNode, TextRange}; @@ -19,7 +19,7 @@ pub(super) fn find_all_methods( fn method_range(item: SyntaxNode) -> Option<(TextRange, Option)> { ast::Fn::cast(item).and_then(|fn_def| { - if test_related_attribute(&fn_def).is_some() { + if test_related_attribute_syn(&fn_def).is_some() { None } else { Some(( 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 4b0961cbbeba..e2d629a02fca 100644 --- a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs +++ b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs @@ -177,7 +177,9 @@ fn _format( use ide_db::base_db::{FileLoader, SourceDatabase}; // hack until we get hygiene working (same character amount to preserve formatting as much as possible) const DOLLAR_CRATE_REPLACE: &str = "__r_a_"; - let expansion = expansion.replace("$crate", DOLLAR_CRATE_REPLACE); + const BUILTIN_REPLACE: &str = "builtin__POUND"; + let expansion = + expansion.replace("$crate", DOLLAR_CRATE_REPLACE).replace("builtin #", BUILTIN_REPLACE); let (prefix, suffix) = match kind { SyntaxKind::MACRO_PAT => ("fn __(", ": u32);"), SyntaxKind::MACRO_EXPR | SyntaxKind::MACRO_STMTS => ("fn __() {", "}"), @@ -206,7 +208,9 @@ fn _format( let captured_stdout = String::from_utf8(output.stdout).ok()?; if output.status.success() && !captured_stdout.trim().is_empty() { - let output = captured_stdout.replace(DOLLAR_CRATE_REPLACE, "$crate"); + let output = captured_stdout + .replace(DOLLAR_CRATE_REPLACE, "$crate") + .replace(BUILTIN_REPLACE, "builtin #"); let output = output.trim().strip_prefix(prefix)?; let output = match kind { SyntaxKind::MACRO_PAT => { 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 813691540f92..568906a098e3 100644 --- a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs +++ b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs @@ -220,7 +220,7 @@ mod tests { use super::*; fn check(ra_fixture: &str, expect: Expect) { - let file = SourceFile::parse(ra_fixture).ok().unwrap(); + let file = SourceFile::parse(ra_fixture, span::Edition::CURRENT).ok().unwrap(); let structure = file_structure(&file); expect.assert_debug_eq(&structure) } diff --git a/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs b/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs index 2bc072112317..c1b7693a6505 100755 --- a/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs +++ b/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs @@ -289,7 +289,7 @@ mod tests { fn check(ra_fixture: &str) { let (ranges, text) = extract_tags(ra_fixture, "fold"); - let parse = SourceFile::parse(&text); + let parse = SourceFile::parse(&text, span::Edition::CURRENT); let mut folds = folding_ranges(&parse.tree()); folds.sort_by_key(|fold| (fold.range.start(), fold.range.end())); 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 e20e0b67f4b3..6f32ce76b22c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs +++ b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs @@ -26,7 +26,7 @@ pub struct HighlightedRange { // FIXME: This needs to be more precise. Reference category makes sense only // for references, but we also have defs. And things like exit points are // neither. - pub category: Option, + pub category: ReferenceCategory, } #[derive(Default, Clone)] @@ -113,7 +113,11 @@ fn highlight_closure_captures( range, category, }); - let category = local.is_mut(sema.db).then_some(ReferenceCategory::Write); + let category = if local.is_mut(sema.db) { + ReferenceCategory::WRITE + } else { + ReferenceCategory::empty() + }; local .sources(sema.db) .into_iter() @@ -137,7 +141,9 @@ fn highlight_references( { match resolution.map(Definition::from) { Some(def) => iter::once(def).collect(), - None => return Some(vec![HighlightedRange { range, category: None }]), + None => { + return Some(vec![HighlightedRange { range, category: ReferenceCategory::empty() }]) + } } } else { find_defs(sema, token.clone()) @@ -211,7 +217,11 @@ fn highlight_references( // highlight the defs themselves match def { Definition::Local(local) => { - let category = local.is_mut(sema.db).then_some(ReferenceCategory::Write); + let category = if local.is_mut(sema.db) { + ReferenceCategory::WRITE + } else { + ReferenceCategory::empty() + }; local .sources(sema.db) .into_iter() @@ -238,8 +248,11 @@ fn highlight_references( continue; } let hl_range = nav.focus_range.map(|range| { - let category = matches!(def, Definition::Local(l) if l.is_mut(sema.db)) - .then_some(ReferenceCategory::Write); + let category = if matches!(def, Definition::Local(l) if l.is_mut(sema.db)) { + ReferenceCategory::WRITE + } else { + ReferenceCategory::empty() + }; HighlightedRange { range, category } }); if let Some(hl_range) = hl_range { @@ -272,24 +285,30 @@ fn highlight_exit_points( def_ranges .into_iter() .flatten() - .map(|range| HighlightedRange { category: None, range }), + .map(|range| HighlightedRange { category: ReferenceCategory::empty(), range }), ); let body = body?; walk_expr(&body, &mut |expr| match expr { ast::Expr::ReturnExpr(expr) => { if let Some(token) = expr.return_token() { - highlights.push(HighlightedRange { category: None, range: token.text_range() }); + highlights.push(HighlightedRange { + category: ReferenceCategory::empty(), + range: token.text_range(), + }); } } ast::Expr::TryExpr(try_) => { if let Some(token) = try_.question_mark_token() { - highlights.push(HighlightedRange { category: None, range: token.text_range() }); + highlights.push(HighlightedRange { + category: ReferenceCategory::empty(), + range: token.text_range(), + }); } } ast::Expr::MethodCallExpr(_) | ast::Expr::CallExpr(_) | ast::Expr::MacroExpr(_) => { if sema.type_of_expr(&expr).map_or(false, |ty| ty.original.is_never()) { highlights.push(HighlightedRange { - category: None, + category: ReferenceCategory::empty(), range: expr.syntax().text_range(), }); } @@ -309,7 +328,7 @@ fn highlight_exit_points( .map_or_else(|| tail.syntax().text_range(), |tok| tok.text_range()), _ => tail.syntax().text_range(), }; - highlights.push(HighlightedRange { category: None, range }) + highlights.push(HighlightedRange { category: ReferenceCategory::empty(), range }) }); } Some(highlights) @@ -354,7 +373,9 @@ fn highlight_break_points(token: SyntaxToken) -> Option> { token.map(|tok| tok.text_range()), label.as_ref().map(|it| it.syntax().text_range()), ); - highlights.extend(range.map(|range| HighlightedRange { category: None, range })); + highlights.extend( + range.map(|range| HighlightedRange { category: ReferenceCategory::empty(), range }), + ); for_each_break_and_continue_expr(label, body, &mut |expr| { let range: Option = match (cursor_token_kind, expr) { (T![for] | T![while] | T![loop] | T![break], ast::Expr::BreakExpr(break_)) => { @@ -372,7 +393,9 @@ fn highlight_break_points(token: SyntaxToken) -> Option> { ), _ => None, }; - highlights.extend(range.map(|range| HighlightedRange { category: None, range })); + highlights.extend( + range.map(|range| HighlightedRange { category: ReferenceCategory::empty(), range }), + ); }); Some(highlights) } @@ -430,14 +453,18 @@ fn highlight_yield_points(token: SyntaxToken) -> Option> { async_token: Option, body: Option, ) -> Option> { - let mut highlights = - vec![HighlightedRange { category: None, range: async_token?.text_range() }]; + let mut highlights = vec![HighlightedRange { + category: ReferenceCategory::empty(), + range: async_token?.text_range(), + }]; if let Some(body) = body { walk_expr(&body, &mut |expr| { if let ast::Expr::AwaitExpr(expr) = expr { if let Some(token) = expr.await_token() { - highlights - .push(HighlightedRange { category: None, range: token.text_range() }); + highlights.push(HighlightedRange { + category: ReferenceCategory::empty(), + range: token.text_range(), + }); } } }); @@ -481,6 +508,8 @@ fn find_defs(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) -> FxHashSe #[cfg(test)] mod tests { + use itertools::Itertools; + use crate::fixture; use super::*; @@ -504,28 +533,18 @@ mod tests { let hls = analysis.highlight_related(config, pos).unwrap().unwrap_or_default(); - let mut expected = annotations - .into_iter() - .map(|(r, access)| (r.range, (!access.is_empty()).then_some(access))) - .collect::>(); + let mut expected = + annotations.into_iter().map(|(r, access)| (r.range, access)).collect::>(); - let mut actual = hls + let mut actual: Vec<(TextRange, String)> = hls .into_iter() .map(|hl| { ( hl.range, - hl.category.map(|it| { - match it { - ReferenceCategory::Read => "read", - ReferenceCategory::Write => "write", - ReferenceCategory::Import => "import", - ReferenceCategory::Test => "test", - } - .to_owned() - }), + hl.category.iter_names().map(|(name, _flag)| name.to_lowercase()).join(","), ) }) - .collect::>(); + .collect(); actual.sort_by_key(|(range, _)| range.start()); expected.sort_by_key(|(range, _)| range.start()); diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs index 822751c0e4ce..95de3c88c8ac 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs @@ -14,7 +14,7 @@ use ide_db::{ helpers::pick_best_token, FxIndexSet, RootDatabase, }; -use itertools::Itertools; +use itertools::{multizip, Itertools}; use syntax::{ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxNode, T}; use crate::{ @@ -149,7 +149,7 @@ fn hover_simple( if let Some(doc_comment) = token_as_doc_comment(&original_token) { cov_mark::hit!(no_highlight_on_comment_hover); return doc_comment.get_definition_with_descend_at(sema, offset, |def, node, range| { - let res = hover_for_definition(sema, file_id, def, &node, config); + let res = hover_for_definition(sema, file_id, def, &node, None, config); Some(RangeInfo::new(range, res)) }); } @@ -162,6 +162,7 @@ fn hover_simple( file_id, Definition::from(resolution?), &original_token.parent()?, + None, config, ); return Some(RangeInfo::new(range, res)); @@ -196,6 +197,29 @@ fn hover_simple( descended() .filter_map(|token| { let node = token.parent()?; + + // special case macro calls, we wanna render the invoked arm index + if let Some(name) = ast::NameRef::cast(node.clone()) { + if let Some(path_seg) = + name.syntax().parent().and_then(ast::PathSegment::cast) + { + if let Some(macro_call) = path_seg + .parent_path() + .syntax() + .parent() + .and_then(ast::MacroCall::cast) + { + if let Some(macro_) = sema.resolve_macro_call(¯o_call) { + return Some(vec![( + Definition::Macro(macro_), + sema.resolve_macro_call_arm(¯o_call), + node, + )]); + } + } + } + } + match IdentClass::classify_node(sema, &node)? { // It's better for us to fall back to the keyword hover here, // rendering poll is very confusing @@ -204,20 +228,19 @@ fn hover_simple( IdentClass::NameRefClass(NameRefClass::ExternCrateShorthand { decl, .. - }) => Some(vec![(Definition::ExternCrateDecl(decl), node)]), + }) => Some(vec![(Definition::ExternCrateDecl(decl), None, node)]), class => Some( - class - .definitions() - .into_iter() - .zip(iter::repeat(node)) + multizip((class.definitions(), iter::repeat(None), iter::repeat(node))) .collect::>(), ), } }) .flatten() - .unique_by(|&(def, _)| def) - .map(|(def, node)| hover_for_definition(sema, file_id, def, &node, config)) + .unique_by(|&(def, _, _)| def) + .map(|(def, macro_arm, node)| { + hover_for_definition(sema, file_id, def, &node, macro_arm, config) + }) .reduce(|mut acc: HoverResult, HoverResult { markup, actions }| { acc.actions.extend(actions); acc.markup = Markup::from(format!("{}\n---\n{markup}", acc.markup)); @@ -374,6 +397,7 @@ pub(crate) fn hover_for_definition( file_id: FileId, def: Definition, scope_node: &SyntaxNode, + macro_arm: Option, config: &HoverConfig, ) -> HoverResult { let famous_defs = match &def { @@ -398,7 +422,8 @@ pub(crate) fn hover_for_definition( }; let notable_traits = def_ty.map(|ty| notable_traits(db, &ty)).unwrap_or_default(); - let markup = render::definition(sema.db, def, famous_defs.as_ref(), ¬able_traits, config); + let markup = + render::definition(sema.db, def, famous_defs.as_ref(), ¬able_traits, macro_arm, config); HoverResult { markup: render::process_markup(sema.db, def, &markup, config), actions: [ 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 abedbff831a5..3f0fc851344e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -101,7 +101,7 @@ pub(super) fn try_expr( if let Some((inner, body)) = error_type_args { inner_ty = inner; body_ty = body; - s = "Try Error".to_owned(); + "Try Error".clone_into(&mut s); } } } @@ -403,6 +403,7 @@ pub(super) fn definition( def: Definition, famous_defs: Option<&FamousDefs<'_, '_>>, notable_traits: &[(Trait, Vec<(Option, Name)>)], + macro_arm: Option, config: &HoverConfig, ) -> Markup { let mod_path = definition_mod_path(db, &def); @@ -413,6 +414,13 @@ pub(super) fn definition( Definition::Adt(Adt::Struct(struct_)) => { struct_.display_limited(db, config.max_struct_field_count).to_string() } + Definition::Macro(it) => { + let mut label = it.display(db).to_string(); + if let Some(macro_arm) = macro_arm { + format_to!(label, " // matched arm #{}", macro_arm); + } + label + } _ => def.label(db), }; let docs = def.docs(db, famous_defs); @@ -637,7 +645,7 @@ fn closure_ty( }) .join("\n"); if captures_rendered.trim().is_empty() { - captures_rendered = "This closure captures nothing".to_owned(); + "This closure captures nothing".clone_into(&mut captures_rendered); } let mut targets: Vec = Vec::new(); let mut push_new_def = |item: hir::ModuleDef| { 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 08925fcdff58..6bbc8b380d6c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -1560,21 +1560,21 @@ fn y() { fn test_hover_macro_invocation() { check( r#" -macro_rules! foo { () => {} } +macro_rules! foo { (a) => {}; () => {} } fn f() { fo$0o!(); } "#, expect![[r#" - *foo* + *foo* - ```rust - test - ``` + ```rust + test + ``` - ```rust - macro_rules! foo - ``` - "#]], + ```rust + macro_rules! foo // matched arm #1 + ``` + "#]], ) } @@ -1590,22 +1590,22 @@ macro foo() {} fn f() { fo$0o!(); } "#, expect![[r#" - *foo* + *foo* - ```rust - test - ``` + ```rust + test + ``` - ```rust - macro foo - ``` + ```rust + macro foo // matched arm #0 + ``` - --- + --- - foo bar + foo bar - foo bar baz - "#]], + foo bar baz + "#]], ) } @@ -2322,6 +2322,49 @@ fn test_hover_layout_of_variant() { ); } +#[test] +fn test_hover_layout_of_variant_generic() { + check( + r#"enum Option { + Some(T), + None$0 +}"#, + expect![[r#" + *None* + + ```rust + test::Option + ``` + + ```rust + None + ``` + "#]], + ); +} + +#[test] +fn test_hover_layout_generic_unused() { + check( + r#" +//- minicore: phantom_data +struct S$0(core::marker::PhantomData); +"#, + expect![[r#" + *S* + + ```rust + test + ``` + + ```rust + // size = 0, align = 1 + struct S(PhantomData) + ``` + "#]], + ); +} + #[test] fn test_hover_layout_of_enum() { check( @@ -3258,12 +3301,12 @@ fn foo(ar$0g: &impl Foo) {} fn test_hover_dyn_return_has_goto_type_action() { check_actions( r#" -trait Foo {} +trait Foo {} struct S; -impl Foo for S {} +impl Foo for S {} struct B{} -fn foo() -> B {} +fn foo() -> B> {} fn main() { let s$0t = foo(); } "#, @@ -3277,8 +3320,8 @@ fn main() { let s$0t = foo(); } file_id: FileId( 0, ), - full_range: 42..55, - focus_range: 49..50, + full_range: 48..61, + focus_range: 55..56, name: "B", kind: Struct, description: "struct B", @@ -3290,11 +3333,24 @@ fn main() { let s$0t = foo(); } file_id: FileId( 0, ), - full_range: 0..12, + full_range: 0..15, focus_range: 6..9, name: "Foo", kind: Trait, - description: "trait Foo", + description: "trait Foo", + }, + }, + HoverGotoTypeData { + mod_path: "test::S", + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 16..25, + focus_range: 23..24, + name: "S", + kind: Struct, + description: "struct S", }, }, ], @@ -3673,6 +3729,7 @@ struct S$0T(T); ``` ```rust + // size = 0, align = 1 struct ST(T) ``` "#]], @@ -3694,6 +3751,7 @@ struct S$0T(T); ``` ```rust + // size = 0, align = 1 struct ST(T) ``` "#]], @@ -3716,6 +3774,7 @@ struct S$0T(T); ``` ```rust + // size = 0, align = 1 struct ST(T) ``` "#]], @@ -4040,7 +4099,6 @@ impl Foo {} ``` "#]], ); - // lifetimes bounds arent being tracked yet check( r#" //- minicore: sized @@ -4051,7 +4109,7 @@ impl Foo {} *T* ```rust - T + T: 'static ``` "#]], ); @@ -4215,6 +4273,10 @@ fn foo() {} ``` "#]], ); + } + + #[test] + fn mixed2() { check( r#" //- minicore: sized @@ -7873,8 +7935,44 @@ struct Pedro$0<'a> { ``` ```rust + // size = 16 (0x10), align = 8, niches = 1 struct Pedro<'a> ``` "#]], ) } + +#[test] +fn hover_impl_trait_arg_self() { + check( + r#" +trait T {} +fn main(a$0: impl T) {} +"#, + expect![[r#" + *a* + + ```rust + a: impl T + ?Sized + ``` + "#]], + ); +} + +#[test] +fn hover_struct_default_arg_self() { + check( + r#" +struct T {} +fn main(a$0: T) {} +"#, + expect![[r#" + *a* + + ```rust + // size = 0, align = 1 + a: T + ``` + "#]], + ); +} 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 dda38ce77e0c..15eecd1b54a0 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -1,6 +1,5 @@ use std::{ fmt::{self, Write}, - hash::{BuildHasher, BuildHasherDefault}, mem::take, }; @@ -9,7 +8,7 @@ use hir::{ known, ClosureStyle, HasVisibility, HirDisplay, HirDisplayError, HirWrite, ModuleDef, ModuleDefId, Semantics, }; -use ide_db::{base_db::FileRange, famous_defs::FamousDefs, FxHasher, RootDatabase}; +use ide_db::{base_db::FileRange, famous_defs::FamousDefs, RootDatabase}; use itertools::Itertools; use smallvec::{smallvec, SmallVec}; use stdx::never; @@ -495,6 +494,7 @@ pub(crate) fn inlay_hints_resolve( position: TextSize, hash: u64, config: &InlayHintsConfig, + hasher: impl Fn(&InlayHint) -> u64, ) -> Option { let _p = tracing::span!(tracing::Level::INFO, "inlay_hints").entered(); let sema = Semantics::new(db); @@ -506,20 +506,16 @@ pub(crate) fn inlay_hints_resolve( let mut acc = Vec::new(); let hints = |node| hints(&mut acc, &famous_defs, config, file_id, node); - match file.token_at_offset(position).left_biased() { - Some(token) => { - if let Some(parent_block) = token.parent_ancestors().find_map(ast::BlockExpr::cast) { - parent_block.syntax().descendants().for_each(hints) - } else if let Some(parent_item) = token.parent_ancestors().find_map(ast::Item::cast) { - parent_item.syntax().descendants().for_each(hints) - } else { - return None; - } - } - None => return None, + let token = file.token_at_offset(position).left_biased()?; + if let Some(parent_block) = token.parent_ancestors().find_map(ast::BlockExpr::cast) { + parent_block.syntax().descendants().for_each(hints) + } else if let Some(parent_item) = token.parent_ancestors().find_map(ast::Item::cast) { + parent_item.syntax().descendants().for_each(hints) + } else { + return None; } - acc.into_iter().find(|hint| BuildHasherDefault::::default().hash_one(hint) == hash) + acc.into_iter().find(|hint| hasher(hint) == hash) } fn hints( diff --git a/src/tools/rust-analyzer/crates/ide/src/join_lines.rs b/src/tools/rust-analyzer/crates/ide/src/join_lines.rs index 815a4ba7fd70..9d8ba90b2ff0 100644 --- a/src/tools/rust-analyzer/crates/ide/src/join_lines.rs +++ b/src/tools/rust-analyzer/crates/ide/src/join_lines.rs @@ -316,7 +316,7 @@ mod tests { }; let (before_cursor_pos, before) = extract_offset(ra_fixture_before); - let file = SourceFile::parse(&before).ok().unwrap(); + let file = SourceFile::parse(&before, span::Edition::CURRENT).ok().unwrap(); let range = TextRange::empty(before_cursor_pos); let result = join_lines(&config, &file, range); @@ -342,7 +342,7 @@ mod tests { }; let (sel, before) = extract_range(ra_fixture_before); - let parse = SourceFile::parse(&before); + let parse = SourceFile::parse(&before, span::Edition::CURRENT); let result = join_lines(&config, &parse.tree(), sel); let actual = { let mut actual = before; diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index ad48d803895f..431aa30e56f2 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -58,13 +58,15 @@ mod view_item_tree; mod view_memory_layout; mod view_mir; +use std::panic::UnwindSafe; + use cfg::CfgOptions; use fetch_crates::CrateInfo; use hir::ChangeWithProcMacros; use ide_db::{ base_db::{ salsa::{self, ParallelDatabase}, - CrateOrigin, Env, FileLoader, FileSet, SourceDatabase, VfsPath, + CrateOrigin, Env, FileLoader, FileSet, SourceDatabase, SourceDatabaseExt, VfsPath, }, prime_caches, symbol_index, FxHashMap, FxIndexSet, LineIndexDatabase, }; @@ -252,7 +254,7 @@ impl Analysis { Edition::CURRENT, None, None, - cfg_options.clone(), + Arc::new(cfg_options), None, Env::default(), false, @@ -271,6 +273,10 @@ impl Analysis { self.with_db(|db| status::status(db, file_id)) } + pub fn source_root(&self, file_id: FileId) -> Cancellable { + self.with_db(|db| db.file_source_root(file_id)) + } + pub fn parallel_prime_caches(&self, num_worker_threads: u8, cb: F) -> Cancellable<()> where F: Fn(ParallelPrimeCachesProgress) + Sync + std::panic::UnwindSafe, @@ -280,7 +286,7 @@ impl Analysis { /// Gets the text of the source file. pub fn file_text(&self, file_id: FileId) -> Cancellable> { - self.with_db(|db| db.file_text(file_id)) + self.with_db(|db| SourceDatabaseExt::file_text(db, file_id)) } /// Gets the syntax tree of the file. @@ -290,7 +296,6 @@ impl Analysis { /// Returns true if this file belongs to an immutable library. pub fn is_library_file(&self, file_id: FileId) -> Cancellable { - use ide_db::base_db::SourceDatabaseExt; self.with_db(|db| db.source_root(db.file_source_root(file_id)).is_library) } @@ -428,8 +433,11 @@ impl Analysis { file_id: FileId, position: TextSize, hash: u64, + hasher: impl Fn(&InlayHint) -> u64 + Send + UnwindSafe, ) -> Cancellable> { - self.with_db(|db| inlay_hints::inlay_hints_resolve(db, file_id, position, hash, config)) + self.with_db(|db| { + inlay_hints::inlay_hints_resolve(db, file_id, position, hash, config, hasher) + }) } /// Returns the set of folding ranges. diff --git a/src/tools/rust-analyzer/crates/ide/src/matching_brace.rs b/src/tools/rust-analyzer/crates/ide/src/matching_brace.rs index 6e8a6d020cc7..573561528366 100644 --- a/src/tools/rust-analyzer/crates/ide/src/matching_brace.rs +++ b/src/tools/rust-analyzer/crates/ide/src/matching_brace.rs @@ -50,7 +50,7 @@ mod tests { fn test_matching_brace() { fn do_check(before: &str, after: &str) { let (pos, before) = extract_offset(before); - let parse = SourceFile::parse(&before); + let parse = SourceFile::parse(&before, span::Edition::CURRENT); let new_pos = match matching_brace(&parse.tree(), pos) { None => pos, Some(pos) => pos, diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs index fef2aba3c616..01af864cdf57 100644 --- a/src/tools/rust-analyzer/crates/ide/src/references.rs +++ b/src/tools/rust-analyzer/crates/ide/src/references.rs @@ -30,7 +30,7 @@ use crate::{FilePosition, NavigationTarget, TryToNav}; #[derive(Debug, Clone)] pub struct ReferenceSearchResult { pub declaration: Option, - pub references: IntMap)>>, + pub references: IntMap>, } #[derive(Debug, Clone)] @@ -66,7 +66,7 @@ pub(crate) fn find_all_refs( retain_adt_literal_usages(&mut usages, def, sema); } - let mut references = usages + let mut references: IntMap> = usages .into_iter() .map(|(file_id, refs)| { ( @@ -77,7 +77,7 @@ pub(crate) fn find_all_refs( .collect(), ) }) - .collect::>>(); + .collect(); let declaration = match def { Definition::Module(module) => { Some(NavigationTarget::from_module_to_decl(sema.db, module)) @@ -93,7 +93,7 @@ pub(crate) fn find_all_refs( references .entry(extra_ref.file_id) .or_default() - .push((extra_ref.focus_or_full_range(), None)); + .push((extra_ref.focus_or_full_range(), ReferenceCategory::empty())); } Declaration { is_mut: matches!(def, Definition::Local(l) if l.is_mut(sema.db)), @@ -300,7 +300,7 @@ fn is_lit_name_ref(name_ref: &ast::NameRef) -> bool { #[cfg(test)] mod tests { use expect_test::{expect, Expect}; - use ide_db::{base_db::FileId, search::ReferenceCategory}; + use ide_db::base_db::FileId; use stdx::format_to; use crate::{fixture, SearchScope}; @@ -324,7 +324,7 @@ fn test() { test_func Function FileId(0) 0..17 3..12 FileId(0) 35..44 - FileId(0) 75..84 Test + FileId(0) 75..84 test "#]], ); @@ -345,7 +345,28 @@ fn test() { test_func Function FileId(0) 0..17 3..12 FileId(0) 35..44 - FileId(0) 96..105 Test + FileId(0) 96..105 test + "#]], + ); + } + + #[test] + fn test_access() { + check( + r#" +struct S { f$0: u32 } + +#[test] +fn test() { + let mut x = S { f: 92 }; + x.f = 92; +} +"#, + expect![[r#" + f Field FileId(0) 11..17 11..12 + + FileId(0) 61..62 read test + FileId(0) 76..77 write test "#]], ); } @@ -600,12 +621,12 @@ fn main() { i = 5; }"#, expect![[r#" - i Local FileId(0) 20..25 24..25 Write + i Local FileId(0) 20..25 24..25 write - FileId(0) 50..51 Write - FileId(0) 54..55 Read - FileId(0) 76..77 Write - FileId(0) 94..95 Write + FileId(0) 50..51 write + FileId(0) 54..55 read + FileId(0) 76..77 write + FileId(0) 94..95 write "#]], ); } @@ -626,8 +647,8 @@ fn bar() { expect![[r#" spam Local FileId(0) 19..23 19..23 - FileId(0) 34..38 Read - FileId(0) 41..45 Read + FileId(0) 34..38 read + FileId(0) 41..45 read "#]], ); } @@ -641,7 +662,7 @@ fn foo(i : u32) -> u32 { i$0 } expect![[r#" i ValueParam FileId(0) 7..8 7..8 - FileId(0) 25..26 Read + FileId(0) 25..26 read "#]], ); } @@ -655,7 +676,7 @@ fn foo(i$0 : u32) -> u32 { i } expect![[r#" i ValueParam FileId(0) 7..8 7..8 - FileId(0) 25..26 Read + FileId(0) 25..26 read "#]], ); } @@ -676,7 +697,7 @@ fn main(s: Foo) { expect![[r#" spam Field FileId(0) 17..30 21..25 - FileId(0) 67..71 Read + FileId(0) 67..71 read "#]], ); } @@ -824,7 +845,7 @@ pub struct Foo { expect![[r#" foo Module FileId(0) 0..8 4..7 - FileId(0) 14..17 Import + FileId(0) 14..17 import "#]], ); } @@ -842,7 +863,7 @@ use self$0; expect![[r#" foo Module FileId(0) 0..8 4..7 - FileId(1) 4..8 Import + FileId(1) 4..8 import "#]], ); } @@ -857,7 +878,7 @@ use self$0; expect![[r#" Module FileId(0) 0..10 - FileId(0) 4..8 Import + FileId(0) 4..8 import "#]], ); } @@ -885,7 +906,7 @@ pub(super) struct Foo$0 { expect![[r#" Foo Struct FileId(2) 0..41 18..21 some - FileId(1) 20..23 Import + FileId(1) 20..23 import FileId(1) 47..50 "#]], ); @@ -960,10 +981,10 @@ fn foo() { } "#, expect![[r#" - i Local FileId(0) 19..24 23..24 Write + i Local FileId(0) 19..24 23..24 write - FileId(0) 34..35 Write - FileId(0) 38..39 Read + FileId(0) 34..35 write + FileId(0) 38..39 read "#]], ); } @@ -984,8 +1005,8 @@ fn foo() { expect![[r#" f Field FileId(0) 15..21 15..16 - FileId(0) 55..56 Read - FileId(0) 68..69 Write + FileId(0) 55..56 read + FileId(0) 68..69 write "#]], ); } @@ -1002,7 +1023,7 @@ fn foo() { expect![[r#" i Local FileId(0) 19..20 19..20 - FileId(0) 26..27 Write + FileId(0) 26..27 write "#]], ); } @@ -1048,7 +1069,7 @@ fn g() { f(); } expect![[r#" f Function FileId(0) 22..31 25..26 - FileId(1) 11..12 Import + FileId(1) 11..12 import FileId(1) 24..25 "#]], ); @@ -1071,7 +1092,7 @@ fn f(s: S) { expect![[r#" field Field FileId(0) 15..24 15..20 - FileId(0) 68..73 Read + FileId(0) 68..73 read "#]], ); } @@ -1095,7 +1116,7 @@ fn f(e: En) { expect![[r#" field Field FileId(0) 32..41 32..37 - FileId(0) 102..107 Read + FileId(0) 102..107 read "#]], ); } @@ -1119,7 +1140,7 @@ fn f() -> m::En { expect![[r#" field Field FileId(0) 56..65 56..61 - FileId(0) 125..130 Read + FileId(0) 125..130 read "#]], ); } @@ -1144,8 +1165,8 @@ impl Foo { expect![[r#" self SelfParam FileId(0) 47..51 47..51 - FileId(0) 71..75 Read - FileId(0) 152..156 Read + FileId(0) 71..75 read + FileId(0) 152..156 read "#]], ); } @@ -1165,7 +1186,7 @@ impl Foo { expect![[r#" self SelfParam FileId(0) 47..51 47..51 - FileId(0) 63..67 Read + FileId(0) 63..67 read "#]], ); } @@ -1185,16 +1206,16 @@ impl Foo { if let Some(decl) = refs.declaration { format_to!(actual, "{}", decl.nav.debug_render()); if decl.is_mut { - format_to!(actual, " {:?}", ReferenceCategory::Write) + format_to!(actual, " write",) } actual += "\n\n"; } for (file_id, references) in &refs.references { - for (range, access) in references { + for (range, category) in references { format_to!(actual, "{:?} {:?}", file_id, range); - if let Some(access) = access { - format_to!(actual, " {:?}", access); + for (name, _flag) in category.iter_names() { + format_to!(actual, " {}", name.to_lowercase()); } actual += "\n"; } @@ -1281,7 +1302,7 @@ fn main() { expect![[r#" a Local FileId(0) 59..60 59..60 - FileId(0) 80..81 Read + FileId(0) 80..81 read "#]], ); } @@ -1299,7 +1320,7 @@ fn main() { expect![[r#" a Local FileId(0) 59..60 59..60 - FileId(0) 80..81 Read + FileId(0) 80..81 read "#]], ); } @@ -1479,7 +1500,7 @@ fn test$0() { expect![[r#" test Function FileId(0) 0..33 11..15 - FileId(0) 24..28 Test + FileId(0) 24..28 test "#]], ); } @@ -1538,9 +1559,9 @@ pub use level1::Foo; expect![[r#" Foo Struct FileId(0) 0..15 11..14 - FileId(1) 16..19 Import - FileId(2) 16..19 Import - FileId(3) 16..19 Import + FileId(1) 16..19 import + FileId(2) 16..19 import + FileId(3) 16..19 import "#]], ); } @@ -1568,7 +1589,7 @@ lib::foo!(); expect![[r#" foo Macro FileId(1) 0..61 29..32 - FileId(0) 46..49 Import + FileId(0) 46..49 import FileId(2) 0..3 FileId(3) 5..8 "#]], @@ -1731,7 +1752,7 @@ struct Foo; expect![[r#" derive_identity Derive FileId(2) 1..107 45..60 - FileId(0) 17..31 Import + FileId(0) 17..31 import FileId(0) 56..70 "#]], ); @@ -2055,7 +2076,7 @@ fn method() {} expect![[r#" method Field FileId(0) 60..70 60..66 - FileId(0) 136..142 Read + FileId(0) 136..142 read "#]], ); check( @@ -2101,7 +2122,7 @@ fn method() {} expect![[r#" method Field FileId(0) 60..70 60..66 - FileId(0) 136..142 Read + FileId(0) 136..142 read "#]], ); check( @@ -2160,9 +2181,9 @@ fn test() { expect![[r#" a Local FileId(0) 20..21 20..21 - FileId(0) 56..57 Read - FileId(0) 60..61 Read - FileId(0) 68..69 Read + FileId(0) 56..57 read + FileId(0) 60..61 read + FileId(0) 68..69 read "#]], ); } diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs index 79324bf3877b..b6c6753755c8 100644 --- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs +++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs @@ -1,9 +1,11 @@ use std::fmt; use ast::HasName; -use cfg::CfgExpr; -use hir::{db::HirDatabase, AsAssocItem, HasAttrs, HasSource, HirFileIdExt, Semantics}; -use ide_assists::utils::test_related_attribute; +use cfg::{CfgAtom, CfgExpr}; +use hir::{ + db::HirDatabase, AsAssocItem, AttrsWithOwner, HasAttrs, HasSource, HirFileIdExt, Semantics, +}; +use ide_assists::utils::{has_test_related_attribute, test_related_attribute_syn}; use ide_db::{ base_db::{FilePosition, FileRange}, defs::Definition, @@ -280,7 +282,7 @@ fn find_related_tests_in_module( } fn as_test_runnable(sema: &Semantics<'_, RootDatabase>, fn_def: &ast::Fn) -> Option { - if test_related_attribute(fn_def).is_some() { + if test_related_attribute_syn(fn_def).is_some() { let function = sema.to_def(fn_def)?; runnable_fn(sema, function) } else { @@ -293,7 +295,7 @@ fn parent_test_module(sema: &Semantics<'_, RootDatabase>, fn_def: &ast::Fn) -> O let module = ast::Module::cast(node)?; let module = sema.to_def(&module)?; - if has_test_function_or_multiple_test_submodules(sema, &module) { + if has_test_function_or_multiple_test_submodules(sema, &module, false) { Some(module) } else { None @@ -305,7 +307,8 @@ pub(crate) fn runnable_fn( sema: &Semantics<'_, RootDatabase>, def: hir::Function, ) -> Option { - let kind = if def.is_main(sema.db) { + let under_cfg_test = has_cfg_test(def.module(sema.db).attrs(sema.db)); + let kind = if !under_cfg_test && def.is_main(sema.db) { RunnableKind::Bin } else { let test_id = || { @@ -342,7 +345,8 @@ pub(crate) fn runnable_mod( sema: &Semantics<'_, RootDatabase>, def: hir::Module, ) -> Option { - if !has_test_function_or_multiple_test_submodules(sema, &def) { + if !has_test_function_or_multiple_test_submodules(sema, &def, has_cfg_test(def.attrs(sema.db))) + { return None; } let path = def @@ -384,12 +388,17 @@ pub(crate) fn runnable_impl( Some(Runnable { use_name_in_title: false, nav, kind: RunnableKind::DocTest { test_id }, cfg }) } +fn has_cfg_test(attrs: AttrsWithOwner) -> bool { + attrs.cfgs().any(|cfg| matches!(cfg, CfgExpr::Atom(CfgAtom::Flag(s)) if s == "test")) +} + /// Creates a test mod runnable for outline modules at the top of their definition. fn runnable_mod_outline_definition( sema: &Semantics<'_, RootDatabase>, def: hir::Module, ) -> Option { - if !has_test_function_or_multiple_test_submodules(sema, &def) { + if !has_test_function_or_multiple_test_submodules(sema, &def, has_cfg_test(def.attrs(sema.db))) + { return None; } let path = def @@ -522,20 +531,28 @@ fn has_runnable_doc_test(attrs: &hir::Attrs) -> bool { fn has_test_function_or_multiple_test_submodules( sema: &Semantics<'_, RootDatabase>, module: &hir::Module, + consider_exported_main: bool, ) -> bool { let mut number_of_test_submodules = 0; for item in module.declarations(sema.db) { match item { hir::ModuleDef::Function(f) => { - if let Some(it) = f.source(sema.db) { - if test_related_attribute(&it.value).is_some() { - return true; - } + if has_test_related_attribute(&f.attrs(sema.db)) { + return true; + } + if consider_exported_main && f.exported_main(sema.db) { + // an exported main in a test module can be considered a test wrt to custom test + // runners + return true; } } hir::ModuleDef::Module(submodule) => { - if has_test_function_or_multiple_test_submodules(sema, &submodule) { + if has_test_function_or_multiple_test_submodules( + sema, + &submodule, + consider_exported_main, + ) { number_of_test_submodules += 1; } } @@ -1484,4 +1501,39 @@ mod r#mod { "#]], ) } + + #[test] + fn exported_main_is_test_in_cfg_test_mod() { + check( + r#" +//- /lib.rs crate:foo cfg:test +$0 +mod not_a_test_module_inline { + #[export_name = "main"] + fn exp_main() {} +} +#[cfg(test)] +mod test_mod_inline { + #[export_name = "main"] + fn exp_main() {} +} +mod not_a_test_module; +#[cfg(test)] +mod test_mod; +//- /not_a_test_module.rs +#[export_name = "main"] +fn exp_main() {} +//- /test_mod.rs +#[export_name = "main"] +fn exp_main() {} +"#, + expect![[r#" + [ + "(Bin, NavigationTarget { file_id: FileId(0), full_range: 36..80, focus_range: 67..75, name: \"exp_main\", kind: Function })", + "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 83..168, focus_range: 100..115, name: \"test_mod_inline\", kind: Module, description: \"mod test_mod_inline\" }, Atom(Flag(\"test\")))", + "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 192..218, focus_range: 209..217, name: \"test_mod\", kind: Module, description: \"mod test_mod\" }, Atom(Flag(\"test\")))", + ] + "#]], + ) + } } 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 3fef16df25e3..ca013da70990 100644 --- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs +++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs @@ -188,7 +188,14 @@ impl StaticIndex<'_> { } else { let it = self.tokens.insert(TokenStaticData { documentation: documentation_for_definition(&sema, def, &node), - hover: Some(hover_for_definition(&sema, file_id, def, &node, &hover_config)), + hover: Some(hover_for_definition( + &sema, + file_id, + def, + &node, + None, + &hover_config, + )), definition: def.try_to_nav(self.db).map(UpmappingResult::call_site).map(|it| { FileRange { file_id: it.file_id, range: it.focus_or_full_range() } }), 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 e7346cbb9925..a72f505eb850 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 @@ -444,7 +444,6 @@ pub(super) fn highlight_def( Definition::Variant(_) => Highlight::new(HlTag::Symbol(SymbolKind::Variant)), Definition::Const(konst) => { let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Const)) | HlMod::Const; - if let Some(item) = konst.as_assoc_item(db) { h |= HlMod::Associated; h |= HlMod::Static; 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 5c7a463ccdc9..e329023606a1 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 @@ -77,6 +77,7 @@ pub enum HlMod { Library, /// Used to differentiate individual elements within macro calls. Macro, + /// Used to differentiate individual elements within proc-macro calls. ProcMacro, /// Mutable binding. Mutable, @@ -113,7 +114,7 @@ pub enum HlPunct { Semi, /// ! (only for macro calls) MacroBang, - /// + /// Other punctutations Other, } @@ -127,7 +128,7 @@ pub enum HlOperator { Logical, /// >, <, ==, >=, <=, != Comparison, - /// + /// Other operators Other, } @@ -225,8 +226,8 @@ impl HlMod { HlMod::IntraDocLink, HlMod::Library, HlMod::Macro, - HlMod::ProcMacro, HlMod::Mutable, + HlMod::ProcMacro, HlMod::Public, HlMod::Reference, HlMod::Static, @@ -262,6 +263,7 @@ impl HlMod { } fn mask(self) -> u32 { + debug_assert!(Self::ALL.len() <= 32, "HlMod::mask is not enough to cover all variants"); 1 << (self as u32) } } 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 7ba1194d675f..5234d362c26d 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 @@ -218,7 +218,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd true } } -const USAGE_OF_BOOL:bool = Bool::True.to_primitive(); +const USAGE_OF_BOOL: bool = Bool::True.to_primitive(); trait Baz { type Qux; 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 c2990fd76eac..901e41d27cde 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 @@ -300,7 +300,7 @@ impl Bool { true } } -const USAGE_OF_BOOL:bool = Bool::True.to_primitive(); +const USAGE_OF_BOOL: bool = Bool::True.to_primitive(); trait Baz { type Qux; 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 1065d5899ab7..05cdf430efbb 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_tree.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_tree.rs @@ -88,7 +88,7 @@ fn syntax_tree_for_token(node: &SyntaxToken, text_range: TextRange) -> Option { map: OnceLock>, } +#[allow(clippy::new_without_default)] // this a const fn, so it can't be default impl InternStorage { pub const fn new() -> Self { Self { map: OnceLock::new() } diff --git a/src/tools/rust-analyzer/crates/limit/Cargo.toml b/src/tools/rust-analyzer/crates/limit/Cargo.toml index c89722cc40de..c1a768833b99 100644 --- a/src/tools/rust-analyzer/crates/limit/Cargo.toml +++ b/src/tools/rust-analyzer/crates/limit/Cargo.toml @@ -10,7 +10,6 @@ rust-version.workspace = true [features] tracking = [] -default = ["tracking"] [lints] -workspace = true \ No newline at end of file +workspace = true diff --git a/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml b/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml index 48e84a7b25eb..b6f90ec53b8b 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml +++ b/src/tools/rust-analyzer/crates/load-cargo/Cargo.toml @@ -28,5 +28,8 @@ tt.workspace = true vfs-notify.workspace = true vfs.workspace = true +[features] +in-rust-tree = ["hir-expand/in-rust-tree"] + [lints] workspace = true diff --git a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs index 79d6fe36b561..31b0c8cdec50 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs @@ -335,7 +335,7 @@ fn load_crate_graph( ) -> RootDatabase { let (ProjectWorkspace::Cargo { toolchain, target_layout, .. } | ProjectWorkspace::Json { toolchain, target_layout, .. } - | ProjectWorkspace::DetachedFiles { toolchain, target_layout, .. }) = ws; + | ProjectWorkspace::DetachedFile { toolchain, target_layout, .. }) = ws; let lru_cap = std::env::var("RA_LRU_CAP").ok().and_then(|it| it.parse::().ok()); let mut db = RootDatabase::new(lru_cap); @@ -407,8 +407,7 @@ impl ProcMacroExpander for Expander { call_site: Span, mixed_site: Span, ) -> Result, ProcMacroExpansionError> { - let env = env.iter().map(|(k, v)| (k.to_owned(), v.to_owned())).collect(); - match self.0.expand(subtree, attrs, env, def_site, call_site, mixed_site) { + match self.0.expand(subtree, attrs, env.clone(), def_site, call_site, mixed_site) { Ok(Ok(subtree)) => Ok(subtree), Ok(Err(err)) => Err(ProcMacroExpansionError::Panic(err.0)), Err(err) => Err(ProcMacroExpansionError::System(err.to_string())), diff --git a/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs b/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs index 4d5531ae307f..f4bbaef7af3f 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs @@ -1,7 +1,7 @@ //! This module add real world mbe example for benchmark tests use rustc_hash::FxHashMap; -use span::Span; +use span::{Edition, Span}; use syntax::{ ast::{self, HasName}, AstNode, SmolStr, @@ -46,9 +46,9 @@ fn benchmark_expand_macro_rules() { invocations .into_iter() .map(|(id, tt)| { - let res = rules[&id].expand(&tt, |_| (), true, DUMMY); + let res = rules[&id].expand(&tt, |_| (), true, DUMMY, Edition::CURRENT); assert!(res.err.is_none()); - res.value.token_trees.len() + res.value.0.token_trees.len() }) .sum() }; @@ -66,7 +66,7 @@ fn macro_rules_fixtures() -> FxHashMap { fn macro_rules_fixtures_tt() -> FxHashMap> { let fixture = bench_fixture::numerous_macro_rules(); - let source_file = ast::SourceFile::parse(&fixture).ok().unwrap(); + let source_file = ast::SourceFile::parse(&fixture, span::Edition::CURRENT).ok().unwrap(); source_file .syntax() @@ -120,7 +120,7 @@ fn invocation_fixtures( }, token_trees: token_trees.into_boxed_slice(), }; - if it.expand(&subtree, |_| (), true, DUMMY).err.is_none() { + if it.expand(&subtree, |_| (), true, DUMMY, Edition::CURRENT).err.is_none() { res.push((name.clone(), subtree)); break; } diff --git a/src/tools/rust-analyzer/crates/mbe/src/expander.rs b/src/tools/rust-analyzer/crates/mbe/src/expander.rs index 2f2c0aa6ff59..cfad8bcc0b45 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/expander.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/expander.rs @@ -6,10 +6,10 @@ mod matcher; mod transcriber; use rustc_hash::FxHashMap; -use span::Span; +use span::{Edition, Span}; use syntax::SmolStr; -use crate::{parser::MetaVarKind, ExpandError, ExpandResult}; +use crate::{parser::MetaVarKind, ExpandError, ExpandResult, MatchedArmIndex}; pub(crate) fn expand_rules( rules: &[crate::Rule], @@ -17,10 +17,11 @@ pub(crate) fn expand_rules( marker: impl Fn(&mut Span) + Copy, new_meta_vars: bool, call_site: Span, -) -> ExpandResult> { - let mut match_: Option<(matcher::Match, &crate::Rule)> = None; - for rule in rules { - let new_match = matcher::match_(&rule.lhs, input); + def_site_edition: Edition, +) -> ExpandResult<(tt::Subtree, MatchedArmIndex)> { + let mut match_: Option<(matcher::Match, &crate::Rule, usize)> = None; + for (idx, rule) in rules.iter().enumerate() { + let new_match = matcher::match_(&rule.lhs, input, def_site_edition); if new_match.err.is_none() { // If we find a rule that applies without errors, we're done. @@ -34,31 +35,34 @@ pub(crate) fn expand_rules( call_site, ); if transcribe_err.is_none() { - return ExpandResult::ok(value); + return ExpandResult::ok((value, Some(idx as u32))); } } // Use the rule if we matched more tokens, or bound variables count - if let Some((prev_match, _)) = &match_ { + if let Some((prev_match, _, _)) = &match_ { if (new_match.unmatched_tts, -(new_match.bound_count as i32)) < (prev_match.unmatched_tts, -(prev_match.bound_count as i32)) { - match_ = Some((new_match, rule)); + match_ = Some((new_match, rule, idx)); } } else { - match_ = Some((new_match, rule)); + match_ = Some((new_match, rule, idx)); } } - if let Some((match_, rule)) = match_ { + if let Some((match_, rule, idx)) = match_ { // if we got here, there was no match without errors let ExpandResult { value, err: transcribe_err } = transcriber::transcribe(&rule.rhs, &match_.bindings, marker, new_meta_vars, call_site); - ExpandResult { value, err: match_.err.or(transcribe_err) } + ExpandResult { value: (value, idx.try_into().ok()), err: match_.err.or(transcribe_err) } } else { ExpandResult::new( - tt::Subtree { - delimiter: tt::Delimiter::invisible_spanned(call_site), - token_trees: Box::new([]), - }, + ( + tt::Subtree { + delimiter: tt::Delimiter::invisible_spanned(call_site), + token_trees: Box::default(), + }, + None, + ), 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 3170834d54ff..78d4bfee2a1e 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs @@ -62,7 +62,7 @@ use std::rc::Rc; use smallvec::{smallvec, SmallVec}; -use span::Span; +use span::{Edition, Span}; use syntax::SmolStr; use tt::DelimSpan; @@ -108,8 +108,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, edition: Edition) -> Match { + let mut res = match_loop(pattern, input, edition); res.bound_count = count(res.bindings.bindings()); return res; @@ -363,6 +363,7 @@ fn match_loop_inner<'t>( eof_items: &mut SmallVec<[MatchState<'t>; 1]>, error_items: &mut SmallVec<[MatchState<'t>; 1]>, delim_span: tt::DelimSpan, + edition: Edition, ) { macro_rules! try_push { ($items: expr, $it:expr) => { @@ -473,7 +474,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, delim_span); + let match_res = match_meta_var(kind, &mut fork, delim_span, edition); match match_res.err { None => { // Some meta variables are optional (e.g. vis) @@ -586,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, edition: Edition) -> Match { let span = src.delimiter.delim_span(); let mut src = TtIter::new(src); let mut stack: SmallVec<[TtIter<'_, Span>; 1]> = SmallVec::new(); @@ -627,6 +628,7 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree) -> Match { &mut eof_items, &mut error_items, span, + edition, ); stdx::always!(cur_items.is_empty()); @@ -740,21 +742,14 @@ fn match_meta_var( kind: MetaVarKind, input: &mut TtIter<'_, Span>, delim_span: DelimSpan, + edition: Edition, ) -> ExpandResult> { let fragment = match kind { MetaVarKind::Path => { - return input.expect_fragment(parser::PrefixEntryPoint::Path).map(|it| { + return input.expect_fragment(parser::PrefixEntryPoint::Path, edition).map(|it| { it.map(|it| tt::TokenTree::subtree_or_wrap(it, delim_span)).map(Fragment::Path) }); } - MetaVarKind::Ty => parser::PrefixEntryPoint::Ty, - MetaVarKind::Pat => parser::PrefixEntryPoint::PatTop, - MetaVarKind::PatParam => parser::PrefixEntryPoint::Pat, - MetaVarKind::Stmt => parser::PrefixEntryPoint::Stmt, - MetaVarKind::Block => parser::PrefixEntryPoint::Block, - MetaVarKind::Meta => parser::PrefixEntryPoint::MetaItem, - MetaVarKind::Item => parser::PrefixEntryPoint::Item, - MetaVarKind::Vis => parser::PrefixEntryPoint::Vis, MetaVarKind::Expr => { // `expr` should not match underscores, let expressions, or inline const. The latter // two are for [backwards compatibility][0]. @@ -770,7 +765,7 @@ fn match_meta_var( } _ => {} }; - return input.expect_fragment(parser::PrefixEntryPoint::Expr).map(|tt| { + return input.expect_fragment(parser::PrefixEntryPoint::Expr, edition).map(|tt| { tt.map(|tt| match tt { tt::TokenTree::Leaf(leaf) => tt::Subtree { delimiter: tt::Delimiter::invisible_spanned(*leaf.span()), @@ -818,8 +813,16 @@ fn match_meta_var( }; return tt_result.map(|it| Some(Fragment::Tokens(it))).into(); } + MetaVarKind::Ty => parser::PrefixEntryPoint::Ty, + MetaVarKind::Pat => parser::PrefixEntryPoint::PatTop, + MetaVarKind::PatParam => parser::PrefixEntryPoint::Pat, + MetaVarKind::Stmt => parser::PrefixEntryPoint::Stmt, + MetaVarKind::Block => parser::PrefixEntryPoint::Block, + MetaVarKind::Meta => parser::PrefixEntryPoint::MetaItem, + MetaVarKind::Item => parser::PrefixEntryPoint::Item, + MetaVarKind::Vis => parser::PrefixEntryPoint::Vis, }; - input.expect_fragment(fragment).map(|it| it.map(Fragment::Tokens)) + input.expect_fragment(fragment, edition).map(|it| it.map(Fragment::Tokens)) } fn collect_vars(collector_fun: &mut impl FnMut(SmolStr), pattern: &MetaTemplate) { diff --git a/src/tools/rust-analyzer/crates/mbe/src/lib.rs b/src/tools/rust-analyzer/crates/mbe/src/lib.rs index 3a853512660e..d5de56312a37 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/lib.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/lib.rs @@ -122,6 +122,9 @@ impl fmt::Display for CountError { } } +/// Index of the matched macro arm on successful expansion. +pub type MatchedArmIndex = Option; + /// This struct contains AST for a single `macro_rules` definition. What might /// be very confusing is that AST has almost exactly the same shape as /// `tt::TokenTree`, but there's a crucial difference: in macro rules, `$ident` @@ -250,8 +253,9 @@ impl DeclarativeMacro { marker: impl Fn(&mut Span) + Copy, new_meta_vars: bool, call_site: Span, - ) -> ExpandResult> { - expander::expand_rules(&self.rules, tt, marker, new_meta_vars, call_site) + def_site_edition: Edition, + ) -> ExpandResult<(tt::Subtree, MatchedArmIndex)> { + expander::expand_rules(&self.rules, tt, marker, new_meta_vars, call_site, def_site_edition) } } @@ -329,6 +333,10 @@ impl ValueResult { Self { value: Default::default(), err: Some(err) } } + pub fn zip_val(self, other: U) -> ValueResult<(T, U), E> { + ValueResult { value: (self.value, other), err: self.err } + } + pub fn map(self, f: impl FnOnce(T) -> U) -> ValueResult { ValueResult { value: f(self.value), err: self.err } } 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 c934db6b715a..3230eeb5bd88 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs @@ -3,7 +3,7 @@ use std::fmt; use rustc_hash::{FxHashMap, FxHashSet}; -use span::{SpanAnchor, SpanData, SpanMap}; +use span::{Edition, SpanAnchor, SpanData, SpanMap}; use stdx::{never, non_empty_vec::NonEmptyVec}; use syntax::{ ast::{self, make::tokens::doc_comment}, @@ -119,6 +119,7 @@ where pub fn token_tree_to_syntax_node( tt: &tt::Subtree>, entry_point: parser::TopEntryPoint, + edition: parser::Edition, ) -> (Parse, SpanMap) where SpanData: Copy + fmt::Debug, @@ -131,7 +132,7 @@ where _ => TokenBuffer::from_subtree(tt), }; let parser_input = to_parser_input(&buffer); - let parser_output = entry_point.parse(&parser_input); + let parser_output = entry_point.parse(&parser_input, edition); let mut tree_sink = TtTreeSink::new(buffer.begin()); for event in parser_output.iter() { match event { @@ -182,7 +183,12 @@ where } /// Split token tree with separate expr: $($e:expr)SEP* -pub fn parse_exprs_with_sep(tt: &tt::Subtree, sep: char, span: S) -> Vec> +pub fn parse_exprs_with_sep( + tt: &tt::Subtree, + sep: char, + span: S, + edition: Edition, +) -> Vec> where S: Copy + fmt::Debug, { @@ -194,7 +200,7 @@ where let mut res = Vec::new(); while iter.peek_n(0).is_some() { - let expanded = iter.expect_fragment(parser::PrefixEntryPoint::Expr); + let expanded = iter.expect_fragment(parser::PrefixEntryPoint::Expr, edition); res.push(match expanded.value { None => break, diff --git a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge/tests.rs b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge/tests.rs index a261b1d43193..bbfe378200d6 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge/tests.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge/tests.rs @@ -10,7 +10,7 @@ use tt::{ use crate::{syntax_node_to_token_tree, DummyTestSpanMap, DUMMY}; fn check_punct_spacing(fixture: &str) { - let source_file = ast::SourceFile::parse(fixture).ok().unwrap(); + let source_file = ast::SourceFile::parse(fixture, span::Edition::CURRENT).ok().unwrap(); let subtree = syntax_node_to_token_tree(source_file.syntax(), DummyTestSpanMap, DUMMY); let mut annotations: FxHashMap<_, _> = extract_annotations(fixture) .into_iter() 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 e3d12d87078b..9c7d7af7b14e 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/tt_iter.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/tt_iter.rs @@ -140,10 +140,11 @@ impl<'a, S: Copy + fmt::Debug> TtIter<'a, S> { pub(crate) fn expect_fragment( &mut self, entry_point: parser::PrefixEntryPoint, + edition: parser::Edition, ) -> ExpandResult>> { let buffer = tt::buffer::TokenBuffer::from_tokens(self.inner.as_slice()); let parser_input = to_parser_input(&buffer); - let tree_traversal = entry_point.parse(&parser_input); + let tree_traversal = entry_point.parse(&parser_input, edition); let mut cursor = buffer.begin(); let mut error = false; for step in tree_traversal.iter() { diff --git a/src/tools/rust-analyzer/crates/parser/src/edition.rs b/src/tools/rust-analyzer/crates/parser/src/edition.rs new file mode 100644 index 000000000000..26178544f9b5 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/src/edition.rs @@ -0,0 +1,55 @@ +//! The edition of the Rust language used in a crate. +// Ideally this would be defined in the span crate, but the dependency chain is all over the place +// wrt to span, parser and syntax. +use std::fmt; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum Edition { + Edition2015, + Edition2018, + Edition2021, + Edition2024, +} + +impl Edition { + pub const CURRENT: Edition = Edition::Edition2021; + pub const DEFAULT: Edition = Edition::Edition2015; +} + +#[derive(Debug)] +pub struct ParseEditionError { + invalid_input: String, +} + +impl std::error::Error for ParseEditionError {} +impl fmt::Display for ParseEditionError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "invalid edition: {:?}", self.invalid_input) + } +} + +impl std::str::FromStr for Edition { + type Err = ParseEditionError; + + fn from_str(s: &str) -> Result { + let res = match s { + "2015" => Edition::Edition2015, + "2018" => Edition::Edition2018, + "2021" => Edition::Edition2021, + "2024" => Edition::Edition2024, + _ => return Err(ParseEditionError { invalid_input: s.to_owned() }), + }; + Ok(res) + } +} + +impl fmt::Display for Edition { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match self { + Edition::Edition2015 => "2015", + Edition::Edition2018 => "2018", + Edition::Edition2021 => "2021", + Edition::Edition2024 => "2024", + }) + } +} diff --git a/src/tools/rust-analyzer/crates/parser/src/lib.rs b/src/tools/rust-analyzer/crates/parser/src/lib.rs index 86c771c00085..c7ad025f6b09 100644 --- a/src/tools/rust-analyzer/crates/parser/src/lib.rs +++ b/src/tools/rust-analyzer/crates/parser/src/lib.rs @@ -26,6 +26,7 @@ extern crate ra_ap_rustc_lexer as rustc_lexer; #[cfg(feature = "in-rust-tree")] extern crate rustc_lexer; +mod edition; mod event; mod grammar; mod input; @@ -42,6 +43,7 @@ mod tests; pub(crate) use token_set::TokenSet; pub use crate::{ + edition::Edition, input::Input, lexed_str::LexedStr, output::{Output, Step}, @@ -86,7 +88,7 @@ pub enum TopEntryPoint { } impl TopEntryPoint { - pub fn parse(&self, input: &Input) -> Output { + pub fn parse(&self, input: &Input, edition: Edition) -> Output { let _p = tracing::span!(tracing::Level::INFO, "TopEntryPoint::parse", ?self).entered(); let entry_point: fn(&'_ mut parser::Parser<'_>) = match self { TopEntryPoint::SourceFile => grammar::entry::top::source_file, @@ -98,7 +100,7 @@ impl TopEntryPoint { TopEntryPoint::MetaItem => grammar::entry::top::meta_item, TopEntryPoint::MacroEagerInput => grammar::entry::top::eager_macro_input, }; - let mut p = parser::Parser::new(input); + let mut p = parser::Parser::new(input, edition); entry_point(&mut p); let events = p.finish(); let res = event::process(events); @@ -150,7 +152,7 @@ pub enum PrefixEntryPoint { } impl PrefixEntryPoint { - pub fn parse(&self, input: &Input) -> Output { + pub fn parse(&self, input: &Input, edition: Edition) -> Output { let entry_point: fn(&'_ mut parser::Parser<'_>) = match self { PrefixEntryPoint::Vis => grammar::entry::prefix::vis, PrefixEntryPoint::Block => grammar::entry::prefix::block, @@ -163,7 +165,7 @@ impl PrefixEntryPoint { PrefixEntryPoint::Item => grammar::entry::prefix::item, PrefixEntryPoint::MetaItem => grammar::entry::prefix::meta_item, }; - let mut p = parser::Parser::new(input); + let mut p = parser::Parser::new(input, edition); entry_point(&mut p); let events = p.finish(); event::process(events) @@ -187,9 +189,9 @@ impl Reparser { /// /// Tokens must start with `{`, end with `}` and form a valid brace /// sequence. - pub fn parse(self, tokens: &Input) -> Output { + pub fn parse(self, tokens: &Input, edition: Edition) -> Output { let Reparser(r) = self; - let mut p = parser::Parser::new(tokens); + let mut p = parser::Parser::new(tokens, edition); r(&mut p); let events = p.finish(); event::process(events) diff --git a/src/tools/rust-analyzer/crates/parser/src/parser.rs b/src/tools/rust-analyzer/crates/parser/src/parser.rs index 051461243afc..5b901f911dcc 100644 --- a/src/tools/rust-analyzer/crates/parser/src/parser.rs +++ b/src/tools/rust-analyzer/crates/parser/src/parser.rs @@ -8,6 +8,7 @@ use limit::Limit; use crate::{ event::Event, input::Input, + Edition, SyntaxKind::{self, EOF, ERROR, TOMBSTONE}, TokenSet, T, }; @@ -26,13 +27,14 @@ pub(crate) struct Parser<'t> { pos: usize, events: Vec, steps: Cell, + _edition: Edition, } static PARSER_STEP_LIMIT: Limit = Limit::new(15_000_000); impl<'t> Parser<'t> { - pub(super) fn new(inp: &'t Input) -> Parser<'t> { - Parser { inp, pos: 0, events: Vec::new(), steps: Cell::new(0) } + pub(super) fn new(inp: &'t Input, edition: Edition) -> Parser<'t> { + Parser { inp, pos: 0, events: Vec::new(), steps: Cell::new(0), _edition: edition } } pub(crate) fn finish(self) -> Vec { 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 6ecfdc9f4664..ef83420c5238 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 @@ -61,9 +61,11 @@ pub enum SyntaxKind { SHR, SHLEQ, SHREQ, + ABSTRACT_KW, AS_KW, ASYNC_KW, AWAIT_KW, + BECOME_KW, BOX_KW, BREAK_KW, CONST_KW, @@ -75,6 +77,7 @@ pub enum SyntaxKind { ENUM_KW, EXTERN_KW, FALSE_KW, + FINAL_KW, FN_KW, FOR_KW, IF_KW, @@ -87,10 +90,11 @@ pub enum SyntaxKind { MOD_KW, MOVE_KW, MUT_KW, + OVERRIDE_KW, + PRIV_KW, PUB_KW, REF_KW, RETURN_KW, - BECOME_KW, SELF_KW, SELF_TYPE_KW, STATIC_KW, @@ -100,8 +104,11 @@ pub enum SyntaxKind { TRUE_KW, TRY_KW, TYPE_KW, + TYPEOF_KW, UNSAFE_KW, + UNSIZED_KW, USE_KW, + VIRTUAL_KW, WHERE_KW, WHILE_KW, YIELD_KW, @@ -280,9 +287,11 @@ impl SyntaxKind { pub fn is_keyword(self) -> bool { matches!( self, - AS_KW + ABSTRACT_KW + | AS_KW | ASYNC_KW | AWAIT_KW + | BECOME_KW | BOX_KW | BREAK_KW | CONST_KW @@ -294,6 +303,7 @@ impl SyntaxKind { | ENUM_KW | EXTERN_KW | FALSE_KW + | FINAL_KW | FN_KW | FOR_KW | IF_KW @@ -306,10 +316,11 @@ impl SyntaxKind { | MOD_KW | MOVE_KW | MUT_KW + | OVERRIDE_KW + | PRIV_KW | PUB_KW | REF_KW | RETURN_KW - | BECOME_KW | SELF_KW | SELF_TYPE_KW | STATIC_KW @@ -319,8 +330,11 @@ impl SyntaxKind { | TRUE_KW | TRY_KW | TYPE_KW + | TYPEOF_KW | UNSAFE_KW + | UNSIZED_KW | USE_KW + | VIRTUAL_KW | WHERE_KW | WHILE_KW | YIELD_KW @@ -399,9 +413,11 @@ impl SyntaxKind { } pub fn from_keyword(ident: &str) -> Option { let kw = match ident { + "abstract" => ABSTRACT_KW, "as" => AS_KW, "async" => ASYNC_KW, "await" => AWAIT_KW, + "become" => BECOME_KW, "box" => BOX_KW, "break" => BREAK_KW, "const" => CONST_KW, @@ -413,6 +429,7 @@ impl SyntaxKind { "enum" => ENUM_KW, "extern" => EXTERN_KW, "false" => FALSE_KW, + "final" => FINAL_KW, "fn" => FN_KW, "for" => FOR_KW, "if" => IF_KW, @@ -425,10 +442,11 @@ impl SyntaxKind { "mod" => MOD_KW, "move" => MOVE_KW, "mut" => MUT_KW, + "override" => OVERRIDE_KW, + "priv" => PRIV_KW, "pub" => PUB_KW, "ref" => REF_KW, "return" => RETURN_KW, - "become" => BECOME_KW, "self" => SELF_KW, "Self" => SELF_TYPE_KW, "static" => STATIC_KW, @@ -438,8 +456,11 @@ impl SyntaxKind { "true" => TRUE_KW, "try" => TRY_KW, "type" => TYPE_KW, + "typeof" => TYPEOF_KW, "unsafe" => UNSAFE_KW, + "unsized" => UNSIZED_KW, "use" => USE_KW, + "virtual" => VIRTUAL_KW, "where" => WHERE_KW, "while" => WHILE_KW, "yield" => YIELD_KW, @@ -500,4 +521,4 @@ impl SyntaxKind { } } #[macro_export] -macro_rules ! T { [;] => { $ crate :: SyntaxKind :: SEMICOLON } ; [,] => { $ crate :: SyntaxKind :: COMMA } ; ['('] => { $ crate :: SyntaxKind :: L_PAREN } ; [')'] => { $ crate :: SyntaxKind :: R_PAREN } ; ['{'] => { $ crate :: SyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: SyntaxKind :: R_CURLY } ; ['['] => { $ crate :: SyntaxKind :: L_BRACK } ; [']'] => { $ crate :: SyntaxKind :: R_BRACK } ; [<] => { $ crate :: SyntaxKind :: L_ANGLE } ; [>] => { $ crate :: SyntaxKind :: R_ANGLE } ; [@] => { $ crate :: SyntaxKind :: AT } ; [#] => { $ crate :: SyntaxKind :: POUND } ; [~] => { $ crate :: SyntaxKind :: TILDE } ; [?] => { $ crate :: SyntaxKind :: QUESTION } ; [$] => { $ crate :: SyntaxKind :: DOLLAR } ; [&] => { $ crate :: SyntaxKind :: AMP } ; [|] => { $ crate :: SyntaxKind :: PIPE } ; [+] => { $ crate :: SyntaxKind :: PLUS } ; [*] => { $ crate :: SyntaxKind :: STAR } ; [/] => { $ crate :: SyntaxKind :: SLASH } ; [^] => { $ crate :: SyntaxKind :: CARET } ; [%] => { $ crate :: SyntaxKind :: PERCENT } ; [_] => { $ crate :: SyntaxKind :: UNDERSCORE } ; [.] => { $ crate :: SyntaxKind :: DOT } ; [..] => { $ crate :: SyntaxKind :: DOT2 } ; [...] => { $ crate :: SyntaxKind :: DOT3 } ; [..=] => { $ crate :: SyntaxKind :: DOT2EQ } ; [:] => { $ crate :: SyntaxKind :: COLON } ; [::] => { $ crate :: SyntaxKind :: COLON2 } ; [=] => { $ crate :: SyntaxKind :: EQ } ; [==] => { $ crate :: SyntaxKind :: EQ2 } ; [=>] => { $ crate :: SyntaxKind :: FAT_ARROW } ; [!] => { $ crate :: SyntaxKind :: BANG } ; [!=] => { $ crate :: SyntaxKind :: NEQ } ; [-] => { $ crate :: SyntaxKind :: MINUS } ; [->] => { $ crate :: SyntaxKind :: THIN_ARROW } ; [<=] => { $ crate :: SyntaxKind :: LTEQ } ; [>=] => { $ crate :: SyntaxKind :: GTEQ } ; [+=] => { $ crate :: SyntaxKind :: PLUSEQ } ; [-=] => { $ crate :: SyntaxKind :: MINUSEQ } ; [|=] => { $ crate :: SyntaxKind :: PIPEEQ } ; [&=] => { $ crate :: SyntaxKind :: AMPEQ } ; [^=] => { $ crate :: SyntaxKind :: CARETEQ } ; [/=] => { $ crate :: SyntaxKind :: SLASHEQ } ; [*=] => { $ crate :: SyntaxKind :: STAREQ } ; [%=] => { $ crate :: SyntaxKind :: PERCENTEQ } ; [&&] => { $ crate :: SyntaxKind :: AMP2 } ; [||] => { $ crate :: SyntaxKind :: PIPE2 } ; [<<] => { $ crate :: SyntaxKind :: SHL } ; [>>] => { $ crate :: SyntaxKind :: SHR } ; [<<=] => { $ crate :: SyntaxKind :: SHLEQ } ; [>>=] => { $ crate :: SyntaxKind :: SHREQ } ; [as] => { $ crate :: SyntaxKind :: AS_KW } ; [async] => { $ crate :: SyntaxKind :: ASYNC_KW } ; [await] => { $ crate :: SyntaxKind :: AWAIT_KW } ; [box] => { $ crate :: SyntaxKind :: BOX_KW } ; [break] => { $ crate :: SyntaxKind :: BREAK_KW } ; [const] => { $ crate :: SyntaxKind :: CONST_KW } ; [continue] => { $ crate :: SyntaxKind :: CONTINUE_KW } ; [crate] => { $ crate :: SyntaxKind :: CRATE_KW } ; [do] => { $ crate :: SyntaxKind :: DO_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [else] => { $ crate :: SyntaxKind :: ELSE_KW } ; [enum] => { $ crate :: SyntaxKind :: ENUM_KW } ; [extern] => { $ crate :: SyntaxKind :: EXTERN_KW } ; [false] => { $ crate :: SyntaxKind :: FALSE_KW } ; [fn] => { $ crate :: SyntaxKind :: FN_KW } ; [for] => { $ crate :: SyntaxKind :: FOR_KW } ; [if] => { $ crate :: SyntaxKind :: IF_KW } ; [impl] => { $ crate :: SyntaxKind :: IMPL_KW } ; [in] => { $ crate :: SyntaxKind :: IN_KW } ; [let] => { $ crate :: SyntaxKind :: LET_KW } ; [loop] => { $ crate :: SyntaxKind :: LOOP_KW } ; [macro] => { $ crate :: SyntaxKind :: MACRO_KW } ; [match] => { $ crate :: SyntaxKind :: MATCH_KW } ; [mod] => { $ crate :: SyntaxKind :: MOD_KW } ; [move] => { $ crate :: SyntaxKind :: MOVE_KW } ; [mut] => { $ crate :: SyntaxKind :: MUT_KW } ; [pub] => { $ crate :: SyntaxKind :: PUB_KW } ; [ref] => { $ crate :: SyntaxKind :: REF_KW } ; [return] => { $ crate :: SyntaxKind :: RETURN_KW } ; [become] => { $ crate :: SyntaxKind :: BECOME_KW } ; [self] => { $ crate :: SyntaxKind :: SELF_KW } ; [Self] => { $ crate :: SyntaxKind :: SELF_TYPE_KW } ; [static] => { $ crate :: SyntaxKind :: STATIC_KW } ; [struct] => { $ crate :: SyntaxKind :: STRUCT_KW } ; [super] => { $ crate :: SyntaxKind :: SUPER_KW } ; [trait] => { $ crate :: SyntaxKind :: TRAIT_KW } ; [true] => { $ crate :: SyntaxKind :: TRUE_KW } ; [try] => { $ crate :: SyntaxKind :: TRY_KW } ; [type] => { $ crate :: SyntaxKind :: TYPE_KW } ; [unsafe] => { $ crate :: SyntaxKind :: UNSAFE_KW } ; [use] => { $ crate :: SyntaxKind :: USE_KW } ; [where] => { $ crate :: SyntaxKind :: WHERE_KW } ; [while] => { $ crate :: SyntaxKind :: WHILE_KW } ; [yield] => { $ crate :: SyntaxKind :: YIELD_KW } ; [auto] => { $ crate :: SyntaxKind :: AUTO_KW } ; [builtin] => { $ crate :: SyntaxKind :: BUILTIN_KW } ; [default] => { $ crate :: SyntaxKind :: DEFAULT_KW } ; [existential] => { $ crate :: SyntaxKind :: EXISTENTIAL_KW } ; [union] => { $ crate :: SyntaxKind :: UNION_KW } ; [raw] => { $ crate :: SyntaxKind :: RAW_KW } ; [macro_rules] => { $ crate :: SyntaxKind :: MACRO_RULES_KW } ; [yeet] => { $ crate :: SyntaxKind :: YEET_KW } ; [offset_of] => { $ crate :: SyntaxKind :: OFFSET_OF_KW } ; [asm] => { $ crate :: SyntaxKind :: ASM_KW } ; [format_args] => { $ crate :: SyntaxKind :: FORMAT_ARGS_KW } ; [lifetime_ident] => { $ crate :: SyntaxKind :: LIFETIME_IDENT } ; [ident] => { $ crate :: SyntaxKind :: IDENT } ; [shebang] => { $ crate :: SyntaxKind :: SHEBANG } ; } +macro_rules ! T { [;] => { $ crate :: SyntaxKind :: SEMICOLON } ; [,] => { $ crate :: SyntaxKind :: COMMA } ; ['('] => { $ crate :: SyntaxKind :: L_PAREN } ; [')'] => { $ crate :: SyntaxKind :: R_PAREN } ; ['{'] => { $ crate :: SyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: SyntaxKind :: R_CURLY } ; ['['] => { $ crate :: SyntaxKind :: L_BRACK } ; [']'] => { $ crate :: SyntaxKind :: R_BRACK } ; [<] => { $ crate :: SyntaxKind :: L_ANGLE } ; [>] => { $ crate :: SyntaxKind :: R_ANGLE } ; [@] => { $ crate :: SyntaxKind :: AT } ; [#] => { $ crate :: SyntaxKind :: POUND } ; [~] => { $ crate :: SyntaxKind :: TILDE } ; [?] => { $ crate :: SyntaxKind :: QUESTION } ; [$] => { $ crate :: SyntaxKind :: DOLLAR } ; [&] => { $ crate :: SyntaxKind :: AMP } ; [|] => { $ crate :: SyntaxKind :: PIPE } ; [+] => { $ crate :: SyntaxKind :: PLUS } ; [*] => { $ crate :: SyntaxKind :: STAR } ; [/] => { $ crate :: SyntaxKind :: SLASH } ; [^] => { $ crate :: SyntaxKind :: CARET } ; [%] => { $ crate :: SyntaxKind :: PERCENT } ; [_] => { $ crate :: SyntaxKind :: UNDERSCORE } ; [.] => { $ crate :: SyntaxKind :: DOT } ; [..] => { $ crate :: SyntaxKind :: DOT2 } ; [...] => { $ crate :: SyntaxKind :: DOT3 } ; [..=] => { $ crate :: SyntaxKind :: DOT2EQ } ; [:] => { $ crate :: SyntaxKind :: COLON } ; [::] => { $ crate :: SyntaxKind :: COLON2 } ; [=] => { $ crate :: SyntaxKind :: EQ } ; [==] => { $ crate :: SyntaxKind :: EQ2 } ; [=>] => { $ crate :: SyntaxKind :: FAT_ARROW } ; [!] => { $ crate :: SyntaxKind :: BANG } ; [!=] => { $ crate :: SyntaxKind :: NEQ } ; [-] => { $ crate :: SyntaxKind :: MINUS } ; [->] => { $ crate :: SyntaxKind :: THIN_ARROW } ; [<=] => { $ crate :: SyntaxKind :: LTEQ } ; [>=] => { $ crate :: SyntaxKind :: GTEQ } ; [+=] => { $ crate :: SyntaxKind :: PLUSEQ } ; [-=] => { $ crate :: SyntaxKind :: MINUSEQ } ; [|=] => { $ crate :: SyntaxKind :: PIPEEQ } ; [&=] => { $ crate :: SyntaxKind :: AMPEQ } ; [^=] => { $ crate :: SyntaxKind :: CARETEQ } ; [/=] => { $ crate :: SyntaxKind :: SLASHEQ } ; [*=] => { $ crate :: SyntaxKind :: STAREQ } ; [%=] => { $ crate :: SyntaxKind :: PERCENTEQ } ; [&&] => { $ crate :: SyntaxKind :: AMP2 } ; [||] => { $ crate :: SyntaxKind :: PIPE2 } ; [<<] => { $ crate :: SyntaxKind :: SHL } ; [>>] => { $ crate :: SyntaxKind :: SHR } ; [<<=] => { $ crate :: SyntaxKind :: SHLEQ } ; [>>=] => { $ crate :: SyntaxKind :: SHREQ } ; [abstract] => { $ crate :: SyntaxKind :: ABSTRACT_KW } ; [as] => { $ crate :: SyntaxKind :: AS_KW } ; [async] => { $ crate :: SyntaxKind :: ASYNC_KW } ; [await] => { $ crate :: SyntaxKind :: AWAIT_KW } ; [become] => { $ crate :: SyntaxKind :: BECOME_KW } ; [box] => { $ crate :: SyntaxKind :: BOX_KW } ; [break] => { $ crate :: SyntaxKind :: BREAK_KW } ; [const] => { $ crate :: SyntaxKind :: CONST_KW } ; [continue] => { $ crate :: SyntaxKind :: CONTINUE_KW } ; [crate] => { $ crate :: SyntaxKind :: CRATE_KW } ; [do] => { $ crate :: SyntaxKind :: DO_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [else] => { $ crate :: SyntaxKind :: ELSE_KW } ; [enum] => { $ crate :: SyntaxKind :: ENUM_KW } ; [extern] => { $ crate :: SyntaxKind :: EXTERN_KW } ; [false] => { $ crate :: SyntaxKind :: FALSE_KW } ; [final] => { $ crate :: SyntaxKind :: FINAL_KW } ; [fn] => { $ crate :: SyntaxKind :: FN_KW } ; [for] => { $ crate :: SyntaxKind :: FOR_KW } ; [if] => { $ crate :: SyntaxKind :: IF_KW } ; [impl] => { $ crate :: SyntaxKind :: IMPL_KW } ; [in] => { $ crate :: SyntaxKind :: IN_KW } ; [let] => { $ crate :: SyntaxKind :: LET_KW } ; [loop] => { $ crate :: SyntaxKind :: LOOP_KW } ; [macro] => { $ crate :: SyntaxKind :: MACRO_KW } ; [match] => { $ crate :: SyntaxKind :: MATCH_KW } ; [mod] => { $ crate :: SyntaxKind :: MOD_KW } ; [move] => { $ crate :: SyntaxKind :: MOVE_KW } ; [mut] => { $ crate :: SyntaxKind :: MUT_KW } ; [override] => { $ crate :: SyntaxKind :: OVERRIDE_KW } ; [priv] => { $ crate :: SyntaxKind :: PRIV_KW } ; [pub] => { $ crate :: SyntaxKind :: PUB_KW } ; [ref] => { $ crate :: SyntaxKind :: REF_KW } ; [return] => { $ crate :: SyntaxKind :: RETURN_KW } ; [self] => { $ crate :: SyntaxKind :: SELF_KW } ; [Self] => { $ crate :: SyntaxKind :: SELF_TYPE_KW } ; [static] => { $ crate :: SyntaxKind :: STATIC_KW } ; [struct] => { $ crate :: SyntaxKind :: STRUCT_KW } ; [super] => { $ crate :: SyntaxKind :: SUPER_KW } ; [trait] => { $ crate :: SyntaxKind :: TRAIT_KW } ; [true] => { $ crate :: SyntaxKind :: TRUE_KW } ; [try] => { $ crate :: SyntaxKind :: TRY_KW } ; [type] => { $ crate :: SyntaxKind :: TYPE_KW } ; [typeof] => { $ crate :: SyntaxKind :: TYPEOF_KW } ; [unsafe] => { $ crate :: SyntaxKind :: UNSAFE_KW } ; [unsized] => { $ crate :: SyntaxKind :: UNSIZED_KW } ; [use] => { $ crate :: SyntaxKind :: USE_KW } ; [virtual] => { $ crate :: SyntaxKind :: VIRTUAL_KW } ; [where] => { $ crate :: SyntaxKind :: WHERE_KW } ; [while] => { $ crate :: SyntaxKind :: WHILE_KW } ; [yield] => { $ crate :: SyntaxKind :: YIELD_KW } ; [auto] => { $ crate :: SyntaxKind :: AUTO_KW } ; [builtin] => { $ crate :: SyntaxKind :: BUILTIN_KW } ; [default] => { $ crate :: SyntaxKind :: DEFAULT_KW } ; [existential] => { $ crate :: SyntaxKind :: EXISTENTIAL_KW } ; [union] => { $ crate :: SyntaxKind :: UNION_KW } ; [raw] => { $ crate :: SyntaxKind :: RAW_KW } ; [macro_rules] => { $ crate :: SyntaxKind :: MACRO_RULES_KW } ; [yeet] => { $ crate :: SyntaxKind :: YEET_KW } ; [offset_of] => { $ crate :: SyntaxKind :: OFFSET_OF_KW } ; [asm] => { $ crate :: SyntaxKind :: ASM_KW } ; [format_args] => { $ crate :: SyntaxKind :: FORMAT_ARGS_KW } ; [lifetime_ident] => { $ crate :: SyntaxKind :: LIFETIME_IDENT } ; [ident] => { $ crate :: SyntaxKind :: IDENT } ; [shebang] => { $ crate :: SyntaxKind :: SHEBANG } ; } diff --git a/src/tools/rust-analyzer/crates/parser/src/tests.rs b/src/tools/rust-analyzer/crates/parser/src/tests.rs index c65219b28dce..0e0409652618 100644 --- a/src/tools/rust-analyzer/crates/parser/src/tests.rs +++ b/src/tools/rust-analyzer/crates/parser/src/tests.rs @@ -88,7 +88,7 @@ fn parse_inline_err() { fn parse(entry: TopEntryPoint, text: &str) -> (String, bool) { let lexed = LexedStr::new(text); let input = lexed.to_input(); - let output = entry.parse(&input); + let output = entry.parse(&input, crate::Edition::CURRENT); let mut buf = String::new(); let mut errors = Vec::new(); 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 2f3c7febc040..f92b39edb765 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 @@ -86,7 +86,7 @@ fn check(entry: PrefixEntryPoint, input: &str, prefix: &str) { let input = lexed.to_input(); let mut n_tokens = 0; - for step in entry.parse(&input).iter() { + for step in entry.parse(&input, crate::Edition::CURRENT).iter() { match step { Step::Token { n_input_tokens, .. } => n_tokens += n_input_tokens as usize, Step::FloatSplit { .. } => n_tokens += 1, diff --git a/src/tools/rust-analyzer/crates/parser/src/token_set.rs b/src/tools/rust-analyzer/crates/parser/src/token_set.rs index cd4894c1e8b5..88a89a53a76d 100644 --- a/src/tools/rust-analyzer/crates/parser/src/token_set.rs +++ b/src/tools/rust-analyzer/crates/parser/src/token_set.rs @@ -4,34 +4,48 @@ use crate::SyntaxKind; /// A bit-set of `SyntaxKind`s #[derive(Clone, Copy)] -pub(crate) struct TokenSet(u128); +pub(crate) struct TokenSet([u64; 3]); + +/// `TokenSet`s should only include token `SyntaxKind`s, so the discriminant of any passed/included +/// `SyntaxKind` must *not* be greater than that of the last token `SyntaxKind`. +/// See #17037. +const LAST_TOKEN_KIND_DISCRIMINANT: usize = SyntaxKind::SHEBANG as usize; impl TokenSet { - pub(crate) const EMPTY: TokenSet = TokenSet(0); + pub(crate) const EMPTY: TokenSet = TokenSet([0; 3]); pub(crate) const fn new(kinds: &[SyntaxKind]) -> TokenSet { - let mut res = 0u128; + let mut res = [0; 3]; let mut i = 0; while i < kinds.len() { - res |= mask(kinds[i]); + let discriminant = kinds[i] as usize; + debug_assert!( + discriminant <= LAST_TOKEN_KIND_DISCRIMINANT, + "Expected a token `SyntaxKind`" + ); + let idx = discriminant / 64; + res[idx] |= 1 << (discriminant % 64); i += 1; } TokenSet(res) } pub(crate) const fn union(self, other: TokenSet) -> TokenSet { - TokenSet(self.0 | other.0) + TokenSet([self.0[0] | other.0[0], self.0[1] | other.0[1], self.0[2] | other.0[2]]) } pub(crate) const fn contains(&self, kind: SyntaxKind) -> bool { - self.0 & mask(kind) != 0 + let discriminant = kind as usize; + debug_assert!( + discriminant <= LAST_TOKEN_KIND_DISCRIMINANT, + "Expected a token `SyntaxKind`" + ); + let idx = discriminant / 64; + let mask = 1 << (discriminant % 64); + self.0[idx] & mask != 0 } } -const fn mask(kind: SyntaxKind) -> u128 { - 1u128 << (kind as usize) -} - #[test] fn token_set_works_for_tokens() { use crate::SyntaxKind::*; 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 fd491644648a..0ab16c38c87b 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 @@ -11,6 +11,7 @@ pub mod msg; mod process; mod version; +use base_db::Env; use indexmap::IndexSet; use paths::AbsPathBuf; use rustc_hash::FxHashMap; @@ -37,7 +38,7 @@ pub enum ProcMacroKind { CustomDerive, Attr, // This used to be called FuncLike, so that's what the server expects currently. - #[serde(alias = "bang")] + #[serde(alias = "Bang")] #[serde(rename(serialize = "FuncLike", deserialize = "FuncLike"))] Bang, } @@ -152,16 +153,13 @@ impl ProcMacro { &self, subtree: &tt::Subtree, attr: Option<&tt::Subtree>, - env: Vec<(String, String)>, + env: Env, def_site: Span, call_site: Span, mixed_site: Span, ) -> Result, PanicMessage>, 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 current_dir = env.get("CARGO_MANIFEST_DIR"); let mut span_data_table = IndexSet::default(); let def_site = span_data_table.insert_full(def_site).0; @@ -172,7 +170,7 @@ impl ProcMacro { macro_name: self.name.to_string(), attributes: attr.map(|subtree| FlatTree::new(subtree, version, &mut span_data_table)), lib: self.dylib_path.to_path_buf().into(), - env, + env: env.into(), current_dir, has_global_spans: ExpnGlobals { serialize: version >= HAS_GLOBAL_SPANS, diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/build.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/build.rs new file mode 100644 index 000000000000..07f914fece0e --- /dev/null +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/build.rs @@ -0,0 +1,5 @@ +//! This teaches cargo about our cfg(rust_analyzer) + +fn main() { + println!("cargo:rustc-check-cfg=cfg(rust_analyzer)"); +} diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/build.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/build.rs index a8c732f31541..874d1c6cd389 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/build.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/build.rs @@ -4,6 +4,8 @@ use std::{env, fs::File, io::Write, path::PathBuf, process::Command}; fn main() { + println!("cargo:rustc-check-cfg=cfg(rust_analyzer)"); + let mut path = PathBuf::from(env::var_os("OUT_DIR").unwrap()); path.push("rustc_version.rs"); let mut f = File::create(&path).unwrap(); diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/build.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/build.rs index c76c201d69e8..6a0ae362d88e 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/build.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/build.rs @@ -53,7 +53,7 @@ fn main() { println!("Creating {}", src_dir.display()); std::fs::create_dir_all(src_dir).unwrap(); - for item_els in [&["Cargo.toml"][..], &["src", "lib.rs"]] { + for item_els in [&["Cargo.toml"][..], &["build.rs"][..], &["src", "lib.rs"]] { let mut src = imp_dir.clone(); let mut dst = staging_dir.clone(); for el in item_els { diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/build.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/build.rs new file mode 100644 index 000000000000..07f914fece0e --- /dev/null +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/build.rs @@ -0,0 +1,5 @@ +//! This teaches cargo about our cfg(rust_analyzer) + +fn main() { + println!("cargo:rustc-check-cfg=cfg(rust_analyzer)"); +} 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 d40eb26063de..fbd423c9eac8 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 @@ -23,16 +23,18 @@ use serde::Deserialize; use toolchain::Tool; use crate::{ - cfg_flag::CfgFlag, utf8_stdout, CargoConfig, CargoFeatures, CargoWorkspace, InvocationLocation, + cfg::CfgFlag, utf8_stdout, CargoConfig, CargoFeatures, CargoWorkspace, InvocationLocation, InvocationStrategy, Package, Sysroot, TargetKind, }; +/// Output of the build script and proc-macro building steps for a workspace. #[derive(Debug, Default, Clone, PartialEq, Eq)] pub struct WorkspaceBuildScripts { outputs: ArenaMap, error: Option, } +/// Output of the build script and proc-macro building step for a concrete package. #[derive(Debug, Clone, Default, PartialEq, Eq)] pub(crate) struct BuildScriptOutput { /// List of config flags defined by this package's build script. @@ -86,7 +88,9 @@ impl WorkspaceBuildScripts { // --all-targets includes tests, benches and examples in addition to the // default lib and bins. This is an independent concept from the --target // flag below. - cmd.arg("--all-targets"); + if config.all_targets { + cmd.arg("--all-targets"); + } if let Some(target) = &config.target { cmd.args(["--target", target]); @@ -235,7 +239,7 @@ impl WorkspaceBuildScripts { }, progress, )?; - res.iter_mut().for_each(|it| it.error = errors.clone()); + res.iter_mut().for_each(|it| it.error.clone_from(&errors)); collisions.into_iter().for_each(|(id, workspace, package)| { if let Some(&(p, w)) = by_id.get(id) { res[workspace].outputs[package] = res[w].outputs[p].clone(); 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 51c1b094f7b3..ff7cf144aa82 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 @@ -76,6 +76,8 @@ impl Default for CargoFeatures { #[derive(Default, Clone, Debug, PartialEq, Eq)] pub struct CargoConfig { + /// Whether to pass `--all-targets` to cargo invocations. + pub all_targets: bool, /// List of features to activate. pub features: CargoFeatures, /// rustc target @@ -133,6 +135,20 @@ pub struct PackageData { pub active_features: Vec, /// String representation of package id pub id: String, + /// Authors as given in the `Cargo.toml` + pub authors: Vec, + /// Description as given in the `Cargo.toml` + pub description: Option, + /// Homepage as given in the `Cargo.toml` + pub homepage: Option, + /// License as given in the `Cargo.toml` + pub license: Option, + /// License file as given in the `Cargo.toml` + pub license_file: Option, + /// Readme file as given in the `Cargo.toml` + pub readme: Option, + /// Rust version as given in the `Cargo.toml` + pub rust_version: Option, /// The contents of [package.metadata.rust-analyzer] pub metadata: RustAnalyzerPackageMetaData, } @@ -223,6 +239,10 @@ impl TargetKind { } TargetKind::Other } + + pub fn is_executable(self) -> bool { + matches!(self, TargetKind::Bin | TargetKind::Example) + } } // Deserialize helper for the cargo metadata @@ -285,6 +305,12 @@ impl CargoWorkspace { .collect(), ); } + // The manifest is a rust file, so this means its a script manifest + if cargo_toml.extension().is_some_and(|ext| ext == "rs") { + // Deliberately don't set up RUSTC_BOOTSTRAP or a nightly override here, the user should + // opt into it themselves. + other_options.push("-Zscript".to_owned()); + } meta.other_options(other_options); // FIXME: Fetching metadata is a slow process, as it might require @@ -328,6 +354,13 @@ impl CargoWorkspace { repository, edition, metadata, + authors, + description, + homepage, + license, + license_file, + readme, + rust_version, .. } = meta_pkg; let meta = from_value::(metadata).unwrap_or_default(); @@ -346,16 +379,24 @@ impl CargoWorkspace { let is_local = source.is_none(); let is_member = ws_members.contains(&id); + let manifest = AbsPathBuf::assert(manifest_path); let pkg = packages.alloc(PackageData { id: id.repr.clone(), name, version, - manifest: AbsPathBuf::assert(manifest_path).try_into().unwrap(), + manifest: manifest.clone().try_into().unwrap(), targets: Vec::new(), is_local, is_member, edition, repository, + authors, + description, + homepage, + license, + license_file, + readme, + rust_version, dependencies: Vec::new(), features: features.into_iter().collect(), active_features: Vec::new(), @@ -366,11 +407,22 @@ impl CargoWorkspace { for meta_tgt in meta_targets { let cargo_metadata::Target { name, kind, required_features, src_path, .. } = meta_tgt; + let kind = TargetKind::new(&kind); let tgt = targets.alloc(TargetData { package: pkg, name, - root: AbsPathBuf::assert(src_path), - kind: TargetKind::new(&kind), + root: if kind == TargetKind::Bin + && manifest.extension().is_some_and(|ext| ext == "rs") + { + // cargo strips the script part of a cargo script away and places the + // modified manifest file into a special target dir which is then used as + // the source path. We don't want that, we want the original here so map it + // back + manifest.clone() + } else { + AbsPathBuf::assert(src_path) + }, + kind, required_features, }); pkg_data.targets.push(tgt); diff --git a/src/tools/rust-analyzer/crates/project-model/src/cfg_flag.rs b/src/tools/rust-analyzer/crates/project-model/src/cfg.rs similarity index 70% rename from src/tools/rust-analyzer/crates/project-model/src/cfg_flag.rs rename to src/tools/rust-analyzer/crates/project-model/src/cfg.rs index af682904b194..b409bc1ce7ad 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/cfg_flag.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/cfg.rs @@ -3,9 +3,11 @@ //! rustc main.rs --cfg foo --cfg 'feature="bar"' use std::{fmt, str::FromStr}; -use cfg::CfgOptions; +use cfg::{CfgDiff, CfgOptions}; +use rustc_hash::FxHashMap; +use serde::Serialize; -#[derive(Clone, Eq, PartialEq, Debug)] +#[derive(Clone, Eq, PartialEq, Debug, Serialize)] pub enum CfgFlag { Atom(String), KeyValue { key: String, value: String }, @@ -69,3 +71,27 @@ impl fmt::Display for CfgFlag { } } } + +/// A set of cfg-overrides per crate. +#[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. + pub selective: FxHashMap, +} + +impl CfgOverrides { + pub fn len(&self) -> usize { + self.global.len() + self.selective.values().map(|it| it.len()).sum::() + } + + pub fn apply(&self, cfg_options: &mut CfgOptions, name: &str) { + if !self.global.is_empty() { + cfg_options.apply_diff(self.global.clone()); + }; + if let Some(diff) = self.selective.get(name) { + cfg_options.apply_diff(diff.clone()); + }; + } +} diff --git a/src/tools/rust-analyzer/crates/project-model/src/env.rs b/src/tools/rust-analyzer/crates/project-model/src/env.rs new file mode 100644 index 000000000000..762e01c91772 --- /dev/null +++ b/src/tools/rust-analyzer/crates/project-model/src/env.rs @@ -0,0 +1,85 @@ +//! Cargo-like environment variables injection. +use base_db::Env; +use rustc_hash::FxHashMap; +use toolchain::Tool; + +use crate::{utf8_stdout, ManifestPath, PackageData, Sysroot, TargetKind}; + +/// Recreates the compile-time environment variables that Cargo sets. +/// +/// Should be synced with +/// +/// +/// FIXME: ask Cargo to provide this data instead of re-deriving. +pub(crate) fn inject_cargo_package_env(env: &mut Env, package: &PackageData) { + // FIXME: Missing variables: + // CARGO_BIN_NAME, CARGO_BIN_EXE_ + + let manifest_dir = package.manifest.parent(); + env.set("CARGO_MANIFEST_DIR", manifest_dir.as_str()); + + env.set("CARGO_PKG_VERSION", package.version.to_string()); + env.set("CARGO_PKG_VERSION_MAJOR", package.version.major.to_string()); + env.set("CARGO_PKG_VERSION_MINOR", package.version.minor.to_string()); + env.set("CARGO_PKG_VERSION_PATCH", package.version.patch.to_string()); + env.set("CARGO_PKG_VERSION_PRE", package.version.pre.to_string()); + + env.set("CARGO_PKG_AUTHORS", package.authors.join(":").clone()); + + env.set("CARGO_PKG_NAME", package.name.clone()); + env.set("CARGO_PKG_DESCRIPTION", package.description.as_deref().unwrap_or_default()); + env.set("CARGO_PKG_HOMEPAGE", package.homepage.as_deref().unwrap_or_default()); + env.set("CARGO_PKG_REPOSITORY", package.repository.as_deref().unwrap_or_default()); + env.set("CARGO_PKG_LICENSE", package.license.as_deref().unwrap_or_default()); + env.set( + "CARGO_PKG_LICENSE_FILE", + package.license_file.as_ref().map(ToString::to_string).unwrap_or_default(), + ); + env.set( + "CARGO_PKG_README", + package.readme.as_ref().map(ToString::to_string).unwrap_or_default(), + ); + + env.set( + "CARGO_PKG_RUST_VERSION", + package.rust_version.as_ref().map(ToString::to_string).unwrap_or_default(), + ); +} + +pub(crate) fn inject_cargo_env(env: &mut Env) { + env.set("CARGO", Tool::Cargo.path().to_string()); +} + +pub(crate) fn inject_rustc_tool_env(env: &mut Env, cargo_name: &str, kind: TargetKind) { + _ = kind; + // FIXME + // if kind.is_executable() { + // env.set("CARGO_BIN_NAME", cargo_name); + // } + env.set("CARGO_CRATE_NAME", cargo_name.replace('-', "_")); +} + +pub(crate) fn cargo_config_env( + cargo_toml: &ManifestPath, + extra_env: &FxHashMap, + sysroot: Option<&Sysroot>, +) -> FxHashMap { + let mut cargo_config = Sysroot::tool(sysroot, Tool::Cargo); + cargo_config.envs(extra_env); + cargo_config + .current_dir(cargo_toml.parent()) + .args(["-Z", "unstable-options", "config", "get", "env"]) + .env("RUSTC_BOOTSTRAP", "1"); + // if successful we receive `env.key.value = "value" per entry + tracing::debug!("Discovering cargo config env by {:?}", cargo_config); + utf8_stdout(cargo_config).map(parse_output_cargo_config_env).unwrap_or_default() +} + +fn parse_output_cargo_config_env(stdout: String) -> FxHashMap { + stdout + .lines() + .filter_map(|l| l.strip_prefix("env.")) + .filter_map(|l| l.split_once(".value = ")) + .map(|(key, value)| (key.to_owned(), value.trim_matches('"').to_owned())) + .collect() +} 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 28696aa32775..7f3e35ca5dbc 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/lib.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/lib.rs @@ -19,7 +19,8 @@ mod build_scripts; mod cargo_workspace; -mod cfg_flag; +mod cfg; +mod env; mod manifest_path; mod project_json; mod rustc_cfg; @@ -47,10 +48,11 @@ pub use crate::{ CargoConfig, CargoFeatures, CargoWorkspace, Package, PackageData, PackageDependency, RustLibSource, Target, TargetData, TargetKind, }, + cfg::CfgOverrides, manifest_path::ManifestPath, project_json::{ProjectJson, ProjectJsonData}, sysroot::Sysroot, - workspace::{CfgOverrides, PackageRoot, ProjectWorkspace}, + workspace::{FileLoader, PackageRoot, ProjectWorkspace}, }; #[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] 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 512588cc8f8f..fac6eb8ad3e8 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,14 +49,13 @@ //! user explores them belongs to that extension (it's totally valid to change //! rust-project.json over time via configuration request!) -use base_db::{CrateDisplayName, CrateId, CrateName, Dependency}; -use la_arena::RawIdx; +use base_db::{CrateDisplayName, CrateName}; use paths::{AbsPath, AbsPathBuf, Utf8PathBuf}; use rustc_hash::FxHashMap; -use serde::{de, Deserialize}; +use serde::{de, Deserialize, Serialize}; use span::Edition; -use crate::cfg_flag::CfgFlag; +use crate::cfg::CfgFlag; /// Roots and crates that compose this Rust project. #[derive(Clone, Debug, Eq, PartialEq)] @@ -74,10 +73,10 @@ pub struct ProjectJson { #[derive(Clone, Debug, Eq, PartialEq)] pub struct Crate { pub(crate) display_name: Option, - pub(crate) root_module: AbsPathBuf, + pub root_module: AbsPathBuf, pub(crate) edition: Edition, pub(crate) version: Option, - pub(crate) deps: Vec, + pub(crate) deps: Vec, pub(crate) cfg: Vec, pub(crate) target: Option, pub(crate) env: FxHashMap, @@ -128,16 +127,7 @@ impl ProjectJson { root_module, edition: crate_data.edition.into(), version: crate_data.version.as_ref().map(ToString::to_string), - deps: crate_data - .deps - .into_iter() - .map(|dep_data| { - Dependency::new( - dep_data.name, - CrateId::from_raw(RawIdx::from(dep_data.krate as u32)), - ) - }) - .collect::>(), + deps: crate_data.deps, cfg: crate_data.cfg, target: crate_data.target, env: crate_data.env, @@ -161,11 +151,8 @@ 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::from_raw(RawIdx::from(idx as u32)), krate)) + pub fn crates(&self) -> impl Iterator { + self.crates.iter().enumerate().map(|(idx, krate)| (CrateArrayIdx(idx), krate)) } /// Returns the path to the project's root folder. @@ -174,21 +161,21 @@ impl ProjectJson { } } -#[derive(Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct ProjectJsonData { sysroot: Option, sysroot_src: Option, crates: Vec, } -#[derive(Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone)] struct CrateData { display_name: Option, root_module: Utf8PathBuf, edition: EditionData, #[serde(default)] version: Option, - deps: Vec, + deps: Vec, #[serde(default)] cfg: Vec, target: Option, @@ -203,7 +190,7 @@ struct CrateData { repository: Option, } -#[derive(Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(rename = "edition")] enum EditionData { #[serde(rename = "2015")] @@ -227,16 +214,25 @@ impl From for Edition { } } -#[derive(Deserialize, Debug, Clone)] -struct DepData { +/// Identifies a crate by position in the crates array. +/// +/// This will differ from `CrateId` when multiple `ProjectJson` +/// workspaces are loaded. +#[derive(Serialize, Deserialize, Debug, Clone, Copy, Eq, PartialEq, Hash)] +#[serde(transparent)] +pub struct CrateArrayIdx(pub usize); + +#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] +pub(crate) struct Dep { /// Identifies a crate by position in the crates array. #[serde(rename = "crate")] - krate: usize, + pub(crate) krate: CrateArrayIdx, + #[serde(serialize_with = "serialize_crate_name")] #[serde(deserialize_with = "deserialize_crate_name")] - name: CrateName, + pub(crate) name: CrateName, } -#[derive(Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone)] struct CrateSource { include_dirs: Vec, exclude_dirs: Vec, @@ -249,3 +245,10 @@ where let name = String::deserialize(de)?; CrateName::new(&name).map_err(|err| de::Error::custom(format!("invalid crate name: {err:?}"))) } + +fn serialize_crate_name(name: &CrateName, se: S) -> Result +where + S: serde::Serializer, +{ + se.serialize_str(name) +} diff --git a/src/tools/rust-analyzer/crates/project-model/src/rustc_cfg.rs b/src/tools/rust-analyzer/crates/project-model/src/rustc_cfg.rs index 501b1fdc8c5b..4f69b2b96f0c 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/rustc_cfg.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/rustc_cfg.rs @@ -4,7 +4,7 @@ use anyhow::Context; use rustc_hash::FxHashMap; use toolchain::Tool; -use crate::{cfg_flag::CfgFlag, utf8_stdout, ManifestPath, Sysroot}; +use crate::{cfg::CfgFlag, utf8_stdout, ManifestPath, Sysroot}; /// Determines how `rustc --print cfg` is discovered and invoked. pub(crate) enum RustcCfgConfig<'a> { @@ -32,9 +32,6 @@ pub(crate) fn get( } } - // Add miri cfg, which is useful for mir eval in stdlib - res.push(CfgFlag::Atom("miri".into())); - let rustc_cfgs = get_rust_cfgs(target, extra_env, config); let rustc_cfgs = match rustc_cfgs { 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 fc0b507b3328..fd09dff503fa 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/tests.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/tests.rs @@ -75,6 +75,7 @@ fn load_rust_project(file: &str) -> (CrateGraph, ProcMacroPaths) { rustc_cfg: Vec::new(), toolchain: None, target_layout: Err(Arc::from("test has no data layout")), + cfg_overrides: Default::default(), }; to_crate_graph(project_workspace) } @@ -97,6 +98,11 @@ fn get_test_json_file(file: &str) -> T { } } +fn replace_cargo(s: &mut String) { + let path = toolchain::Tool::Cargo.path().to_string().escape_debug().collect::(); + *s = s.replace(&path, "$CARGO$"); +} + fn replace_root(s: &mut String, direction: bool) { if direction { let root = if cfg!(windows) { r#"C:\\ROOT\"# } else { "/ROOT/" }; @@ -155,7 +161,9 @@ fn to_crate_graph(project_workspace: ProjectWorkspace) -> (CrateGraph, ProcMacro fn check_crate_graph(crate_graph: CrateGraph, expect: ExpectFile) { let mut crate_graph = format!("{crate_graph:#?}"); + replace_root(&mut crate_graph, false); + replace_cargo(&mut crate_graph); replace_fake_sys_root(&mut crate_graph); expect.assert_eq(&crate_graph); } 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 b8c5885108d3..98c5a02dcdcd 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs @@ -2,7 +2,7 @@ //! metadata` or `rust-project.json`) into representation stored in the salsa //! database -- `CrateGraph`. -use std::{collections::VecDeque, fmt, fs, iter, str::FromStr, sync}; +use std::{collections::VecDeque, fmt, fs, iter, sync}; use anyhow::{format_err, Context}; use base_db::{ @@ -21,8 +21,9 @@ use triomphe::Arc; use crate::{ build_scripts::BuildScriptOutput, cargo_workspace::{DepKind, PackageData, RustLibSource}, - cfg_flag::CfgFlag, - project_json::Crate, + cfg::{CfgFlag, CfgOverrides}, + env::{cargo_config_env, inject_cargo_env, inject_cargo_package_env, inject_rustc_tool_env}, + project_json::{Crate, CrateArrayIdx}, rustc_cfg::{self, RustcCfgConfig}, sysroot::{SysrootCrate, SysrootMode}, target_data_layout::{self, RustcDataLayoutConfig}, @@ -30,20 +31,7 @@ use crate::{ ProjectJson, ProjectManifest, Sysroot, TargetData, TargetKind, WorkspaceBuildScripts, }; -/// A set of cfg-overrides per crate. -#[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. - pub selective: FxHashMap, -} - -impl CfgOverrides { - pub fn len(&self) -> usize { - self.global.len() + self.selective.values().map(|it| it.len()).sum::() - } -} +pub type FileLoader<'a> = &'a mut dyn for<'b> FnMut(&'b AbsPath) -> Option; /// `PackageRoot` describes a package root folder. /// Which may be an external dependency, or a member of @@ -60,30 +48,46 @@ pub struct PackageRoot { pub enum ProjectWorkspace { /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`. Cargo { + /// The workspace as returned by `cargo metadata`. cargo: CargoWorkspace, + /// The build script results for the workspace. build_scripts: WorkspaceBuildScripts, + /// The sysroot loaded for this workspace. sysroot: Result>, + /// The rustc workspace loaded for this workspace. An `Err(None)` means loading has been + /// disabled or was otherwise not requested. rustc: Result, Option>, /// Holds cfg flags for the current target. We get those by running /// `rustc --print cfg`. - /// - /// FIXME: make this a per-crate map, as, eg, build.rs might have a - /// different target. + // FIXME: make this a per-crate map, as, eg, build.rs might have a + // different target. rustc_cfg: Vec, + /// A set of cfg overrides for this workspace. cfg_overrides: CfgOverrides, + /// The toolchain version used by this workspace. toolchain: Option, + /// The target data layout queried for workspace. target_layout: TargetLayoutLoadResult, + /// Environment variables set in the `.cargo/config` file. cargo_config_extra_env: FxHashMap, }, /// Project workspace was manually specified using a `rust-project.json` file. Json { + /// The loaded project json file. project: ProjectJson, + /// The sysroot loaded for this workspace. sysroot: Result>, /// Holds cfg flags for the current target. We get those by running /// `rustc --print cfg`. + // FIXME: make this a per-crate map, as, eg, build.rs might have a + // different target. rustc_cfg: Vec, + /// The toolchain version used by this workspace. toolchain: Option, + /// The target data layout queried for workspace. target_layout: TargetLayoutLoadResult, + /// A set of cfg overrides for this workspace. + cfg_overrides: CfgOverrides, }, // 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. @@ -95,14 +99,24 @@ pub enum ProjectWorkspace { // // /// Project with a set of disjoint files, not belonging to any particular workspace. /// Backed by basic sysroot crates for basic completion and highlighting. - DetachedFiles { - files: Vec, + DetachedFile { + /// The file in question. + file: AbsPathBuf, + /// The sysroot loaded for this workspace. sysroot: Result>, /// Holds cfg flags for the current target. We get those by running /// `rustc --print cfg`. + // FIXME: make this a per-crate map, as, eg, build.rs might have a + // different target. rustc_cfg: Vec, + /// The toolchain version used by this workspace. toolchain: Option, + /// The target data layout queried for workspace. target_layout: TargetLayoutLoadResult, + /// A set of cfg overrides for the files. + cfg_overrides: CfgOverrides, + /// Is this file a cargo script file? + cargo_script: Option, }, } @@ -141,6 +155,7 @@ impl fmt::Debug for ProjectWorkspace { rustc_cfg, toolchain, target_layout: data_layout, + cfg_overrides, } => { let mut debug_struct = f.debug_struct("Json"); debug_struct.field("n_crates", &project.n_crates()); @@ -150,22 +165,28 @@ impl fmt::Debug for ProjectWorkspace { debug_struct .field("n_rustc_cfg", &rustc_cfg.len()) .field("toolchain", &toolchain) - .field("data_layout", &data_layout); + .field("data_layout", &data_layout) + .field("n_cfg_overrides", &cfg_overrides.len()); debug_struct.finish() } - ProjectWorkspace::DetachedFiles { - files, + ProjectWorkspace::DetachedFile { + file, sysroot, rustc_cfg, toolchain, target_layout, + cfg_overrides, + cargo_script, } => f .debug_struct("DetachedFiles") - .field("n_files", &files.len()) + .field("file", &file) + .field("cargo_script", &cargo_script.is_some()) .field("sysroot", &sysroot.is_ok()) + .field("cargo_script", &cargo_script.is_some()) .field("n_rustc_cfg", &rustc_cfg.len()) .field("toolchain", &toolchain) .field("data_layout", &target_layout) + .field("n_cfg_overrides", &cfg_overrides.len()) .finish(), } } @@ -219,6 +240,7 @@ impl ProjectWorkspace { project_json, config.target.as_deref(), &config.extra_env, + &config.cfg_overrides, ) } ProjectManifest::CargoToml(cargo_toml) => { @@ -360,6 +382,7 @@ impl ProjectWorkspace { project_json: ProjectJson, target: Option<&str>, extra_env: &FxHashMap, + cfg_overrides: &CfgOverrides, ) -> ProjectWorkspace { let sysroot = match (project_json.sysroot.clone(), project_json.sysroot_src.clone()) { (Some(sysroot), Some(sysroot_src)) => { @@ -406,57 +429,86 @@ impl ProjectWorkspace { rustc_cfg, toolchain, target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())), + cfg_overrides: cfg_overrides.clone(), } } pub fn load_detached_files( detached_files: Vec, config: &CargoConfig, - ) -> anyhow::Result { - let dir = detached_files - .first() - .and_then(|it| it.parent()) - .ok_or_else(|| format_err!("No detached files to load"))?; - let sysroot = match &config.sysroot { - Some(RustLibSource::Path(path)) => { - Sysroot::with_sysroot_dir(path.clone(), config.sysroot_query_metadata) - .map_err(|e| Some(format!("Failed to find sysroot at {path}:{e}"))) - } - Some(RustLibSource::Discover) => Sysroot::discover( - dir, - &config.extra_env, - config.sysroot_query_metadata, - ) - .map_err(|e| { - Some(format!("Failed to find sysroot for {dir}. Is rust-src installed? {e}")) - }), - None => Err(None), - }; + ) -> Vec> { + detached_files + .into_iter() + .map(|detached_file| { + let dir = detached_file + .parent() + .ok_or_else(|| format_err!("detached file has no parent"))?; + let sysroot = match &config.sysroot { + Some(RustLibSource::Path(path)) => { + Sysroot::with_sysroot_dir(path.clone(), config.sysroot_query_metadata) + .map_err(|e| Some(format!("Failed to find sysroot at {path}:{e}"))) + } + Some(RustLibSource::Discover) => { + Sysroot::discover(dir, &config.extra_env, config.sysroot_query_metadata) + .map_err(|e| { + Some(format!( + "Failed to find sysroot for {dir}. Is rust-src installed? {e}" + )) + }) + } + None => Err(None), + }; - let sysroot_ref = sysroot.as_ref().ok(); - let toolchain = - match get_toolchain_version(dir, sysroot_ref, Tool::Rustc, &config.extra_env, "rustc ") - { - Ok(it) => it, - Err(e) => { - tracing::error!("{e}"); - None - } - }; + let sysroot_ref = sysroot.as_ref().ok(); + let toolchain = match get_toolchain_version( + dir, + sysroot_ref, + Tool::Rustc, + &config.extra_env, + "rustc ", + ) { + Ok(it) => it, + Err(e) => { + tracing::error!("{e}"); + None + } + }; - let rustc_cfg = rustc_cfg::get(None, &config.extra_env, RustcCfgConfig::Rustc(sysroot_ref)); - let data_layout = target_data_layout::get( - RustcDataLayoutConfig::Rustc(sysroot_ref), - None, - &config.extra_env, - ); - Ok(ProjectWorkspace::DetachedFiles { - files: detached_files, - sysroot, - rustc_cfg, - toolchain, - target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())), - }) + let rustc_cfg = + rustc_cfg::get(None, &config.extra_env, RustcCfgConfig::Rustc(sysroot_ref)); + let data_layout = target_data_layout::get( + RustcDataLayoutConfig::Rustc(sysroot_ref), + None, + &config.extra_env, + ); + + let cargo_script = ManifestPath::try_from(detached_file.clone()) + .ok() + .and_then(|file| { + CargoWorkspace::fetch_metadata( + &file, + file.parent(), + config, + sysroot_ref, + &|_| (), + ) + .ok() + }) + .map(CargoWorkspace::new); + + Ok(ProjectWorkspace::DetachedFile { + file: detached_file, + sysroot, + rustc_cfg, + toolchain, + target_layout: data_layout + .map(Arc::from) + .map_err(|it| Arc::from(it.to_string())), + cfg_overrides: config.cfg_overrides.clone(), + cargo_script, + }) + }) + .collect() } /// Runs the build scripts for this [`ProjectWorkspace`]. @@ -466,7 +518,13 @@ impl ProjectWorkspace { progress: &dyn Fn(String), ) -> anyhow::Result { match self { - ProjectWorkspace::Cargo { cargo, toolchain, sysroot, .. } => { + ProjectWorkspace::DetachedFile { + cargo_script: Some(cargo), + toolchain, + sysroot, + .. + } + | ProjectWorkspace::Cargo { cargo, toolchain, sysroot, .. } => { WorkspaceBuildScripts::run_for_workspace( config, cargo, @@ -478,9 +536,8 @@ impl ProjectWorkspace { format!("Failed to run build scripts for {}", cargo.workspace_root()) }) } - ProjectWorkspace::Json { .. } | ProjectWorkspace::DetachedFiles { .. } => { - Ok(WorkspaceBuildScripts::default()) - } + ProjectWorkspace::DetachedFile { cargo_script: None, .. } + | ProjectWorkspace::Json { .. } => Ok(WorkspaceBuildScripts::default()), } } @@ -536,11 +593,11 @@ impl ProjectWorkspace { } } - pub fn workspace_definition_path(&self) -> Option<&AbsPath> { + pub fn workspace_definition_path(&self) -> &AbsPath { match self { - ProjectWorkspace::Cargo { cargo, .. } => Some(cargo.workspace_root()), - ProjectWorkspace::Json { project, .. } => Some(project.path()), - ProjectWorkspace::DetachedFiles { .. } => None, + ProjectWorkspace::Cargo { cargo, .. } => cargo.workspace_root(), + ProjectWorkspace::Json { project, .. } => project.path(), + ProjectWorkspace::DetachedFile { file, .. } => file, } } @@ -548,10 +605,10 @@ impl ProjectWorkspace { match self { ProjectWorkspace::Cargo { sysroot: Ok(sysroot), .. } | ProjectWorkspace::Json { sysroot: Ok(sysroot), .. } - | ProjectWorkspace::DetachedFiles { sysroot: Ok(sysroot), .. } => { + | ProjectWorkspace::DetachedFile { sysroot: Ok(sysroot), .. } => { sysroot.discover_proc_macro_srv() } - ProjectWorkspace::DetachedFiles { .. } => { + ProjectWorkspace::DetachedFile { .. } => { Err(anyhow::format_err!("cannot find proc-macro server, no sysroot was found")) } ProjectWorkspace::Cargo { cargo, .. } => Err(anyhow::format_err!( @@ -611,6 +668,7 @@ impl ProjectWorkspace { rustc_cfg: _, toolchain: _, target_layout: _, + cfg_overrides: _, } => project .crates() .map(|(_, krate)| PackageRoot { @@ -681,15 +739,50 @@ impl ProjectWorkspace { })) .collect() } - ProjectWorkspace::DetachedFiles { files, sysroot, .. } => files - .iter() - .map(|detached_file| PackageRoot { + ProjectWorkspace::DetachedFile { file, cargo_script, sysroot, .. } => { + iter::once(PackageRoot { is_local: true, - include: vec![detached_file.clone()], + include: vec![file.clone()], exclude: Vec::new(), }) + .chain(cargo_script.iter().flat_map(|cargo| { + cargo.packages().map(|pkg| { + let is_local = cargo[pkg].is_local; + let pkg_root = cargo[pkg].manifest.parent().to_path_buf(); + + let mut include = vec![pkg_root.clone()]; + + // In case target's path is manually set in Cargo.toml to be + // outside the package root, add its parent as an extra include. + // An example of this situation would look like this: + // + // ```toml + // [lib] + // path = "../../src/lib.rs" + // ``` + let extra_targets = cargo[pkg] + .targets + .iter() + .filter(|&&tgt| matches!(cargo[tgt].kind, TargetKind::Lib { .. })) + .filter_map(|&tgt| cargo[tgt].root.parent()) + .map(|tgt| tgt.normalize().to_path_buf()) + .filter(|path| !path.starts_with(&pkg_root)); + include.extend(extra_targets); + + let mut exclude = vec![pkg_root.join(".git")]; + if is_local { + exclude.push(pkg_root.join("target")); + } else { + exclude.push(pkg_root.join("tests")); + exclude.push(pkg_root.join("examples")); + exclude.push(pkg_root.join("benches")); + } + PackageRoot { is_local, include, exclude } + }) + })) .chain(mk_sysroot(sysroot.as_ref())) - .collect(), + .collect() + } } } @@ -705,16 +798,17 @@ impl ProjectWorkspace { let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.num_packages()); cargo.packages().len() + sysroot_package_len + rustc_package_len } - ProjectWorkspace::DetachedFiles { sysroot, files, .. } => { + ProjectWorkspace::DetachedFile { sysroot, cargo_script, .. } => { let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.num_packages()); - sysroot_package_len + files.len() + sysroot_package_len + + cargo_script.as_ref().map_or(1, |cargo| cargo.packages().len()) } } } pub fn to_crate_graph( &self, - load: &mut dyn FnMut(&AbsPath) -> Option, + load: FileLoader<'_>, extra_env: &FxHashMap, ) -> (CrateGraph, ProcMacroPaths) { let _p = tracing::span!(tracing::Level::INFO, "ProjectWorkspace::to_crate_graph").entered(); @@ -726,6 +820,7 @@ impl ProjectWorkspace { rustc_cfg, toolchain: _, target_layout: _, + cfg_overrides, } => ( project_json_to_crate_graph( rustc_cfg.clone(), @@ -733,6 +828,7 @@ impl ProjectWorkspace { project, sysroot.as_ref().ok(), extra_env, + cfg_overrides, ), sysroot, ), @@ -758,24 +854,39 @@ impl ProjectWorkspace { ), sysroot, ), - ProjectWorkspace::DetachedFiles { - files, + ProjectWorkspace::DetachedFile { + file, sysroot, rustc_cfg, toolchain: _, target_layout: _, + cfg_overrides, + cargo_script, } => ( - detached_files_to_crate_graph( - rustc_cfg.clone(), - load, - files, - sysroot.as_ref().ok(), - ), + if let Some(cargo) = cargo_script { + cargo_to_crate_graph( + &mut |path| load(path), + None, + cargo, + sysroot.as_ref().ok(), + rustc_cfg.clone(), + cfg_overrides, + &WorkspaceBuildScripts::default(), + ) + } else { + detached_file_to_crate_graph( + rustc_cfg.clone(), + load, + file, + sysroot.as_ref().ok(), + cfg_overrides, + ) + }, sysroot, ), }; - if matches!(sysroot.as_ref().map(|it| it.mode()), Ok(SysrootMode::Workspace(_))) + if matches!(sysroot.as_ref().map(|it| it.mode()), Ok(SysrootMode::Stitched(_))) && crate_graph.patch_cfg_if() { tracing::debug!("Patched std to depend on cfg-if") @@ -820,35 +931,56 @@ impl ProjectWorkspace { && cargo_config_extra_env == o_cargo_config_extra_env } ( - Self::Json { project, sysroot, rustc_cfg, toolchain, target_layout: _ }, + Self::Json { + project, + sysroot, + rustc_cfg, + toolchain, + target_layout: _, + cfg_overrides, + }, Self::Json { project: o_project, sysroot: o_sysroot, rustc_cfg: o_rustc_cfg, toolchain: o_toolchain, target_layout: _, + cfg_overrides: o_cfg_overrides, }, ) => { project == o_project && rustc_cfg == o_rustc_cfg && sysroot == o_sysroot && toolchain == o_toolchain + && cfg_overrides == o_cfg_overrides } ( - Self::DetachedFiles { files, sysroot, rustc_cfg, toolchain, target_layout }, - Self::DetachedFiles { - files: o_files, + Self::DetachedFile { + file, + sysroot, + rustc_cfg, + cargo_script, + toolchain, + target_layout, + cfg_overrides, + }, + Self::DetachedFile { + file: o_file, sysroot: o_sysroot, rustc_cfg: o_rustc_cfg, + cargo_script: o_cargo_script, toolchain: o_toolchain, target_layout: o_target_layout, + cfg_overrides: o_cfg_overrides, }, ) => { - files == o_files + file == o_file && sysroot == o_sysroot && rustc_cfg == o_rustc_cfg && toolchain == o_toolchain && target_layout == o_target_layout + && cfg_overrides == o_cfg_overrides + && cargo_script == o_cargo_script } _ => false, } @@ -865,10 +997,11 @@ impl ProjectWorkspace { fn project_json_to_crate_graph( rustc_cfg: Vec, - load: &mut dyn FnMut(&AbsPath) -> Option, + load: FileLoader<'_>, project: &ProjectJson, sysroot: Option<&Sysroot>, extra_env: &FxHashMap, + override_cfg: &CfgOverrides, ) -> (CrateGraph, ProcMacroPaths) { let mut res = (CrateGraph::default(), ProcMacroPaths::default()); let (crate_graph, proc_macros) = &mut res; @@ -878,12 +1011,13 @@ fn project_json_to_crate_graph( let r_a_cfg_flag = CfgFlag::Atom("rust_analyzer".to_owned()); let mut cfg_cache: FxHashMap<&str, Vec> = FxHashMap::default(); - let crates: FxHashMap = project + + let idx_to_crate_id: FxHashMap = project .crates() - .filter_map(|(crate_id, krate)| Some((crate_id, krate, load(&krate.root_module)?))) + .filter_map(|(idx, krate)| Some((idx, krate, load(&krate.root_module)?))) .map( |( - crate_id, + idx, Crate { display_name, edition, @@ -907,17 +1041,22 @@ fn project_json_to_crate_graph( None => &rustc_cfg, }; + let mut cfg_options = target_cfgs + .iter() + .chain(cfg.iter()) + .chain(iter::once(&r_a_cfg_flag)) + .cloned() + .collect(); + override_cfg.apply( + &mut cfg_options, + display_name.as_ref().map(|it| it.canonical_name()).unwrap_or_default(), + ); let crate_graph_crate_id = crate_graph.add_crate_root( file_id, *edition, display_name.clone(), version.clone(), - target_cfgs - .iter() - .chain(cfg.iter()) - .chain(iter::once(&r_a_cfg_flag)) - .cloned() - .collect(), + Arc::new(cfg_options), None, env, *is_proc_macro, @@ -939,13 +1078,13 @@ fn project_json_to_crate_graph( proc_macros.insert(crate_graph_crate_id, node); } } - (crate_id, crate_graph_crate_id) + (idx, crate_graph_crate_id) }, ) .collect(); - for (from, krate) in project.crates() { - if let Some(&from) = crates.get(&from) { + for (from_idx, krate) in project.crates() { + if let Some(&from) = idx_to_crate_id.get(&from_idx) { if let Some((public_deps, libproc_macro)) = &sysroot_deps { public_deps.add_to_crate_graph(crate_graph, from); if let Some(proc_macro) = libproc_macro { @@ -954,8 +1093,8 @@ fn project_json_to_crate_graph( } for dep in &krate.deps { - if let Some(&to) = crates.get(&dep.crate_id) { - add_dep(crate_graph, from, dep.name.clone(), to) + if let Some(&to) = idx_to_crate_id.get(&dep.krate) { + add_dep(crate_graph, from, dep.name.clone(), to); } } } @@ -964,7 +1103,7 @@ fn project_json_to_crate_graph( } fn cargo_to_crate_graph( - load: &mut dyn FnMut(&AbsPath) -> Option, + load: FileLoader<'_>, rustc: Option<&(CargoWorkspace, WorkspaceBuildScripts)>, cargo: &CargoWorkspace, sysroot: Option<&Sysroot>, @@ -981,7 +1120,7 @@ fn cargo_to_crate_graph( None => (SysrootPublicDeps::default(), None), }; - let cfg_options = create_cfg_options(rustc_cfg); + let cfg_options = CfgOptions::from_iter(rustc_cfg); // Mapping of a package to its library target let mut pkg_to_lib_crate = FxHashMap::default(); @@ -996,25 +1135,13 @@ fn cargo_to_crate_graph( let cfg_options = { let mut cfg_options = cfg_options.clone(); - // Add test cfg for local crates if cargo[pkg].is_local { + // Add test cfg for local crates cfg_options.insert_atom("test".into()); cfg_options.insert_atom("rust_analyzer".into()); } - 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(diff.clone()); - }; + override_cfg.apply(&mut cfg_options, &cargo[pkg].name); cfg_options }; @@ -1150,6 +1277,7 @@ fn cargo_to_crate_graph( &pkg_crates, &cfg_options, override_cfg, + // FIXME: Remove this once rustc switched over to rust-project.json if rustc_workspace.workspace_root() == cargo.workspace_root() { // the rustc workspace does not use the installed toolchain's proc-macro server // so we need to make sure we don't use the pre compiled proc-macros there either @@ -1163,11 +1291,12 @@ fn cargo_to_crate_graph( res } -fn detached_files_to_crate_graph( +fn detached_file_to_crate_graph( rustc_cfg: Vec, - load: &mut dyn FnMut(&AbsPath) -> Option, - detached_files: &[AbsPathBuf], + load: FileLoader<'_>, + detached_file: &AbsPathBuf, sysroot: Option<&Sysroot>, + override_cfg: &CfgOverrides, ) -> (CrateGraph, ProcMacroPaths) { let _p = tracing::span!(tracing::Level::INFO, "detached_files_to_crate_graph").entered(); let mut crate_graph = CrateGraph::default(); @@ -1176,37 +1305,38 @@ fn detached_files_to_crate_graph( None => (SysrootPublicDeps::default(), None), }; - let mut cfg_options = create_cfg_options(rustc_cfg); + let mut cfg_options = CfgOptions::from_iter(rustc_cfg); + cfg_options.insert_atom("test".into()); cfg_options.insert_atom("rust_analyzer".into()); + override_cfg.apply(&mut cfg_options, ""); + let cfg_options = Arc::new(cfg_options); - for detached_file in detached_files { - let file_id = match load(detached_file) { - Some(file_id) => file_id, - None => { - tracing::error!("Failed to load detached file {:?}", detached_file); - continue; - } - }; - let display_name = detached_file - .file_stem() - .map(|file_stem| CrateDisplayName::from_canonical_name(file_stem.to_owned())); - let detached_file_crate = crate_graph.add_crate_root( - file_id, - Edition::CURRENT, - display_name.clone(), - None, - cfg_options.clone(), - None, - Env::default(), - false, - CrateOrigin::Local { - repo: None, - name: display_name.map(|n| n.canonical_name().to_owned()), - }, - ); + let file_id = match load(detached_file) { + Some(file_id) => file_id, + None => { + tracing::error!("Failed to load detached file {:?}", detached_file); + return (crate_graph, FxHashMap::default()); + } + }; + let display_name = detached_file + .file_stem() + .map(|file_stem| CrateDisplayName::from_canonical_name(file_stem.to_owned())); + let detached_file_crate = crate_graph.add_crate_root( + file_id, + Edition::CURRENT, + display_name.clone(), + None, + cfg_options.clone(), + None, + Env::default(), + false, + CrateOrigin::Local { + repo: None, + name: display_name.map(|n| n.canonical_name().to_owned()), + }, + ); - public_deps.add_to_crate_graph(&mut crate_graph, detached_file_crate); - } + public_deps.add_to_crate_graph(&mut crate_graph, detached_file_crate); (crate_graph, FxHashMap::default()) } @@ -1214,7 +1344,7 @@ 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: FileLoader<'_>, rustc_workspace: &CargoWorkspace, cargo: &CargoWorkspace, public_deps: &SysrootPublicDeps, @@ -1246,20 +1376,7 @@ fn handle_rustc_crates( } let mut cfg_options = cfg_options.clone(); - - if !override_cfg.global.is_empty() { - cfg_options.apply_diff(override_cfg.global.clone()); - }; - 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). - // - // 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(diff.clone()); - }; + override_cfg.apply(&mut cfg_options, &rustc_workspace[pkg].name); for &tgt in rustc_workspace[pkg].targets.iter() { let kind @ TargetKind::Lib { is_proc_macro } = rustc_workspace[tgt].kind else { @@ -1361,26 +1478,22 @@ fn add_target_crate_root( }; let mut env = Env::default(); - inject_cargo_env(pkg, &mut env); - if let Ok(cname) = String::from_str(cargo_name) { - // CARGO_CRATE_NAME is the name of the Cargo target with - converted to _, such as the name of the library, binary, example, integration test, or benchmark. - env.set("CARGO_CRATE_NAME", cname.replace('-', "_")); - } + inject_cargo_package_env(&mut env, pkg); + inject_cargo_env(&mut env); + inject_rustc_tool_env(&mut env, cargo_name, kind); if let Some(envs) = build_data.map(|it| &it.envs) { for (k, v) in envs { env.set(k, v.clone()); } } - - let display_name = CrateDisplayName::from_canonical_name(cargo_name.to_owned()); let crate_id = crate_graph.add_crate_root( file_id, edition, - Some(display_name), + Some(CrateDisplayName::from_canonical_name(cargo_name.to_owned())), Some(pkg.version.to_string()), - cfg_options, - potential_cfg_options, + Arc::new(cfg_options), + potential_cfg_options.map(Arc::new), env, matches!(kind, TargetKind::Lib { is_proc_macro: true }), origin, @@ -1416,7 +1529,7 @@ fn sysroot_to_crate_graph( crate_graph: &mut CrateGraph, sysroot: &Sysroot, rustc_cfg: Vec, - load: &mut dyn FnMut(&AbsPath) -> Option, + load: FileLoader<'_>, ) -> (SysrootPublicDeps, Option) { let _p = tracing::span!(tracing::Level::INFO, "sysroot_to_crate_graph").entered(); match sysroot.mode() { @@ -1427,7 +1540,17 @@ fn sysroot_to_crate_graph( cargo, None, rustc_cfg, - &CfgOverrides::default(), + &CfgOverrides { + global: CfgDiff::new( + vec![ + CfgAtom::Flag("debug_assertions".into()), + CfgAtom::Flag("miri".into()), + ], + vec![], + ) + .unwrap(), + ..Default::default() + }, &WorkspaceBuildScripts::default(), ); @@ -1436,7 +1559,7 @@ fn sysroot_to_crate_graph( let diff = CfgDiff::new(vec![], vec![CfgAtom::Flag("test".into())]).unwrap(); for (cid, c) in cg.iter_mut() { // uninject `test` flag so `core` keeps working. - c.cfg_options.apply_diff(diff.clone()); + Arc::make_mut(&mut c.cfg_options).apply_diff(diff.clone()); // patch the origin if c.origin.is_local() { let lang_crate = LangCrateOrigin::from( @@ -1485,13 +1608,18 @@ fn sysroot_to_crate_graph( (SysrootPublicDeps { deps: pub_deps }, libproc_macro) } SysrootMode::Stitched(stitched) => { - let cfg_options = create_cfg_options(rustc_cfg); + let cfg_options = Arc::new({ + let mut cfg_options = CfgOptions::default(); + cfg_options.extend(rustc_cfg); + cfg_options.insert_atom("debug_assertions".into()); + cfg_options.insert_atom("miri".into()); + cfg_options + }); let sysroot_crates: FxHashMap = stitched .crates() .filter_map(|krate| { let file_id = load(&stitched[krate].root)?; - let env = Env::default(); let display_name = CrateDisplayName::from_canonical_name(stitched[krate].name.clone()); let crate_id = crate_graph.add_crate_root( @@ -1501,7 +1629,7 @@ fn sysroot_to_crate_graph( None, cfg_options.clone(), None, - env, + Env::default(), false, CrateOrigin::Lang(LangCrateOrigin::from(&*stitched[krate].name)), ); @@ -1559,71 +1687,3 @@ fn add_dep_inner(graph: &mut CrateGraph, from: CrateId, dep: Dependency) { tracing::error!("{}", err) } } - -/// Recreates the compile-time environment variables that Cargo sets. -/// -/// Should be synced with -/// -/// -/// FIXME: ask Cargo to provide this data instead of re-deriving. -fn inject_cargo_env(package: &PackageData, env: &mut Env) { - // FIXME: Missing variables: - // CARGO_BIN_NAME, CARGO_BIN_EXE_ - - let manifest_dir = package.manifest.parent(); - env.set("CARGO_MANIFEST_DIR", manifest_dir.as_str().to_owned()); - - // Not always right, but works for common cases. - env.set("CARGO", "cargo".into()); - - env.set("CARGO_PKG_VERSION", package.version.to_string()); - env.set("CARGO_PKG_VERSION_MAJOR", package.version.major.to_string()); - env.set("CARGO_PKG_VERSION_MINOR", package.version.minor.to_string()); - env.set("CARGO_PKG_VERSION_PATCH", package.version.patch.to_string()); - env.set("CARGO_PKG_VERSION_PRE", package.version.pre.to_string()); - - env.set("CARGO_PKG_AUTHORS", String::new()); - - env.set("CARGO_PKG_NAME", package.name.clone()); - // FIXME: This isn't really correct (a package can have many crates with different names), but - // it's better than leaving the variable unset. - env.set("CARGO_CRATE_NAME", CrateName::normalize_dashes(&package.name).to_string()); - env.set("CARGO_PKG_DESCRIPTION", String::new()); - env.set("CARGO_PKG_HOMEPAGE", String::new()); - env.set("CARGO_PKG_REPOSITORY", String::new()); - env.set("CARGO_PKG_LICENSE", String::new()); - - env.set("CARGO_PKG_LICENSE_FILE", String::new()); -} - -fn create_cfg_options(rustc_cfg: Vec) -> CfgOptions { - let mut cfg_options = CfgOptions::default(); - cfg_options.extend(rustc_cfg); - cfg_options.insert_atom("debug_assertions".into()); - cfg_options -} - -fn cargo_config_env( - cargo_toml: &ManifestPath, - extra_env: &FxHashMap, - sysroot: Option<&Sysroot>, -) -> FxHashMap { - let mut cargo_config = Sysroot::tool(sysroot, Tool::Cargo); - cargo_config.envs(extra_env); - cargo_config - .current_dir(cargo_toml.parent()) - .args(["-Z", "unstable-options", "config", "get", "env"]) - .env("RUSTC_BOOTSTRAP", "1"); - // if successful we receive `env.key.value = "value" per entry - tracing::debug!("Discovering cargo config env by {:?}", cargo_config); - utf8_stdout(cargo_config).map(parse_output_cargo_config_env).unwrap_or_default() -} - -fn parse_output_cargo_config_env(stdout: String) -> FxHashMap { - stdout - .lines() - .filter_map(|l| l.strip_prefix("env.")) - .filter_map(|l| l.split_once(".value = ")) - .map(|(key, value)| (key.to_owned(), value.trim_matches('"').to_owned())) - .collect() -} 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 index 0ad19ca9f759..c2a2d6ed911f 100644 --- 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 @@ -17,7 +17,6 @@ ), cfg_options: CfgOptions( [ - "debug_assertions", "rust_analyzer", "test", ], @@ -25,20 +24,22 @@ 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": "$CARGO$", "CARGO_CRATE_NAME": "hello_world", - "CARGO_PKG_LICENSE_FILE": "", - "CARGO_PKG_HOMEPAGE": "", + "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_PKG_AUTHORS": "", "CARGO_PKG_DESCRIPTION": "", + "CARGO_PKG_HOMEPAGE": "", + "CARGO_PKG_LICENSE": "", + "CARGO_PKG_LICENSE_FILE": "", "CARGO_PKG_NAME": "hello-world", - "CARGO_PKG_VERSION_PATCH": "0", - "CARGO": "cargo", + "CARGO_PKG_README": "", "CARGO_PKG_REPOSITORY": "", + "CARGO_PKG_RUST_VERSION": "", + "CARGO_PKG_VERSION": "0.1.0", + "CARGO_PKG_VERSION_MAJOR": "0", "CARGO_PKG_VERSION_MINOR": "1", + "CARGO_PKG_VERSION_PATCH": "0", "CARGO_PKG_VERSION_PRE": "", }, }, @@ -77,7 +78,6 @@ ), cfg_options: CfgOptions( [ - "debug_assertions", "rust_analyzer", "test", ], @@ -85,20 +85,22 @@ 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": "$CARGO$", "CARGO_CRATE_NAME": "hello_world", - "CARGO_PKG_LICENSE_FILE": "", - "CARGO_PKG_HOMEPAGE": "", + "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_PKG_AUTHORS": "", "CARGO_PKG_DESCRIPTION": "", + "CARGO_PKG_HOMEPAGE": "", + "CARGO_PKG_LICENSE": "", + "CARGO_PKG_LICENSE_FILE": "", "CARGO_PKG_NAME": "hello-world", - "CARGO_PKG_VERSION_PATCH": "0", - "CARGO": "cargo", + "CARGO_PKG_README": "", "CARGO_PKG_REPOSITORY": "", + "CARGO_PKG_RUST_VERSION": "", + "CARGO_PKG_VERSION": "0.1.0", + "CARGO_PKG_VERSION_MAJOR": "0", "CARGO_PKG_VERSION_MINOR": "1", + "CARGO_PKG_VERSION_PATCH": "0", "CARGO_PKG_VERSION_PRE": "", }, }, @@ -144,7 +146,6 @@ ), cfg_options: CfgOptions( [ - "debug_assertions", "rust_analyzer", "test", ], @@ -152,20 +153,22 @@ 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": "$CARGO$", "CARGO_CRATE_NAME": "an_example", - "CARGO_PKG_LICENSE_FILE": "", - "CARGO_PKG_HOMEPAGE": "", + "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_PKG_AUTHORS": "", "CARGO_PKG_DESCRIPTION": "", + "CARGO_PKG_HOMEPAGE": "", + "CARGO_PKG_LICENSE": "", + "CARGO_PKG_LICENSE_FILE": "", "CARGO_PKG_NAME": "hello-world", - "CARGO_PKG_VERSION_PATCH": "0", - "CARGO": "cargo", + "CARGO_PKG_README": "", "CARGO_PKG_REPOSITORY": "", + "CARGO_PKG_RUST_VERSION": "", + "CARGO_PKG_VERSION": "0.1.0", + "CARGO_PKG_VERSION_MAJOR": "0", "CARGO_PKG_VERSION_MINOR": "1", + "CARGO_PKG_VERSION_PATCH": "0", "CARGO_PKG_VERSION_PRE": "", }, }, @@ -211,7 +214,6 @@ ), cfg_options: CfgOptions( [ - "debug_assertions", "rust_analyzer", "test", ], @@ -219,20 +221,22 @@ 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": "$CARGO$", "CARGO_CRATE_NAME": "it", - "CARGO_PKG_LICENSE_FILE": "", - "CARGO_PKG_HOMEPAGE": "", + "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_PKG_AUTHORS": "", "CARGO_PKG_DESCRIPTION": "", + "CARGO_PKG_HOMEPAGE": "", + "CARGO_PKG_LICENSE": "", + "CARGO_PKG_LICENSE_FILE": "", "CARGO_PKG_NAME": "hello-world", - "CARGO_PKG_VERSION_PATCH": "0", - "CARGO": "cargo", + "CARGO_PKG_README": "", "CARGO_PKG_REPOSITORY": "", + "CARGO_PKG_RUST_VERSION": "", + "CARGO_PKG_VERSION": "0.1.0", + "CARGO_PKG_VERSION_MAJOR": "0", "CARGO_PKG_VERSION_MINOR": "1", + "CARGO_PKG_VERSION_PATCH": "0", "CARGO_PKG_VERSION_PRE": "", }, }, @@ -278,7 +282,6 @@ ), cfg_options: CfgOptions( [ - "debug_assertions", "feature=default", "feature=std", ], @@ -286,7 +289,6 @@ potential_cfg_options: Some( CfgOptions( [ - "debug_assertions", "feature=align", "feature=const-extern-fn", "feature=default", @@ -299,20 +301,22 @@ ), 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": "$CARGO$", "CARGO_CRATE_NAME": "libc", + "CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98", + "CARGO_PKG_AUTHORS": "The Rust Project Developers", + "CARGO_PKG_DESCRIPTION": "Raw FFI bindings to platform libraries like libc.\n", + "CARGO_PKG_HOMEPAGE": "https://github.com/rust-lang/libc", + "CARGO_PKG_LICENSE": "MIT OR Apache-2.0", "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_README": "README.md", + "CARGO_PKG_REPOSITORY": "https://github.com/rust-lang/libc", + "CARGO_PKG_RUST_VERSION": "", + "CARGO_PKG_VERSION": "0.2.98", + "CARGO_PKG_VERSION_MAJOR": "0", "CARGO_PKG_VERSION_MINOR": "2", + "CARGO_PKG_VERSION_PATCH": "98", "CARGO_PKG_VERSION_PRE": "", }, }, 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 index 0ad19ca9f759..c2a2d6ed911f 100644 --- 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 @@ -17,7 +17,6 @@ ), cfg_options: CfgOptions( [ - "debug_assertions", "rust_analyzer", "test", ], @@ -25,20 +24,22 @@ 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": "$CARGO$", "CARGO_CRATE_NAME": "hello_world", - "CARGO_PKG_LICENSE_FILE": "", - "CARGO_PKG_HOMEPAGE": "", + "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_PKG_AUTHORS": "", "CARGO_PKG_DESCRIPTION": "", + "CARGO_PKG_HOMEPAGE": "", + "CARGO_PKG_LICENSE": "", + "CARGO_PKG_LICENSE_FILE": "", "CARGO_PKG_NAME": "hello-world", - "CARGO_PKG_VERSION_PATCH": "0", - "CARGO": "cargo", + "CARGO_PKG_README": "", "CARGO_PKG_REPOSITORY": "", + "CARGO_PKG_RUST_VERSION": "", + "CARGO_PKG_VERSION": "0.1.0", + "CARGO_PKG_VERSION_MAJOR": "0", "CARGO_PKG_VERSION_MINOR": "1", + "CARGO_PKG_VERSION_PATCH": "0", "CARGO_PKG_VERSION_PRE": "", }, }, @@ -77,7 +78,6 @@ ), cfg_options: CfgOptions( [ - "debug_assertions", "rust_analyzer", "test", ], @@ -85,20 +85,22 @@ 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": "$CARGO$", "CARGO_CRATE_NAME": "hello_world", - "CARGO_PKG_LICENSE_FILE": "", - "CARGO_PKG_HOMEPAGE": "", + "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_PKG_AUTHORS": "", "CARGO_PKG_DESCRIPTION": "", + "CARGO_PKG_HOMEPAGE": "", + "CARGO_PKG_LICENSE": "", + "CARGO_PKG_LICENSE_FILE": "", "CARGO_PKG_NAME": "hello-world", - "CARGO_PKG_VERSION_PATCH": "0", - "CARGO": "cargo", + "CARGO_PKG_README": "", "CARGO_PKG_REPOSITORY": "", + "CARGO_PKG_RUST_VERSION": "", + "CARGO_PKG_VERSION": "0.1.0", + "CARGO_PKG_VERSION_MAJOR": "0", "CARGO_PKG_VERSION_MINOR": "1", + "CARGO_PKG_VERSION_PATCH": "0", "CARGO_PKG_VERSION_PRE": "", }, }, @@ -144,7 +146,6 @@ ), cfg_options: CfgOptions( [ - "debug_assertions", "rust_analyzer", "test", ], @@ -152,20 +153,22 @@ 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": "$CARGO$", "CARGO_CRATE_NAME": "an_example", - "CARGO_PKG_LICENSE_FILE": "", - "CARGO_PKG_HOMEPAGE": "", + "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_PKG_AUTHORS": "", "CARGO_PKG_DESCRIPTION": "", + "CARGO_PKG_HOMEPAGE": "", + "CARGO_PKG_LICENSE": "", + "CARGO_PKG_LICENSE_FILE": "", "CARGO_PKG_NAME": "hello-world", - "CARGO_PKG_VERSION_PATCH": "0", - "CARGO": "cargo", + "CARGO_PKG_README": "", "CARGO_PKG_REPOSITORY": "", + "CARGO_PKG_RUST_VERSION": "", + "CARGO_PKG_VERSION": "0.1.0", + "CARGO_PKG_VERSION_MAJOR": "0", "CARGO_PKG_VERSION_MINOR": "1", + "CARGO_PKG_VERSION_PATCH": "0", "CARGO_PKG_VERSION_PRE": "", }, }, @@ -211,7 +214,6 @@ ), cfg_options: CfgOptions( [ - "debug_assertions", "rust_analyzer", "test", ], @@ -219,20 +221,22 @@ 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": "$CARGO$", "CARGO_CRATE_NAME": "it", - "CARGO_PKG_LICENSE_FILE": "", - "CARGO_PKG_HOMEPAGE": "", + "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_PKG_AUTHORS": "", "CARGO_PKG_DESCRIPTION": "", + "CARGO_PKG_HOMEPAGE": "", + "CARGO_PKG_LICENSE": "", + "CARGO_PKG_LICENSE_FILE": "", "CARGO_PKG_NAME": "hello-world", - "CARGO_PKG_VERSION_PATCH": "0", - "CARGO": "cargo", + "CARGO_PKG_README": "", "CARGO_PKG_REPOSITORY": "", + "CARGO_PKG_RUST_VERSION": "", + "CARGO_PKG_VERSION": "0.1.0", + "CARGO_PKG_VERSION_MAJOR": "0", "CARGO_PKG_VERSION_MINOR": "1", + "CARGO_PKG_VERSION_PATCH": "0", "CARGO_PKG_VERSION_PRE": "", }, }, @@ -278,7 +282,6 @@ ), cfg_options: CfgOptions( [ - "debug_assertions", "feature=default", "feature=std", ], @@ -286,7 +289,6 @@ potential_cfg_options: Some( CfgOptions( [ - "debug_assertions", "feature=align", "feature=const-extern-fn", "feature=default", @@ -299,20 +301,22 @@ ), 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": "$CARGO$", "CARGO_CRATE_NAME": "libc", + "CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98", + "CARGO_PKG_AUTHORS": "The Rust Project Developers", + "CARGO_PKG_DESCRIPTION": "Raw FFI bindings to platform libraries like libc.\n", + "CARGO_PKG_HOMEPAGE": "https://github.com/rust-lang/libc", + "CARGO_PKG_LICENSE": "MIT OR Apache-2.0", "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_README": "README.md", + "CARGO_PKG_REPOSITORY": "https://github.com/rust-lang/libc", + "CARGO_PKG_RUST_VERSION": "", + "CARGO_PKG_VERSION": "0.2.98", + "CARGO_PKG_VERSION_MAJOR": "0", "CARGO_PKG_VERSION_MINOR": "2", + "CARGO_PKG_VERSION_PATCH": "98", "CARGO_PKG_VERSION_PRE": "", }, }, 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 index e2334dca8757..c291ffcca7bc 100644 --- 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 @@ -17,27 +17,28 @@ ), cfg_options: CfgOptions( [ - "debug_assertions", "rust_analyzer", ], ), 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": "$CARGO$", "CARGO_CRATE_NAME": "hello_world", - "CARGO_PKG_LICENSE_FILE": "", - "CARGO_PKG_HOMEPAGE": "", + "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_PKG_AUTHORS": "", "CARGO_PKG_DESCRIPTION": "", + "CARGO_PKG_HOMEPAGE": "", + "CARGO_PKG_LICENSE": "", + "CARGO_PKG_LICENSE_FILE": "", "CARGO_PKG_NAME": "hello-world", - "CARGO_PKG_VERSION_PATCH": "0", - "CARGO": "cargo", + "CARGO_PKG_README": "", "CARGO_PKG_REPOSITORY": "", + "CARGO_PKG_RUST_VERSION": "", + "CARGO_PKG_VERSION": "0.1.0", + "CARGO_PKG_VERSION_MAJOR": "0", "CARGO_PKG_VERSION_MINOR": "1", + "CARGO_PKG_VERSION_PATCH": "0", "CARGO_PKG_VERSION_PRE": "", }, }, @@ -76,27 +77,28 @@ ), cfg_options: CfgOptions( [ - "debug_assertions", "rust_analyzer", ], ), 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": "$CARGO$", "CARGO_CRATE_NAME": "hello_world", - "CARGO_PKG_LICENSE_FILE": "", - "CARGO_PKG_HOMEPAGE": "", + "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_PKG_AUTHORS": "", "CARGO_PKG_DESCRIPTION": "", + "CARGO_PKG_HOMEPAGE": "", + "CARGO_PKG_LICENSE": "", + "CARGO_PKG_LICENSE_FILE": "", "CARGO_PKG_NAME": "hello-world", - "CARGO_PKG_VERSION_PATCH": "0", - "CARGO": "cargo", + "CARGO_PKG_README": "", "CARGO_PKG_REPOSITORY": "", + "CARGO_PKG_RUST_VERSION": "", + "CARGO_PKG_VERSION": "0.1.0", + "CARGO_PKG_VERSION_MAJOR": "0", "CARGO_PKG_VERSION_MINOR": "1", + "CARGO_PKG_VERSION_PATCH": "0", "CARGO_PKG_VERSION_PRE": "", }, }, @@ -142,27 +144,28 @@ ), cfg_options: CfgOptions( [ - "debug_assertions", "rust_analyzer", ], ), 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": "$CARGO$", "CARGO_CRATE_NAME": "an_example", - "CARGO_PKG_LICENSE_FILE": "", - "CARGO_PKG_HOMEPAGE": "", + "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_PKG_AUTHORS": "", "CARGO_PKG_DESCRIPTION": "", + "CARGO_PKG_HOMEPAGE": "", + "CARGO_PKG_LICENSE": "", + "CARGO_PKG_LICENSE_FILE": "", "CARGO_PKG_NAME": "hello-world", - "CARGO_PKG_VERSION_PATCH": "0", - "CARGO": "cargo", + "CARGO_PKG_README": "", "CARGO_PKG_REPOSITORY": "", + "CARGO_PKG_RUST_VERSION": "", + "CARGO_PKG_VERSION": "0.1.0", + "CARGO_PKG_VERSION_MAJOR": "0", "CARGO_PKG_VERSION_MINOR": "1", + "CARGO_PKG_VERSION_PATCH": "0", "CARGO_PKG_VERSION_PRE": "", }, }, @@ -208,27 +211,28 @@ ), cfg_options: CfgOptions( [ - "debug_assertions", "rust_analyzer", ], ), 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": "$CARGO$", "CARGO_CRATE_NAME": "it", - "CARGO_PKG_LICENSE_FILE": "", - "CARGO_PKG_HOMEPAGE": "", + "CARGO_MANIFEST_DIR": "$ROOT$hello-world", + "CARGO_PKG_AUTHORS": "", "CARGO_PKG_DESCRIPTION": "", + "CARGO_PKG_HOMEPAGE": "", + "CARGO_PKG_LICENSE": "", + "CARGO_PKG_LICENSE_FILE": "", "CARGO_PKG_NAME": "hello-world", - "CARGO_PKG_VERSION_PATCH": "0", - "CARGO": "cargo", + "CARGO_PKG_README": "", "CARGO_PKG_REPOSITORY": "", + "CARGO_PKG_RUST_VERSION": "", + "CARGO_PKG_VERSION": "0.1.0", + "CARGO_PKG_VERSION_MAJOR": "0", "CARGO_PKG_VERSION_MINOR": "1", + "CARGO_PKG_VERSION_PATCH": "0", "CARGO_PKG_VERSION_PRE": "", }, }, @@ -274,7 +278,6 @@ ), cfg_options: CfgOptions( [ - "debug_assertions", "feature=default", "feature=std", ], @@ -282,7 +285,6 @@ potential_cfg_options: Some( CfgOptions( [ - "debug_assertions", "feature=align", "feature=const-extern-fn", "feature=default", @@ -295,20 +297,22 @@ ), 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": "$CARGO$", "CARGO_CRATE_NAME": "libc", + "CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98", + "CARGO_PKG_AUTHORS": "The Rust Project Developers", + "CARGO_PKG_DESCRIPTION": "Raw FFI bindings to platform libraries like libc.\n", + "CARGO_PKG_HOMEPAGE": "https://github.com/rust-lang/libc", + "CARGO_PKG_LICENSE": "MIT OR Apache-2.0", "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_README": "README.md", + "CARGO_PKG_REPOSITORY": "https://github.com/rust-lang/libc", + "CARGO_PKG_RUST_VERSION": "", + "CARGO_PKG_VERSION": "0.2.98", + "CARGO_PKG_VERSION_MAJOR": "0", "CARGO_PKG_VERSION_MINOR": "2", + "CARGO_PKG_VERSION_PATCH": "98", "CARGO_PKG_VERSION_PRE": "", }, }, 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 index ccaba963deda..80c913658941 100644 --- 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 @@ -16,6 +16,7 @@ cfg_options: CfgOptions( [ "debug_assertions", + "miri", ], ), potential_cfg_options: None, @@ -53,6 +54,7 @@ cfg_options: CfgOptions( [ "debug_assertions", + "miri", ], ), potential_cfg_options: None, @@ -82,6 +84,7 @@ cfg_options: CfgOptions( [ "debug_assertions", + "miri", ], ), potential_cfg_options: None, @@ -111,6 +114,7 @@ cfg_options: CfgOptions( [ "debug_assertions", + "miri", ], ), potential_cfg_options: None, @@ -140,6 +144,7 @@ cfg_options: CfgOptions( [ "debug_assertions", + "miri", ], ), potential_cfg_options: None, @@ -184,6 +189,7 @@ cfg_options: CfgOptions( [ "debug_assertions", + "miri", ], ), potential_cfg_options: None, @@ -213,6 +219,7 @@ cfg_options: CfgOptions( [ "debug_assertions", + "miri", ], ), potential_cfg_options: None, @@ -299,6 +306,7 @@ cfg_options: CfgOptions( [ "debug_assertions", + "miri", ], ), potential_cfg_options: None, @@ -328,6 +336,7 @@ cfg_options: CfgOptions( [ "debug_assertions", + "miri", ], ), potential_cfg_options: None, @@ -357,6 +366,7 @@ cfg_options: CfgOptions( [ "debug_assertions", + "miri", ], ), potential_cfg_options: None, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml index 6d70124188d9..cd3349899e95 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml @@ -39,11 +39,13 @@ tracing.workspace = true tracing-subscriber.workspace = true tracing-tree.workspace = true triomphe.workspace = true +toml = "0.8.8" nohash-hasher.workspace = true always-assert = "0.2.0" walkdir = "2.3.2" semver.workspace = true memchr = "2.7.1" +indexmap = { workspace = true, features = ["serde"] } cfg.workspace = true flycheck.workspace = true @@ -92,6 +94,7 @@ in-rust-tree = [ "hir/in-rust-tree", "hir-def/in-rust-tree", "hir-ty/in-rust-tree", + "load-cargo/in-rust-tree", ] [lints] 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 815a98980b93..6a5f7b026244 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 @@ -33,53 +33,55 @@ impl CargoTargetSpec { kind: &RunnableKind, cfg: &Option, ) -> (Vec, Vec) { - let mut args = Vec::new(); - let mut extra_args = Vec::new(); + let extra_test_binary_args = snap.config.runnables().extra_test_binary_args; + + let mut cargo_args = Vec::new(); + let mut executable_args = Vec::new(); match kind { RunnableKind::Test { test_id, attr } => { - args.push("test".to_owned()); - extra_args.push(test_id.to_string()); + cargo_args.push("test".to_owned()); + executable_args.push(test_id.to_string()); if let TestId::Path(_) = test_id { - extra_args.push("--exact".to_owned()); + executable_args.push("--exact".to_owned()); } - extra_args.push("--nocapture".to_owned()); + executable_args.extend(extra_test_binary_args); if attr.ignore { - extra_args.push("--ignored".to_owned()); + executable_args.push("--ignored".to_owned()); } } RunnableKind::TestMod { path } => { - args.push("test".to_owned()); - extra_args.push(path.clone()); - extra_args.push("--nocapture".to_owned()); + cargo_args.push("test".to_owned()); + executable_args.push(path.clone()); + executable_args.extend(extra_test_binary_args); } RunnableKind::Bench { test_id } => { - args.push("bench".to_owned()); - extra_args.push(test_id.to_string()); + cargo_args.push("bench".to_owned()); + executable_args.push(test_id.to_string()); if let TestId::Path(_) = test_id { - extra_args.push("--exact".to_owned()); + executable_args.push("--exact".to_owned()); } - extra_args.push("--nocapture".to_owned()); + executable_args.extend(extra_test_binary_args); } RunnableKind::DocTest { test_id } => { - args.push("test".to_owned()); - args.push("--doc".to_owned()); - extra_args.push(test_id.to_string()); - extra_args.push("--nocapture".to_owned()); + cargo_args.push("test".to_owned()); + cargo_args.push("--doc".to_owned()); + executable_args.push(test_id.to_string()); + executable_args.extend(extra_test_binary_args); } RunnableKind::Bin => { let subcommand = match spec { Some(CargoTargetSpec { target_kind: TargetKind::Test, .. }) => "test", _ => "run", }; - args.push(subcommand.to_owned()); + cargo_args.push(subcommand.to_owned()); } } 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); + spec.push_to(&mut cargo_args, kind); (allowed_features, required_features) } else { (Default::default(), Default::default()) @@ -89,10 +91,10 @@ impl CargoTargetSpec { match &cargo_config.features { CargoFeatures::All => { - args.push("--all-features".to_owned()); + cargo_args.push("--all-features".to_owned()); for feature in target_required_features { - args.push("--features".to_owned()); - args.push(feature); + cargo_args.push("--features".to_owned()); + cargo_args.push(feature); } } CargoFeatures::Selected { features, no_default_features } => { @@ -108,16 +110,16 @@ impl CargoTargetSpec { feats.dedup(); for feature in feats { - args.push("--features".to_owned()); - args.push(feature); + cargo_args.push("--features".to_owned()); + cargo_args.push(feature); } if *no_default_features { - args.push("--no-default-features".to_owned()); + cargo_args.push("--no-default-features".to_owned()); } } } - (args, extra_args) + (cargo_args, executable_args) } pub(crate) fn for_file( @@ -208,6 +210,7 @@ fn required_features(cfg_expr: &CfgExpr, features: &mut Vec) { mod tests { use super::*; + use ide::Edition; use mbe::{syntax_node_to_token_tree, DummyTestSpanMap, DUMMY}; use syntax::{ ast::{self, AstNode}, @@ -216,7 +219,7 @@ mod tests { fn check(cfg: &str, expected_features: &[&str]) { let cfg_expr = { - let source_file = ast::SourceFile::parse(cfg).ok().unwrap(); + let source_file = ast::SourceFile::parse(cfg, Edition::CURRENT).ok().unwrap(); let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); let tt = syntax_node_to_token_tree(tt.syntax(), &DummyTestSpanMap, DUMMY); CfgExpr::parse(&tt) 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 fdd77199aa00..a1eea8839ebf 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 @@ -280,7 +280,9 @@ impl flags::AnalysisStats { let mut fail = 0; for &a in adts { let generic_params = db.generic_params(a.into()); - if generic_params.iter().next().is_some() || generic_params.iter_lt().next().is_some() { + if generic_params.iter_type_or_consts().next().is_some() + || generic_params.iter_lt().next().is_some() + { // Data types with generics don't have layout. continue; } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/parse.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/parse.rs index 757f2dd70cad..ead4d706e655 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/parse.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/parse.rs @@ -1,4 +1,5 @@ //! Read Rust code on stdin, print syntax tree on stdout. +use ide::Edition; use syntax::{AstNode, SourceFile}; use crate::cli::{flags, read_stdin}; @@ -7,7 +8,7 @@ impl flags::Parse { pub fn run(self) -> anyhow::Result<()> { let _p = tracing::span!(tracing::Level::INFO, "parsing").entered(); let text = read_stdin()?; - let file = SourceFile::parse(&text).tree(); + let file = SourceFile::parse(&text, Edition::CURRENT).tree(); if !self.no_dump { println!("{:#?}", file.syntax()); } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/progress_report.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/progress_report.rs index b23373009973..6964977840a0 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/progress_report.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/progress_report.rs @@ -92,7 +92,7 @@ impl<'a> ProgressReport<'a> { let _ = io::stdout().write(output.as_bytes()); let _ = io::stdout().flush(); - self.text = text.to_owned(); + text.clone_into(&mut self.text); } fn set_value(&mut self, value: f32) { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs index eeec13a14bee..2f9394d0ee1b 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs @@ -75,12 +75,14 @@ impl Tester { &cargo_config.extra_env, ); - let workspace = ProjectWorkspace::DetachedFiles { - files: vec![tmp_file], + let workspace = ProjectWorkspace::DetachedFile { + file: tmp_file, sysroot, rustc_cfg: vec![], toolchain: None, target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())), + cfg_overrides: Default::default(), + cargo_script: None, }; let load_cargo_config = LoadCargoConfig { load_out_dirs_from_check: false, 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 7475a8e6e6d9..e956791d9df7 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -6,21 +6,21 @@ //! Of particular interest is the `feature_flags` hash map: while other fields //! configure the server itself, feature flags are passed into analysis, and //! tweak things like automatic insertion of `()` in completions. - use std::{fmt, iter, ops::Not}; use cfg::{CfgAtom, CfgDiff}; -use flycheck::FlycheckConfig; +use flycheck::{CargoOptions, FlycheckConfig}; use ide::{ AssistConfig, CallableSnippets, CompletionConfig, DiagnosticsConfig, ExprFillDefaultMode, HighlightConfig, HighlightRelatedConfig, HoverConfig, HoverDocFormat, InlayFieldsToResolve, InlayHintsConfig, JoinLinesConfig, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, - Snippet, SnippetScope, + Snippet, SnippetScope, SourceRootId, }; use ide_db::{ imports::insert_use::{ImportGranularity, InsertUseConfig, PrefixKind}, SnippetCap, }; +use indexmap::IndexMap; use itertools::Itertools; use lsp_types::{ClientCapabilities, MarkupKind}; use paths::{Utf8Path, Utf8PathBuf}; @@ -29,7 +29,7 @@ use project_model::{ }; use rustc_hash::{FxHashMap, FxHashSet}; use semver::Version; -use serde::{de::DeserializeOwned, Deserialize}; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; use stdx::format_to_acc; use vfs::{AbsPath, AbsPathBuf}; @@ -59,36 +59,45 @@ mod patch_old_style; // To deprecate an option by replacing it with another name use `new_name | `old_name` so that we keep // parsing the old name. config_data! { - struct ConfigData { + /// Configs that apply on a workspace-wide scope. There are 3 levels on which a global configuration can be configured + /// + /// 1. `rust-analyzer.toml` file under user's config directory (e.g ~/.config/rust-analyzer.toml) + /// 2. Client's own configurations (e.g `settings.json` on VS Code) + /// 3. `rust-analyzer.toml` file located at the workspace root + /// + /// A config is searched for by traversing a "config tree" in a bottom up fashion. It is chosen by the nearest first principle. + global: struct GlobalDefaultConfigData <- GlobalConfigInput -> { /// Whether to insert #[must_use] when generating `as_` methods /// for enum variants. - assist_emitMustUse: bool = "false", + assist_emitMustUse: bool = false, /// Placeholder expression to use for missing expressions in assists. - assist_expressionFillDefault: ExprFillDefaultDef = "\"todo\"", + assist_expressionFillDefault: ExprFillDefaultDef = ExprFillDefaultDef::Todo, /// Warm up caches on project load. - cachePriming_enable: bool = "true", + cachePriming_enable: bool = true, /// How many worker threads to handle priming caches. The default `0` means to pick automatically. - cachePriming_numThreads: ParallelCachePrimingNumThreads = "0", + cachePriming_numThreads: ParallelCachePrimingNumThreads = 0u8, + /// Pass `--all-targets` to cargo invocation. + cargo_allTargets: bool = true, /// Automatically refresh project info via `cargo metadata` on /// `Cargo.toml` or `.cargo/config.toml` changes. - cargo_autoreload: bool = "true", + pub(crate) cargo_autoreload: bool = true, /// Run build scripts (`build.rs`) for more precise code analysis. - cargo_buildScripts_enable: bool = "true", + cargo_buildScripts_enable: bool = true, /// Specifies the working directory for running build scripts. /// - "workspace": run build scripts for a workspace in the workspace's root directory. /// This is incompatible with `#rust-analyzer.cargo.buildScripts.invocationStrategy#` set to `once`. /// - "root": run build scripts in the project's root directory. /// This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#` /// is set. - cargo_buildScripts_invocationLocation: InvocationLocation = "\"workspace\"", + cargo_buildScripts_invocationLocation: InvocationLocation = InvocationLocation::Workspace, /// Specifies the invocation strategy to use when running the build scripts command. /// If `per_workspace` is set, the command will be executed for each workspace. /// If `once` is set, the command will be executed once. /// This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#` /// is set. - cargo_buildScripts_invocationStrategy: InvocationStrategy = "\"per_workspace\"", + cargo_buildScripts_invocationStrategy: InvocationStrategy = InvocationStrategy::PerWorkspace, /// Override the command rust-analyzer uses to run build scripts and /// build procedural macros. The command is required to output json /// and should therefore include `--message-format=json` or a similar @@ -107,80 +116,84 @@ config_data! { /// cargo check --quiet --workspace --message-format=json --all-targets /// ``` /// . - cargo_buildScripts_overrideCommand: Option> = "null", + cargo_buildScripts_overrideCommand: Option> = None, /// Rerun proc-macros building/build-scripts running when proc-macro /// or build-script sources change and are saved. - cargo_buildScripts_rebuildOnSave: bool = "true", + cargo_buildScripts_rebuildOnSave: bool = true, /// Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to /// avoid checking unnecessary things. - cargo_buildScripts_useRustcWrapper: bool = "true", + cargo_buildScripts_useRustcWrapper: bool = true, /// List of cfg options to enable with the given values. - cargo_cfgs: FxHashMap = "{}", + cargo_cfgs: FxHashMap> = { + let mut m = FxHashMap::default(); + m.insert("debug_assertions".to_owned(), None); + m.insert("miri".to_owned(), None); + m + }, /// Extra arguments that are passed to every cargo invocation. - cargo_extraArgs: Vec = "[]", + cargo_extraArgs: Vec = vec![], /// Extra environment variables that will be set when running cargo, rustc /// or other commands within the workspace. Useful for setting RUSTFLAGS. - cargo_extraEnv: FxHashMap = "{}", + cargo_extraEnv: FxHashMap = FxHashMap::default(), /// List of features to activate. /// /// Set this to `"all"` to pass `--all-features` to cargo. - cargo_features: CargoFeaturesDef = "[]", + cargo_features: CargoFeaturesDef = CargoFeaturesDef::Selected(vec![]), /// Whether to pass `--no-default-features` to cargo. - cargo_noDefaultFeatures: bool = "false", + cargo_noDefaultFeatures: bool = false, /// Relative path to the sysroot, or "discover" to try to automatically find it via /// "rustc --print sysroot". /// /// Unsetting this disables sysroot loading. /// /// This option does not take effect until rust-analyzer is restarted. - cargo_sysroot: Option = "\"discover\"", + cargo_sysroot: Option = Some("discover".to_owned()), /// Whether to run cargo metadata on the sysroot library allowing rust-analyzer to analyze /// third-party dependencies of the standard libraries. /// /// This will cause `cargo` to create a lockfile in your sysroot directory. rust-analyzer /// will attempt to clean up afterwards, but nevertheless requires the location to be /// writable to. - cargo_sysrootQueryMetadata: bool = "false", + cargo_sysrootQueryMetadata: bool = false, /// Relative path to the sysroot library sources. If left unset, this will default to /// `{cargo.sysroot}/lib/rustlib/src/rust/library`. /// /// This option does not take effect until rust-analyzer is restarted. - cargo_sysrootSrc: Option = "null", + cargo_sysrootSrc: Option = None, /// Compilation target override (target triple). // FIXME(@poliorcetics): move to multiple targets here too, but this will need more work // than `checkOnSave_target` - cargo_target: Option = "null", + cargo_target: Option = None, /// Optional path to a rust-analyzer specific target directory. /// This prevents rust-analyzer's `cargo check` and initial build-script and proc-macro /// building from locking the `Cargo.lock` at the expense of duplicating build artifacts. /// /// Set to `true` to use a subdirectory of the existing target directory or /// set to a path relative to the workspace to use that path. - cargo_targetDir | rust_analyzerTargetDir: Option = "null", - /// Unsets the implicit `#[cfg(test)]` for the specified crates. - cargo_unsetTest: Vec = "[\"core\"]", + cargo_targetDir | rust_analyzerTargetDir: Option = None, /// Run the check command for diagnostics on save. - checkOnSave | checkOnSave_enable: bool = "true", + checkOnSave | checkOnSave_enable: bool = true, - /// Check all targets and tests (`--all-targets`). - check_allTargets | checkOnSave_allTargets: bool = "true", + /// Check all targets and tests (`--all-targets`). Defaults to + /// `#rust-analyzer.cargo.allTargets#`. + check_allTargets | checkOnSave_allTargets: Option = None, /// Cargo command to use for `cargo check`. - check_command | checkOnSave_command: String = "\"check\"", + check_command | checkOnSave_command: String = "check".to_owned(), /// Extra arguments for `cargo check`. - check_extraArgs | checkOnSave_extraArgs: Vec = "[]", + check_extraArgs | checkOnSave_extraArgs: Vec = vec![], /// Extra environment variables that will be set when running `cargo check`. /// Extends `#rust-analyzer.cargo.extraEnv#`. - check_extraEnv | checkOnSave_extraEnv: FxHashMap = "{}", + check_extraEnv | checkOnSave_extraEnv: FxHashMap = FxHashMap::default(), /// List of features to activate. Defaults to /// `#rust-analyzer.cargo.features#`. /// /// Set to `"all"` to pass `--all-features` to Cargo. - check_features | checkOnSave_features: Option = "null", + check_features | checkOnSave_features: Option = None, /// List of `cargo check` (or other command specified in `check.command`) diagnostics to ignore. /// /// For example for `cargo check`: `dead_code`, `unused_imports`, `unused_variables`,... - check_ignore: FxHashSet = "[]", + check_ignore: FxHashSet = FxHashSet::default(), /// Specifies the working directory for running checks. /// - "workspace": run checks for workspaces in the corresponding workspaces' root directories. // FIXME: Ideally we would support this in some way @@ -188,16 +201,16 @@ config_data! { /// - "root": run checks in the project's root directory. /// This config only has an effect when `#rust-analyzer.check.overrideCommand#` /// is set. - check_invocationLocation | checkOnSave_invocationLocation: InvocationLocation = "\"workspace\"", + check_invocationLocation | checkOnSave_invocationLocation: InvocationLocation = InvocationLocation::Workspace, /// Specifies the invocation strategy to use when running the check command. /// If `per_workspace` is set, the command will be executed for each workspace. /// If `once` is set, the command will be executed once. /// This config only has an effect when `#rust-analyzer.check.overrideCommand#` /// is set. - check_invocationStrategy | checkOnSave_invocationStrategy: InvocationStrategy = "\"per_workspace\"", + check_invocationStrategy | checkOnSave_invocationStrategy: InvocationStrategy = InvocationStrategy::PerWorkspace, /// Whether to pass `--no-default-features` to Cargo. Defaults to /// `#rust-analyzer.cargo.noDefaultFeatures#`. - check_noDefaultFeatures | checkOnSave_noDefaultFeatures: Option = "null", + check_noDefaultFeatures | checkOnSave_noDefaultFeatures: Option = None, /// Override the command rust-analyzer uses instead of `cargo check` for /// diagnostics on save. The command is required to output json and /// should therefore include `--message-format=json` or a similar option @@ -225,37 +238,238 @@ config_data! { /// cargo check --workspace --message-format=json --all-targets /// ``` /// . - check_overrideCommand | checkOnSave_overrideCommand: Option> = "null", + check_overrideCommand | checkOnSave_overrideCommand: Option> = None, /// Check for specific targets. Defaults to `#rust-analyzer.cargo.target#` if empty. /// /// Can be a single target, e.g. `"x86_64-unknown-linux-gnu"` or a list of targets, e.g. /// `["aarch64-apple-darwin", "x86_64-apple-darwin"]`. /// /// Aliased as `"checkOnSave.targets"`. - check_targets | checkOnSave_targets | checkOnSave_target: Option = "null", + check_targets | checkOnSave_targets | checkOnSave_target: Option = None, /// Whether `--workspace` should be passed to `cargo check`. /// If false, `-p ` will be passed instead. - check_workspace: bool = "true", + check_workspace: bool = true, + /// List of rust-analyzer diagnostics to disable. + diagnostics_disabled: FxHashSet = FxHashSet::default(), + /// Whether to show native rust-analyzer diagnostics. + diagnostics_enable: bool = true, + /// Whether to show experimental rust-analyzer diagnostics that might + /// have more false positives than usual. + diagnostics_experimental_enable: bool = false, + /// Map of prefixes to be substituted when parsing diagnostic file paths. + /// This should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`. + diagnostics_remapPrefix: FxHashMap = FxHashMap::default(), + /// Whether to run additional style lints. + diagnostics_styleLints_enable: bool = false, + /// List of warnings that should be displayed with hint severity. + /// + /// The warnings will be indicated by faded text or three dots in code + /// and will not show up in the `Problems Panel`. + diagnostics_warningsAsHint: Vec = vec![], + /// List of warnings that should be displayed with info severity. + /// + /// The warnings will be indicated by a blue squiggly underline in code + /// and a blue icon in the `Problems Panel`. + diagnostics_warningsAsInfo: Vec = vec![], + /// These directories will be ignored by rust-analyzer. They are + /// relative to the workspace root, and globs are not supported. You may + /// also need to add the folders to Code's `files.watcherExclude`. + files_excludeDirs: Vec = vec![], + /// Controls file watching implementation. + files_watcher: FilesWatcherDef = FilesWatcherDef::Client, + + /// Whether to show `Debug` action. Only applies when + /// `#rust-analyzer.hover.actions.enable#` is set. + hover_actions_debug_enable: bool = true, + /// Whether to show HoverActions in Rust files. + hover_actions_enable: bool = true, + /// Whether to show `Go to Type Definition` action. Only applies when + /// `#rust-analyzer.hover.actions.enable#` is set. + hover_actions_gotoTypeDef_enable: bool = true, + /// Whether to show `Implementations` action. Only applies when + /// `#rust-analyzer.hover.actions.enable#` is set. + hover_actions_implementations_enable: bool = true, + /// Whether to show `References` action. Only applies when + /// `#rust-analyzer.hover.actions.enable#` is set. + hover_actions_references_enable: bool = false, + /// Whether to show `Run` action. Only applies when + /// `#rust-analyzer.hover.actions.enable#` is set. + hover_actions_run_enable: bool = true, + + /// Whether to show documentation on hover. + hover_documentation_enable: bool = true, + /// Whether to show keyword hover popups. Only applies when + /// `#rust-analyzer.hover.documentation.enable#` is set. + hover_documentation_keywords_enable: bool = true, + /// Use markdown syntax for links on hover. + hover_links_enable: bool = true, + /// How to render the align information in a memory layout hover. + hover_memoryLayout_alignment: Option = Some(MemoryLayoutHoverRenderKindDef::Hexadecimal), + /// Whether to show memory layout data on hover. + hover_memoryLayout_enable: bool = true, + /// How to render the niche information in a memory layout hover. + hover_memoryLayout_niches: Option = Some(false), + /// How to render the offset information in a memory layout hover. + hover_memoryLayout_offset: Option = Some(MemoryLayoutHoverRenderKindDef::Hexadecimal), + /// How to render the size information in a memory layout hover. + hover_memoryLayout_size: Option = Some(MemoryLayoutHoverRenderKindDef::Both), + + /// How many fields of a struct to display when hovering a struct. + hover_show_structFields: Option = None, + /// How many associated items of a trait to display when hovering a trait. + hover_show_traitAssocItems: Option = None, + + /// Enables the experimental support for interpreting tests. + interpret_tests: bool = false, + + /// Whether to show `Debug` lens. Only applies when + /// `#rust-analyzer.lens.enable#` is set. + lens_debug_enable: bool = true, + /// Whether to show CodeLens in Rust files. + lens_enable: bool = true, + /// Internal config: use custom client-side commands even when the + /// client doesn't set the corresponding capability. + lens_forceCustomCommands: bool = true, + /// Whether to show `Implementations` lens. Only applies when + /// `#rust-analyzer.lens.enable#` is set. + lens_implementations_enable: bool = true, + /// Where to render annotations. + lens_location: AnnotationLocation = AnnotationLocation::AboveName, + /// Whether to show `References` lens for Struct, Enum, and Union. + /// Only applies when `#rust-analyzer.lens.enable#` is set. + lens_references_adt_enable: bool = false, + /// Whether to show `References` lens for Enum Variants. + /// Only applies when `#rust-analyzer.lens.enable#` is set. + lens_references_enumVariant_enable: bool = false, + /// Whether to show `Method References` lens. Only applies when + /// `#rust-analyzer.lens.enable#` is set. + lens_references_method_enable: bool = false, + /// Whether to show `References` lens for Trait. + /// Only applies when `#rust-analyzer.lens.enable#` is set. + lens_references_trait_enable: bool = false, + /// Whether to show `Run` lens. Only applies when + /// `#rust-analyzer.lens.enable#` is set. + lens_run_enable: bool = true, + + /// Disable project auto-discovery in favor of explicitly specified set + /// of projects. + /// + /// Elements must be paths pointing to `Cargo.toml`, + /// `rust-project.json`, or JSON objects in `rust-project.json` format. + linkedProjects: Vec = vec![], + + /// Number of syntax trees rust-analyzer keeps in memory. Defaults to 128. + lru_capacity: Option = None, + /// Sets the LRU capacity of the specified queries. + lru_query_capacities: FxHashMap, usize> = FxHashMap::default(), + + /// Whether to show `can't find Cargo.toml` error message. + notifications_cargoTomlNotFound: bool = true, + + /// Whether to send an UnindexedProject notification to the client. + notifications_unindexedProject: bool = false, + + /// How many worker threads in the main loop. The default `null` means to pick automatically. + numThreads: Option = None, + + /// Expand attribute macros. Requires `#rust-analyzer.procMacro.enable#` to be set. + procMacro_attributes_enable: bool = true, + /// Enable support for procedural macros, implies `#rust-analyzer.cargo.buildScripts.enable#`. + procMacro_enable: bool = true, + /// These proc-macros will be ignored when trying to expand them. + /// + /// This config takes a map of crate names with the exported proc-macro names to ignore as values. + procMacro_ignored: FxHashMap, Box<[Box]>> = FxHashMap::default(), + /// Internal config, path to proc-macro server executable. + procMacro_server: Option = None, + + /// Exclude imports from find-all-references. + references_excludeImports: bool = false, + + /// Exclude tests from find-all-references. + references_excludeTests: bool = false, + + /// Command to be executed instead of 'cargo' for runnables. + runnables_command: Option = None, + /// Additional arguments to be passed to cargo for runnables such as + /// tests or binaries. For example, it may be `--release`. + runnables_extraArgs: Vec = vec![], + /// Additional arguments to be passed through Cargo to launched tests, benchmarks, or + /// doc-tests. + /// + /// Unless the launched target uses a + /// [custom test harness](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#the-harness-field), + /// they will end up being interpreted as options to + /// [`rustc`’s built-in test harness (“libtest”)](https://doc.rust-lang.org/rustc/tests/index.html#cli-arguments). + runnables_extraTestBinaryArgs: Vec = vec!["--show-output".to_owned()], + + /// Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private + /// projects, or "discover" to try to automatically find it if the `rustc-dev` component + /// is installed. + /// + /// Any project which uses rust-analyzer with the rustcPrivate + /// crates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it. + /// + /// This option does not take effect until rust-analyzer is restarted. + rustc_source: Option = None, + + /// Additional arguments to `rustfmt`. + rustfmt_extraArgs: Vec = vec![], + /// Advanced option, fully override the command rust-analyzer uses for + /// formatting. This should be the equivalent of `rustfmt` here, and + /// not that of `cargo fmt`. The file contents will be passed on the + /// standard input and the formatted result will be read from the + /// standard output. + rustfmt_overrideCommand: Option> = None, + /// Enables the use of rustfmt's unstable range formatting command for the + /// `textDocument/rangeFormatting` request. The rustfmt option is unstable and only + /// available on a nightly build. + rustfmt_rangeFormatting_enable: bool = false, + + + /// Show full signature of the callable. Only shows parameters if disabled. + signatureInfo_detail: SignatureDetail = SignatureDetail::Full, + /// Show documentation. + signatureInfo_documentation_enable: bool = true, + + /// Whether to insert closing angle brackets when typing an opening angle bracket of a generic argument list. + typing_autoClosingAngleBrackets_enable: bool = false, + + /// Workspace symbol search kind. + workspace_symbol_search_kind: WorkspaceSymbolSearchKindDef = WorkspaceSymbolSearchKindDef::OnlyTypes, + /// Limits the number of items returned from a workspace symbol search (Defaults to 128). + /// Some clients like vs-code issue new searches on result filtering and don't require all results to be returned in the initial search. + /// Other clients requires all results upfront and might require a higher limit. + workspace_symbol_search_limit: usize = 128, + /// Workspace symbol search scope. + workspace_symbol_search_scope: WorkspaceSymbolSearchScopeDef = WorkspaceSymbolSearchScopeDef::Workspace, + } +} + +config_data! { + /// Local configurations can be overridden for every crate by placing a `rust-analyzer.toml` on crate root. + /// A config is searched for by traversing a "config tree" in a bottom up fashion. It is chosen by the nearest first principle. + local: struct LocalDefaultConfigData <- LocalConfigInput -> { /// Toggles the additional completions that automatically add imports when completed. /// Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled. - completion_autoimport_enable: bool = "true", + completion_autoimport_enable: bool = true, /// Toggles the additional completions that automatically show method calls and field accesses /// with `self` prefixed to them when inside a method. - completion_autoself_enable: bool = "true", + completion_autoself_enable: bool = true, /// Whether to add parenthesis and argument snippets when completing function. - completion_callable_snippets: CallableCompletionDef = "\"fill_arguments\"", + completion_callable_snippets: CallableCompletionDef = CallableCompletionDef::FillArguments, /// Whether to show full function/method signatures in completion docs. - completion_fullFunctionSignatures_enable: bool = "false", + completion_fullFunctionSignatures_enable: bool = false, /// Maximum number of completions to return. If `None`, the limit is infinite. - completion_limit: Option = "null", + completion_limit: Option = None, /// Whether to show postfix snippets like `dbg`, `if`, `not`, etc. - completion_postfix_enable: bool = "true", + completion_postfix_enable: bool = true, /// Enables completions of private items and fields that are defined in the current workspace even if they are not visible at the current position. - completion_privateEditable_enable: bool = "false", + completion_privateEditable_enable: bool = false, /// Custom completion snippets. - // NOTE: Keep this list in sync with the feature docs of user snippets. - completion_snippets_custom: FxHashMap = r#"{ + // NOTE: we use IndexMap for deterministic serialization ordering + completion_snippets_custom: IndexMap = serde_json::from_str(r#"{ "Arc::new": { "postfix": "arc", "body": "Arc::new(${receiver})", @@ -295,323 +509,139 @@ config_data! { "description": "Wrap the expression in an `Option::Some`", "scope": "expr" } - }"#, + }"#).unwrap(), /// Whether to enable term search based snippets like `Some(foo.bar().baz())`. - completion_termSearch_enable: bool = "false", - - /// List of rust-analyzer diagnostics to disable. - diagnostics_disabled: FxHashSet = "[]", - /// Whether to show native rust-analyzer diagnostics. - diagnostics_enable: bool = "true", - /// Whether to show experimental rust-analyzer diagnostics that might - /// have more false positives than usual. - diagnostics_experimental_enable: bool = "false", - /// Map of prefixes to be substituted when parsing diagnostic file paths. - /// This should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`. - diagnostics_remapPrefix: FxHashMap = "{}", - /// Whether to run additional style lints. - diagnostics_styleLints_enable: bool = "false", - /// List of warnings that should be displayed with hint severity. - /// - /// The warnings will be indicated by faded text or three dots in code - /// and will not show up in the `Problems Panel`. - diagnostics_warningsAsHint: Vec = "[]", - /// List of warnings that should be displayed with info severity. - /// - /// The warnings will be indicated by a blue squiggly underline in code - /// and a blue icon in the `Problems Panel`. - diagnostics_warningsAsInfo: Vec = "[]", - /// These directories will be ignored by rust-analyzer. They are - /// relative to the workspace root, and globs are not supported. You may - /// also need to add the folders to Code's `files.watcherExclude`. - files_excludeDirs: Vec = "[]", - /// Controls file watching implementation. - files_watcher: FilesWatcherDef = "\"client\"", + completion_termSearch_enable: bool = false, /// Enables highlighting of related references while the cursor is on `break`, `loop`, `while`, or `for` keywords. - highlightRelated_breakPoints_enable: bool = "true", + 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", + 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", + highlightRelated_exitPoints_enable: bool = true, /// Enables highlighting of related references while the cursor is on any identifier. - highlightRelated_references_enable: bool = "true", + highlightRelated_references_enable: bool = true, /// Enables highlighting of all break points for a loop or block context while the cursor is on any `async` or `await` keywords. - highlightRelated_yieldPoints_enable: bool = "true", - - /// Whether to show `Debug` action. Only applies when - /// `#rust-analyzer.hover.actions.enable#` is set. - hover_actions_debug_enable: bool = "true", - /// Whether to show HoverActions in Rust files. - hover_actions_enable: bool = "true", - /// Whether to show `Go to Type Definition` action. Only applies when - /// `#rust-analyzer.hover.actions.enable#` is set. - hover_actions_gotoTypeDef_enable: bool = "true", - /// Whether to show `Implementations` action. Only applies when - /// `#rust-analyzer.hover.actions.enable#` is set. - hover_actions_implementations_enable: bool = "true", - /// Whether to show `References` action. Only applies when - /// `#rust-analyzer.hover.actions.enable#` is set. - hover_actions_references_enable: bool = "false", - /// Whether to show `Run` action. Only applies when - /// `#rust-analyzer.hover.actions.enable#` is set. - hover_actions_run_enable: bool = "true", - - /// Whether to show documentation on hover. - hover_documentation_enable: bool = "true", - /// Whether to show keyword hover popups. Only applies when - /// `#rust-analyzer.hover.documentation.enable#` is set. - hover_documentation_keywords_enable: bool = "true", - /// Use markdown syntax for links on hover. - hover_links_enable: bool = "true", - /// How to render the align information in a memory layout hover. - hover_memoryLayout_alignment: Option = "\"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\"", - - /// How many fields of a struct to display when hovering a struct. - hover_show_structFields: Option = "null", - /// How many associated items of a trait to display when hovering a trait. - hover_show_traitAssocItems: Option = "null", + highlightRelated_yieldPoints_enable: bool = true, /// 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", + imports_granularity_enforce: bool = false, /// How imports should be grouped into use statements. - imports_granularity_group: ImportGranularityDef = "\"crate\"", + imports_granularity_group: ImportGranularityDef = ImportGranularityDef::Crate, /// Group inserted imports by the https://rust-analyzer.github.io/manual.html#auto-import[following order]. Groups are separated by newlines. - imports_group_enable: bool = "true", + imports_group_enable: bool = true, /// Whether to allow import insertion to merge new imports into single path glob imports like `use std::fmt::*;`. - imports_merge_glob: bool = "true", + imports_merge_glob: bool = true, /// Prefer to unconditionally use imports of the core and alloc crate, over the std crate. - imports_preferNoStd | imports_prefer_no_std: bool = "false", - /// Whether to prefer import paths containing a `prelude` module. - imports_preferPrelude: bool = "false", + imports_preferNoStd | imports_prefer_no_std: bool = false, + /// Whether to prefer import paths containing a `prelude` module. + imports_preferPrelude: bool = false, /// The path structure for newly inserted paths to use. - imports_prefix: ImportPrefixDef = "\"plain\"", + imports_prefix: ImportPrefixDef = ImportPrefixDef::Plain, + /// Whether to show inlay type hints for binding modes. - inlayHints_bindingModeHints_enable: bool = "false", + inlayHints_bindingModeHints_enable: bool = false, /// Whether to show inlay type hints for method chains. - inlayHints_chainingHints_enable: bool = "true", + inlayHints_chainingHints_enable: bool = true, /// Whether to show inlay hints after a closing `}` to indicate what item it belongs to. - inlayHints_closingBraceHints_enable: bool = "true", + inlayHints_closingBraceHints_enable: bool = true, /// Minimum number of lines required before the `}` until the hint is shown (set to 0 or 1 /// to always show them). - inlayHints_closingBraceHints_minLines: usize = "25", + inlayHints_closingBraceHints_minLines: usize = 25, /// Whether to show inlay hints for closure captures. - inlayHints_closureCaptureHints_enable: bool = "false", + inlayHints_closureCaptureHints_enable: bool = false, /// Whether to show inlay type hints for return types of closures. - inlayHints_closureReturnTypeHints_enable: ClosureReturnTypeHintsDef = "\"never\"", + inlayHints_closureReturnTypeHints_enable: ClosureReturnTypeHintsDef = ClosureReturnTypeHintsDef::Never, /// Closure notation in type and chaining inlay hints. - inlayHints_closureStyle: ClosureStyle = "\"impl_fn\"", + inlayHints_closureStyle: ClosureStyle = ClosureStyle::ImplFn, /// Whether to show enum variant discriminant hints. - inlayHints_discriminantHints_enable: DiscriminantHintsDef = "\"never\"", + inlayHints_discriminantHints_enable: DiscriminantHintsDef = DiscriminantHintsDef::Never, /// Whether to show inlay hints for type adjustments. - inlayHints_expressionAdjustmentHints_enable: AdjustmentHintsDef = "\"never\"", + inlayHints_expressionAdjustmentHints_enable: AdjustmentHintsDef = AdjustmentHintsDef::Never, /// Whether to hide inlay hints for type adjustments outside of `unsafe` blocks. - inlayHints_expressionAdjustmentHints_hideOutsideUnsafe: bool = "false", + inlayHints_expressionAdjustmentHints_hideOutsideUnsafe: bool = false, /// Whether to show inlay hints as postfix ops (`.*` instead of `*`, etc). - inlayHints_expressionAdjustmentHints_mode: AdjustmentHintsModeDef = "\"prefix\"", + inlayHints_expressionAdjustmentHints_mode: AdjustmentHintsModeDef = AdjustmentHintsModeDef::Prefix, /// Whether to show implicit drop hints. - inlayHints_implicitDrops_enable: bool = "false", + inlayHints_implicitDrops_enable: bool = false, /// Whether to show inlay type hints for elided lifetimes in function signatures. - inlayHints_lifetimeElisionHints_enable: LifetimeElisionDef = "\"never\"", + inlayHints_lifetimeElisionHints_enable: LifetimeElisionDef = LifetimeElisionDef::Never, /// Whether to prefer using parameter names as the name for elided lifetime hints if possible. - inlayHints_lifetimeElisionHints_useParameterNames: bool = "false", + inlayHints_lifetimeElisionHints_useParameterNames: bool = false, /// Maximum length for inlay hints. Set to null to have an unlimited length. - inlayHints_maxLength: Option = "25", + inlayHints_maxLength: Option = Some(25), /// Whether to show function parameter name inlay hints at the call /// site. - inlayHints_parameterHints_enable: bool = "true", + inlayHints_parameterHints_enable: bool = true, /// Whether to show exclusive range inlay hints. - inlayHints_rangeExclusiveHints_enable: bool = "false", + inlayHints_rangeExclusiveHints_enable: bool = false, /// Whether to show inlay hints for compiler inserted reborrows. /// This setting is deprecated in favor of #rust-analyzer.inlayHints.expressionAdjustmentHints.enable#. - inlayHints_reborrowHints_enable: ReborrowHintsDef = "\"never\"", + inlayHints_reborrowHints_enable: ReborrowHintsDef = ReborrowHintsDef::Never, /// Whether to render leading colons for type hints, and trailing colons for parameter hints. - inlayHints_renderColons: bool = "true", + inlayHints_renderColons: bool = true, /// Whether to show inlay type hints for variables. - inlayHints_typeHints_enable: bool = "true", + inlayHints_typeHints_enable: bool = true, /// Whether to hide inlay type hints for `let` statements that initialize to a closure. /// Only applies to closures with blocks, same as `#rust-analyzer.inlayHints.closureReturnTypeHints.enable#`. - inlayHints_typeHints_hideClosureInitialization: bool = "false", + inlayHints_typeHints_hideClosureInitialization: bool = false, /// Whether to hide inlay type hints for constructors. - inlayHints_typeHints_hideNamedConstructor: bool = "false", - /// Enables the experimental support for interpreting tests. - interpret_tests: bool = "false", + inlayHints_typeHints_hideNamedConstructor: bool = false, + /// Join lines merges consecutive declaration and initialization of an assignment. - joinLines_joinAssignments: bool = "true", + joinLines_joinAssignments: bool = true, /// Join lines inserts else between consecutive ifs. - joinLines_joinElseIf: bool = "true", + joinLines_joinElseIf: bool = true, /// Join lines removes trailing commas. - joinLines_removeTrailingComma: bool = "true", + joinLines_removeTrailingComma: bool = true, /// Join lines unwraps trivial blocks. - joinLines_unwrapTrivialBlock: bool = "true", - - - /// Whether to show `Debug` lens. Only applies when - /// `#rust-analyzer.lens.enable#` is set. - lens_debug_enable: bool = "true", - /// Whether to show CodeLens in Rust files. - lens_enable: bool = "true", - /// Internal config: use custom client-side commands even when the - /// client doesn't set the corresponding capability. - lens_forceCustomCommands: bool = "true", - /// Whether to show `Implementations` lens. Only applies when - /// `#rust-analyzer.lens.enable#` is set. - lens_implementations_enable: bool = "true", - /// Where to render annotations. - lens_location: AnnotationLocation = "\"above_name\"", - /// Whether to show `References` lens for Struct, Enum, and Union. - /// Only applies when `#rust-analyzer.lens.enable#` is set. - lens_references_adt_enable: bool = "false", - /// Whether to show `References` lens for Enum Variants. - /// Only applies when `#rust-analyzer.lens.enable#` is set. - lens_references_enumVariant_enable: bool = "false", - /// Whether to show `Method References` lens. Only applies when - /// `#rust-analyzer.lens.enable#` is set. - lens_references_method_enable: bool = "false", - /// Whether to show `References` lens for Trait. - /// Only applies when `#rust-analyzer.lens.enable#` is set. - lens_references_trait_enable: bool = "false", - /// Whether to show `Run` lens. Only applies when - /// `#rust-analyzer.lens.enable#` is set. - lens_run_enable: bool = "true", - - /// Disable project auto-discovery in favor of explicitly specified set - /// of projects. - /// - /// Elements must be paths pointing to `Cargo.toml`, - /// `rust-project.json`, or JSON objects in `rust-project.json` format. - linkedProjects: Vec = "[]", - - /// 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", - - /// Whether to send an UnindexedProject notification to the client. - notifications_unindexedProject: bool = "false", - - /// How many worker threads in the main loop. The default `null` means to pick automatically. - numThreads: Option = "null", - - /// Expand attribute macros. Requires `#rust-analyzer.procMacro.enable#` to be set. - procMacro_attributes_enable: bool = "true", - /// Enable support for procedural macros, implies `#rust-analyzer.cargo.buildScripts.enable#`. - procMacro_enable: bool = "true", - /// These proc-macros will be ignored when trying to expand them. - /// - /// This config takes a map of crate names with the exported proc-macro names to ignore as values. - procMacro_ignored: FxHashMap, Box<[Box]>> = "{}", - /// Internal config, path to proc-macro server executable. - procMacro_server: Option = "null", - - /// Exclude imports from find-all-references. - references_excludeImports: bool = "false", - - /// Exclude tests from find-all-references. - references_excludeTests: bool = "false", - - /// Command to be executed instead of 'cargo' for runnables. - runnables_command: Option = "null", - /// Additional arguments to be passed to cargo for runnables such as - /// tests or binaries. For example, it may be `--release`. - runnables_extraArgs: Vec = "[]", - - /// Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private - /// projects, or "discover" to try to automatically find it if the `rustc-dev` component - /// is installed. - /// - /// Any project which uses rust-analyzer with the rustcPrivate - /// crates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it. - /// - /// This option does not take effect until rust-analyzer is restarted. - rustc_source: Option = "null", - - /// Additional arguments to `rustfmt`. - rustfmt_extraArgs: Vec = "[]", - /// Advanced option, fully override the command rust-analyzer uses for - /// formatting. This should be the equivalent of `rustfmt` here, and - /// not that of `cargo fmt`. The file contents will be passed on the - /// standard input and the formatted result will be read from the - /// standard output. - rustfmt_overrideCommand: Option> = "null", - /// Enables the use of rustfmt's unstable range formatting command for the - /// `textDocument/rangeFormatting` request. The rustfmt option is unstable and only - /// available on a nightly build. - rustfmt_rangeFormatting_enable: bool = "false", + joinLines_unwrapTrivialBlock: bool = true, /// Inject additional highlighting into doc comments. /// /// When enabled, rust-analyzer will highlight rust source in doc comments as well as intra /// doc links. - semanticHighlighting_doc_comment_inject_enable: bool = "true", + semanticHighlighting_doc_comment_inject_enable: bool = true, /// Whether the server is allowed to emit non-standard tokens and modifiers. - semanticHighlighting_nonStandardTokens: bool = "true", + semanticHighlighting_nonStandardTokens: bool = true, /// Use semantic tokens for operators. /// /// When disabled, rust-analyzer will emit semantic tokens only for operator tokens when /// they are tagged with modifiers. - semanticHighlighting_operator_enable: bool = "true", + semanticHighlighting_operator_enable: bool = true, /// Use specialized semantic tokens for operators. /// /// When enabled, rust-analyzer will emit special token types for operator tokens instead /// of the generic `operator` token type. - semanticHighlighting_operator_specialization_enable: bool = "false", + semanticHighlighting_operator_specialization_enable: bool = false, /// Use semantic tokens for punctuation. /// /// When disabled, rust-analyzer will emit semantic tokens only for punctuation tokens when /// they are tagged with modifiers or have a special role. - semanticHighlighting_punctuation_enable: bool = "false", + semanticHighlighting_punctuation_enable: bool = false, /// When enabled, rust-analyzer will emit a punctuation semantic token for the `!` of macro /// calls. - semanticHighlighting_punctuation_separate_macro_bang: bool = "false", + semanticHighlighting_punctuation_separate_macro_bang: bool = false, /// Use specialized semantic tokens for punctuation. /// /// When enabled, rust-analyzer will emit special token types for punctuation tokens instead /// of the generic `punctuation` token type. - semanticHighlighting_punctuation_specialization_enable: bool = "false", + semanticHighlighting_punctuation_specialization_enable: bool = false, /// Use semantic tokens for strings. /// /// In some editors (e.g. vscode) semantic tokens override other highlighting grammars. /// By disabling semantic tokens for strings, other grammars can be used to highlight /// their contents. - semanticHighlighting_strings_enable: bool = "true", - - /// Show full signature of the callable. Only shows parameters if disabled. - signatureInfo_detail: SignatureDetail = "\"full\"", - /// Show documentation. - signatureInfo_documentation_enable: bool = "true", - - /// Whether to insert closing angle brackets when typing an opening angle bracket of a generic argument list. - typing_autoClosingAngleBrackets_enable: bool = "false", - - /// Workspace symbol search kind. - workspace_symbol_search_kind: WorkspaceSymbolSearchKindDef = "\"only_types\"", - /// Limits the number of items returned from a workspace symbol search (Defaults to 128). - /// Some clients like vs-code issue new searches on result filtering and don't require all results to be returned in the initial search. - /// Other clients requires all results upfront and might require a higher limit. - workspace_symbol_search_limit: usize = "128", - /// Workspace symbol search scope. - workspace_symbol_search_scope: WorkspaceSymbolSearchScopeDef = "\"workspace\"", + semanticHighlighting_strings_enable: bool = true, } } -impl Default for ConfigData { - fn default() -> Self { - ConfigData::from_json(serde_json::Value::Null, &mut Vec::new()) - } +config_data! { + /// Configs that only make sense when they are set by a client. As such they can only be defined + /// by setting them using client's settings (e.g `settings.json` on VS Code). + client: struct ClientDefaultConfigData <- ClientConfigInput -> {} } #[derive(Debug, Clone)] @@ -621,10 +651,40 @@ pub struct Config { workspace_roots: Vec, caps: lsp_types::ClientCapabilities, root_path: AbsPathBuf, - data: ConfigData, detached_files: Vec, snippets: Vec, visual_studio_code_version: Option, + + default_config: DefaultConfigData, + client_config: FullConfigInput, + user_config: GlobalLocalConfigInput, + #[allow(dead_code)] + ratoml_files: FxHashMap, +} + +#[derive(Clone, Debug)] +struct RatomlNode { + #[allow(dead_code)] + node: GlobalLocalConfigInput, + #[allow(dead_code)] + parent: Option, +} + +macro_rules! try_ { + ($expr:expr) => { + || -> _ { Some($expr) }() + }; +} +macro_rules! try_or { + ($expr:expr, $or:expr) => { + try_!($expr).unwrap_or($or) + }; +} + +macro_rules! try_or_def { + ($expr:expr) => { + try_!($expr).unwrap_or_default() + }; } type ParallelCachePrimingNumThreads = u8; @@ -672,7 +732,7 @@ pub struct LensConfig { pub location: AnnotationLocation, } -#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum AnnotationLocation { AboveName, @@ -774,6 +834,8 @@ pub struct RunnablesConfig { pub override_cargo: Option, /// Additional arguments for the `cargo`, e.g. `--release`. pub cargo_extra_args: Vec, + /// Additional arguments for the binary being run, if it is a test or benchmark. + pub extra_test_binary_args: Vec, } /// Configuration for workspace symbol search requests. @@ -827,13 +889,16 @@ impl Config { ) -> Self { Config { caps, - data: ConfigData::default(), detached_files: Vec::new(), discovered_projects: Vec::new(), root_path, snippets: Default::default(), workspace_roots, visual_studio_code_version, + client_config: FullConfigInput::default(), + user_config: GlobalLocalConfigInput::default(), + ratoml_files: FxHashMap::default(), + default_config: DefaultConfigData::default(), } } @@ -863,15 +928,19 @@ impl Config { } let mut errors = Vec::new(); self.detached_files = - get_field::>(&mut json, &mut errors, "detachedFiles", None, "[]") + get_field::>(&mut json, &mut errors, "detachedFiles", None) + .unwrap_or_default() .into_iter() .map(AbsPathBuf::assert) .collect(); patch_old_style::patch_json_for_outdated_configs(&mut json); - self.data = ConfigData::from_json(json, &mut errors); - tracing::debug!("deserialized config data: {:#?}", self.data); + self.client_config = FullConfigInput::from_json(json, &mut errors); + tracing::debug!(?self.client_config, "deserialized config data"); self.snippets.clear(); - for (name, def) in self.data.completion_snippets_custom.iter() { + + let snips = self.completion_snippets_custom(None).to_owned(); + + for (name, def) in snips.iter() { if def.prefix.is_empty() && def.postfix.is_empty() { continue; } @@ -909,7 +978,7 @@ impl Config { fn validate(&self, error_sink: &mut Vec<(String, serde_json::Error)>) { use serde::de::Error; - if self.data.check_command.is_empty() { + if self.check_command().is_empty() { error_sink.push(( "/check/command".to_owned(), serde_json::Error::custom("expected a non-empty string"), @@ -918,7 +987,7 @@ impl Config { } pub fn json_schema() -> serde_json::Value { - ConfigData::json_schema() + FullConfigInput::json_schema() } pub fn root_path(&self) -> &AbsPathBuf { @@ -934,44 +1003,302 @@ impl Config { } } -macro_rules! try_ { - ($expr:expr) => { - || -> _ { Some($expr) }() - }; -} -macro_rules! try_or { - ($expr:expr, $or:expr) => { - try_!($expr).unwrap_or($or) - }; -} - -macro_rules! try_or_def { - ($expr:expr) => { - try_!($expr).unwrap_or_default() - }; -} - impl Config { + pub fn assist(&self, source_root: Option) -> AssistConfig { + AssistConfig { + snippet_cap: SnippetCap::new(self.experimental("snippetTextEdit")), + allowed: None, + insert_use: self.insert_use_config(source_root), + prefer_no_std: self.imports_preferNoStd(source_root).to_owned(), + assist_emit_must_use: self.assist_emitMustUse().to_owned(), + prefer_prelude: self.imports_preferPrelude(source_root).to_owned(), + } + } + + pub fn completion(&self, source_root: Option) -> CompletionConfig { + CompletionConfig { + enable_postfix_completions: self.completion_postfix_enable(source_root).to_owned(), + enable_imports_on_the_fly: self.completion_autoimport_enable(source_root).to_owned() + && completion_item_edit_resolve(&self.caps), + enable_self_on_the_fly: self.completion_autoself_enable(source_root).to_owned(), + enable_private_editable: self.completion_privateEditable_enable(source_root).to_owned(), + full_function_signatures: self + .completion_fullFunctionSignatures_enable(source_root) + .to_owned(), + callable: match self.completion_callable_snippets(source_root) { + CallableCompletionDef::FillArguments => Some(CallableSnippets::FillArguments), + CallableCompletionDef::AddParentheses => Some(CallableSnippets::AddParentheses), + CallableCompletionDef::None => None, + }, + insert_use: self.insert_use_config(source_root), + prefer_no_std: self.imports_preferNoStd(source_root).to_owned(), + snippet_cap: SnippetCap::new(try_or_def!( + self.caps + .text_document + .as_ref()? + .completion + .as_ref()? + .completion_item + .as_ref()? + .snippet_support? + )), + snippets: self.snippets.clone().to_vec(), + limit: self.completion_limit(source_root).to_owned(), + enable_term_search: self.completion_termSearch_enable(source_root).to_owned(), + prefer_prelude: self.imports_preferPrelude(source_root).to_owned(), + } + } + + pub fn diagnostics(&self, source_root: Option) -> DiagnosticsConfig { + DiagnosticsConfig { + enabled: *self.diagnostics_enable(), + proc_attr_macros_enabled: self.expand_proc_attr_macros(), + proc_macros_enabled: *self.procMacro_enable(), + disable_experimental: !self.diagnostics_experimental_enable(), + disabled: self.diagnostics_disabled().clone(), + expr_fill_default: match self.assist_expressionFillDefault() { + ExprFillDefaultDef::Todo => ExprFillDefaultMode::Todo, + ExprFillDefaultDef::Default => ExprFillDefaultMode::Default, + }, + insert_use: self.insert_use_config(source_root), + prefer_no_std: self.imports_preferNoStd(source_root).to_owned(), + prefer_prelude: self.imports_preferPrelude(source_root).to_owned(), + style_lints: self.diagnostics_styleLints_enable().to_owned(), + } + } + pub fn expand_proc_attr_macros(&self) -> bool { + self.procMacro_enable().to_owned() && self.procMacro_attributes_enable().to_owned() + } + + pub fn highlight_related(&self, source_root: Option) -> HighlightRelatedConfig { + HighlightRelatedConfig { + references: self.highlightRelated_references_enable(source_root).to_owned(), + break_points: self.highlightRelated_breakPoints_enable(source_root).to_owned(), + exit_points: self.highlightRelated_exitPoints_enable(source_root).to_owned(), + yield_points: self.highlightRelated_yieldPoints_enable(source_root).to_owned(), + closure_captures: self.highlightRelated_closureCaptures_enable(source_root).to_owned(), + } + } + + pub fn hover_actions(&self) -> HoverActionsConfig { + let enable = self.experimental("hoverActions") && self.hover_actions_enable().to_owned(); + HoverActionsConfig { + implementations: enable && self.hover_actions_implementations_enable().to_owned(), + references: enable && self.hover_actions_references_enable().to_owned(), + run: enable && self.hover_actions_run_enable().to_owned(), + debug: enable && self.hover_actions_debug_enable().to_owned(), + goto_type_def: enable && self.hover_actions_gotoTypeDef_enable().to_owned(), + } + } + + 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.hover_links_enable().to_owned(), + memory_layout: self.hover_memoryLayout_enable().then_some(MemoryLayoutHoverConfig { + size: self.hover_memoryLayout_size().map(mem_kind), + offset: self.hover_memoryLayout_offset().map(mem_kind), + alignment: self.hover_memoryLayout_alignment().map(mem_kind), + niches: self.hover_memoryLayout_niches().unwrap_or_default(), + }), + documentation: self.hover_documentation_enable().to_owned(), + format: { + let is_markdown = try_or_def!(self + .caps + .text_document + .as_ref()? + .hover + .as_ref()? + .content_format + .as_ref()? + .as_slice()) + .contains(&MarkupKind::Markdown); + if is_markdown { + HoverDocFormat::Markdown + } else { + HoverDocFormat::PlainText + } + }, + keywords: self.hover_documentation_keywords_enable().to_owned(), + max_trait_assoc_items_count: self.hover_show_traitAssocItems().to_owned(), + max_struct_field_count: self.hover_show_structFields().to_owned(), + } + } + + pub fn inlay_hints(&self, source_root: Option) -> InlayHintsConfig { + let client_capability_fields = self + .caps + .text_document + .as_ref() + .and_then(|text| text.inlay_hint.as_ref()) + .and_then(|inlay_hint_caps| inlay_hint_caps.resolve_support.as_ref()) + .map(|inlay_resolve| inlay_resolve.properties.iter()) + .into_iter() + .flatten() + .cloned() + .collect::>(); + + InlayHintsConfig { + render_colons: self.inlayHints_renderColons(source_root).to_owned(), + type_hints: self.inlayHints_typeHints_enable(source_root).to_owned(), + parameter_hints: self.inlayHints_parameterHints_enable(source_root).to_owned(), + chaining_hints: self.inlayHints_chainingHints_enable(source_root).to_owned(), + discriminant_hints: match self.inlayHints_discriminantHints_enable(source_root) { + DiscriminantHintsDef::Always => ide::DiscriminantHints::Always, + DiscriminantHintsDef::Never => ide::DiscriminantHints::Never, + DiscriminantHintsDef::Fieldless => ide::DiscriminantHints::Fieldless, + }, + closure_return_type_hints: match self + .inlayHints_closureReturnTypeHints_enable(source_root) + { + ClosureReturnTypeHintsDef::Always => ide::ClosureReturnTypeHints::Always, + ClosureReturnTypeHintsDef::Never => ide::ClosureReturnTypeHints::Never, + ClosureReturnTypeHintsDef::WithBlock => ide::ClosureReturnTypeHints::WithBlock, + }, + lifetime_elision_hints: match self.inlayHints_lifetimeElisionHints_enable(source_root) { + LifetimeElisionDef::Always => ide::LifetimeElisionHints::Always, + LifetimeElisionDef::Never => ide::LifetimeElisionHints::Never, + LifetimeElisionDef::SkipTrivial => ide::LifetimeElisionHints::SkipTrivial, + }, + hide_named_constructor_hints: self + .inlayHints_typeHints_hideNamedConstructor(source_root) + .to_owned(), + hide_closure_initialization_hints: self + .inlayHints_typeHints_hideClosureInitialization(source_root) + .to_owned(), + closure_style: match self.inlayHints_closureStyle(source_root) { + ClosureStyle::ImplFn => hir::ClosureStyle::ImplFn, + ClosureStyle::RustAnalyzer => hir::ClosureStyle::RANotation, + ClosureStyle::WithId => hir::ClosureStyle::ClosureWithId, + ClosureStyle::Hide => hir::ClosureStyle::Hide, + }, + closure_capture_hints: self + .inlayHints_closureCaptureHints_enable(source_root) + .to_owned(), + adjustment_hints: match self.inlayHints_expressionAdjustmentHints_enable(source_root) { + AdjustmentHintsDef::Always => ide::AdjustmentHints::Always, + AdjustmentHintsDef::Never => { + match self.inlayHints_reborrowHints_enable(source_root) { + ReborrowHintsDef::Always | ReborrowHintsDef::Mutable => { + ide::AdjustmentHints::ReborrowOnly + } + ReborrowHintsDef::Never => ide::AdjustmentHints::Never, + } + } + AdjustmentHintsDef::Reborrow => ide::AdjustmentHints::ReborrowOnly, + }, + adjustment_hints_mode: match self.inlayHints_expressionAdjustmentHints_mode(source_root) + { + AdjustmentHintsModeDef::Prefix => ide::AdjustmentHintsMode::Prefix, + AdjustmentHintsModeDef::Postfix => ide::AdjustmentHintsMode::Postfix, + AdjustmentHintsModeDef::PreferPrefix => ide::AdjustmentHintsMode::PreferPrefix, + AdjustmentHintsModeDef::PreferPostfix => ide::AdjustmentHintsMode::PreferPostfix, + }, + adjustment_hints_hide_outside_unsafe: self + .inlayHints_expressionAdjustmentHints_hideOutsideUnsafe(source_root) + .to_owned(), + binding_mode_hints: self.inlayHints_bindingModeHints_enable(source_root).to_owned(), + param_names_for_lifetime_elision_hints: self + .inlayHints_lifetimeElisionHints_useParameterNames(source_root) + .to_owned(), + max_length: self.inlayHints_maxLength(source_root).to_owned(), + closing_brace_hints_min_lines: if self + .inlayHints_closingBraceHints_enable(source_root) + .to_owned() + { + Some(self.inlayHints_closingBraceHints_minLines(source_root).to_owned()) + } else { + None + }, + fields_to_resolve: InlayFieldsToResolve { + resolve_text_edits: client_capability_fields.contains("textEdits"), + resolve_hint_tooltip: client_capability_fields.contains("tooltip"), + resolve_label_tooltip: client_capability_fields.contains("label.tooltip"), + resolve_label_location: client_capability_fields.contains("label.location"), + resolve_label_command: client_capability_fields.contains("label.command"), + }, + implicit_drop_hints: self.inlayHints_implicitDrops_enable(source_root).to_owned(), + range_exclusive_hints: self + .inlayHints_rangeExclusiveHints_enable(source_root) + .to_owned(), + } + } + + fn insert_use_config(&self, source_root: Option) -> InsertUseConfig { + InsertUseConfig { + granularity: match self.imports_granularity_group(source_root) { + ImportGranularityDef::Preserve => ImportGranularity::Preserve, + ImportGranularityDef::Item => ImportGranularity::Item, + ImportGranularityDef::Crate => ImportGranularity::Crate, + ImportGranularityDef::Module => ImportGranularity::Module, + ImportGranularityDef::One => ImportGranularity::One, + }, + enforce_granularity: self.imports_granularity_enforce(source_root).to_owned(), + prefix_kind: match self.imports_prefix(source_root) { + ImportPrefixDef::Plain => PrefixKind::Plain, + ImportPrefixDef::ByCrate => PrefixKind::ByCrate, + ImportPrefixDef::BySelf => PrefixKind::BySelf, + }, + group: self.imports_group_enable(source_root).to_owned(), + skip_glob_imports: !self.imports_merge_glob(source_root), + } + } + + pub fn join_lines(&self, source_root: Option) -> JoinLinesConfig { + JoinLinesConfig { + join_else_if: self.joinLines_joinElseIf(source_root).to_owned(), + remove_trailing_comma: self.joinLines_removeTrailingComma(source_root).to_owned(), + unwrap_trivial_blocks: self.joinLines_unwrapTrivialBlock(source_root).to_owned(), + join_assignments: self.joinLines_joinAssignments(source_root).to_owned(), + } + } + + pub fn highlighting_non_standard_tokens(&self, source_root: Option) -> bool { + self.semanticHighlighting_nonStandardTokens(source_root).to_owned() + } + + pub fn highlighting_config(&self, source_root: Option) -> HighlightConfig { + HighlightConfig { + strings: self.semanticHighlighting_strings_enable(source_root).to_owned(), + punctuation: self.semanticHighlighting_punctuation_enable(source_root).to_owned(), + specialize_punctuation: self + .semanticHighlighting_punctuation_specialization_enable(source_root) + .to_owned(), + macro_bang: self + .semanticHighlighting_punctuation_separate_macro_bang(source_root) + .to_owned(), + operator: self.semanticHighlighting_operator_enable(source_root).to_owned(), + specialize_operator: self + .semanticHighlighting_operator_specialization_enable(source_root) + .to_owned(), + inject_doc_comment: self + .semanticHighlighting_doc_comment_inject_enable(source_root) + .to_owned(), + syntactic_name_ref_highlighting: false, + } + } + pub fn has_linked_projects(&self) -> bool { - !self.data.linkedProjects.is_empty() + !self.linkedProjects().is_empty() } pub fn linked_manifests(&self) -> impl Iterator + '_ { - self.data.linkedProjects.iter().filter_map(|it| match it { + self.linkedProjects().iter().filter_map(|it| match it { ManifestOrProjectJson::Manifest(p) => Some(&**p), ManifestOrProjectJson::ProjectJson(_) => None, }) } pub fn has_linked_project_jsons(&self) -> bool { - self.data - .linkedProjects - .iter() - .any(|it| matches!(it, ManifestOrProjectJson::ProjectJson(_))) + self.linkedProjects().iter().any(|it| matches!(it, ManifestOrProjectJson::ProjectJson(_))) } pub fn linked_or_discovered_projects(&self) -> Vec { - match self.data.linkedProjects.as_slice() { + match self.linkedProjects().as_slice() { [] => { let exclude_dirs: Vec<_> = - self.data.files_excludeDirs.iter().map(|p| self.root_path.join(p)).collect(); + self.files_excludeDirs().iter().map(|p| self.root_path.join(p)).collect(); self.discovered_projects .iter() .filter( @@ -1025,7 +1352,7 @@ impl Config { } pub fn prefill_caches(&self) -> bool { - self.data.cachePriming_enable + self.cachePriming_enable().to_owned() } pub fn location_link(&self) -> bool { @@ -1162,117 +1489,95 @@ impl Config { } pub fn publish_diagnostics(&self) -> bool { - self.data.diagnostics_enable - } - - pub fn diagnostics(&self) -> DiagnosticsConfig { - DiagnosticsConfig { - enabled: self.data.diagnostics_enable, - proc_attr_macros_enabled: self.expand_proc_attr_macros(), - proc_macros_enabled: self.data.procMacro_enable, - disable_experimental: !self.data.diagnostics_experimental_enable, - disabled: self.data.diagnostics_disabled.clone(), - expr_fill_default: match self.data.assist_expressionFillDefault { - ExprFillDefaultDef::Todo => ExprFillDefaultMode::Todo, - ExprFillDefaultDef::Default => ExprFillDefaultMode::Default, - }, - insert_use: self.insert_use_config(), - prefer_no_std: self.data.imports_preferNoStd, - prefer_prelude: self.data.imports_preferPrelude, - style_lints: self.data.diagnostics_styleLints_enable, - } + self.diagnostics_enable().to_owned() } pub fn diagnostics_map(&self) -> DiagnosticsMapConfig { DiagnosticsMapConfig { - remap_prefix: self.data.diagnostics_remapPrefix.clone(), - warnings_as_info: self.data.diagnostics_warningsAsInfo.clone(), - warnings_as_hint: self.data.diagnostics_warningsAsHint.clone(), - check_ignore: self.data.check_ignore.clone(), + remap_prefix: self.diagnostics_remapPrefix().clone(), + warnings_as_info: self.diagnostics_warningsAsInfo().clone(), + warnings_as_hint: self.diagnostics_warningsAsHint().clone(), + check_ignore: self.check_ignore().clone(), } } pub fn extra_args(&self) -> &Vec { - &self.data.cargo_extraArgs + self.cargo_extraArgs() } pub fn extra_env(&self) -> &FxHashMap { - &self.data.cargo_extraEnv + self.cargo_extraEnv() } pub fn check_extra_args(&self) -> Vec { let mut extra_args = self.extra_args().clone(); - extra_args.extend_from_slice(&self.data.check_extraArgs); + extra_args.extend_from_slice(self.check_extraArgs()); extra_args } pub fn check_extra_env(&self) -> FxHashMap { - let mut extra_env = self.data.cargo_extraEnv.clone(); - extra_env.extend(self.data.check_extraEnv.clone()); + let mut extra_env = self.cargo_extraEnv().clone(); + extra_env.extend(self.check_extraEnv().clone()); extra_env } pub fn lru_parse_query_capacity(&self) -> Option { - self.data.lru_capacity + self.lru_capacity().to_owned() } - pub fn lru_query_capacities(&self) -> Option<&FxHashMap, usize>> { - self.data.lru_query_capacities.is_empty().not().then_some(&self.data.lru_query_capacities) + pub fn lru_query_capacities_config(&self) -> Option<&FxHashMap, usize>> { + self.lru_query_capacities().is_empty().not().then(|| self.lru_query_capacities()) } pub fn proc_macro_srv(&self) -> Option { - let path = self.data.procMacro_server.clone()?; + let path = self.procMacro_server().clone()?; Some(AbsPathBuf::try_from(path).unwrap_or_else(|path| self.root_path.join(path))) } pub fn ignored_proc_macros(&self) -> &FxHashMap, Box<[Box]>> { - &self.data.procMacro_ignored + self.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 + self.procMacro_enable().to_owned() } pub fn files(&self) -> FilesConfig { FilesConfig { - watcher: match self.data.files_watcher { + watcher: match self.files_watcher() { FilesWatcherDef::Client if self.did_change_watched_files_dynamic_registration() => { FilesWatcher::Client } _ => FilesWatcher::Server, }, - exclude: self.data.files_excludeDirs.iter().map(|it| self.root_path.join(it)).collect(), + exclude: self.files_excludeDirs().iter().map(|it| self.root_path.join(it)).collect(), } } pub fn notifications(&self) -> NotificationsConfig { NotificationsConfig { - cargo_toml_not_found: self.data.notifications_cargoTomlNotFound, - unindexed_project: self.data.notifications_unindexedProject, + cargo_toml_not_found: self.notifications_cargoTomlNotFound().to_owned(), + unindexed_project: self.notifications_unindexedProject().to_owned(), } } - pub fn cargo_autoreload(&self) -> bool { - self.data.cargo_autoreload + pub fn cargo_autoreload_config(&self) -> bool { + self.cargo_autoreload().to_owned() } pub fn run_build_scripts(&self) -> bool { - self.data.cargo_buildScripts_enable || self.data.procMacro_enable + self.cargo_buildScripts_enable().to_owned() || self.procMacro_enable().to_owned() } pub fn cargo(&self) -> CargoConfig { - let rustc_source = self.data.rustc_source.as_ref().map(|rustc_src| { + let rustc_source = self.rustc_source().as_ref().map(|rustc_src| { if rustc_src == "discover" { RustLibSource::Discover } else { RustLibSource::Path(self.root_path.join(rustc_src)) } }); - let sysroot = self.data.cargo_sysroot.as_ref().map(|sysroot| { + let sysroot = self.cargo_sysroot().as_ref().map(|sysroot| { if sysroot == "discover" { RustLibSource::Discover } else { @@ -1280,88 +1585,91 @@ impl Config { } }); let sysroot_src = - self.data.cargo_sysrootSrc.as_ref().map(|sysroot| self.root_path.join(sysroot)); - let sysroot_query_metadata = self.data.cargo_sysrootQueryMetadata; + self.cargo_sysrootSrc().as_ref().map(|sysroot| self.root_path.join(sysroot)); + let sysroot_query_metadata = self.cargo_sysrootQueryMetadata(); CargoConfig { - features: match &self.data.cargo_features { + all_targets: *self.cargo_allTargets(), + features: match &self.cargo_features() { CargoFeaturesDef::All => CargoFeatures::All, CargoFeaturesDef::Selected(features) => CargoFeatures::Selected { features: features.clone(), - no_default_features: self.data.cargo_noDefaultFeatures, + no_default_features: self.cargo_noDefaultFeatures().to_owned(), }, }, - target: self.data.cargo_target.clone(), + target: self.cargo_target().clone(), sysroot, - sysroot_query_metadata, + sysroot_query_metadata: *sysroot_query_metadata, sysroot_src, rustc_source, cfg_overrides: project_model::CfgOverrides { global: CfgDiff::new( - self.data - .cargo_cfgs + self.cargo_cfgs() .iter() - .map(|(key, val)| { - if val.is_empty() { - CfgAtom::Flag(key.into()) - } else { - CfgAtom::KeyValue { key: key.into(), value: val.into() } - } + .map(|(key, val)| match val { + Some(val) => CfgAtom::KeyValue { key: key.into(), value: val.into() }, + None => CfgAtom::Flag(key.into()), }) .collect(), vec![], ) .unwrap(), - selective: self - .data - .cargo_unsetTest - .iter() - .map(|it| { - ( - it.clone(), - CfgDiff::new(vec![], vec![CfgAtom::Flag("test".into())]).unwrap(), - ) - }) - .collect(), + selective: Default::default(), }, - wrap_rustc_in_build_scripts: self.data.cargo_buildScripts_useRustcWrapper, - invocation_strategy: match self.data.cargo_buildScripts_invocationStrategy { + wrap_rustc_in_build_scripts: *self.cargo_buildScripts_useRustcWrapper(), + invocation_strategy: match self.cargo_buildScripts_invocationStrategy() { InvocationStrategy::Once => project_model::InvocationStrategy::Once, InvocationStrategy::PerWorkspace => project_model::InvocationStrategy::PerWorkspace, }, - invocation_location: match self.data.cargo_buildScripts_invocationLocation { + invocation_location: match self.cargo_buildScripts_invocationLocation() { InvocationLocation::Root => { project_model::InvocationLocation::Root(self.root_path.clone()) } InvocationLocation::Workspace => project_model::InvocationLocation::Workspace, }, - run_build_script_command: self.data.cargo_buildScripts_overrideCommand.clone(), - extra_args: self.data.cargo_extraArgs.clone(), - extra_env: self.data.cargo_extraEnv.clone(), + run_build_script_command: self.cargo_buildScripts_overrideCommand().clone(), + extra_args: self.cargo_extraArgs().clone(), + extra_env: self.cargo_extraEnv().clone(), target_dir: self.target_dir_from_config(), } } pub fn rustfmt(&self) -> RustfmtConfig { - match &self.data.rustfmt_overrideCommand { + match &self.rustfmt_overrideCommand() { Some(args) if !args.is_empty() => { let mut args = args.clone(); let command = args.remove(0); RustfmtConfig::CustomCommand { command, args } } Some(_) | None => RustfmtConfig::Rustfmt { - extra_args: self.data.rustfmt_extraArgs.clone(), - enable_range_formatting: self.data.rustfmt_rangeFormatting_enable, + extra_args: self.rustfmt_extraArgs().clone(), + enable_range_formatting: *self.rustfmt_rangeFormatting_enable(), }, } } pub fn flycheck_workspace(&self) -> bool { - self.data.check_workspace + *self.check_workspace() + } + + pub fn cargo_test_options(&self) -> CargoOptions { + CargoOptions { + target_triples: self.cargo_target().clone().into_iter().collect(), + all_targets: false, + no_default_features: *self.cargo_noDefaultFeatures(), + all_features: matches!(self.cargo_features(), CargoFeaturesDef::All), + features: match self.cargo_features().clone() { + CargoFeaturesDef::All => vec![], + CargoFeaturesDef::Selected(it) => it, + }, + extra_args: self.extra_args().clone(), + extra_env: self.extra_env().clone(), + target_dir: self.target_dir_from_config(), + } } pub fn flycheck(&self) -> FlycheckConfig { - match &self.data.check_overrideCommand { + match &self.check_overrideCommand() { Some(args) if !args.is_empty() => { let mut args = args.clone(); let command = args.remove(0); @@ -1369,13 +1677,13 @@ impl Config { command, args, extra_env: self.check_extra_env(), - invocation_strategy: match self.data.check_invocationStrategy { + invocation_strategy: match self.check_invocationStrategy() { InvocationStrategy::Once => flycheck::InvocationStrategy::Once, InvocationStrategy::PerWorkspace => { flycheck::InvocationStrategy::PerWorkspace } }, - invocation_location: match self.data.check_invocationLocation { + invocation_location: match self.check_invocationLocation() { InvocationLocation::Root => { flycheck::InvocationLocation::Root(self.root_path.clone()) } @@ -1384,44 +1692,43 @@ impl Config { } } Some(_) | None => FlycheckConfig::CargoCommand { - command: self.data.check_command.clone(), - target_triples: self - .data - .check_targets - .clone() - .and_then(|targets| match &targets.0[..] { - [] => None, - targets => Some(targets.into()), - }) - .unwrap_or_else(|| self.data.cargo_target.clone().into_iter().collect()), - all_targets: self.data.check_allTargets, - no_default_features: self - .data - .check_noDefaultFeatures - .unwrap_or(self.data.cargo_noDefaultFeatures), - all_features: matches!( - self.data.check_features.as_ref().unwrap_or(&self.data.cargo_features), - CargoFeaturesDef::All - ), - features: match self - .data - .check_features - .clone() - .unwrap_or_else(|| self.data.cargo_features.clone()) - { - CargoFeaturesDef::All => vec![], - CargoFeaturesDef::Selected(it) => it, + command: self.check_command().clone(), + options: CargoOptions { + target_triples: self + .check_targets() + .clone() + .and_then(|targets| match &targets.0[..] { + [] => None, + targets => Some(targets.into()), + }) + .unwrap_or_else(|| self.cargo_target().clone().into_iter().collect()), + all_targets: self.check_allTargets().unwrap_or(*self.cargo_allTargets()), + no_default_features: self + .check_noDefaultFeatures() + .unwrap_or(*self.cargo_noDefaultFeatures()), + all_features: matches!( + self.check_features().as_ref().unwrap_or(self.cargo_features()), + CargoFeaturesDef::All + ), + features: match self + .check_features() + .clone() + .unwrap_or_else(|| self.cargo_features().clone()) + { + CargoFeaturesDef::All => vec![], + CargoFeaturesDef::Selected(it) => it, + }, + extra_args: self.check_extra_args(), + extra_env: self.check_extra_env(), + target_dir: self.target_dir_from_config(), }, - extra_args: self.check_extra_args(), - extra_env: self.check_extra_env(), ansi_color_output: self.color_diagnostic_output(), - target_dir: self.target_dir_from_config(), }, } } fn target_dir_from_config(&self) -> Option { - self.data.cargo_targetDir.as_ref().and_then(|target_dir| match target_dir { + self.cargo_targetDir().as_ref().and_then(|target_dir| match target_dir { TargetDirectory::UseSubdirectory(true) => { Some(Utf8PathBuf::from("target/rust-analyzer")) } @@ -1432,294 +1739,67 @@ impl Config { } pub fn check_on_save(&self) -> bool { - self.data.checkOnSave + *self.checkOnSave() } pub fn script_rebuild_on_save(&self) -> bool { - self.data.cargo_buildScripts_rebuildOnSave + *self.cargo_buildScripts_rebuildOnSave() } pub fn runnables(&self) -> RunnablesConfig { RunnablesConfig { - override_cargo: self.data.runnables_command.clone(), - cargo_extra_args: self.data.runnables_extraArgs.clone(), - } - } - - pub fn inlay_hints(&self) -> InlayHintsConfig { - let client_capability_fields = self - .caps - .text_document - .as_ref() - .and_then(|text| text.inlay_hint.as_ref()) - .and_then(|inlay_hint_caps| inlay_hint_caps.resolve_support.as_ref()) - .map(|inlay_resolve| inlay_resolve.properties.iter()) - .into_iter() - .flatten() - .cloned() - .collect::>(); - - InlayHintsConfig { - render_colons: self.data.inlayHints_renderColons, - type_hints: self.data.inlayHints_typeHints_enable, - parameter_hints: self.data.inlayHints_parameterHints_enable, - chaining_hints: self.data.inlayHints_chainingHints_enable, - implicit_drop_hints: self.data.inlayHints_implicitDrops_enable, - discriminant_hints: match self.data.inlayHints_discriminantHints_enable { - DiscriminantHintsDef::Always => ide::DiscriminantHints::Always, - DiscriminantHintsDef::Never => ide::DiscriminantHints::Never, - DiscriminantHintsDef::Fieldless => ide::DiscriminantHints::Fieldless, - }, - closure_return_type_hints: match self.data.inlayHints_closureReturnTypeHints_enable { - ClosureReturnTypeHintsDef::Always => ide::ClosureReturnTypeHints::Always, - ClosureReturnTypeHintsDef::Never => ide::ClosureReturnTypeHints::Never, - ClosureReturnTypeHintsDef::WithBlock => ide::ClosureReturnTypeHints::WithBlock, - }, - lifetime_elision_hints: match self.data.inlayHints_lifetimeElisionHints_enable { - LifetimeElisionDef::Always => ide::LifetimeElisionHints::Always, - LifetimeElisionDef::Never => ide::LifetimeElisionHints::Never, - LifetimeElisionDef::SkipTrivial => ide::LifetimeElisionHints::SkipTrivial, - }, - hide_named_constructor_hints: self.data.inlayHints_typeHints_hideNamedConstructor, - 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 { - ReborrowHintsDef::Always | ReborrowHintsDef::Mutable => { - ide::AdjustmentHints::ReborrowOnly - } - ReborrowHintsDef::Never => ide::AdjustmentHints::Never, - }, - AdjustmentHintsDef::Reborrow => ide::AdjustmentHints::ReborrowOnly, - }, - adjustment_hints_mode: match self.data.inlayHints_expressionAdjustmentHints_mode { - AdjustmentHintsModeDef::Prefix => ide::AdjustmentHintsMode::Prefix, - AdjustmentHintsModeDef::Postfix => ide::AdjustmentHintsMode::Postfix, - AdjustmentHintsModeDef::PreferPrefix => ide::AdjustmentHintsMode::PreferPrefix, - AdjustmentHintsModeDef::PreferPostfix => ide::AdjustmentHintsMode::PreferPostfix, - }, - adjustment_hints_hide_outside_unsafe: self - .data - .inlayHints_expressionAdjustmentHints_hideOutsideUnsafe, - binding_mode_hints: self.data.inlayHints_bindingModeHints_enable, - param_names_for_lifetime_elision_hints: self - .data - .inlayHints_lifetimeElisionHints_useParameterNames, - max_length: self.data.inlayHints_maxLength, - closing_brace_hints_min_lines: if self.data.inlayHints_closingBraceHints_enable { - Some(self.data.inlayHints_closingBraceHints_minLines) - } else { - None - }, - range_exclusive_hints: self.data.inlayHints_rangeExclusiveHints_enable, - fields_to_resolve: InlayFieldsToResolve { - resolve_text_edits: client_capability_fields.contains("textEdits"), - resolve_hint_tooltip: client_capability_fields.contains("tooltip"), - resolve_label_tooltip: client_capability_fields.contains("label.tooltip"), - resolve_label_location: client_capability_fields.contains("label.location"), - resolve_label_command: client_capability_fields.contains("label.command"), - }, - } - } - - fn insert_use_config(&self) -> InsertUseConfig { - InsertUseConfig { - granularity: match self.data.imports_granularity_group { - ImportGranularityDef::Preserve => ImportGranularity::Preserve, - ImportGranularityDef::Item => ImportGranularity::Item, - ImportGranularityDef::Crate => ImportGranularity::Crate, - ImportGranularityDef::Module => ImportGranularity::Module, - ImportGranularityDef::One => ImportGranularity::One, - }, - enforce_granularity: self.data.imports_granularity_enforce, - prefix_kind: match self.data.imports_prefix { - ImportPrefixDef::Plain => PrefixKind::Plain, - ImportPrefixDef::ByCrate => PrefixKind::ByCrate, - ImportPrefixDef::BySelf => PrefixKind::BySelf, - }, - group: self.data.imports_group_enable, - skip_glob_imports: !self.data.imports_merge_glob, - } - } - - pub fn completion(&self) -> CompletionConfig { - CompletionConfig { - enable_postfix_completions: self.data.completion_postfix_enable, - enable_imports_on_the_fly: self.data.completion_autoimport_enable - && completion_item_edit_resolve(&self.caps), - enable_self_on_the_fly: self.data.completion_autoself_enable, - enable_private_editable: self.data.completion_privateEditable_enable, - enable_term_search: self.data.completion_termSearch_enable, - full_function_signatures: self.data.completion_fullFunctionSignatures_enable, - callable: match self.data.completion_callable_snippets { - CallableCompletionDef::FillArguments => Some(CallableSnippets::FillArguments), - CallableCompletionDef::AddParentheses => Some(CallableSnippets::AddParentheses), - CallableCompletionDef::None => None, - }, - insert_use: self.insert_use_config(), - prefer_no_std: self.data.imports_preferNoStd, - prefer_prelude: self.data.imports_preferPrelude, - snippet_cap: SnippetCap::new(try_or_def!( - self.caps - .text_document - .as_ref()? - .completion - .as_ref()? - .completion_item - .as_ref()? - .snippet_support? - )), - snippets: self.snippets.clone(), - limit: self.data.completion_limit, + override_cargo: self.runnables_command().clone(), + cargo_extra_args: self.runnables_extraArgs().clone(), + extra_test_binary_args: self.runnables_extraTestBinaryArgs().clone(), } } pub fn find_all_refs_exclude_imports(&self) -> bool { - self.data.references_excludeImports + *self.references_excludeImports() } pub fn find_all_refs_exclude_tests(&self) -> bool { - self.data.references_excludeTests + *self.references_excludeTests() } pub fn snippet_cap(&self) -> bool { self.experimental("snippetTextEdit") } - pub fn assist(&self) -> AssistConfig { - AssistConfig { - snippet_cap: SnippetCap::new(self.experimental("snippetTextEdit")), - allowed: None, - insert_use: self.insert_use_config(), - prefer_no_std: self.data.imports_preferNoStd, - prefer_prelude: self.data.imports_preferPrelude, - assist_emit_must_use: self.data.assist_emitMustUse, - } - } - - pub fn join_lines(&self) -> JoinLinesConfig { - JoinLinesConfig { - join_else_if: self.data.joinLines_joinElseIf, - remove_trailing_comma: self.data.joinLines_removeTrailingComma, - unwrap_trivial_blocks: self.data.joinLines_unwrapTrivialBlock, - join_assignments: self.data.joinLines_joinAssignments, - } - } - pub fn call_info(&self) -> CallInfoConfig { CallInfoConfig { - params_only: matches!(self.data.signatureInfo_detail, SignatureDetail::Parameters), - docs: self.data.signatureInfo_documentation_enable, + params_only: matches!(self.signatureInfo_detail(), SignatureDetail::Parameters), + docs: *self.signatureInfo_documentation_enable(), } } pub fn lens(&self) -> LensConfig { 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, - refs_trait: self.data.lens_enable && self.data.lens_references_trait_enable, - enum_variant_refs: self.data.lens_enable - && self.data.lens_references_enumVariant_enable, - location: self.data.lens_location, - } - } - - pub fn hover_actions(&self) -> HoverActionsConfig { - let enable = self.experimental("hoverActions") && self.data.hover_actions_enable; - HoverActionsConfig { - implementations: enable && self.data.hover_actions_implementations_enable, - references: enable && self.data.hover_actions_references_enable, - run: enable && self.data.hover_actions_run_enable, - debug: enable && self.data.hover_actions_debug_enable, - goto_type_def: enable && self.data.hover_actions_gotoTypeDef_enable, - } - } - - 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, - punctuation: self.data.semanticHighlighting_punctuation_enable, - specialize_punctuation: self - .data - .semanticHighlighting_punctuation_specialization_enable, - macro_bang: self.data.semanticHighlighting_punctuation_separate_macro_bang, - operator: self.data.semanticHighlighting_operator_enable, - specialize_operator: self.data.semanticHighlighting_operator_specialization_enable, - inject_doc_comment: self.data.semanticHighlighting_doc_comment_inject_enable, - syntactic_name_ref_highlighting: false, - } - } - - 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 - .caps - .text_document - .as_ref()? - .hover - .as_ref()? - .content_format - .as_ref()? - .as_slice()) - .contains(&MarkupKind::Markdown); - if is_markdown { - HoverDocFormat::Markdown - } else { - HoverDocFormat::PlainText - } - }, - keywords: self.data.hover_documentation_keywords_enable, - max_trait_assoc_items_count: self.data.hover_show_traitAssocItems, - max_struct_field_count: self.data.hover_show_structFields, + run: *self.lens_run_enable(), + debug: *self.lens_enable() && *self.lens_debug_enable(), + interpret: *self.lens_enable() && *self.lens_run_enable() && *self.interpret_tests(), + implementations: *self.lens_enable() && *self.lens_implementations_enable(), + method_refs: *self.lens_enable() && *self.lens_references_method_enable(), + refs_adt: *self.lens_enable() && *self.lens_references_adt_enable(), + refs_trait: *self.lens_enable() && *self.lens_references_trait_enable(), + enum_variant_refs: *self.lens_enable() && *self.lens_references_enumVariant_enable(), + location: *self.lens_location(), } } pub fn workspace_symbol(&self) -> WorkspaceSymbolConfig { WorkspaceSymbolConfig { - search_scope: match self.data.workspace_symbol_search_scope { + search_scope: match self.workspace_symbol_search_scope() { WorkspaceSymbolSearchScopeDef::Workspace => WorkspaceSymbolSearchScope::Workspace, WorkspaceSymbolSearchScopeDef::WorkspaceAndDependencies => { WorkspaceSymbolSearchScope::WorkspaceAndDependencies } }, - search_kind: match self.data.workspace_symbol_search_kind { + search_kind: match self.workspace_symbol_search_kind() { WorkspaceSymbolSearchKindDef::OnlyTypes => WorkspaceSymbolSearchKind::OnlyTypes, WorkspaceSymbolSearchKindDef::AllSymbols => WorkspaceSymbolSearchKind::AllSymbols, }, - search_limit: self.data.workspace_symbol_search_limit, + search_limit: *self.workspace_symbol_search_limit(), } } @@ -1753,7 +1833,7 @@ impl Config { try_or!(self.caps.experimental.as_ref()?.get("commands")?, &serde_json::Value::Null); let commands: Option = serde_json::from_value(commands.clone()).ok(); - let force = commands.is_none() && self.data.lens_forceCustomCommands; + let force = commands.is_none() && *self.lens_forceCustomCommands(); let commands = commands.map(|it| it.commands).unwrap_or_default(); let get = |name: &str| commands.iter().any(|it| it == name) || force; @@ -1767,29 +1847,19 @@ impl Config { } } - pub fn highlight_related(&self) -> HighlightRelatedConfig { - HighlightRelatedConfig { - references: self.data.highlightRelated_references_enable, - 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, - } - } - pub fn prime_caches_num_threads(&self) -> u8 { - match self.data.cachePriming_numThreads { + match *self.cachePriming_numThreads() { 0 => num_cpus::get_physical().try_into().unwrap_or(u8::MAX), n => n, } } pub fn main_loop_num_threads(&self) -> usize { - self.data.numThreads.unwrap_or(num_cpus::get_physical()) + self.numThreads().unwrap_or(num_cpus::get_physical()) } pub fn typing_autoclose_angle(&self) -> bool { - self.data.typing_autoClosingAngleBrackets_enable + *self.typing_autoClosingAngleBrackets_enable() } // VSCode is our reference implementation, so we allow ourselves to work around issues by @@ -1800,100 +1870,120 @@ impl Config { } // Deserialization definitions -macro_rules! create_bool_or_string_de { +macro_rules! create_bool_or_string_serde { ($ident:ident<$bool:literal, $string:literal>) => { - fn $ident<'de, D>(d: D) -> Result<(), D::Error> - where - D: serde::Deserializer<'de>, - { - struct V; - impl<'de> serde::de::Visitor<'de> for V { - type Value = (); + mod $ident { + pub(super) fn deserialize<'de, D>(d: D) -> Result<(), D::Error> + where + D: serde::Deserializer<'de>, + { + struct V; + impl<'de> serde::de::Visitor<'de> for V { + type Value = (); - fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - formatter.write_str(concat!( - stringify!($bool), - " or \"", - stringify!($string), - "\"" - )) - } + fn expecting( + &self, + formatter: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + formatter.write_str(concat!( + stringify!($bool), + " or \"", + stringify!($string), + "\"" + )) + } - fn visit_bool(self, v: bool) -> Result - where - E: serde::de::Error, - { - match v { - $bool => Ok(()), - _ => Err(serde::de::Error::invalid_value( - serde::de::Unexpected::Bool(v), - &self, - )), - } - } - - fn visit_str(self, v: &str) -> Result - where - E: serde::de::Error, - { - match v { - $string => Ok(()), - _ => Err(serde::de::Error::invalid_value( - serde::de::Unexpected::Str(v), - &self, - )), - } - } - - fn visit_enum(self, a: A) -> Result - where - A: serde::de::EnumAccess<'de>, - { - use serde::de::VariantAccess; - let (variant, va) = a.variant::<&'de str>()?; - va.unit_variant()?; - match variant { - $string => Ok(()), - _ => Err(serde::de::Error::invalid_value( - serde::de::Unexpected::Str(variant), - &self, - )), + fn visit_bool(self, v: bool) -> Result + where + E: serde::de::Error, + { + match v { + $bool => Ok(()), + _ => Err(serde::de::Error::invalid_value( + serde::de::Unexpected::Bool(v), + &self, + )), + } + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + match v { + $string => Ok(()), + _ => Err(serde::de::Error::invalid_value( + serde::de::Unexpected::Str(v), + &self, + )), + } + } + + fn visit_enum(self, a: A) -> Result + where + A: serde::de::EnumAccess<'de>, + { + use serde::de::VariantAccess; + let (variant, va) = a.variant::<&'de str>()?; + va.unit_variant()?; + match variant { + $string => Ok(()), + _ => Err(serde::de::Error::invalid_value( + serde::de::Unexpected::Str(variant), + &self, + )), + } } } + d.deserialize_any(V) + } + + pub(super) fn serialize(serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str($string) } - d.deserialize_any(V) } }; } -create_bool_or_string_de!(true_or_always); -create_bool_or_string_de!(false_or_never); +create_bool_or_string_serde!(true_or_always); +create_bool_or_string_serde!(false_or_never); macro_rules! named_unit_variant { ($variant:ident) => { - pub(super) fn $variant<'de, D>(deserializer: D) -> Result<(), D::Error> - where - D: serde::Deserializer<'de>, - { - struct V; - impl<'de> serde::de::Visitor<'de> for V { - type Value = (); - fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str(concat!("\"", stringify!($variant), "\"")) - } - fn visit_str(self, value: &str) -> Result { - if value == stringify!($variant) { - Ok(()) - } else { - Err(E::invalid_value(serde::de::Unexpected::Str(value), &self)) + pub(super) mod $variant { + pub(in super::super) fn deserialize<'de, D>(deserializer: D) -> Result<(), D::Error> + where + D: serde::Deserializer<'de>, + { + struct V; + impl<'de> serde::de::Visitor<'de> for V { + type Value = (); + fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(concat!("\"", stringify!($variant), "\"")) + } + fn visit_str(self, value: &str) -> Result { + if value == stringify!($variant) { + Ok(()) + } else { + Err(E::invalid_value(serde::de::Unexpected::Str(value), &self)) + } } } + deserializer.deserialize_str(V) + } + pub(in super::super) fn serialize(serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(stringify!($variant)) } - deserializer.deserialize_str(V) } }; } -mod de_unit_v { +mod unit_v { named_unit_variant!(all); named_unit_variant!(skip_trivial); named_unit_variant!(mutable); @@ -1905,7 +1995,7 @@ mod de_unit_v { named_unit_variant!(both); } -#[derive(Deserialize, Debug, Clone, Copy)] +#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)] #[serde(rename_all = "snake_case")] #[derive(Default)] enum SnippetScopeDef { @@ -1915,67 +2005,92 @@ enum SnippetScopeDef { Type, } -#[derive(Deserialize, Debug, Clone, Default)] +#[derive(Serialize, Deserialize, Debug, Clone, Default)] #[serde(default)] struct SnippetDef { - #[serde(deserialize_with = "single_or_array")] + #[serde(with = "single_or_array")] + #[serde(skip_serializing_if = "Vec::is_empty")] prefix: Vec, - #[serde(deserialize_with = "single_or_array")] + + #[serde(with = "single_or_array")] + #[serde(skip_serializing_if = "Vec::is_empty")] postfix: Vec, - description: Option, - #[serde(deserialize_with = "single_or_array")] + + #[serde(with = "single_or_array")] + #[serde(skip_serializing_if = "Vec::is_empty")] body: Vec, - #[serde(deserialize_with = "single_or_array")] + + #[serde(with = "single_or_array")] + #[serde(skip_serializing_if = "Vec::is_empty")] requires: Vec, + + #[serde(skip_serializing_if = "Option::is_none")] + description: Option, + scope: SnippetScopeDef, } -fn single_or_array<'de, D>(deserializer: D) -> Result, D::Error> -where - D: serde::Deserializer<'de>, -{ - struct SingleOrVec; +mod single_or_array { + use serde::{Deserialize, Serialize}; - impl<'de> serde::de::Visitor<'de> for SingleOrVec { - type Value = Vec; + pub(super) fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> + where + D: serde::Deserializer<'de>, + { + struct SingleOrVec; - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("string or array of strings") + impl<'de> serde::de::Visitor<'de> for SingleOrVec { + type Value = Vec; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("string or array of strings") + } + + fn visit_str(self, value: &str) -> Result + where + E: serde::de::Error, + { + Ok(vec![value.to_owned()]) + } + + fn visit_seq(self, seq: A) -> Result + where + A: serde::de::SeqAccess<'de>, + { + Deserialize::deserialize(serde::de::value::SeqAccessDeserializer::new(seq)) + } } - fn visit_str(self, value: &str) -> Result - where - E: serde::de::Error, - { - Ok(vec![value.to_owned()]) - } - - fn visit_seq(self, seq: A) -> Result - where - A: serde::de::SeqAccess<'de>, - { - Deserialize::deserialize(serde::de::value::SeqAccessDeserializer::new(seq)) - } + deserializer.deserialize_any(SingleOrVec) } - deserializer.deserialize_any(SingleOrVec) + pub(super) fn serialize(vec: &[String], serializer: S) -> Result + where + S: serde::Serializer, + { + match vec { + // [] case is handled by skip_serializing_if + [single] => serializer.serialize_str(single), + slice => slice.serialize(serializer), + } + } } -#[derive(Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(untagged)] enum ManifestOrProjectJson { Manifest(Utf8PathBuf), ProjectJson(ProjectJsonData), } -#[derive(Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "snake_case")] enum ExprFillDefaultDef { Todo, Default, } -#[derive(Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "snake_case")] enum ImportGranularityDef { Preserve, @@ -1985,7 +2100,7 @@ enum ImportGranularityDef { One, } -#[derive(Deserialize, Debug, Copy, Clone)] +#[derive(Serialize, Deserialize, Debug, Copy, Clone)] #[serde(rename_all = "snake_case")] enum CallableCompletionDef { FillArguments, @@ -1993,54 +2108,54 @@ enum CallableCompletionDef { None, } -#[derive(Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(untagged)] enum CargoFeaturesDef { - #[serde(deserialize_with = "de_unit_v::all")] + #[serde(with = "unit_v::all")] All, Selected(Vec), } -#[derive(Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "snake_case")] -enum InvocationStrategy { +pub(crate) enum InvocationStrategy { Once, PerWorkspace, } -#[derive(Deserialize, Debug, Clone)] -struct CheckOnSaveTargets(#[serde(deserialize_with = "single_or_array")] Vec); +#[derive(Serialize, Deserialize, Debug, Clone)] +struct CheckOnSaveTargets(#[serde(with = "single_or_array")] Vec); -#[derive(Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "snake_case")] enum InvocationLocation { Root, Workspace, } -#[derive(Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(untagged)] enum LifetimeElisionDef { - #[serde(deserialize_with = "true_or_always")] + #[serde(with = "true_or_always")] Always, - #[serde(deserialize_with = "false_or_never")] + #[serde(with = "false_or_never")] Never, - #[serde(deserialize_with = "de_unit_v::skip_trivial")] + #[serde(with = "unit_v::skip_trivial")] SkipTrivial, } -#[derive(Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(untagged)] enum ClosureReturnTypeHintsDef { - #[serde(deserialize_with = "true_or_always")] + #[serde(with = "true_or_always")] Always, - #[serde(deserialize_with = "false_or_never")] + #[serde(with = "false_or_never")] Never, - #[serde(deserialize_with = "de_unit_v::with_block")] + #[serde(with = "unit_v::with_block")] WithBlock, } -#[derive(Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "snake_case")] enum ClosureStyle { ImplFn, @@ -2049,40 +2164,40 @@ enum ClosureStyle { Hide, } -#[derive(Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(untagged)] enum ReborrowHintsDef { - #[serde(deserialize_with = "true_or_always")] + #[serde(with = "true_or_always")] Always, - #[serde(deserialize_with = "false_or_never")] + #[serde(with = "false_or_never")] Never, - #[serde(deserialize_with = "de_unit_v::mutable")] + #[serde(with = "unit_v::mutable")] Mutable, } -#[derive(Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(untagged)] enum AdjustmentHintsDef { - #[serde(deserialize_with = "true_or_always")] + #[serde(with = "true_or_always")] Always, - #[serde(deserialize_with = "false_or_never")] + #[serde(with = "false_or_never")] Never, - #[serde(deserialize_with = "de_unit_v::reborrow")] + #[serde(with = "unit_v::reborrow")] Reborrow, } -#[derive(Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(untagged)] enum DiscriminantHintsDef { - #[serde(deserialize_with = "true_or_always")] + #[serde(with = "true_or_always")] Always, - #[serde(deserialize_with = "false_or_never")] + #[serde(with = "false_or_never")] Never, - #[serde(deserialize_with = "de_unit_v::fieldless")] + #[serde(with = "unit_v::fieldless")] Fieldless, } -#[derive(Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "snake_case")] enum AdjustmentHintsModeDef { Prefix, @@ -2091,7 +2206,7 @@ enum AdjustmentHintsModeDef { PreferPostfix, } -#[derive(Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "snake_case")] enum FilesWatcherDef { Client, @@ -2099,7 +2214,7 @@ enum FilesWatcherDef { Server, } -#[derive(Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "snake_case")] enum ImportPrefixDef { Plain, @@ -2109,40 +2224,51 @@ enum ImportPrefixDef { ByCrate, } -#[derive(Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "snake_case")] enum WorkspaceSymbolSearchScopeDef { Workspace, WorkspaceAndDependencies, } -#[derive(Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "snake_case")] enum SignatureDetail { Full, Parameters, } -#[derive(Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "snake_case")] enum WorkspaceSymbolSearchKindDef { OnlyTypes, AllSymbols, } -#[derive(Deserialize, Debug, Copy, Clone)] +#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq)] #[serde(rename_all = "snake_case")] #[serde(untagged)] -pub enum MemoryLayoutHoverRenderKindDef { - #[serde(deserialize_with = "de_unit_v::decimal")] +enum MemoryLayoutHoverRenderKindDef { + #[serde(with = "unit_v::decimal")] Decimal, - #[serde(deserialize_with = "de_unit_v::hexadecimal")] + #[serde(with = "unit_v::hexadecimal")] Hexadecimal, - #[serde(deserialize_with = "de_unit_v::both")] + #[serde(with = "unit_v::both")] Both, } -#[derive(Deserialize, Debug, Clone, PartialEq)] +#[test] +fn untagged_option_hover_render_kind() { + let hex = MemoryLayoutHoverRenderKindDef::Hexadecimal; + + let ser = serde_json::to_string(&Some(hex)).unwrap(); + assert_eq!(&ser, "\"hexadecimal\""); + + let opt: Option<_> = serde_json::from_str("\"hexadecimal\"").unwrap(); + assert_eq!(opt, Some(hex)); +} + +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[serde(rename_all = "snake_case")] #[serde(untagged)] pub enum TargetDirectory { @@ -2150,68 +2276,326 @@ pub enum TargetDirectory { Directory(Utf8PathBuf), } +macro_rules! _default_val { + (@verbatim: $s:literal, $ty:ty) => {{ + let default_: $ty = serde_json::from_str(&$s).unwrap(); + default_ + }}; + ($default:expr, $ty:ty) => {{ + let default_: $ty = $default; + default_ + }}; +} +use _default_val as default_val; + +macro_rules! _default_str { + (@verbatim: $s:literal, $_ty:ty) => { + $s.to_owned() + }; + ($default:expr, $ty:ty) => {{ + let val = default_val!($default, $ty); + serde_json::to_string_pretty(&val).unwrap() + }}; +} +use _default_str as default_str; + +macro_rules! _impl_for_config_data { + (local, $( + $(#[doc=$doc:literal])* + $vis:vis $field:ident : $ty:ty = $default:expr, + )* + ) => { + impl Config { + $( + $($doc)* + #[allow(non_snake_case)] + $vis fn $field(&self, _source_root: Option) -> &$ty { + if let Some(v) = self.client_config.local.$field.as_ref() { + return &v; + } + + if let Some(v) = self.user_config.local.$field.as_ref() { + return &v; + } + + &self.default_config.local.$field + } + )* + } + }; + (global, $( + $(#[doc=$doc:literal])* + $vis:vis $field:ident : $ty:ty = $default:expr, + )* + ) => { + impl Config { + $( + $($doc)* + #[allow(non_snake_case)] + $vis fn $field(&self) -> &$ty { + if let Some(v) = self.client_config.global.$field.as_ref() { + return &v; + } + + if let Some(v) = self.user_config.global.$field.as_ref() { + return &v; + } + + &self.default_config.global.$field + } + )* + } + }; + (client, $( + $(#[doc=$doc:literal])* + $vis:vis $field:ident : $ty:ty = $default:expr, + )* + ) => { + impl Config { + $( + $($doc)* + #[allow(non_snake_case)] + $vis fn $field(&self) -> &$ty { + if let Some(v) = self.client_config.global.$field.as_ref() { + return &v; + } + + &self.default_config.client.$field + } + )* + } + }; +} +use _impl_for_config_data as impl_for_config_data; + macro_rules! _config_data { - (struct $name:ident { + // modname is for the tests + ($(#[doc=$dox:literal])* $modname:ident: struct $name:ident <- $input:ident -> { $( $(#[doc=$doc:literal])* - $field:ident $(| $alias:ident)*: $ty:ty = $default:expr, + $vis:vis $field:ident $(| $alias:ident)*: $ty:ty = $(@$marker:ident: )? $default:expr, )* }) => { + /// Default config values for this grouping. #[allow(non_snake_case)] - #[derive(Debug, Clone)] + #[derive(Debug, Clone, Serialize)] struct $name { $($field: $ty,)* } - impl $name { - fn from_json(mut json: serde_json::Value, error_sink: &mut Vec<(String, serde_json::Error)>) -> $name { + + impl_for_config_data!{ + $modname, + $( + $vis $field : $ty = $default, + )* + } + + /// All fields `Option`, `None` representing fields not set in a particular JSON/TOML blob. + #[allow(non_snake_case)] + #[derive(Clone, Serialize, Default)] + struct $input { $( + #[serde(skip_serializing_if = "Option::is_none")] + $field: Option<$ty>, + )* } + + impl std::fmt::Debug for $input { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut s = f.debug_struct(stringify!($input)); + $( + if let Some(val) = self.$field.as_ref() { + s.field(stringify!($field), val); + } + )* + s.finish() + } + } + + impl Default for $name { + fn default() -> Self { $name {$( + $field: default_val!($(@$marker:)? $default, $ty), + )*} + } + } + + #[allow(unused)] + impl $name { + /// Applies overrides from some more local config blob, to self. + fn apply_input(&mut self, input: $input) { + $( + if let Some(value) = input.$field { + self.$field = value; + } + )* + } + + fn clone_with_overrides(&self, input: $input) -> Self { + Self {$( + $field: input.$field.unwrap_or_else(|| self.$field.clone()), + )*} + } + } + + #[allow(unused, clippy::ptr_arg)] + impl $input { + fn from_json(json: &mut serde_json::Value, error_sink: &mut Vec<(String, serde_json::Error)>) -> Self { + Self {$( $field: get_field( - &mut json, + json, error_sink, stringify!($field), None$(.or(Some(stringify!($alias))))*, - $default, ), )*} } - fn json_schema() -> serde_json::Value { - schema(&[ - $({ - let field = stringify!($field); - let ty = stringify!($ty); - - (field, ty, &[$($doc),*], $default) - },)* - ]) + fn from_toml(toml: &mut toml::Table, error_sink: &mut Vec<(String, toml::de::Error)>) -> Self { + Self {$( + $field: get_field_toml::<$ty>( + toml, + error_sink, + stringify!($field), + None$(.or(Some(stringify!($alias))))*, + ), + )*} } - #[cfg(test)] - fn manual() -> String { - manual(&[ + fn schema_fields(sink: &mut Vec) { + sink.extend_from_slice(&[ $({ let field = stringify!($field); let ty = stringify!($ty); + let default = default_str!($(@$marker:)? $default, $ty); - (field, ty, &[$($doc),*], $default) + (field, ty, &[$($doc),*], default) },)* ]) } } - #[test] - fn fields_are_sorted() { - [$(stringify!($field)),*].windows(2).for_each(|w| assert!(w[0] <= w[1], "{} <= {} does not hold", w[0], w[1])); + mod $modname { + #[test] + fn fields_are_sorted() { + let field_names: &'static [&'static str] = &[$(stringify!($field)),*]; + field_names.windows(2).for_each(|w| assert!(w[0] <= w[1], "{} <= {} does not hold", w[0], w[1])); + } } }; } use _config_data as config_data; +#[derive(Default, Debug, Clone)] +struct DefaultConfigData { + global: GlobalDefaultConfigData, + local: LocalDefaultConfigData, + #[allow(dead_code)] + client: ClientDefaultConfigData, +} + +/// All of the config levels, all fields `Option`, to describe fields that are actually set by +/// some rust-analyzer.toml file or JSON blob. An empty rust-analyzer.toml corresponds to +/// all fields being None. +#[derive(Debug, Clone, Default)] +struct FullConfigInput { + global: GlobalConfigInput, + local: LocalConfigInput, + #[allow(dead_code)] + client: ClientConfigInput, +} + +impl FullConfigInput { + fn from_json( + mut json: serde_json::Value, + error_sink: &mut Vec<(String, serde_json::Error)>, + ) -> FullConfigInput { + FullConfigInput { + global: GlobalConfigInput::from_json(&mut json, error_sink), + local: LocalConfigInput::from_json(&mut json, error_sink), + client: ClientConfigInput::from_json(&mut json, error_sink), + } + } + + fn schema_fields() -> Vec { + let mut fields = Vec::new(); + GlobalConfigInput::schema_fields(&mut fields); + LocalConfigInput::schema_fields(&mut fields); + ClientConfigInput::schema_fields(&mut fields); + // HACK: sort the fields, so the diffs on the generated docs/schema are smaller + fields.sort_by_key(|&(x, ..)| x); + fields + } + + fn json_schema() -> serde_json::Value { + schema(&Self::schema_fields()) + } + + #[cfg(test)] + fn manual() -> String { + manual(&Self::schema_fields()) + } +} + +/// All of the config levels, all fields `Option`, to describe fields that are actually set by +/// some rust-analyzer.toml file or JSON blob. An empty rust-analyzer.toml corresponds to +/// all fields being None. +#[derive(Debug, Clone, Default)] +struct GlobalLocalConfigInput { + global: GlobalConfigInput, + local: LocalConfigInput, +} + +impl GlobalLocalConfigInput { + #[allow(dead_code)] + fn from_toml( + mut toml: toml::Table, + error_sink: &mut Vec<(String, toml::de::Error)>, + ) -> GlobalLocalConfigInput { + GlobalLocalConfigInput { + global: GlobalConfigInput::from_toml(&mut toml, error_sink), + local: LocalConfigInput::from_toml(&mut toml, error_sink), + } + } +} + +fn get_field_toml( + val: &toml::Table, + error_sink: &mut Vec<(String, toml::de::Error)>, + field: &'static str, + alias: Option<&'static str>, +) -> Option { + alias + .into_iter() + .chain(iter::once(field)) + .filter_map(move |field| { + let subkeys = field.split('_'); + let mut v = val; + for subkey in subkeys { + if let Some(val) = v.get(subkey) { + if let Some(map) = val.as_table() { + v = map; + } else { + return Some(toml::Value::try_into(val.clone()).map_err(|e| (e, v))); + } + } else { + return None; + } + } + None + }) + .find(Result::is_ok) + .and_then(|res| match res { + Ok(it) => Some(it), + Err((e, pointer)) => { + error_sink.push((pointer.to_string(), e)); + None + } + }) +} + fn get_field( json: &mut serde_json::Value, error_sink: &mut Vec<(String, serde_json::Error)>, field: &'static str, alias: Option<&'static str>, - default: &str, -) -> T { +) -> Option { // XXX: check alias first, to work around the VS Code where it pre-fills the // defaults instead of sending an empty object. alias @@ -2232,12 +2616,11 @@ fn get_field( None } }) - .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 { +type SchemaField = (&'static str, &'static str, &'static [&'static str], String); + +fn schema(fields: &[SchemaField]) -> serde_json::Value { let map = fields .iter() .map(|(field, ty, doc, default)| { @@ -2288,7 +2671,7 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json "FxHashMap, Box<[Box]>>" => set! { "type": "object", }, - "FxHashMap" => set! { + "IndexMap" => set! { "type": "object", }, "FxHashMap" => set! { @@ -2297,6 +2680,9 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json "FxHashMap, usize>" => set! { "type": "object", }, + "FxHashMap>" => set! { + "type": "object", + }, "Option" => set! { "type": ["null", "integer"], "minimum": 0, @@ -2599,7 +2985,7 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json } #[cfg(test)] -fn manual(fields: &[(&'static str, &'static str, &[&str], &str)]) -> String { +fn manual(fields: &[SchemaField]) -> String { fields.iter().fold(String::new(), |mut acc, (field, _ty, doc, default)| { let name = format!("rust-analyzer.{}", field.replace('_', ".")); let doc = doc_comment_to_string(doc); @@ -2694,7 +3080,7 @@ mod tests { #[test] fn generate_config_documentation() { let docs_path = project_root().join("docs/user/generated_config.adoc"); - let expected = ConfigData::manual(); + let expected = FullConfigInput::manual(); ensure_file_contents(&docs_path, &expected); } @@ -2766,9 +3152,9 @@ mod tests { "rust": { "analyzerTargetDir": null } })) .unwrap(); - assert_eq!(config.data.cargo_targetDir, None); + assert_eq!(config.cargo_targetDir(), &None); assert!( - matches!(config.flycheck(), FlycheckConfig::CargoCommand { target_dir, .. } if target_dir.is_none()) + matches!(config.flycheck(), FlycheckConfig::CargoCommand { options, .. } if options.target_dir.is_none()) ); } @@ -2785,9 +3171,9 @@ mod tests { "rust": { "analyzerTargetDir": true } })) .unwrap(); - assert_eq!(config.data.cargo_targetDir, Some(TargetDirectory::UseSubdirectory(true))); + assert_eq!(config.cargo_targetDir(), &Some(TargetDirectory::UseSubdirectory(true))); assert!( - matches!(config.flycheck(), FlycheckConfig::CargoCommand { target_dir, .. } if target_dir == Some(Utf8PathBuf::from("target/rust-analyzer"))) + matches!(config.flycheck(), FlycheckConfig::CargoCommand { options, .. } if options.target_dir == Some(Utf8PathBuf::from("target/rust-analyzer"))) ); } @@ -2805,11 +3191,11 @@ mod tests { })) .unwrap(); assert_eq!( - config.data.cargo_targetDir, - Some(TargetDirectory::Directory(Utf8PathBuf::from("other_folder"))) + config.cargo_targetDir(), + &Some(TargetDirectory::Directory(Utf8PathBuf::from("other_folder"))) ); assert!( - matches!(config.flycheck(), FlycheckConfig::CargoCommand { target_dir, .. } if target_dir == Some(Utf8PathBuf::from("other_folder"))) + matches!(config.flycheck(), FlycheckConfig::CargoCommand { options, .. } if options.target_dir == Some(Utf8PathBuf::from("other_folder"))) ); } } 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 a0a53f545c9e..65a9a4914934 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs @@ -154,10 +154,12 @@ pub(crate) fn fetch_native_diagnostics( .copied() .filter_map(|file_id| { let line_index = snapshot.file_line_index(file_id).ok()?; + let source_root = snapshot.analysis.source_root(file_id).ok()?; + let diagnostics = snapshot .analysis .diagnostics( - &snapshot.config.diagnostics(), + &snapshot.config.diagnostics(Some(source_root)), ide::AssistResolveStrategy::None, file_id, ) 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 7c4deac93f21..3d3f94401991 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 @@ -68,8 +68,13 @@ fn location( let range = { let position_encoding = snap.config.position_encoding(); lsp_types::Range::new( - position(&position_encoding, span, span.line_start, span.column_start), - position(&position_encoding, span, span.line_end, span.column_end), + position( + &position_encoding, + span, + span.line_start, + span.column_start.saturating_sub(1), + ), + position(&position_encoding, span, span.line_end, span.column_end.saturating_sub(1)), ) }; lsp_types::Location::new(uri, range) @@ -78,10 +83,10 @@ fn location( fn position( position_encoding: &PositionEncoding, span: &DiagnosticSpan, - line_offset: usize, + line_number: usize, column_offset_utf32: usize, ) -> lsp_types::Position { - let line_index = line_offset - span.line_start; + let line_index = line_number - span.line_start; let column_offset_encoded = match span.text.get(line_index) { // Fast path. @@ -104,8 +109,8 @@ fn position( }; lsp_types::Position { - line: (line_offset as u32).saturating_sub(1), - character: (column_offset_encoded as u32).saturating_sub(1), + line: (line_number as u32).saturating_sub(1), + character: column_offset_encoded as u32, } } 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 8516ffa0dfae..e9bca19af646 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 @@ -72,7 +72,6 @@ pub(crate) struct GlobalState { // status pub(crate) shutdown_requested: bool, - pub(crate) send_hint_refresh_query: bool, pub(crate) last_reported_status: Option, // proc macros @@ -86,7 +85,10 @@ pub(crate) struct GlobalState { pub(crate) last_flycheck_error: Option, // Test explorer - pub(crate) test_run_session: Option, + pub(crate) test_run_session: Option>, + pub(crate) test_run_sender: Sender, + pub(crate) test_run_receiver: Receiver, + pub(crate) test_run_remaining_jobs: usize, // VFS pub(crate) loader: Handle, Receiver>, @@ -123,6 +125,7 @@ pub(crate) struct GlobalState { /// to invalidate any salsa caches. pub(crate) workspaces: Arc>, pub(crate) crate_graph_file_dependencies: FxHashSet, + pub(crate) detached_files: FxHashSet, // op queues pub(crate) fetch_workspaces_queue: @@ -187,10 +190,11 @@ impl GlobalState { }; let mut analysis_host = AnalysisHost::new(config.lru_parse_query_capacity()); - if let Some(capacities) = config.lru_query_capacities() { + if let Some(capacities) = config.lru_query_capacities_config() { analysis_host.update_lru_capacities(capacities); } let (flycheck_sender, flycheck_receiver) = unbounded(); + let (test_run_sender, test_run_receiver) = unbounded(); let mut this = GlobalState { sender, req_queue: ReqQueue::default(), @@ -203,7 +207,6 @@ impl GlobalState { mem_docs: MemDocs::default(), semantic_tokens_cache: Arc::new(Default::default()), shutdown_requested: false, - send_hint_refresh_query: false, last_reported_status: None, source_root_config: SourceRootConfig::default(), local_roots_parent_map: FxHashMap::default(), @@ -219,6 +222,9 @@ impl GlobalState { last_flycheck_error: None, test_run_session: None, + test_run_sender, + test_run_receiver, + test_run_remaining_jobs: 0, vfs: Arc::new(RwLock::new((vfs::Vfs::default(), IntMap::default()))), vfs_config_version: 0, @@ -228,6 +234,7 @@ impl GlobalState { workspaces: Arc::from(Vec::new()), crate_graph_file_dependencies: FxHashSet::default(), + detached_files: FxHashSet::default(), fetch_workspaces_queue: OpQueue::default(), fetch_build_data_queue: OpQueue::default(), fetch_proc_macros_queue: OpQueue::default(), @@ -514,7 +521,7 @@ impl GlobalStateSnapshot { cargo.target_by_root(path).map(|it| (cargo, it)) } ProjectWorkspace::Json { .. } => None, - ProjectWorkspace::DetachedFiles { .. } => None, + ProjectWorkspace::DetachedFile { .. } => None, }) } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs index b5c4a4f435e7..4b8c3d06ce4c 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs @@ -105,7 +105,7 @@ pub(crate) fn handle_did_change_text_document( ) .into_bytes(); if *data != new_contents { - *data = new_contents.clone(); + data.clone_from(&new_contents); state.vfs.write().0.set_file_contents(path, Some(new_contents)); } } @@ -154,6 +154,10 @@ pub(crate) fn handle_did_save_text_document( state .fetch_workspaces_queue .request_op(format!("workspace vfs file change saved {abs_path}"), false); + } else if state.detached_files.contains(abs_path) { + state + .fetch_workspaces_queue + .request_op(format!("detached file saved {abs_path}"), false); } } @@ -296,15 +300,15 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { }) } project_model::ProjectWorkspace::Json { project, .. } => { - if !project - .crates() - .any(|(c, _)| crate_ids.iter().any(|&crate_id| crate_id == c)) - { + if !project.crates().any(|(_, krate)| { + crate_root_paths.contains(&krate.root_module.as_path()) + }) { return None; } None } - project_model::ProjectWorkspace::DetachedFiles { .. } => return None, + // FIXME + project_model::ProjectWorkspace::DetachedFile { .. } => return None, }; Some((idx, package)) }); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs index 77692ed3ae76..cf97d7d9d234 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs @@ -101,7 +101,7 @@ pub(crate) fn handle_analyzer_status( "Workspace root folders: {:?}", snap.workspaces .iter() - .flat_map(|ws| ws.workspace_definition_path()) + .map(|ws| ws.workspace_definition_path()) .collect::>() ); } @@ -219,14 +219,28 @@ pub(crate) fn handle_run_test( .unwrap_or_default(), None => "".to_owned(), }; - let handle = if lca.is_empty() { - flycheck::CargoTestHandle::new(None) + let test_path = if lca.is_empty() { + None } else if let Some((_, path)) = lca.split_once("::") { - flycheck::CargoTestHandle::new(Some(path)) + Some(path) } else { - flycheck::CargoTestHandle::new(None) + None }; - state.test_run_session = Some(handle?); + let mut handles = vec![]; + for ws in &*state.workspaces { + if let ProjectWorkspace::Cargo { cargo, .. } = ws { + let handle = flycheck::CargoTestHandle::new( + test_path, + state.config.cargo_test_options(), + cargo.workspace_root(), + state.test_run_sender.clone(), + )?; + handles.push(handle); + } + } + // Each process send finished signal twice, once for stdout and once for stderr + state.test_run_remaining_jobs = 2 * handles.len(); + state.test_run_session = Some(handles); Ok(()) } @@ -355,8 +369,9 @@ pub(crate) fn handle_join_lines( ) -> anyhow::Result> { let _p = tracing::span!(tracing::Level::INFO, "handle_join_lines").entered(); - let config = snap.config.join_lines(); let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; + let source_root = snap.analysis.source_root(file_id)?; + let config = snap.config.join_lines(Some(source_root)); let line_index = snap.file_line_index(file_id)?; let mut res = TextEdit::default(); @@ -923,7 +938,8 @@ pub(crate) fn handle_completion( let completion_trigger_character = params.context.and_then(|ctx| ctx.trigger_character).and_then(|s| s.chars().next()); - let completion_config = &snap.config.completion(); + let source_root = snap.analysis.source_root(position.file_id)?; + let completion_config = &snap.config.completion(Some(source_root)); let items = match snap.analysis.completions( completion_config, position, @@ -964,11 +980,12 @@ pub(crate) fn handle_completion_resolve( let file_id = from_proto::file_id(&snap, &resolve_data.position.text_document.uri)?; let line_index = snap.file_line_index(file_id)?; let offset = from_proto::offset(&line_index, resolve_data.position.position)?; + let source_root = snap.analysis.source_root(file_id)?; let additional_edits = snap .analysis .resolve_completion_edits( - &snap.config.completion(), + &snap.config.completion(Some(source_root)), FilePosition { file_id, offset }, resolve_data .imports @@ -1038,16 +1055,17 @@ pub(crate) fn handle_hover( PositionOrRange::Position(position) => Range::new(position, position), PositionOrRange::Range(range) => range, }; - let file_range = from_proto::file_range(&snap, ¶ms.text_document, range)?; - let info = match snap.analysis.hover(&snap.config.hover(), file_range)? { + + let hover = snap.config.hover(); + let info = match snap.analysis.hover(&hover, file_range)? { None => return Ok(None), Some(info) => info, }; let line_index = snap.file_line_index(file_range.file_id)?; let range = to_proto::range(&line_index, info.range); - let markup_kind = snap.config.hover().format; + let markup_kind = hover.format; let hover = lsp_ext::Hover { hover: lsp_types::Hover { contents: HoverContents::Markup(to_proto::markup_content( @@ -1146,8 +1164,8 @@ pub(crate) fn handle_references( .flat_map(|(file_id, refs)| { refs.into_iter() .filter(|&(_, category)| { - (!exclude_imports || category != Some(ReferenceCategory::Import)) - && (!exclude_tests || category != Some(ReferenceCategory::Test)) + (!exclude_imports || !category.contains(ReferenceCategory::IMPORT)) + && (!exclude_tests || !category.contains(ReferenceCategory::TEST)) }) .map(move |(range, _)| FileRange { file_id, range }) }) @@ -1191,11 +1209,12 @@ pub(crate) fn handle_code_action( return Ok(None); } - let line_index = - snap.file_line_index(from_proto::file_id(&snap, ¶ms.text_document.uri)?)?; + let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; + let line_index = snap.file_line_index(file_id)?; let frange = from_proto::file_range(&snap, ¶ms.text_document, params.range)?; + let source_root = snap.analysis.source_root(file_id)?; - let mut assists_config = snap.config.assist(); + let mut assists_config = snap.config.assist(Some(source_root)); assists_config.allowed = params .context .only @@ -1212,7 +1231,7 @@ pub(crate) fn handle_code_action( }; let assists = snap.analysis.assists_with_fixes( &assists_config, - &snap.config.diagnostics(), + &snap.config.diagnostics(Some(source_root)), resolve, frange, )?; @@ -1266,8 +1285,9 @@ pub(crate) fn handle_code_action_resolve( let line_index = snap.file_line_index(file_id)?; let range = from_proto::text_range(&line_index, params.code_action_params.range)?; let frange = FileRange { file_id, range }; + let source_root = snap.analysis.source_root(file_id)?; - let mut assists_config = snap.config.assist(); + let mut assists_config = snap.config.assist(Some(source_root)); assists_config.allowed = params .code_action_params .context @@ -1290,7 +1310,7 @@ pub(crate) fn handle_code_action_resolve( let assists = snap.analysis.assists_with_fixes( &assists_config, - &snap.config.diagnostics(), + &snap.config.diagnostics(Some(source_root)), AssistResolveStrategy::Single(assist_resolve), frange, )?; @@ -1419,8 +1439,12 @@ pub(crate) fn handle_document_highlight( let _p = tracing::span!(tracing::Level::INFO, "handle_document_highlight").entered(); let position = from_proto::file_position(&snap, params.text_document_position_params)?; let line_index = snap.file_line_index(position.file_id)?; + let source_root = snap.analysis.source_root(position.file_id)?; - let refs = match snap.analysis.highlight_related(snap.config.highlight_related(), position)? { + let refs = match snap + .analysis + .highlight_related(snap.config.highlight_related(Some(source_root)), position)? + { None => return Ok(None), Some(refs) => refs, }; @@ -1428,7 +1452,7 @@ pub(crate) fn handle_document_highlight( .into_iter() .map(|ide::HighlightedRange { range, category }| lsp_types::DocumentHighlight { range: to_proto::range(&line_index, range), - kind: category.and_then(to_proto::document_highlight_kind), + kind: to_proto::document_highlight_kind(category), }) .collect(); Ok(Some(res)) @@ -1466,7 +1490,9 @@ pub(crate) fn handle_inlay_hints( params.range, )?; let line_index = snap.file_line_index(file_id)?; - let inlay_hints_config = snap.config.inlay_hints(); + let source_root = snap.analysis.source_root(file_id)?; + + let inlay_hints_config = snap.config.inlay_hints(Some(source_root)); Ok(Some( snap.analysis .inlay_hints(&inlay_hints_config, file_id, Some(range))? @@ -1490,29 +1516,33 @@ pub(crate) fn handle_inlay_hints_resolve( ) -> anyhow::Result { let _p = tracing::span!(tracing::Level::INFO, "handle_inlay_hints_resolve").entered(); - let data = match original_hint.data.take() { - Some(it) => it, - None => return Ok(original_hint), - }; - + let Some(data) = original_hint.data.take() else { return Ok(original_hint) }; let resolve_data: lsp_ext::InlayHintResolveData = serde_json::from_value(data)?; + let Some(hash) = resolve_data.hash.parse().ok() else { return Ok(original_hint) }; let file_id = FileId::from_raw(resolve_data.file_id); anyhow::ensure!(snap.file_exists(file_id), "Invalid LSP resolve data"); let line_index = snap.file_line_index(file_id)?; let hint_position = from_proto::offset(&line_index, original_hint.position)?; - let mut forced_resolve_inlay_hints_config = snap.config.inlay_hints(); + let source_root = snap.analysis.source_root(file_id)?; + + let mut forced_resolve_inlay_hints_config = snap.config.inlay_hints(Some(source_root)); forced_resolve_inlay_hints_config.fields_to_resolve = InlayFieldsToResolve::empty(); let resolve_hints = snap.analysis.inlay_hints_resolve( &forced_resolve_inlay_hints_config, file_id, hint_position, - resolve_data.hash, + hash, + |hint| { + std::hash::BuildHasher::hash_one( + &std::hash::BuildHasherDefault::::default(), + hint, + ) + }, )?; - let mut resolved_hints = resolve_hints - .into_iter() - .filter_map(|it| { + Ok(resolve_hints + .and_then(|it| { to_proto::inlay_hint( &snap, &forced_resolve_inlay_hints_config.fields_to_resolve, @@ -1523,13 +1553,8 @@ pub(crate) fn handle_inlay_hints_resolve( .ok() }) .filter(|hint| hint.position == original_hint.position) - .filter(|hint| hint.kind == original_hint.kind); - if let Some(resolved_hint) = resolved_hints.next() { - if resolved_hints.next().is_none() { - return Ok(resolved_hint); - } - } - Ok(original_hint) + .filter(|hint| hint.kind == original_hint.kind) + .unwrap_or(original_hint)) } pub(crate) fn handle_call_hierarchy_prepare( @@ -1633,8 +1658,9 @@ pub(crate) fn handle_semantic_tokens_full( let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; let text = snap.analysis.file_text(file_id)?; let line_index = snap.file_line_index(file_id)?; + let source_root = snap.analysis.source_root(file_id)?; - let mut highlight_config = snap.config.highlighting_config(); + let mut highlight_config = snap.config.highlighting_config(Some(source_root)); // Avoid flashing a bunch of unresolved references when the proc-macro servers haven't been spawned yet. highlight_config.syntactic_name_ref_highlighting = snap.workspaces.is_empty() || !snap.proc_macros_loaded; @@ -1645,7 +1671,7 @@ pub(crate) fn handle_semantic_tokens_full( &line_index, highlights, snap.config.semantics_tokens_augments_syntax_tokens(), - snap.config.highlighting_non_standard_tokens(), + snap.config.highlighting_non_standard_tokens(Some(source_root)), ); // Unconditionally cache the tokens @@ -1663,8 +1689,9 @@ pub(crate) fn handle_semantic_tokens_full_delta( let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; let text = snap.analysis.file_text(file_id)?; let line_index = snap.file_line_index(file_id)?; + let source_root = snap.analysis.source_root(file_id)?; - let mut highlight_config = snap.config.highlighting_config(); + let mut highlight_config = snap.config.highlighting_config(Some(source_root)); // Avoid flashing a bunch of unresolved references when the proc-macro servers haven't been spawned yet. highlight_config.syntactic_name_ref_highlighting = snap.workspaces.is_empty() || !snap.proc_macros_loaded; @@ -1675,7 +1702,7 @@ pub(crate) fn handle_semantic_tokens_full_delta( &line_index, highlights, snap.config.semantics_tokens_augments_syntax_tokens(), - snap.config.highlighting_non_standard_tokens(), + snap.config.highlighting_non_standard_tokens(Some(source_root)), ); let cached_tokens = snap.semantic_tokens_cache.lock().remove(¶ms.text_document.uri); @@ -1706,8 +1733,9 @@ pub(crate) fn handle_semantic_tokens_range( let frange = from_proto::file_range(&snap, ¶ms.text_document, params.range)?; let text = snap.analysis.file_text(frange.file_id)?; let line_index = snap.file_line_index(frange.file_id)?; + let source_root = snap.analysis.source_root(frange.file_id)?; - let mut highlight_config = snap.config.highlighting_config(); + let mut highlight_config = snap.config.highlighting_config(Some(source_root)); // Avoid flashing a bunch of unresolved references when the proc-macro servers haven't been spawned yet. highlight_config.syntactic_name_ref_highlighting = snap.workspaces.is_empty() || !snap.proc_macros_loaded; @@ -1718,7 +1746,7 @@ pub(crate) fn handle_semantic_tokens_range( &line_index, highlights, snap.config.semantics_tokens_augments_syntax_tokens(), - snap.config.highlighting_non_standard_tokens(), + snap.config.highlighting_non_standard_tokens(Some(source_root)), ); Ok(Some(semantic_tokens.into())) } @@ -1733,7 +1761,9 @@ pub(crate) fn handle_open_docs( 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, + ProjectWorkspace::DetachedFile { cargo_script, sysroot, .. } => { + cargo_script.as_ref().zip(Some(sysroot.as_ref().ok())) + } }); let (cargo, sysroot) = match ws_and_sysroot { @@ -1931,8 +1961,8 @@ fn goto_type_action_links( snap: &GlobalStateSnapshot, nav_targets: &[HoverGotoTypeData], ) -> Option { - if !snap.config.hover_actions().goto_type_def - || nav_targets.is_empty() + if nav_targets.is_empty() + || !snap.config.hover_actions().goto_type_def || !snap.config.client_commands().goto_location { return None; 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 2731e845f351..7b385ca9d960 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 @@ -40,7 +40,7 @@ fn integrated_highlighting_benchmark() { }; let load_cargo_config = LoadCargoConfig { load_out_dirs_from_check: true, - with_proc_macro_server: ProcMacroServerChoice::None, + with_proc_macro_server: ProcMacroServerChoice::Sysroot, prefill_caches: false, }; @@ -100,7 +100,7 @@ fn integrated_completion_benchmark() { }; let load_cargo_config = LoadCargoConfig { load_out_dirs_from_check: true, - with_proc_macro_server: ProcMacroServerChoice::None, + with_proc_macro_server: ProcMacroServerChoice::Sysroot, prefill_caches: true, }; @@ -262,7 +262,7 @@ fn integrated_diagnostics_benchmark() { }; let load_cargo_config = LoadCargoConfig { load_out_dirs_from_check: true, - with_proc_macro_server: ProcMacroServerChoice::None, + with_proc_macro_server: ProcMacroServerChoice::Sysroot, prefill_caches: true, }; @@ -300,7 +300,7 @@ fn integrated_diagnostics_benchmark() { .diagnostics(&diagnostics_config, ide::AssistResolveStrategy::None, file_id) .unwrap(); - let _g = crate::tracing::hprof::init("*>1"); + let _g = crate::tracing::hprof::init("*"); { let _it = stdx::timeit("change"); 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 eac982f1b27b..12f8e71c9811 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 @@ -463,13 +463,6 @@ pub struct TestInfo { pub runnable: Runnable, } -#[derive(Serialize, Deserialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct InlayHintsParams { - pub text_document: TextDocumentIdentifier, - pub range: Option, -} - pub enum Ssr {} impl Request for Ssr { @@ -801,7 +794,8 @@ pub struct CompletionResolveData { #[derive(Debug, Serialize, Deserialize)] pub struct InlayHintResolveData { pub file_id: u32, - pub hash: u64, + // This is a string instead of a u64 as javascript can't represent u64 fully + pub hash: String, } #[derive(Debug, Serialize, Deserialize)] diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs index 3e00222b752d..991c10743f7a 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs @@ -17,15 +17,19 @@ macro_rules! define_semantic_token_types { } ) => { - $(pub(crate) const $standard: SemanticTokenType = SemanticTokenType::$standard;)* - $(pub(crate) const $custom: SemanticTokenType = SemanticTokenType::new($string);)* + pub(crate) mod types { + use super::SemanticTokenType; + $(pub(crate) const $standard: SemanticTokenType = SemanticTokenType::$standard;)* + $(pub(crate) const $custom: SemanticTokenType = SemanticTokenType::new($string);)* + } pub(crate) const SUPPORTED_TYPES: &[SemanticTokenType] = &[ $(SemanticTokenType::$standard,)* - $($custom),* + $(self::types::$custom),* ]; pub(crate) fn standard_fallback_type(token: SemanticTokenType) -> Option { + use self::types::*; $( if token == $custom { None $(.or(Some(SemanticTokenType::$fallback)))? @@ -61,39 +65,41 @@ define_semantic_token_types![ custom { (ANGLE, "angle"), (ARITHMETIC, "arithmetic") => OPERATOR, - (ATTRIBUTE, "attribute") => DECORATOR, (ATTRIBUTE_BRACKET, "attributeBracket") => DECORATOR, + (ATTRIBUTE, "attribute") => DECORATOR, (BITWISE, "bitwise") => OPERATOR, (BOOLEAN, "boolean"), (BRACE, "brace"), (BRACKET, "bracket"), (BUILTIN_ATTRIBUTE, "builtinAttribute") => DECORATOR, - (BUILTIN_TYPE, "builtinType"), + (BUILTIN_TYPE, "builtinType") => TYPE, (CHAR, "character") => STRING, (COLON, "colon"), (COMMA, "comma"), (COMPARISON, "comparison") => OPERATOR, (CONST_PARAMETER, "constParameter"), - (DERIVE, "derive") => DECORATOR, + (CONST, "const") => VARIABLE, (DERIVE_HELPER, "deriveHelper") => DECORATOR, + (DERIVE, "derive") => DECORATOR, (DOT, "dot"), (ESCAPE_SEQUENCE, "escapeSequence") => STRING, - (INVALID_ESCAPE_SEQUENCE, "invalidEscapeSequence") => STRING, (FORMAT_SPECIFIER, "formatSpecifier") => STRING, (GENERIC, "generic") => TYPE_PARAMETER, + (INVALID_ESCAPE_SEQUENCE, "invalidEscapeSequence") => STRING, (LABEL, "label"), (LIFETIME, "lifetime"), (LOGICAL, "logical") => OPERATOR, (MACRO_BANG, "macroBang") => MACRO, - (PROC_MACRO, "procMacro") => MACRO, (PARENTHESIS, "parenthesis"), + (PROC_MACRO, "procMacro") => MACRO, (PUNCTUATION, "punctuation"), (SELF_KEYWORD, "selfKeyword") => KEYWORD, (SELF_TYPE_KEYWORD, "selfTypeKeyword") => KEYWORD, (SEMICOLON, "semicolon"), - (TYPE_ALIAS, "typeAlias"), + (STATIC, "static") => VARIABLE, (TOOL_MODULE, "toolModule") => DECORATOR, - (UNION, "union"), + (TYPE_ALIAS, "typeAlias") => TYPE, + (UNION, "union") => TYPE, (UNRESOLVED_REFERENCE, "unresolvedReference"), } ]; @@ -112,13 +118,16 @@ macro_rules! define_semantic_token_modifiers { } ) => { + pub(crate) mod modifiers { + use super::SemanticTokenModifier; - $(pub(crate) const $standard: SemanticTokenModifier = SemanticTokenModifier::$standard;)* - $(pub(crate) const $custom: SemanticTokenModifier = SemanticTokenModifier::new($string);)* + $(pub(crate) const $standard: SemanticTokenModifier = SemanticTokenModifier::$standard;)* + $(pub(crate) const $custom: SemanticTokenModifier = SemanticTokenModifier::new($string);)* + } pub(crate) const SUPPORTED_MODIFIERS: &[SemanticTokenModifier] = &[ $(SemanticTokenModifier::$standard,)* - $($custom),* + $(self::modifiers::$custom),* ]; const LAST_STANDARD_MOD: usize = count_tts!($($standard)*); @@ -145,8 +154,8 @@ define_semantic_token_modifiers![ (INTRA_DOC_LINK, "intraDocLink"), (LIBRARY, "library"), (MACRO_MODIFIER, "macro"), - (PROC_MACRO_MODIFIER, "proc_macro"), (MUTABLE, "mutable"), + (PROC_MACRO_MODIFIER, "procMacro"), (PUBLIC, "public"), (REFERENCE, "reference"), (TRAIT_MODIFIER, "trait"), diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs index d8bb12528b98..d02f4612dc1e 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs @@ -92,12 +92,13 @@ pub(crate) fn structure_node_kind(kind: StructureNodeKind) -> lsp_types::SymbolK pub(crate) fn document_highlight_kind( category: ReferenceCategory, ) -> Option { - match category { - ReferenceCategory::Read => Some(lsp_types::DocumentHighlightKind::READ), - ReferenceCategory::Write => Some(lsp_types::DocumentHighlightKind::WRITE), - ReferenceCategory::Import => None, - ReferenceCategory::Test => None, + if category.contains(ReferenceCategory::WRITE) { + return Some(lsp_types::DocumentHighlightKind::WRITE); } + if category.contains(ReferenceCategory::READ) { + return Some(lsp_types::DocumentHighlightKind::READ); + } + None } pub(crate) fn diagnostic_severity(severity: Severity) -> lsp_types::DiagnosticSeverity { @@ -233,7 +234,7 @@ pub(crate) fn completion_items( completion_item(&mut res, config, line_index, &tdpp, max_relevance, item); } - if let Some(limit) = config.completion().limit { + if let Some(limit) = config.completion(None).limit { res.sort_by(|item1, item2| item1.sort_text.cmp(&item2.sort_text)); res.truncate(limit); } @@ -317,7 +318,7 @@ fn completion_item( set_score(&mut lsp_item, max_relevance, item.relevance); - if config.completion().enable_imports_on_the_fly && !item.import_to_add.is_empty() { + if config.completion(None).enable_imports_on_the_fly && !item.import_to_add.is_empty() { let imports = item .import_to_add .into_iter() @@ -479,7 +480,11 @@ pub(crate) fn inlay_hint( let data = match resolve_hash { Some(hash) if something_to_resolve => Some( - to_value(lsp_ext::InlayHintResolveData { file_id: file_id.index(), hash }).unwrap(), + to_value(lsp_ext::InlayHintResolveData { + file_id: file_id.index(), + hash: hash.to_string(), + }) + .unwrap(), ), _ => None, }; @@ -650,97 +655,99 @@ pub(crate) fn semantic_token_delta( fn semantic_token_type_and_modifiers( highlight: Highlight, ) -> (lsp_types::SemanticTokenType, semantic_tokens::ModifierSet) { + use semantic_tokens::{modifiers as mods, types}; + let ty = match highlight.tag { HlTag::Symbol(symbol) => match symbol { - SymbolKind::Attribute => semantic_tokens::DECORATOR, - SymbolKind::Derive => semantic_tokens::DERIVE, - SymbolKind::DeriveHelper => semantic_tokens::DERIVE_HELPER, - SymbolKind::Module => semantic_tokens::NAMESPACE, - SymbolKind::Impl => semantic_tokens::TYPE_ALIAS, - SymbolKind::Field => semantic_tokens::PROPERTY, - SymbolKind::TypeParam => semantic_tokens::TYPE_PARAMETER, - SymbolKind::ConstParam => semantic_tokens::CONST_PARAMETER, - SymbolKind::LifetimeParam => semantic_tokens::LIFETIME, - SymbolKind::Label => semantic_tokens::LABEL, - SymbolKind::ValueParam => semantic_tokens::PARAMETER, - SymbolKind::SelfParam => semantic_tokens::SELF_KEYWORD, - SymbolKind::SelfType => semantic_tokens::SELF_TYPE_KEYWORD, - SymbolKind::Local => semantic_tokens::VARIABLE, - SymbolKind::Method => semantic_tokens::METHOD, - SymbolKind::Function => semantic_tokens::FUNCTION, - SymbolKind::Const => semantic_tokens::VARIABLE, - SymbolKind::Static => semantic_tokens::VARIABLE, - SymbolKind::Struct => semantic_tokens::STRUCT, - SymbolKind::Enum => semantic_tokens::ENUM, - SymbolKind::Variant => semantic_tokens::ENUM_MEMBER, - SymbolKind::Union => semantic_tokens::UNION, - SymbolKind::TypeAlias => semantic_tokens::TYPE_ALIAS, - SymbolKind::Trait => semantic_tokens::INTERFACE, - SymbolKind::TraitAlias => semantic_tokens::INTERFACE, - SymbolKind::Macro => semantic_tokens::MACRO, - SymbolKind::ProcMacro => semantic_tokens::PROC_MACRO, - SymbolKind::BuiltinAttr => semantic_tokens::BUILTIN_ATTRIBUTE, - SymbolKind::ToolModule => semantic_tokens::TOOL_MODULE, + SymbolKind::Attribute => types::DECORATOR, + SymbolKind::Derive => types::DERIVE, + SymbolKind::DeriveHelper => types::DERIVE_HELPER, + SymbolKind::Module => types::NAMESPACE, + SymbolKind::Impl => types::TYPE_ALIAS, + SymbolKind::Field => types::PROPERTY, + SymbolKind::TypeParam => types::TYPE_PARAMETER, + SymbolKind::ConstParam => types::CONST_PARAMETER, + SymbolKind::LifetimeParam => types::LIFETIME, + SymbolKind::Label => types::LABEL, + SymbolKind::ValueParam => types::PARAMETER, + SymbolKind::SelfParam => types::SELF_KEYWORD, + SymbolKind::SelfType => types::SELF_TYPE_KEYWORD, + SymbolKind::Local => types::VARIABLE, + SymbolKind::Method => types::METHOD, + SymbolKind::Function => types::FUNCTION, + SymbolKind::Const => types::CONST, + SymbolKind::Static => types::STATIC, + SymbolKind::Struct => types::STRUCT, + SymbolKind::Enum => types::ENUM, + SymbolKind::Variant => types::ENUM_MEMBER, + SymbolKind::Union => types::UNION, + SymbolKind::TypeAlias => types::TYPE_ALIAS, + SymbolKind::Trait => types::INTERFACE, + SymbolKind::TraitAlias => types::INTERFACE, + SymbolKind::Macro => types::MACRO, + SymbolKind::ProcMacro => types::PROC_MACRO, + SymbolKind::BuiltinAttr => types::BUILTIN_ATTRIBUTE, + SymbolKind::ToolModule => types::TOOL_MODULE, }, - HlTag::AttributeBracket => semantic_tokens::ATTRIBUTE_BRACKET, - HlTag::BoolLiteral => semantic_tokens::BOOLEAN, - HlTag::BuiltinType => semantic_tokens::BUILTIN_TYPE, - HlTag::ByteLiteral | HlTag::NumericLiteral => semantic_tokens::NUMBER, - HlTag::CharLiteral => semantic_tokens::CHAR, - HlTag::Comment => semantic_tokens::COMMENT, - HlTag::EscapeSequence => semantic_tokens::ESCAPE_SEQUENCE, - HlTag::InvalidEscapeSequence => semantic_tokens::INVALID_ESCAPE_SEQUENCE, - HlTag::FormatSpecifier => semantic_tokens::FORMAT_SPECIFIER, - HlTag::Keyword => semantic_tokens::KEYWORD, - HlTag::None => semantic_tokens::GENERIC, + HlTag::AttributeBracket => types::ATTRIBUTE_BRACKET, + HlTag::BoolLiteral => types::BOOLEAN, + HlTag::BuiltinType => types::BUILTIN_TYPE, + HlTag::ByteLiteral | HlTag::NumericLiteral => types::NUMBER, + HlTag::CharLiteral => types::CHAR, + HlTag::Comment => types::COMMENT, + HlTag::EscapeSequence => types::ESCAPE_SEQUENCE, + HlTag::InvalidEscapeSequence => types::INVALID_ESCAPE_SEQUENCE, + HlTag::FormatSpecifier => types::FORMAT_SPECIFIER, + HlTag::Keyword => types::KEYWORD, + HlTag::None => types::GENERIC, HlTag::Operator(op) => match op { - HlOperator::Bitwise => semantic_tokens::BITWISE, - HlOperator::Arithmetic => semantic_tokens::ARITHMETIC, - HlOperator::Logical => semantic_tokens::LOGICAL, - HlOperator::Comparison => semantic_tokens::COMPARISON, - HlOperator::Other => semantic_tokens::OPERATOR, + HlOperator::Bitwise => types::BITWISE, + HlOperator::Arithmetic => types::ARITHMETIC, + HlOperator::Logical => types::LOGICAL, + HlOperator::Comparison => types::COMPARISON, + HlOperator::Other => types::OPERATOR, }, - HlTag::StringLiteral => semantic_tokens::STRING, - HlTag::UnresolvedReference => semantic_tokens::UNRESOLVED_REFERENCE, + HlTag::StringLiteral => types::STRING, + HlTag::UnresolvedReference => types::UNRESOLVED_REFERENCE, HlTag::Punctuation(punct) => match punct { - HlPunct::Bracket => semantic_tokens::BRACKET, - HlPunct::Brace => semantic_tokens::BRACE, - HlPunct::Parenthesis => semantic_tokens::PARENTHESIS, - HlPunct::Angle => semantic_tokens::ANGLE, - HlPunct::Comma => semantic_tokens::COMMA, - HlPunct::Dot => semantic_tokens::DOT, - HlPunct::Colon => semantic_tokens::COLON, - HlPunct::Semi => semantic_tokens::SEMICOLON, - HlPunct::Other => semantic_tokens::PUNCTUATION, - HlPunct::MacroBang => semantic_tokens::MACRO_BANG, + HlPunct::Bracket => types::BRACKET, + HlPunct::Brace => types::BRACE, + HlPunct::Parenthesis => types::PARENTHESIS, + HlPunct::Angle => types::ANGLE, + HlPunct::Comma => types::COMMA, + HlPunct::Dot => types::DOT, + HlPunct::Colon => types::COLON, + HlPunct::Semi => types::SEMICOLON, + HlPunct::Other => types::PUNCTUATION, + HlPunct::MacroBang => types::MACRO_BANG, }, }; let mut mods = semantic_tokens::ModifierSet::default(); for modifier in highlight.mods.iter() { let modifier = match modifier { - HlMod::Associated => semantic_tokens::ASSOCIATED, - HlMod::Async => semantic_tokens::ASYNC, - HlMod::Attribute => semantic_tokens::ATTRIBUTE_MODIFIER, - HlMod::Callable => semantic_tokens::CALLABLE, - HlMod::Const => semantic_tokens::CONSTANT, - HlMod::Consuming => semantic_tokens::CONSUMING, - HlMod::ControlFlow => semantic_tokens::CONTROL_FLOW, - HlMod::CrateRoot => semantic_tokens::CRATE_ROOT, - HlMod::DefaultLibrary => semantic_tokens::DEFAULT_LIBRARY, - HlMod::Definition => semantic_tokens::DECLARATION, - HlMod::Documentation => semantic_tokens::DOCUMENTATION, - HlMod::Injected => semantic_tokens::INJECTED, - HlMod::IntraDocLink => semantic_tokens::INTRA_DOC_LINK, - HlMod::Library => semantic_tokens::LIBRARY, - HlMod::Macro => semantic_tokens::MACRO_MODIFIER, - HlMod::ProcMacro => semantic_tokens::PROC_MACRO_MODIFIER, - HlMod::Mutable => semantic_tokens::MUTABLE, - HlMod::Public => semantic_tokens::PUBLIC, - HlMod::Reference => semantic_tokens::REFERENCE, - HlMod::Static => semantic_tokens::STATIC, - HlMod::Trait => semantic_tokens::TRAIT_MODIFIER, - HlMod::Unsafe => semantic_tokens::UNSAFE, + HlMod::Associated => mods::ASSOCIATED, + HlMod::Async => mods::ASYNC, + HlMod::Attribute => mods::ATTRIBUTE_MODIFIER, + HlMod::Callable => mods::CALLABLE, + HlMod::Const => mods::CONSTANT, + HlMod::Consuming => mods::CONSUMING, + HlMod::ControlFlow => mods::CONTROL_FLOW, + HlMod::CrateRoot => mods::CRATE_ROOT, + HlMod::DefaultLibrary => mods::DEFAULT_LIBRARY, + HlMod::Definition => mods::DECLARATION, + HlMod::Documentation => mods::DOCUMENTATION, + HlMod::Injected => mods::INJECTED, + HlMod::IntraDocLink => mods::INTRA_DOC_LINK, + HlMod::Library => mods::LIBRARY, + HlMod::Macro => mods::MACRO_MODIFIER, + HlMod::ProcMacro => mods::PROC_MACRO_MODIFIER, + HlMod::Mutable => mods::MUTABLE, + HlMod::Public => mods::PUBLIC, + HlMod::Reference => mods::REFERENCE, + HlMod::Static => mods::STATIC, + HlMod::Trait => mods::TRAIT_MODIFIER, + HlMod::Unsafe => mods::UNSAFE, }; mods |= modifier; } 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 38df32351256..f37b25fb9558 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 @@ -7,7 +7,7 @@ use std::{ }; use always_assert::always; -use crossbeam_channel::{never, select, Receiver}; +use crossbeam_channel::{select, Receiver}; use ide_db::base_db::{SourceDatabase, SourceDatabaseExt, VfsPath}; use lsp_server::{Connection, Notification, Request}; use lsp_types::{notification::Notification as _, TextDocumentIdentifier}; @@ -220,7 +220,7 @@ impl GlobalState { recv(self.flycheck_receiver) -> task => Some(Event::Flycheck(task.unwrap())), - recv(self.test_run_session.as_ref().map(|s| s.receiver()).unwrap_or(&never())) -> task => + recv(self.test_run_receiver) -> task => Some(Event::TestResult(task.unwrap())), } @@ -337,9 +337,7 @@ impl GlobalState { .entered(); self.handle_cargo_test_msg(message); // Coalesce many test result event into a single loop turn - while let Some(message) = - self.test_run_session.as_ref().and_then(|r| r.receiver().try_recv().ok()) - { + while let Ok(message) = self.test_run_receiver.try_recv() { self.handle_cargo_test_msg(message); } } @@ -350,11 +348,7 @@ impl GlobalState { let memdocs_added_or_removed = self.mem_docs.take_changes(); if self.is_quiescent() { - let became_quiescent = !(was_quiescent - || self.fetch_workspaces_queue.op_requested() - || self.fetch_build_data_queue.op_requested() - || self.fetch_proc_macros_queue.op_requested()); - + let became_quiescent = !was_quiescent; if became_quiescent { if self.config.check_on_save() { // Project has loaded properly, kick off initial flycheck @@ -365,7 +359,7 @@ impl GlobalState { } } - let client_refresh = !was_quiescent || state_changed; + let client_refresh = became_quiescent || state_changed; if client_refresh { // Refresh semantic tokens if the client supports it. if self.config.semantic_tokens_refresh() { @@ -379,17 +373,17 @@ impl GlobalState { } // Refresh inlay hints if the client supports it. - if self.send_hint_refresh_query && self.config.inlay_hints_refresh() { + if self.config.inlay_hints_refresh() { self.send_request::((), |_, _| ()); - self.send_hint_refresh_query = false; } } - let things_changed = !was_quiescent || state_changed || memdocs_added_or_removed; - if things_changed && self.config.publish_diagnostics() { + let project_or_mem_docs_changed = + became_quiescent || state_changed || memdocs_added_or_removed; + if project_or_mem_docs_changed && self.config.publish_diagnostics() { self.update_diagnostics(); } - if things_changed && self.config.test_explorer() { + if project_or_mem_docs_changed && self.config.test_explorer() { self.update_tests(); } } @@ -411,7 +405,7 @@ impl GlobalState { // See https://github.com/rust-lang/rust-analyzer/issues/13130 let patch_empty = |message: &mut String| { if message.is_empty() { - *message = " ".to_owned(); + " ".clone_into(message); } }; @@ -434,7 +428,7 @@ impl GlobalState { } } - if self.config.cargo_autoreload() { + if self.config.cargo_autoreload_config() { if let Some((cause, force_crate_graph_reload)) = self.fetch_workspaces_queue.should_start_op() { @@ -643,7 +637,6 @@ impl GlobalState { } self.switch_workspaces("fetched build data".to_owned()); - self.send_hint_refresh_query = true; (Some(Progress::End), None) } @@ -660,7 +653,6 @@ impl GlobalState { ProcMacroProgress::End(proc_macro_load_result) => { self.fetch_proc_macros_queue.op_completed(true); self.set_proc_macros(proc_macro_load_result); - self.send_hint_refresh_query = true; (Some(Progress::End), None) } }; @@ -792,8 +784,11 @@ impl GlobalState { } flycheck::CargoTestMessage::Suite => (), flycheck::CargoTestMessage::Finished => { - self.send_notification::(()); - self.test_run_session = None; + self.test_run_remaining_jobs = self.test_run_remaining_jobs.saturating_sub(1); + if self.test_run_remaining_jobs == 0 { + self.send_notification::(()); + self.test_run_session = None; + } } flycheck::CargoTestMessage::Custom { text } => { self.send_notification::(text); 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 771a5599f6f5..5d8a66cabc7d 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs @@ -76,9 +76,9 @@ impl GlobalState { 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() { + if self.config.lru_query_capacities_config() != old_config.lru_query_capacities_config() { self.analysis_host.update_lru_capacities( - &self.config.lru_query_capacities().cloned().unwrap_or_default(), + &self.config.lru_query_capacities_config().cloned().unwrap_or_default(), ); } if self.config.linked_or_discovered_projects() != old_config.linked_or_discovered_projects() @@ -153,7 +153,7 @@ impl GlobalState { for ws in self.workspaces.iter() { let (ProjectWorkspace::Cargo { sysroot, .. } | ProjectWorkspace::Json { sysroot, .. } - | ProjectWorkspace::DetachedFiles { sysroot, .. }) = ws; + | ProjectWorkspace::DetachedFile { sysroot, .. }) = ws; match sysroot { Err(None) => (), Err(Some(e)) => { @@ -234,6 +234,7 @@ impl GlobalState { it.clone(), cargo_config.target.as_deref(), &cargo_config.extra_env, + &cargo_config.cfg_overrides, )) } }) @@ -254,7 +255,7 @@ impl GlobalState { } if !detached_files.is_empty() { - workspaces.push(project_model::ProjectWorkspace::load_detached_files( + workspaces.extend(project_model::ProjectWorkspace::load_detached_files( detached_files, &cargo_config, )); @@ -539,9 +540,6 @@ impl GlobalState { } fn recreate_crate_graph(&mut self, cause: String) { - // crate graph construction relies on these paths, record them so when one of them gets - // deleted or created we trigger a reconstruction of the crate graph - let mut crate_graph_file_dependencies = mem::take(&mut self.crate_graph_file_dependencies); self.report_progress( "Building CrateGraph", crate::lsp::utils::Progress::Begin, @@ -550,13 +548,25 @@ impl GlobalState { None, ); + // crate graph construction relies on these paths, record them so when one of them gets + // deleted or created we trigger a reconstruction of the crate graph + self.crate_graph_file_dependencies.clear(); + self.detached_files = self + .workspaces + .iter() + .filter_map(|ws| match ws { + ProjectWorkspace::DetachedFile { file, .. } => Some(file.clone()), + _ => None, + }) + .collect(); + let (crate_graph, proc_macro_paths, layouts, toolchains) = { // Create crate graph from all the workspaces let vfs = &mut self.vfs.write().0; let load = |path: &AbsPath| { let vfs_path = vfs::VfsPath::from(path.to_path_buf()); - crate_graph_file_dependencies.insert(vfs_path.clone()); + self.crate_graph_file_dependencies.insert(vfs_path.clone()); vfs.file_id(&vfs_path) }; @@ -576,7 +586,6 @@ impl GlobalState { change.set_target_data_layouts(layouts); change.set_toolchains(toolchains); self.analysis_host.apply_change(change); - self.crate_graph_file_dependencies = crate_graph_file_dependencies; self.report_progress( "Building CrateGraph", crate::lsp::utils::Progress::End, @@ -673,7 +682,8 @@ impl GlobalState { _ => None, } } - ProjectWorkspace::DetachedFiles { .. } => None, + // FIXME + ProjectWorkspace::DetachedFile { .. } => None, }) .map(|(id, root, sysroot_root)| { let sender = sender.clone(); @@ -712,15 +722,9 @@ pub fn ws_to_crate_graph( let (other, mut crate_proc_macros) = ws.to_crate_graph(&mut load, extra_env); let num_layouts = layouts.len(); let num_toolchains = toolchains.len(); - let (toolchain, layout) = match ws { - ProjectWorkspace::Cargo { toolchain, target_layout, .. } - | ProjectWorkspace::Json { toolchain, target_layout, .. } => { - (toolchain.clone(), target_layout.clone()) - } - ProjectWorkspace::DetachedFiles { .. } => { - (None, Err("detached files have no layout".into())) - } - }; + let (ProjectWorkspace::Cargo { toolchain, target_layout, .. } + | ProjectWorkspace::Json { toolchain, target_layout, .. } + | ProjectWorkspace::DetachedFile { toolchain, target_layout, .. }) = ws; let mapping = crate_graph.extend( other, @@ -729,7 +733,7 @@ pub fn ws_to_crate_graph( // if the newly created crate graph's layout is equal to the crate of the merged graph, then // we can merge the crates. let id = cg_id.into_raw().into_u32() as usize; - layouts[id] == layout && toolchains[id] == toolchain && cg_data == o_data + layouts[id] == *target_layout && toolchains[id] == *toolchain && cg_data == o_data }, ); // Populate the side tables for the newly merged crates @@ -741,13 +745,13 @@ pub fn ws_to_crate_graph( if layouts.len() <= idx { layouts.resize(idx + 1, e.clone()); } - layouts[idx] = layout.clone(); + layouts[idx].clone_from(target_layout); } if idx >= num_toolchains { if toolchains.len() <= idx { toolchains.resize(idx + 1, None); } - toolchains[idx] = toolchain.clone(); + toolchains[idx].clone_from(toolchain); } }); proc_macro_paths.push(crate_proc_macros); 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 439b006977d3..b87f02947bf3 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 @@ -23,12 +23,12 @@ use lsp_types::{ notification::DidOpenTextDocument, request::{ CodeActionRequest, Completion, Formatting, GotoTypeDefinition, HoverRequest, - WillRenameFiles, WorkspaceSymbolRequest, + InlayHintRequest, InlayHintResolveRequest, WillRenameFiles, WorkspaceSymbolRequest, }, CodeActionContext, CodeActionParams, CompletionParams, DidOpenTextDocumentParams, DocumentFormattingParams, FileRename, FormattingOptions, GotoDefinitionParams, HoverParams, - PartialResultParams, Position, Range, RenameFilesParams, TextDocumentItem, - TextDocumentPositionParams, WorkDoneProgressParams, + InlayHint, InlayHintLabel, InlayHintParams, PartialResultParams, Position, Range, + RenameFilesParams, TextDocumentItem, TextDocumentPositionParams, WorkDoneProgressParams, }; use rust_analyzer::lsp::ext::{OnEnter, Runnables, RunnablesParams, UnindexedProject}; use serde_json::json; @@ -75,6 +75,147 @@ use std::collections::Spam; assert!(res.to_string().contains("HashMap")); } +#[test] +fn resolves_inlay_hints() { + if skip_slow_tests() { + return; + } + + let server = Project::with_fixture( + r#" +//- /Cargo.toml +[package] +name = "foo" +version = "0.0.0" + +//- /src/lib.rs +struct Foo; +fn f() { + let x = Foo; +} +"#, + ) + .server() + .wait_until_workspace_is_loaded(); + + let res = server.send_request::(InlayHintParams { + range: Range::new(Position::new(0, 0), Position::new(3, 1)), + text_document: server.doc_id("src/lib.rs"), + work_done_progress_params: WorkDoneProgressParams::default(), + }); + let mut hints = serde_json::from_value::>>(res).unwrap().unwrap(); + let hint = hints.pop().unwrap(); + assert!(hint.data.is_some()); + assert!( + matches!(&hint.label, InlayHintLabel::LabelParts(parts) if parts[1].location.is_none()) + ); + let res = server.send_request::(hint); + let hint = serde_json::from_value::(res).unwrap(); + assert!(hint.data.is_none()); + assert!( + matches!(&hint.label, InlayHintLabel::LabelParts(parts) if parts[1].location.is_some()) + ); +} + +#[test] +fn completes_items_from_standard_library_in_cargo_script() { + // this test requires nightly so CI can't run it + if skip_slow_tests() || std::env::var("CI").is_ok() { + return; + } + + let server = Project::with_fixture( + r#" +//- /dependency/Cargo.toml +[package] +name = "dependency" +version = "0.1.0" +//- /dependency/src/lib.rs +pub struct SpecialHashMap; +//- /dependency2/Cargo.toml +[package] +name = "dependency2" +version = "0.1.0" +//- /dependency2/src/lib.rs +pub struct SpecialHashMap2; +//- /src/lib.rs +#!/usr/bin/env -S cargo +nightly -Zscript +--- +[dependencies] +dependency = { path = "../dependency" } +--- +use dependency::Spam; +use dependency2::Spam; +"#, + ) + .with_config(serde_json::json!({ + "cargo": { "sysroot": null }, + })) + .server() + .wait_until_workspace_is_loaded(); + + let res = server.send_request::(CompletionParams { + text_document_position: TextDocumentPositionParams::new( + server.doc_id("src/lib.rs"), + Position::new(5, 18), + ), + context: None, + partial_result_params: PartialResultParams::default(), + work_done_progress_params: WorkDoneProgressParams::default(), + }); + assert!(res.to_string().contains("SpecialHashMap"), "{}", res.to_string()); + + let res = server.send_request::(CompletionParams { + text_document_position: TextDocumentPositionParams::new( + server.doc_id("src/lib.rs"), + Position::new(6, 18), + ), + context: None, + partial_result_params: PartialResultParams::default(), + work_done_progress_params: WorkDoneProgressParams::default(), + }); + assert!(!res.to_string().contains("SpecialHashMap")); + + server.write_file_and_save( + "src/lib.rs", + r#"#!/usr/bin/env -S cargo +nightly -Zscript +--- +[dependencies] +dependency2 = { path = "../dependency2" } +--- +use dependency::Spam; +use dependency2::Spam; +"# + .to_owned(), + ); + + let server = server.wait_until_workspace_is_loaded(); + + std::thread::sleep(std::time::Duration::from_secs(3)); + + let res = server.send_request::(CompletionParams { + text_document_position: TextDocumentPositionParams::new( + server.doc_id("src/lib.rs"), + Position::new(5, 18), + ), + context: None, + partial_result_params: PartialResultParams::default(), + work_done_progress_params: WorkDoneProgressParams::default(), + }); + assert!(!res.to_string().contains("SpecialHashMap")); + + let res = server.send_request::(CompletionParams { + text_document_position: TextDocumentPositionParams::new( + server.doc_id("src/lib.rs"), + Position::new(6, 18), + ), + context: None, + partial_result_params: PartialResultParams::default(), + work_done_progress_params: WorkDoneProgressParams::default(), + }); + assert!(res.to_string().contains("SpecialHashMap")); +} + #[test] fn test_runnables_project() { if skip_slow_tests() { @@ -115,7 +256,7 @@ fn main() {} { "args": { "cargoArgs": ["test", "--package", "foo", "--test", "spam"], - "executableArgs": ["test_eggs", "--exact", "--nocapture"], + "executableArgs": ["test_eggs", "--exact", "--show-output"], "cargoExtraArgs": [], "overrideCargo": null, "workspaceRoot": server.path().join("foo") @@ -148,7 +289,7 @@ fn main() {} "cargoExtraArgs": [], "executableArgs": [ "", - "--nocapture" + "--show-output" ] }, "kind": "cargo", 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 8bbe6ff37247..f04962a7a276 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 @@ -125,7 +125,7 @@ impl Project<'_> { } let mut config = Config::new( - tmp_dir_path, + tmp_dir_path.clone(), lsp_types::ClientCapabilities { workspace: Some(lsp_types::WorkspaceClientCapabilities { did_change_watched_files: Some( @@ -159,6 +159,18 @@ impl Project<'_> { content_format: Some(vec![lsp_types::MarkupKind::Markdown]), ..Default::default() }), + inlay_hint: Some(lsp_types::InlayHintClientCapabilities { + resolve_support: Some(lsp_types::InlayHintResolveClientCapabilities { + properties: vec![ + "textEdits".to_owned(), + "tooltip".to_owned(), + "label.tooltip".to_owned(), + "label.location".to_owned(), + "label.command".to_owned(), + ], + }), + ..Default::default() + }), ..Default::default() }), window: Some(lsp_types::WindowClientCapabilities { @@ -173,10 +185,14 @@ impl Project<'_> { roots, None, ); - config.update(self.config).expect("invalid config"); + // TODO: don't hardcode src/lib.rs as detached file + let mut c = self.config; + let p = tmp_dir_path.join("src/lib.rs").to_string(); + c["detachedFiles"] = serde_json::json!([p]); + config.update(c).expect("invalid config"); config.rediscover_workspaces(); - Server::new(tmp_dir, config) + Server::new(tmp_dir.keep(), config) } } @@ -271,6 +287,7 @@ impl Server { } } + #[track_caller] pub(crate) fn send_request(&self, params: R::Params) -> Value where R: lsp_types::request::Request, @@ -282,6 +299,7 @@ impl Server { let r = Request::new(id.into(), R::METHOD.to_owned(), params); self.send_request_(r) } + #[track_caller] fn send_request_(&self, r: Request) -> Value { let id = r.id.clone(); self.client.sender.send(r.clone().into()).unwrap(); @@ -362,6 +380,16 @@ impl Server { pub(crate) fn path(&self) -> &Utf8Path { self.dir.path() } + + pub(crate) fn write_file_and_save(&self, path: &str, text: String) { + fs::write(self.dir.path().join(path), &text).unwrap(); + self.notification::( + lsp_types::DidSaveTextDocumentParams { + text_document: self.doc_id(path), + text: Some(text), + }, + ) + } } impl Drop for Server { 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 78da4487d4c9..34439391333d 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 @@ -243,7 +243,7 @@ struct TidyDocs { impl TidyDocs { fn visit(&mut self, path: &Path, text: &str) { // Tests and diagnostic fixes don't need module level comments. - if is_exclude_dir(path, &["tests", "test_data", "fixes", "grammar"]) { + if is_exclude_dir(path, &["tests", "test_data", "fixes", "grammar", "salsa"]) { return; } diff --git a/src/tools/rust-analyzer/crates/salsa/salsa-macros/src/database_storage.rs b/src/tools/rust-analyzer/crates/salsa/salsa-macros/src/database_storage.rs index 223da9b5290f..14238e2fed55 100644 --- a/src/tools/rust-analyzer/crates/salsa/salsa-macros/src/database_storage.rs +++ b/src/tools/rust-analyzer/crates/salsa/salsa-macros/src/database_storage.rs @@ -1,4 +1,5 @@ -//! +//! Implementation for `[salsa::database]` decorator. + use heck::ToSnakeCase; use proc_macro::TokenStream; use syn::parse::{Parse, ParseStream}; diff --git a/src/tools/rust-analyzer/crates/salsa/salsa-macros/src/parenthesized.rs b/src/tools/rust-analyzer/crates/salsa/salsa-macros/src/parenthesized.rs index 9df41e03c162..5ecd1b8a0583 100644 --- a/src/tools/rust-analyzer/crates/salsa/salsa-macros/src/parenthesized.rs +++ b/src/tools/rust-analyzer/crates/salsa/salsa-macros/src/parenthesized.rs @@ -1,4 +1,4 @@ -//! +//! Parenthesis helper pub(crate) struct Parenthesized(pub(crate) T); impl syn::parse::Parse for Parenthesized diff --git a/src/tools/rust-analyzer/crates/salsa/salsa-macros/src/query_group.rs b/src/tools/rust-analyzer/crates/salsa/salsa-macros/src/query_group.rs index 5983765eec7b..659797d6d4a4 100644 --- a/src/tools/rust-analyzer/crates/salsa/salsa-macros/src/query_group.rs +++ b/src/tools/rust-analyzer/crates/salsa/salsa-macros/src/query_group.rs @@ -1,4 +1,4 @@ -//! +//! Implementation for `[salsa::query_group]` decorator. use crate::parenthesized::Parenthesized; use heck::ToUpperCamelCase; diff --git a/src/tools/rust-analyzer/crates/salsa/src/derived.rs b/src/tools/rust-analyzer/crates/salsa/src/derived.rs index 3b5bd7f9e3bb..fd31ab204169 100644 --- a/src/tools/rust-analyzer/crates/salsa/src/derived.rs +++ b/src/tools/rust-analyzer/crates/salsa/src/derived.rs @@ -1,4 +1,3 @@ -//! use crate::debug::TableEntry; use crate::durability::Durability; use crate::hash::FxIndexMap; diff --git a/src/tools/rust-analyzer/crates/salsa/src/derived/slot.rs b/src/tools/rust-analyzer/crates/salsa/src/derived/slot.rs index 75204c8ff604..cfafa40ce33a 100644 --- a/src/tools/rust-analyzer/crates/salsa/src/derived/slot.rs +++ b/src/tools/rust-analyzer/crates/salsa/src/derived/slot.rs @@ -1,4 +1,3 @@ -//! use crate::debug::TableEntry; use crate::derived::MemoizationPolicy; use crate::durability::Durability; diff --git a/src/tools/rust-analyzer/crates/salsa/src/durability.rs b/src/tools/rust-analyzer/crates/salsa/src/durability.rs index 44abae3170f6..7b8e6840fc94 100644 --- a/src/tools/rust-analyzer/crates/salsa/src/durability.rs +++ b/src/tools/rust-analyzer/crates/salsa/src/durability.rs @@ -1,4 +1,3 @@ -//! /// Describes how likely a value is to change -- how "durable" it is. /// By default, inputs have `Durability::LOW` and interned values have /// `Durability::HIGH`. But inputs can be explicitly set with other diff --git a/src/tools/rust-analyzer/crates/salsa/src/hash.rs b/src/tools/rust-analyzer/crates/salsa/src/hash.rs index 47a2dd1ce0c0..3b2d7df3fbea 100644 --- a/src/tools/rust-analyzer/crates/salsa/src/hash.rs +++ b/src/tools/rust-analyzer/crates/salsa/src/hash.rs @@ -1,4 +1,3 @@ -//! pub(crate) type FxHasher = std::hash::BuildHasherDefault; pub(crate) type FxIndexSet = indexmap::IndexSet; pub(crate) type FxIndexMap = indexmap::IndexMap; diff --git a/src/tools/rust-analyzer/crates/salsa/src/input.rs b/src/tools/rust-analyzer/crates/salsa/src/input.rs index 922ec5a77521..f04f48e3bab8 100644 --- a/src/tools/rust-analyzer/crates/salsa/src/input.rs +++ b/src/tools/rust-analyzer/crates/salsa/src/input.rs @@ -1,4 +1,3 @@ -//! use crate::debug::TableEntry; use crate::durability::Durability; use crate::hash::FxIndexMap; diff --git a/src/tools/rust-analyzer/crates/salsa/src/intern_id.rs b/src/tools/rust-analyzer/crates/salsa/src/intern_id.rs index a7bbc088f9c0..b060d8aab688 100644 --- a/src/tools/rust-analyzer/crates/salsa/src/intern_id.rs +++ b/src/tools/rust-analyzer/crates/salsa/src/intern_id.rs @@ -1,4 +1,3 @@ -//! use std::fmt; use std::num::NonZeroU32; diff --git a/src/tools/rust-analyzer/crates/salsa/src/interned.rs b/src/tools/rust-analyzer/crates/salsa/src/interned.rs index c065e7e2bde5..bfa9cc0591a8 100644 --- a/src/tools/rust-analyzer/crates/salsa/src/interned.rs +++ b/src/tools/rust-analyzer/crates/salsa/src/interned.rs @@ -1,4 +1,3 @@ -//! use crate::debug::TableEntry; use crate::durability::Durability; use crate::intern_id::InternId; diff --git a/src/tools/rust-analyzer/crates/salsa/src/lib.rs b/src/tools/rust-analyzer/crates/salsa/src/lib.rs index fe8075988730..f86683ee77ae 100644 --- a/src/tools/rust-analyzer/crates/salsa/src/lib.rs +++ b/src/tools/rust-analyzer/crates/salsa/src/lib.rs @@ -1,8 +1,7 @@ -//! #![allow(clippy::type_complexity)] #![allow(clippy::question_mark)] +#![allow(missing_docs)] #![warn(rust_2018_idioms)] -#![warn(missing_docs)] //! The salsa crate is a crate for incremental recomputation. It //! permits you to define a "database" of queries with both inputs and @@ -124,9 +123,9 @@ pub struct Event { impl Event { /// Returns a type that gives a user-readable debug output. /// Use like `println!("{:?}", index.debug(db))`. - pub fn debug<'me, D: ?Sized>(&'me self, db: &'me D) -> impl std::fmt::Debug + 'me + pub fn debug<'me, D>(&'me self, db: &'me D) -> impl std::fmt::Debug + 'me where - D: plumbing::DatabaseOps, + D: ?Sized + plumbing::DatabaseOps, { EventDebug { event: self, db } } @@ -206,9 +205,9 @@ pub enum EventKind { impl EventKind { /// Returns a type that gives a user-readable debug output. /// Use like `println!("{:?}", index.debug(db))`. - pub fn debug<'me, D: ?Sized>(&'me self, db: &'me D) -> impl std::fmt::Debug + 'me + pub fn debug<'me, D>(&'me self, db: &'me D) -> impl std::fmt::Debug + 'me where - D: plumbing::DatabaseOps, + D: ?Sized + plumbing::DatabaseOps, { EventKindDebug { kind: self, db } } @@ -400,9 +399,9 @@ impl DatabaseKeyIndex { /// Returns a type that gives a user-readable debug output. /// Use like `println!("{:?}", index.debug(db))`. - pub fn debug(self, db: &D) -> impl std::fmt::Debug + '_ + pub fn debug(self, db: &D) -> impl std::fmt::Debug + '_ where - D: plumbing::DatabaseOps, + D: ?Sized + plumbing::DatabaseOps, { DatabaseKeyIndexDebug { index: self, db } } diff --git a/src/tools/rust-analyzer/crates/salsa/src/lru.rs b/src/tools/rust-analyzer/crates/salsa/src/lru.rs index 1ff85a3ea458..edad551842d2 100644 --- a/src/tools/rust-analyzer/crates/salsa/src/lru.rs +++ b/src/tools/rust-analyzer/crates/salsa/src/lru.rs @@ -1,4 +1,3 @@ -//! use oorandom::Rand64; use parking_lot::Mutex; use std::fmt::Debug; diff --git a/src/tools/rust-analyzer/crates/salsa/src/plumbing.rs b/src/tools/rust-analyzer/crates/salsa/src/plumbing.rs index 1a8ff33b2efc..1dfde639869a 100644 --- a/src/tools/rust-analyzer/crates/salsa/src/plumbing.rs +++ b/src/tools/rust-analyzer/crates/salsa/src/plumbing.rs @@ -1,4 +1,3 @@ -//! #![allow(missing_docs)] use crate::debug::TableEntry; diff --git a/src/tools/rust-analyzer/crates/salsa/src/revision.rs b/src/tools/rust-analyzer/crates/salsa/src/revision.rs index 559b03386087..204c0883b852 100644 --- a/src/tools/rust-analyzer/crates/salsa/src/revision.rs +++ b/src/tools/rust-analyzer/crates/salsa/src/revision.rs @@ -1,4 +1,3 @@ -//! use std::num::NonZeroU32; use std::sync::atomic::{AtomicU32, Ordering}; diff --git a/src/tools/rust-analyzer/crates/salsa/src/runtime.rs b/src/tools/rust-analyzer/crates/salsa/src/runtime.rs index e11cabfe11ed..4f3341f51509 100644 --- a/src/tools/rust-analyzer/crates/salsa/src/runtime.rs +++ b/src/tools/rust-analyzer/crates/salsa/src/runtime.rs @@ -1,4 +1,3 @@ -//! use crate::durability::Durability; use crate::hash::FxIndexSet; use crate::plumbing::CycleRecoveryStrategy; @@ -605,7 +604,7 @@ impl ActiveQuery { pub(crate) fn take_inputs_from(&mut self, cycle_query: &ActiveQuery) { self.changed_at = cycle_query.changed_at; self.durability = cycle_query.durability; - self.dependencies = cycle_query.dependencies.clone(); + self.dependencies.clone_from(&cycle_query.dependencies); } } diff --git a/src/tools/rust-analyzer/crates/salsa/src/runtime/dependency_graph.rs b/src/tools/rust-analyzer/crates/salsa/src/runtime/dependency_graph.rs index dd223eeeba9f..ed1d499f6373 100644 --- a/src/tools/rust-analyzer/crates/salsa/src/runtime/dependency_graph.rs +++ b/src/tools/rust-analyzer/crates/salsa/src/runtime/dependency_graph.rs @@ -1,4 +1,3 @@ -//! use triomphe::Arc; use crate::{DatabaseKeyIndex, RuntimeId}; diff --git a/src/tools/rust-analyzer/crates/salsa/src/runtime/local_state.rs b/src/tools/rust-analyzer/crates/salsa/src/runtime/local_state.rs index 7ac21dec1a89..0dbea1d563ef 100644 --- a/src/tools/rust-analyzer/crates/salsa/src/runtime/local_state.rs +++ b/src/tools/rust-analyzer/crates/salsa/src/runtime/local_state.rs @@ -1,4 +1,3 @@ -//! use tracing::debug; use triomphe::ThinArc; diff --git a/src/tools/rust-analyzer/crates/salsa/src/storage.rs b/src/tools/rust-analyzer/crates/salsa/src/storage.rs index c0e6416f4a33..e0acf44041b4 100644 --- a/src/tools/rust-analyzer/crates/salsa/src/storage.rs +++ b/src/tools/rust-analyzer/crates/salsa/src/storage.rs @@ -1,4 +1,3 @@ -//! use crate::{plumbing::DatabaseStorageTypes, Runtime}; use triomphe::Arc; diff --git a/src/tools/rust-analyzer/crates/sourcegen/src/lib.rs b/src/tools/rust-analyzer/crates/sourcegen/src/lib.rs index 295b716b4e90..829b4d5b0ff2 100644 --- a/src/tools/rust-analyzer/crates/sourcegen/src/lib.rs +++ b/src/tools/rust-analyzer/crates/sourcegen/src/lib.rs @@ -69,7 +69,7 @@ impl CommentBlock { panic!("Use plain (non-doc) comments with tags like {tag}:\n {first}"); } - block.id = id.trim().to_owned(); + id.trim().clone_into(&mut block.id); true }); blocks diff --git a/src/tools/rust-analyzer/crates/span/Cargo.toml b/src/tools/rust-analyzer/crates/span/Cargo.toml index cbda91f0a59a..9f85f0107cc1 100644 --- a/src/tools/rust-analyzer/crates/span/Cargo.toml +++ b/src/tools/rust-analyzer/crates/span/Cargo.toml @@ -14,6 +14,7 @@ la-arena.workspace = true salsa.workspace = true rustc-hash.workspace = true hashbrown.workspace = true +text-size.workspace = true # local deps vfs.workspace = true diff --git a/src/tools/rust-analyzer/crates/span/src/lib.rs b/src/tools/rust-analyzer/crates/span/src/lib.rs index c9109c72d0d2..8ca7bc2d38a1 100644 --- a/src/tools/rust-analyzer/crates/span/src/lib.rs +++ b/src/tools/rust-analyzer/crates/span/src/lib.rs @@ -13,59 +13,10 @@ pub use self::{ map::{RealSpanMap, SpanMap}, }; -pub use syntax::{TextRange, TextSize}; +pub use syntax::Edition; +pub use text_size::{TextRange, TextSize}; pub use vfs::FileId; -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum Edition { - Edition2015, - Edition2018, - Edition2021, - Edition2024, -} - -impl Edition { - pub const CURRENT: Edition = Edition::Edition2021; - pub const DEFAULT: Edition = Edition::Edition2015; -} - -#[derive(Debug)] -pub struct ParseEditionError { - invalid_input: String, -} - -impl std::error::Error for ParseEditionError {} -impl fmt::Display for ParseEditionError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "invalid edition: {:?}", self.invalid_input) - } -} - -impl std::str::FromStr for Edition { - type Err = ParseEditionError; - - fn from_str(s: &str) -> Result { - let res = match s { - "2015" => Edition::Edition2015, - "2018" => Edition::Edition2018, - "2021" => Edition::Edition2021, - "2024" => Edition::Edition2024, - _ => return Err(ParseEditionError { invalid_input: s.to_owned() }), - }; - Ok(res) - } -} - -impl fmt::Display for Edition { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(match self { - Edition::Edition2015 => "2015", - Edition::Edition2018 => "2018", - Edition::Edition2021 => "2021", - Edition::Edition2024 => "2024", - }) - } -} #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct FilePosition { pub file_id: FileId, diff --git a/src/tools/rust-analyzer/crates/span/src/map.rs b/src/tools/rust-analyzer/crates/span/src/map.rs index 1f396a1e97b4..81fc56c961e4 100644 --- a/src/tools/rust-analyzer/crates/span/src/map.rs +++ b/src/tools/rust-analyzer/crates/span/src/map.rs @@ -4,17 +4,20 @@ use std::{fmt, hash::Hash}; use stdx::{always, itertools::Itertools}; -use syntax::{TextRange, TextSize}; use vfs::FileId; use crate::{ - ErasedFileAstId, Span, SpanAnchor, SpanData, SyntaxContextId, ROOT_ERASED_FILE_AST_ID, + ErasedFileAstId, Span, SpanAnchor, SpanData, SyntaxContextId, TextRange, TextSize, + ROOT_ERASED_FILE_AST_ID, }; /// Maps absolute text ranges for the corresponding file to the relevant span data. #[derive(Debug, PartialEq, Eq, Clone, Hash)] pub struct SpanMap { spans: Vec<(TextSize, SpanData)>, + /// Index of the matched macro arm on successful expansion for declarative macros. + // FIXME: Does it make sense to have this here? + pub matched_arm: Option, } impl SpanMap @@ -23,7 +26,7 @@ where { /// Creates a new empty [`SpanMap`]. pub fn empty() -> Self { - Self { spans: Vec::new() } + Self { spans: Vec::new(), matched_arm: None } } /// Finalizes the [`SpanMap`], shrinking its backing storage and validating that the offsets are diff --git a/src/tools/rust-analyzer/crates/syntax/src/algo.rs b/src/tools/rust-analyzer/crates/syntax/src/algo.rs index 01f2af419ed8..0e62de5febb9 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/algo.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/algo.rs @@ -255,7 +255,7 @@ pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff { mod tests { use expect_test::{expect, Expect}; use itertools::Itertools; - use parser::SyntaxKind; + use parser::{Edition, SyntaxKind}; use text_edit::TextEdit; use crate::{AstNode, SyntaxElement}; @@ -607,8 +607,8 @@ fn main() { } fn check_diff(from: &str, to: &str, expected_diff: Expect) { - let from_node = crate::SourceFile::parse(from).tree().syntax().clone(); - let to_node = crate::SourceFile::parse(to).tree().syntax().clone(); + let from_node = crate::SourceFile::parse(from, Edition::CURRENT).tree().syntax().clone(); + let to_node = crate::SourceFile::parse(to, Edition::CURRENT).tree().syntax().clone(); let diff = super::diff(&from_node, &to_node); let line_number = diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast.rs b/src/tools/rust-analyzer/crates/syntax/src/ast.rs index e9ab7a4320b0..168ca9f1328d 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast.rs @@ -174,6 +174,7 @@ fn test_doc_comment_none() { // non-doc mod foo {} "#, + parser::Edition::CURRENT, ) .ok() .unwrap(); @@ -189,6 +190,7 @@ fn test_outer_doc_comment_of_items() { // non-doc mod foo {} "#, + parser::Edition::CURRENT, ) .ok() .unwrap(); @@ -204,6 +206,7 @@ fn test_inner_doc_comment_of_items() { // non-doc mod foo {} "#, + parser::Edition::CURRENT, ) .ok() .unwrap(); @@ -218,6 +221,7 @@ fn test_doc_comment_of_statics() { /// Number of levels static LEVELS: i32 = 0; "#, + parser::Edition::CURRENT, ) .ok() .unwrap(); @@ -237,6 +241,7 @@ fn test_doc_comment_preserves_indents() { /// ``` mod foo {} "#, + parser::Edition::CURRENT, ) .ok() .unwrap(); @@ -257,6 +262,7 @@ fn test_doc_comment_preserves_newlines() { /// foo mod foo {} "#, + parser::Edition::CURRENT, ) .ok() .unwrap(); @@ -271,6 +277,7 @@ fn test_doc_comment_single_line_block_strips_suffix() { /** this is mod foo*/ mod foo {} "#, + parser::Edition::CURRENT, ) .ok() .unwrap(); @@ -285,6 +292,7 @@ fn test_doc_comment_single_line_block_strips_suffix_whitespace() { /** this is mod foo */ mod foo {} "#, + parser::Edition::CURRENT, ) .ok() .unwrap(); @@ -303,6 +311,7 @@ fn test_doc_comment_multi_line_block_strips_suffix() { */ mod foo {} "#, + parser::Edition::CURRENT, ) .ok() .unwrap(); @@ -316,7 +325,7 @@ fn test_doc_comment_multi_line_block_strips_suffix() { #[test] fn test_comments_preserve_trailing_whitespace() { let file = SourceFile::parse( - "\n/// Representation of a Realm. \n/// In the specification these are called Realm Records.\nstruct Realm {}", + "\n/// Representation of a Realm. \n/// In the specification these are called Realm Records.\nstruct Realm {}", parser::Edition::CURRENT, ) .ok() .unwrap(); @@ -335,6 +344,7 @@ fn test_four_slash_line_comment() { /// doc comment mod foo {} "#, + parser::Edition::CURRENT, ) .ok() .unwrap(); @@ -360,6 +370,7 @@ where for<'a> F: Fn(&'a str) {} "#, + parser::Edition::CURRENT, ) .ok() .unwrap(); 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 41d33c457ce7..2445e4f1a32d 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 @@ -1054,6 +1054,7 @@ impl Indent for N {} mod tests { use std::fmt; + use parser::Edition; use stdx::trim_indent; use test_utils::assert_eq_text; @@ -1062,7 +1063,7 @@ mod tests { use super::*; fn ast_mut_from_text(text: &str) -> N { - let parse = SourceFile::parse(text); + let parse = SourceFile::parse(text, Edition::CURRENT); parse.tree().syntax().descendants().find_map(N::cast).unwrap().clone_for_update() } 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 18a56e2823ab..28a9dadacefb 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 @@ -89,6 +89,7 @@ fn if_block_condition() { else { "else" } } "#, + parser::Edition::CURRENT, ); let if_ = parse.tree().syntax().descendants().find_map(ast::IfExpr::cast).unwrap(); assert_eq!(if_.then_branch().unwrap().syntax().text(), r#"{ "if" }"#); @@ -123,6 +124,7 @@ fn if_condition_with_if_inside() { else { "else" } } "#, + parser::Edition::CURRENT, ); let if_ = parse.tree().syntax().descendants().find_map(ast::IfExpr::cast).unwrap(); assert_eq!(if_.then_branch().unwrap().syntax().text(), r#"{ "if" }"#); @@ -386,7 +388,8 @@ impl ast::BlockExpr { #[test] fn test_literal_with_attr() { - let parse = ast::SourceFile::parse(r#"const _: &str = { #[attr] "Hello" };"#); + let parse = + ast::SourceFile::parse(r#"const _: &str = { #[attr] "Hello" };"#, parser::Edition::CURRENT); let lit = parse.tree().syntax().descendants().find_map(ast::Literal::cast).unwrap(); assert_eq!(lit.token().text(), r#""Hello""#); } 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 ff18fee9bab2..186f1b01da42 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs @@ -11,7 +11,7 @@ //! term, it will be replaced with direct tree manipulation. use itertools::Itertools; -use parser::T; +use parser::{Edition, T}; use rowan::NodeOrToken; use stdx::{format_to, format_to_acc, never}; @@ -1127,7 +1127,7 @@ pub fn token_tree( #[track_caller] fn ast_from_text(text: &str) -> N { - let parse = SourceFile::parse(text); + let parse = SourceFile::parse(text, Edition::CURRENT); let node = match parse.tree().syntax().descendants().find_map(N::cast) { Some(it) => it, None => { @@ -1153,12 +1153,13 @@ pub fn token(kind: SyntaxKind) -> SyntaxToken { pub mod tokens { use once_cell::sync::Lazy; + use parser::Edition; use crate::{ast, AstNode, Parse, SourceFile, SyntaxKind::*, SyntaxToken}; pub(super) static SOURCE_FILE: Lazy> = Lazy::new(|| { SourceFile::parse( - "const C: <()>::Item = ( true && true , true || true , 1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p, &p , &mut p, { let a @ [] })\n;\n\nimpl A for B where: {}", + "const C: <()>::Item = ( true && true , true || true , 1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p, &p , &mut p, { let a @ [] })\n;\n\nimpl A for B where: {}", Edition::CURRENT, ) }); @@ -1186,13 +1187,13 @@ pub mod tokens { pub fn whitespace(text: &str) -> SyntaxToken { assert!(text.trim().is_empty()); - let sf = SourceFile::parse(text).ok().unwrap(); + let sf = SourceFile::parse(text, Edition::CURRENT).ok().unwrap(); sf.syntax().clone_for_update().first_child_or_token().unwrap().into_token().unwrap() } pub fn doc_comment(text: &str) -> SyntaxToken { assert!(!text.trim().is_empty()); - let sf = SourceFile::parse(text).ok().unwrap(); + let sf = SourceFile::parse(text, Edition::CURRENT).ok().unwrap(); sf.syntax().first_child_or_token().unwrap().into_token().unwrap() } @@ -1240,7 +1241,7 @@ pub mod tokens { impl WsBuilder { pub fn new(text: &str) -> WsBuilder { - WsBuilder(SourceFile::parse(text).ok().unwrap()) + WsBuilder(SourceFile::parse(text, Edition::CURRENT).ok().unwrap()) } pub fn ws(&self) -> SyntaxToken { self.0.syntax().first_child_or_token().unwrap().into_token().unwrap() diff --git a/src/tools/rust-analyzer/crates/syntax/src/fuzz.rs b/src/tools/rust-analyzer/crates/syntax/src/fuzz.rs index 28738671790c..682dcd7cc445 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/fuzz.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/fuzz.rs @@ -4,6 +4,7 @@ use std::str::{self, FromStr}; +use parser::Edition; use text_edit::Indel; use crate::{validation, AstNode, SourceFile, TextRange}; @@ -14,7 +15,7 @@ fn check_file_invariants(file: &SourceFile) { } pub fn check_parser(text: &str) { - let file = SourceFile::parse(text); + let file = SourceFile::parse(text, Edition::CURRENT); check_file_invariants(&file.tree()); } @@ -48,11 +49,11 @@ impl CheckReparse { #[allow(clippy::print_stderr)] pub fn run(&self) { - let parse = SourceFile::parse(&self.text); - let new_parse = parse.reparse(&self.edit); + let parse = SourceFile::parse(&self.text, Edition::CURRENT); + let new_parse = parse.reparse(&self.edit, Edition::CURRENT); check_file_invariants(&new_parse.tree()); assert_eq!(&new_parse.tree().syntax().text().to_string(), &self.edited_text); - let full_reparse = SourceFile::parse(&self.edited_text); + let full_reparse = SourceFile::parse(&self.edited_text, Edition::CURRENT); for (a, b) in new_parse.tree().syntax().descendants().zip(full_reparse.tree().syntax().descendants()) { diff --git a/src/tools/rust-analyzer/crates/syntax/src/hacks.rs b/src/tools/rust-analyzer/crates/syntax/src/hacks.rs index a3023c3195f3..36615d11d85f 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/hacks.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/hacks.rs @@ -2,11 +2,13 @@ //! //! Please avoid adding new usages of the functions in this module +use parser::Edition; + use crate::{ast, AstNode}; pub fn parse_expr_from_str(s: &str) -> Option { let s = s.trim(); - let file = ast::SourceFile::parse(&format!("const _: () = {s};")); + let file = ast::SourceFile::parse(&format!("const _: () = {s};"), Edition::CURRENT); let expr = file.syntax_node().descendants().find_map(ast::Expr::cast)?; if expr.syntax().text() != s { return None; diff --git a/src/tools/rust-analyzer/crates/syntax/src/lib.rs b/src/tools/rust-analyzer/crates/syntax/src/lib.rs index 1bb82cc191ff..3a9ebafe87dc 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/lib.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/lib.rs @@ -60,11 +60,12 @@ pub use crate::{ }, token_text::TokenText, }; -pub use parser::{SyntaxKind, T}; +pub use parser::{Edition, SyntaxKind, T}; pub use rowan::{ api::Preorder, Direction, GreenNode, NodeOrToken, SyntaxText, TextRange, TextSize, TokenAtOffset, WalkEvent, }; +pub use rustc_lexer::unescape; pub use smol_str::{format_smolstr, SmolStr}; /// `Parse` is the result of the parsing: a syntax tree and a collection of @@ -141,8 +142,8 @@ impl Parse { buf } - pub fn reparse(&self, indel: &Indel) -> Parse { - self.incremental_reparse(indel).unwrap_or_else(|| self.full_reparse(indel)) + pub fn reparse(&self, indel: &Indel, edition: Edition) -> Parse { + self.incremental_reparse(indel).unwrap_or_else(|| self.full_reparse(indel, edition)) } fn incremental_reparse(&self, indel: &Indel) -> Option> { @@ -159,10 +160,10 @@ impl Parse { }) } - fn full_reparse(&self, indel: &Indel) -> Parse { + fn full_reparse(&self, indel: &Indel, edition: Edition) -> Parse { let mut text = self.tree().syntax().text().to_string(); indel.apply(&mut text); - SourceFile::parse(&text) + SourceFile::parse(&text, edition) } } @@ -170,9 +171,9 @@ impl Parse { pub use crate::ast::SourceFile; impl SourceFile { - pub fn parse(text: &str) -> Parse { + pub fn parse(text: &str, edition: Edition) -> Parse { let _p = tracing::span!(tracing::Level::INFO, "SourceFile::parse").entered(); - let (green, errors) = parsing::parse_text(text); + let (green, errors) = parsing::parse_text(text, edition); let root = SyntaxNode::new_root(green.clone()); assert_eq!(root.kind(), SyntaxKind::SOURCE_FILE); @@ -185,7 +186,10 @@ impl SourceFile { } impl ast::TokenTree { - pub fn reparse_as_comma_separated_expr(self) -> Parse { + pub fn reparse_as_comma_separated_expr( + self, + edition: parser::Edition, + ) -> Parse { let tokens = self.syntax().descendants_with_tokens().filter_map(NodeOrToken::into_token); let mut parser_input = parser::Input::default(); @@ -219,7 +223,7 @@ impl ast::TokenTree { } } - let parser_output = parser::TopEntryPoint::MacroEagerInput.parse(&parser_input); + let parser_output = parser::TopEntryPoint::MacroEagerInput.parse(&parser_input, edition); let mut tokens = self.syntax().descendants_with_tokens().filter_map(NodeOrToken::into_token); @@ -337,7 +341,7 @@ fn api_walkthrough() { // // The `parse` method returns a `Parse` -- a pair of syntax tree and a list // of errors. That is, syntax tree is constructed even in presence of errors. - let parse = SourceFile::parse(source_code); + let parse = SourceFile::parse(source_code, parser::Edition::CURRENT); assert!(parse.errors().is_empty()); // The `tree` method returns an owned syntax node of type `SourceFile`. diff --git a/src/tools/rust-analyzer/crates/syntax/src/parsing.rs b/src/tools/rust-analyzer/crates/syntax/src/parsing.rs index d750476f63cb..420f4938e540 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/parsing.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/parsing.rs @@ -9,11 +9,11 @@ use crate::{syntax_node::GreenNode, SyntaxError, SyntaxTreeBuilder}; pub(crate) use crate::parsing::reparsing::incremental_reparse; -pub(crate) fn parse_text(text: &str) -> (GreenNode, Vec) { +pub(crate) fn parse_text(text: &str, edition: parser::Edition) -> (GreenNode, Vec) { let _p = tracing::span!(tracing::Level::INFO, "parse_text").entered(); let lexed = parser::LexedStr::new(text); let parser_input = lexed.to_input(); - let parser_output = parser::TopEntryPoint::SourceFile.parse(&parser_input); + let parser_output = parser::TopEntryPoint::SourceFile.parse(&parser_input, edition); let (node, errors, _eof) = build_tree(lexed, parser_output); (node, errors) } 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 14715b572534..354b89fd4903 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/parsing/reparsing.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/parsing/reparsing.rs @@ -26,7 +26,9 @@ pub(crate) fn incremental_reparse( return Some((green, merge_errors(errors, new_errors, old_range, edit), old_range)); } - if let Some((green, new_errors, old_range)) = reparse_block(node, edit) { + if let Some((green, new_errors, old_range)) = + reparse_block(node, edit, parser::Edition::CURRENT) + { return Some((green, merge_errors(errors, new_errors, old_range, edit), old_range)); } None @@ -84,6 +86,7 @@ fn reparse_token( fn reparse_block( root: &SyntaxNode, edit: &Indel, + edition: parser::Edition, ) -> Option<(GreenNode, Vec, TextRange)> { let (node, reparser) = find_reparsable_node(root, edit.delete)?; let text = get_text_after_edit(node.clone().into(), edit); @@ -94,7 +97,7 @@ fn reparse_block( return None; } - let tree_traversal = reparser.parse(&parser_input); + let tree_traversal = reparser.parse(&parser_input, edition); let (green, new_parser_errors, _eof) = build_tree(lexed, tree_traversal); @@ -174,6 +177,7 @@ fn merge_errors( #[cfg(test)] mod tests { + use parser::Edition; use test_utils::{assert_eq_text, extract_range}; use super::*; @@ -188,9 +192,9 @@ mod tests { after }; - let fully_reparsed = SourceFile::parse(&after); + let fully_reparsed = SourceFile::parse(&after, Edition::CURRENT); let incrementally_reparsed: Parse = { - let before = SourceFile::parse(&before); + let before = SourceFile::parse(&before, Edition::CURRENT); let (green, new_errors, range) = incremental_reparse( before.tree().syntax(), &edit, diff --git a/src/tools/rust-analyzer/crates/syntax/src/ptr.rs b/src/tools/rust-analyzer/crates/syntax/src/ptr.rs index fb8aee9c3b0d..ed4894f9b9c3 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ptr.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ptr.rs @@ -120,7 +120,7 @@ impl From> for SyntaxNodePtr { fn test_local_syntax_ptr() { use crate::{ast, AstNode, SourceFile}; - let file = SourceFile::parse("struct Foo { f: u32, }").ok().unwrap(); + let file = SourceFile::parse("struct Foo { f: u32, }", parser::Edition::CURRENT).ok().unwrap(); let field = file.syntax().descendants().find_map(ast::RecordField::cast).unwrap(); let ptr = SyntaxNodePtr::new(field.syntax()); let field_syntax = ptr.to_node(file.syntax()); diff --git a/src/tools/rust-analyzer/crates/syntax/src/tests.rs b/src/tools/rust-analyzer/crates/syntax/src/tests.rs index 439daa358a8e..f0d58efc01ed 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/tests.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/tests.rs @@ -5,6 +5,7 @@ use std::{ use ast::HasName; use expect_test::expect_file; +use parser::Edition; use rayon::prelude::*; use stdx::format_to_acc; use test_utils::{bench, bench_fixture, project_root}; @@ -19,7 +20,7 @@ fn main() { } "#; - let parse = SourceFile::parse(code); + let parse = SourceFile::parse(code, Edition::CURRENT); // eprintln!("{:#?}", parse.syntax_node()); assert!(parse.ok().is_ok()); } @@ -33,7 +34,7 @@ fn benchmark_parser() { let data = bench_fixture::glorious_old_parser(); let tree = { let _b = bench("parsing"); - let p = SourceFile::parse(&data); + let p = SourceFile::parse(&data, Edition::CURRENT); assert!(p.errors().is_empty()); assert_eq!(p.tree().syntax.text_range().len(), 352474.into()); p.tree() @@ -50,7 +51,7 @@ fn benchmark_parser() { #[test] fn validation_tests() { dir_tests(&test_data_dir(), &["parser/validation"], "rast", |text, path| { - let parse = SourceFile::parse(text); + let parse = SourceFile::parse(text, Edition::CURRENT); let errors = parse.errors(); assert_errors_are_present(&errors, path); parse.debug_dump() @@ -110,7 +111,7 @@ fn self_hosting_parsing() { .into_par_iter() .filter_map(|file| { let text = read_text(&file); - match SourceFile::parse(&text).ok() { + match SourceFile::parse(&text, Edition::CURRENT).ok() { Ok(_) => None, Err(err) => Some((file, err)), } diff --git a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs index c8d785f83e8f..89ed6a615797 100644 --- a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs +++ b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs @@ -189,8 +189,8 @@ impl ChangeFixture { meta.edition, Some(crate_name.clone().into()), version, - meta.cfg.clone(), - Some(meta.cfg), + From::from(meta.cfg.clone()), + Some(From::from(meta.cfg)), meta.env, false, origin, @@ -209,7 +209,7 @@ impl ChangeFixture { assert!(default_crate_root.is_none()); default_crate_root = Some(file_id); default_cfg.extend(meta.cfg.into_iter()); - default_env.extend(meta.env.iter().map(|(x, y)| (x.to_owned(), y.to_owned()))); + default_env.extend_from_other(&meta.env); } source_change.change_file(file_id, Some(text)); @@ -227,8 +227,8 @@ impl ChangeFixture { Edition::CURRENT, Some(CrateName::new("test").unwrap().into()), None, - default_cfg.clone(), - Some(default_cfg), + From::from(default_cfg.clone()), + Some(From::from(default_cfg)), default_env, false, CrateOrigin::Local { repo: None, name: None }, @@ -260,7 +260,7 @@ impl ChangeFixture { let core_crate = crate_graph.add_crate_root( core_file, - Edition::Edition2021, + Edition::CURRENT, Some(CrateDisplayName::from_canonical_name("core".to_owned())), None, Default::default(), @@ -299,7 +299,7 @@ impl ChangeFixture { let proc_macros_crate = crate_graph.add_crate_root( proc_lib_file, - Edition::Edition2021, + Edition::CURRENT, Some(CrateDisplayName::from_canonical_name("proc_macros".to_owned())), None, Default::default(), 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 7e34c3618995..aafe4fb5b10b 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs @@ -186,7 +186,7 @@ impl FixtureWithProjectMeta { if let Some(meta) = fixture.strip_prefix("//- target_data_layout:") { let (meta, remain) = meta.split_once('\n').unwrap(); - target_data_layout = meta.trim().to_owned(); + meta.trim().clone_into(&mut target_data_layout); fixture = remain; } 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 4cfdec2b5c5d..45bb777d4d2b 100644 --- a/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs +++ b/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs @@ -9,7 +9,10 @@ #![warn(rust_2018_idioms, unused_lifetimes)] -use std::fs; +use std::{ + fs, + path::{Component, Path}, +}; use crossbeam_channel::{never, select, unbounded, Receiver, Sender}; use notify::{Config, RecommendedWatcher, RecursiveMode, Watcher}; @@ -206,6 +209,11 @@ impl NotifyActor { return true; } let path = entry.path(); + + if path_is_parent_symlink(path) { + return false; + } + root == path || dirs.exclude.iter().chain(&dirs.include).all(|it| it != path) }); @@ -258,3 +266,21 @@ fn read(path: &AbsPath) -> Option> { fn log_notify_error(res: notify::Result) -> Option { res.map_err(|err| tracing::warn!("notify error: {}", err)).ok() } + +/// Is `path` a symlink to a parent directory? +/// +/// Including this path is guaranteed to cause an infinite loop. This +/// heuristic is not sufficient to catch all symlink cycles (it's +/// possible to construct cycle using two or more symlinks), but it +/// catches common cases. +fn path_is_parent_symlink(path: &Path) -> bool { + let Ok(destination) = std::fs::read_link(path) else { + return false; + }; + + // If the symlink is of the form "../..", it's a parent symlink. + let is_relative_parent = + destination.components().all(|c| matches!(c, Component::CurDir | Component::ParentDir)); + + is_relative_parent || path.starts_with(destination) +} 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 7eeb10d544af..d7d283c3eb85 100644 --- a/src/tools/rust-analyzer/crates/vfs/src/file_set.rs +++ b/src/tools/rust-analyzer/crates/vfs/src/file_set.rs @@ -132,6 +132,10 @@ impl FileSetConfig { /// /// `scratch_space` is used as a buffer and will be entirely replaced. fn classify(&self, path: &VfsPath, scratch_space: &mut Vec) -> usize { + // `path` is a file, but r-a only cares about the containing directory. We don't + // want `/foo/bar_baz.rs` to be attributed to source root directory `/foo/bar`. + let path = path.parent().unwrap_or_else(|| path.clone()); + scratch_space.clear(); path.encode(scratch_space); let automaton = PrefixOf::new(scratch_space.as_slice()); diff --git a/src/tools/rust-analyzer/crates/vfs/src/file_set/tests.rs b/src/tools/rust-analyzer/crates/vfs/src/file_set/tests.rs index 2146df185d3a..3cdb60dcb260 100644 --- a/src/tools/rust-analyzer/crates/vfs/src/file_set/tests.rs +++ b/src/tools/rust-analyzer/crates/vfs/src/file_set/tests.rs @@ -40,3 +40,26 @@ fn name_prefix() { let partition = file_set.partition(&vfs).into_iter().map(|it| it.len()).collect::>(); assert_eq!(partition, vec![1, 1, 0]); } + +/// Ensure that we don't consider `/foo/bar_baz.rs` to be in the +/// `/foo/bar/` root. +#[test] +fn name_prefix_partially_matches() { + let mut file_set = FileSetConfig::builder(); + file_set.add_file_set(vec![VfsPath::new_virtual_path("/foo".into())]); + file_set.add_file_set(vec![VfsPath::new_virtual_path("/foo/bar".into())]); + let file_set = file_set.build(); + + let mut vfs = Vfs::default(); + + // These two are both in /foo. + vfs.set_file_contents(VfsPath::new_virtual_path("/foo/lib.rs".into()), Some(Vec::new())); + vfs.set_file_contents(VfsPath::new_virtual_path("/foo/bar_baz.rs".into()), Some(Vec::new())); + + // Only this file is in /foo/bar. + vfs.set_file_contents(VfsPath::new_virtual_path("/foo/bar/biz.rs".into()), Some(Vec::new())); + + let partition = file_set.partition(&vfs).into_iter().map(|it| it.len()).collect::>(); + + assert_eq!(partition, vec![2, 1, 0]); +} diff --git a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md index 939b1819c7e8..f1815082e2e0 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 @@ MC/DC Decision Region (LL:8) to (LL:14) + | + | Number of Conditions: 2 + | Condition C1 --> (LL:8) + | Condition C2 --> (LL:13) + | + | Executed MC/DC Test Vectors: + | + | C1, C2 Result + | 1 { F, - = F } + | + | C1-Pair: not covered + | C2-Pair: not covered + | MC/DC Coverage for Decision: 0.00% + | + ------------------ + LL| 0| say("a and b"); + LL| 2| } else { + LL| 2| say("not both"); + LL| 2| } + LL| 2|} + LL| | + LL| 2|fn mcdc_check_a(a: bool, b: bool) { + LL| 2| if a && b { + ^1 + ------------------ + |---> MC/DC Decision Region (LL:8) to (LL:14) + | + | Number of Conditions: 2 + | Condition C1 --> (LL:8) + | Condition C2 --> (LL:13) + | + | Executed MC/DC Test Vectors: + | + | C1, C2 Result + | 1 { F, - = F } + | 2 { T, T = T } + | + | C1-Pair: covered: (1,2) + | C2-Pair: not covered + | MC/DC Coverage for Decision: 50.00% + | + ------------------ + LL| 1| say("a and b"); + LL| 1| } else { + LL| 1| say("not both"); + LL| 1| } + LL| 2|} + LL| | + LL| 2|fn mcdc_check_b(a: bool, b: bool) { + LL| 2| if a && b { + ------------------ + |---> MC/DC Decision Region (LL:8) to (LL:14) + | + | Number of Conditions: 2 + | Condition C1 --> (LL:8) + | Condition C2 --> (LL:13) + | + | Executed MC/DC Test Vectors: + | + | C1, C2 Result + | 1 { T, F = F } + | 2 { T, T = T } + | + | C1-Pair: not covered + | C2-Pair: covered: (1,2) + | MC/DC Coverage for Decision: 50.00% + | + ------------------ + LL| 1| say("a and b"); + LL| 1| } else { + LL| 1| say("not both"); + LL| 1| } + LL| 2|} + LL| | + LL| 3|fn mcdc_check_both(a: bool, b: bool) { + LL| 3| if a && b { + ^2 + ------------------ + |---> MC/DC Decision Region (LL:8) to (LL:14) + | + | Number of Conditions: 2 + | Condition C1 --> (LL:8) + | Condition C2 --> (LL:13) + | + | Executed MC/DC Test Vectors: + | + | C1, C2 Result + | 1 { F, - = F } + | 2 { T, F = F } + | 3 { T, T = T } + | + | C1-Pair: covered: (1,3) + | C2-Pair: covered: (2,3) + | MC/DC Coverage for Decision: 100.00% + | + ------------------ + LL| 1| say("a and b"); + LL| 2| } else { + LL| 2| say("not both"); + LL| 2| } + LL| 3|} + LL| | + LL| 4|fn mcdc_check_tree_decision(a: bool, b: bool, c: bool) { + LL| 4| // This expression is intentionally written in a way + LL| 4| // where 100% branch coverage indicates 100% mcdc coverage. + LL| 4| if a && (b || c) { + ^3 ^2 + ------------------ + |---> MC/DC Decision Region (LL:8) to (LL:21) + | + | Number of Conditions: 3 + | Condition C1 --> (LL:8) + | Condition C2 --> (LL:14) + | Condition C3 --> (LL:19) + | + | Executed MC/DC Test Vectors: + | + | C1, C2, C3 Result + | 1 { F, -, - = F } + | 2 { T, F, F = F } + | 3 { T, T, - = T } + | 4 { T, F, T = T } + | + | C1-Pair: covered: (1,3) + | C2-Pair: covered: (2,3) + | C3-Pair: covered: (2,4) + | MC/DC Coverage for Decision: 100.00% + | + ------------------ + LL| 2| say("pass"); + LL| 2| } else { + LL| 2| say("reject"); + LL| 2| } + LL| 4|} + LL| | + LL| 4|fn mcdc_check_not_tree_decision(a: bool, b: bool, c: bool) { + LL| 4| // Contradict to `mcdc_check_tree_decision`, + LL| 4| // 100% branch coverage of this expression does not mean indicates 100% mcdc coverage. + LL| 4| if (a || b) && c { + ^1 + ------------------ + |---> MC/DC Decision Region (LL:8) to (LL:21) + | + | Number of Conditions: 3 + | Condition C1 --> (LL:9) + | Condition C2 --> (LL:14) + | Condition C3 --> (LL:20) + | + | Executed MC/DC Test Vectors: + | + | C1, C2, C3 Result + | 1 { T, -, F = F } + | 2 { T, -, T = T } + | 3 { F, T, T = T } + | + | C1-Pair: not covered + | C2-Pair: not covered + | C3-Pair: covered: (1,2) + | MC/DC Coverage for Decision: 33.33% + | + ------------------ + LL| 2| say("pass"); + LL| 2| } else { + LL| 2| say("reject"); + LL| 2| } + LL| 4|} + LL| | + LL| 3|fn mcdc_nested_if(a: bool, b: bool, c: bool) { + LL| 3| if a || b { + ^0 + ------------------ + |---> MC/DC Decision Region (LL:8) to (LL:14) + | + | Number of Conditions: 2 + | Condition C1 --> (LL:8) + | Condition C2 --> (LL:13) + | + | Executed MC/DC Test Vectors: + | + | C1, C2 Result + | 1 { T, - = T } + | + | C1-Pair: not covered + | C2-Pair: not covered + | MC/DC Coverage for Decision: 0.00% + | + ------------------ + LL| 3| say("a or b"); + LL| 3| if b && c { + ^2 + ------------------ + |---> MC/DC Decision Region (LL:12) to (LL:18) + | + | Number of Conditions: 2 + | Condition C1 --> (LL:12) + | Condition C2 --> (LL:17) + | + | Executed MC/DC Test Vectors: + | + | C1, C2 Result + | 1 { F, - = F } + | 2 { T, F = F } + | 3 { T, T = T } + | + | C1-Pair: covered: (1,3) + | C2-Pair: covered: (2,3) + | MC/DC Coverage for Decision: 100.00% + | + ------------------ + LL| 1| say("b and c"); + LL| 2| } + LL| 0| } else { + LL| 0| say("neither a nor b"); + LL| 0| } + LL| 3|} + LL| | + LL| |#[coverage(off)] + LL| |fn main() { + LL| | mcdc_check_neither(false, false); + LL| | mcdc_check_neither(false, true); + LL| | + LL| | mcdc_check_a(true, true); + LL| | mcdc_check_a(false, true); + LL| | + LL| | mcdc_check_b(true, true); + LL| | mcdc_check_b(true, false); + LL| | + LL| | mcdc_check_both(false, true); + LL| | mcdc_check_both(true, true); + LL| | mcdc_check_both(true, false); + LL| | + LL| | mcdc_check_tree_decision(false, true, true); + LL| | mcdc_check_tree_decision(true, true, false); + LL| | mcdc_check_tree_decision(true, false, false); + LL| | mcdc_check_tree_decision(true, false, true); + LL| | + LL| | mcdc_check_not_tree_decision(false, true, true); + LL| | mcdc_check_not_tree_decision(true, true, false); + LL| | mcdc_check_not_tree_decision(true, false, false); + LL| | mcdc_check_not_tree_decision(true, false, true); + LL| | + LL| | mcdc_nested_if(true, false, true); + LL| | mcdc_nested_if(true, true, true); + LL| | mcdc_nested_if(true, true, false); + LL| |} + LL| | + LL| |#[coverage(off)] + LL| |fn say(message: &str) { + LL| | core::hint::black_box(message); + LL| |} + diff --git a/tests/coverage/mcdc_if.rs b/tests/coverage/mcdc_if.rs new file mode 100644 index 000000000000..a85843721c6c --- /dev/null +++ b/tests/coverage/mcdc_if.rs @@ -0,0 +1,103 @@ +#![feature(coverage_attribute)] +//@ edition: 2021 +//@ min-llvm-version: 18 +//@ compile-flags: -Zcoverage-options=mcdc +//@ llvm-cov-flags: --show-mcdc + +fn mcdc_check_neither(a: bool, b: bool) { + if a && b { + say("a and b"); + } else { + say("not both"); + } +} + +fn mcdc_check_a(a: bool, b: bool) { + if a && b { + say("a and b"); + } else { + say("not both"); + } +} + +fn mcdc_check_b(a: bool, b: bool) { + if a && b { + say("a and b"); + } else { + say("not both"); + } +} + +fn mcdc_check_both(a: bool, b: bool) { + if a && b { + say("a and b"); + } else { + say("not both"); + } +} + +fn mcdc_check_tree_decision(a: bool, b: bool, c: bool) { + // This expression is intentionally written in a way + // where 100% branch coverage indicates 100% mcdc coverage. + if a && (b || c) { + say("pass"); + } else { + say("reject"); + } +} + +fn mcdc_check_not_tree_decision(a: bool, b: bool, c: bool) { + // Contradict to `mcdc_check_tree_decision`, + // 100% branch coverage of this expression does not mean indicates 100% mcdc coverage. + if (a || b) && c { + say("pass"); + } else { + say("reject"); + } +} + +fn mcdc_nested_if(a: bool, b: bool, c: bool) { + if a || b { + say("a or b"); + if b && c { + say("b and c"); + } + } else { + say("neither a nor b"); + } +} + +#[coverage(off)] +fn main() { + mcdc_check_neither(false, false); + mcdc_check_neither(false, true); + + mcdc_check_a(true, true); + mcdc_check_a(false, true); + + mcdc_check_b(true, true); + mcdc_check_b(true, false); + + mcdc_check_both(false, true); + mcdc_check_both(true, true); + mcdc_check_both(true, false); + + mcdc_check_tree_decision(false, true, true); + mcdc_check_tree_decision(true, true, false); + mcdc_check_tree_decision(true, false, false); + mcdc_check_tree_decision(true, false, true); + + mcdc_check_not_tree_decision(false, true, true); + mcdc_check_not_tree_decision(true, true, false); + mcdc_check_not_tree_decision(true, false, false); + mcdc_check_not_tree_decision(true, false, true); + + mcdc_nested_if(true, false, true); + mcdc_nested_if(true, true, true); + mcdc_nested_if(true, true, false); +} + +#[coverage(off)] +fn say(message: &str) { + core::hint::black_box(message); +} diff --git a/tests/crashes/103708.rs b/tests/crashes/103708.rs new file mode 100644 index 000000000000..f3154279cf54 --- /dev/null +++ b/tests/crashes/103708.rs @@ -0,0 +1,16 @@ +//@ known-bug: #103708 +#![feature(min_specialization)] + +trait MySpecTrait { + fn f(); +} + +impl<'a, T: ?Sized> MySpecTrait for T { + default fn f() {} +} + +impl<'a, T: ?Sized> MySpecTrait for &'a T { + fn f() {} +} + +fn main() {} diff --git a/tests/crashes/104685.rs b/tests/crashes/104685.rs new file mode 100644 index 000000000000..25ddbb2efaa2 --- /dev/null +++ b/tests/crashes/104685.rs @@ -0,0 +1,15 @@ +//@ known-bug: #104685 +//@ compile-flags: -Zextra-const-ub-checks +#![feature(extern_types)] + +extern { + pub type ExternType; +} + +extern "C" { + pub static EXTERN: ExternType; +} + +pub static EMPTY: () = unsafe { &EXTERN; }; + +fn main() {} diff --git a/tests/crashes/105249.rs b/tests/crashes/105249.rs new file mode 100644 index 000000000000..592ed5b6dbc8 --- /dev/null +++ b/tests/crashes/105249.rs @@ -0,0 +1,16 @@ +//@ known-bug: #105249 +//@ compile-flags: -Zpolymorphize=on + +trait Foo { + fn print<'a>(&'a self) where T: 'a { println!("foo"); } +} + +impl<'a> Foo<&'a ()> for () { } + +trait Bar: for<'a> Foo<&'a ()> { } + +impl Bar for () {} + +fn main() { + (&() as &dyn Bar).print(); // Segfault +} diff --git a/tests/crashes/115994.rs b/tests/crashes/115994.rs new file mode 100644 index 000000000000..23d1507136f7 --- /dev/null +++ b/tests/crashes/115994.rs @@ -0,0 +1,17 @@ +//@ known-bug: #115994 +//@ compile-flags: -Cdebuginfo=2 --crate-type lib + +// To prevent "overflow while adding drop-check rules". +use std::mem::ManuallyDrop; + +pub enum Foo { + Leaf(U), + + Branch(BoxedFoo>), +} + +pub type BoxedFoo = ManuallyDrop>>; + +pub fn test() -> Foo { + todo!() +} diff --git a/tests/crashes/116721.rs b/tests/crashes/116721.rs new file mode 100644 index 000000000000..fc1a6530bc82 --- /dev/null +++ b/tests/crashes/116721.rs @@ -0,0 +1,9 @@ +//@ known-bug: #116721 +//@ compile-flags: -Zmir-opt-level=3 --emit=mir +fn hey(it: &[T]) +where + [T]: Clone, +{ +} + +fn main() {} diff --git a/tests/crashes/118244.rs b/tests/crashes/118244.rs new file mode 100644 index 000000000000..bfa1f5b7dd48 --- /dev/null +++ b/tests/crashes/118244.rs @@ -0,0 +1,22 @@ +//@ known-bug: #118244 +//@ compile-flags: -Cdebuginfo=2 + +#![allow(incomplete_features)] +#![feature(generic_const_exprs)] +struct Inner; +impl Inner where [(); N + M]: { + fn i() -> Self { + Self + } +} + +struct Outer(Inner) where [(); A + (B * 2)]:; +impl Outer where [(); A + (B * 2)]: { + fn o() -> Self { + Self(Inner::i()) + } +} + +fn main() { + Outer::<1, 1>::o(); +} diff --git a/tests/crashes/123664.rs b/tests/crashes/123664.rs new file mode 100644 index 000000000000..80c415fe07bd --- /dev/null +++ b/tests/crashes/123664.rs @@ -0,0 +1,4 @@ +//@ known-bug: #123664 +#![feature(generic_const_exprs, effects)] +const fn with_positive() {} +pub fn main() {} diff --git a/tests/crashes/123955.rs b/tests/crashes/123955.rs new file mode 100644 index 000000000000..fdd58c84794c --- /dev/null +++ b/tests/crashes/123955.rs @@ -0,0 +1,6 @@ +//@ known-bug: #123955 +//@ compile-flags: -Clto -Zvirtual-function-elimination +//@ only-x86_64 +pub fn main() { + _ = Box::new(()) as Box; +} diff --git a/tests/crashes/124083.rs b/tests/crashes/124083.rs new file mode 100644 index 000000000000..e9cbf3f70861 --- /dev/null +++ b/tests/crashes/124083.rs @@ -0,0 +1,9 @@ +//@ known-bug: #124083 + +struct Outest(&'a ()); + +fn make() -> Outest {} + +fn main() { + if let Outest("foo") = make() {} +} diff --git a/tests/crashes/124092.rs b/tests/crashes/124092.rs new file mode 100644 index 000000000000..c03db384e76e --- /dev/null +++ b/tests/crashes/124092.rs @@ -0,0 +1,7 @@ +//@ known-bug: #124092 +//@ compile-flags: -Zvirtual-function-elimination=true -Clto=true +//@ only-x86_64 +const X: for<'b> fn(&'b ()) = |&()| (); +fn main() { + let dyn_debug = Box::new(X) as Box as Box; +} diff --git a/tests/crashes/124151.rs b/tests/crashes/124151.rs new file mode 100644 index 000000000000..5e55ac2aa943 --- /dev/null +++ b/tests/crashes/124151.rs @@ -0,0 +1,14 @@ +//@ known-bug: #124151 +#![feature(generic_const_exprs)] + +use std::ops::Add; + +pub struct Dimension; + +pub struct Quantity(S); + +impl Add for Quantity {} + +pub fn add(x: Quantity) -> Quantity { + x + y +} diff --git a/tests/crashes/124164.rs b/tests/crashes/124164.rs new file mode 100644 index 000000000000..8c9b4bddbe80 --- /dev/null +++ b/tests/crashes/124164.rs @@ -0,0 +1,4 @@ +//@ known-bug: #124164 +static S_COUNT: = std::sync::atomic::AtomicUsize::new(0); + +fn main() {} diff --git a/tests/crashes/124182.rs b/tests/crashes/124182.rs new file mode 100644 index 000000000000..46948207df38 --- /dev/null +++ b/tests/crashes/124182.rs @@ -0,0 +1,22 @@ +//@ known-bug: #124182 +struct LazyLock { + data: (Copy, fn() -> T), +} + +impl LazyLock { + pub const fn new(f: fn() -> T) -> LazyLock { + LazyLock { data: (None, f) } + } +} + +struct A(Option); + +impl Default for A { + fn default() -> Self { + A(None) + } +} + +static EMPTY_SET: LazyLock> = LazyLock::new(A::default); + +fn main() {} diff --git a/tests/crashes/124189.rs b/tests/crashes/124189.rs new file mode 100644 index 000000000000..7c193ec1bce8 --- /dev/null +++ b/tests/crashes/124189.rs @@ -0,0 +1,14 @@ +//@ known-bug: #124189 +trait Trait { + type Type; +} + +impl Trait for T { + type Type = (); +} + +fn f(_: <&Copy as Trait>::Type) {} + +fn main() { + f(()); +} diff --git a/tests/crashes/124207.rs b/tests/crashes/124207.rs new file mode 100644 index 000000000000..a4e1c5518900 --- /dev/null +++ b/tests/crashes/124207.rs @@ -0,0 +1,9 @@ +//@ known-bug: #124207 +#![feature(transmutability)] +#![feature(type_alias_impl_trait)] +trait OpaqueTrait {} +type OpaqueType = impl OpaqueTrait; +trait AnotherTrait {} +impl> AnotherTrait for T {} +impl AnotherTrait for OpaqueType {} +pub fn main() {} diff --git a/tests/debuginfo/msvc-pretty-enums.rs b/tests/debuginfo/msvc-pretty-enums.rs index cfac14a22c41..0293ec0ec397 100644 --- a/tests/debuginfo/msvc-pretty-enums.rs +++ b/tests/debuginfo/msvc-pretty-enums.rs @@ -132,7 +132,6 @@ // cdb-command: dx -r2 arbitrary_discr2,d // cdb-check: arbitrary_discr2,d : Def [Type: enum2$] // cdb-check: [+0x[...]] __0 : 5678 [Type: unsigned int] -#![feature(generic_nonzero)] #![feature(rustc_attrs)] #![feature(repr128)] #![feature(arbitrary_enum_discriminant)] diff --git a/tests/debuginfo/numeric-types.rs b/tests/debuginfo/numeric-types.rs index 1ff72d34fbdf..98bc31e88557 100644 --- a/tests/debuginfo/numeric-types.rs +++ b/tests/debuginfo/numeric-types.rs @@ -237,7 +237,6 @@ // lldb-command:v nz_usize // lldb-check:[...] 122 { __0 = { 0 = 122 } } -#![feature(generic_nonzero)] use std::num::*; use std::sync::atomic::*; diff --git a/tests/mir-opt/README.md b/tests/mir-opt/README.md index 39a7b5aea123..e4b252f5565b 100644 --- a/tests/mir-opt/README.md +++ b/tests/mir-opt/README.md @@ -14,17 +14,18 @@ presence of pointers in constants or other bit width dependent things. In that c to your test, causing separate files to be generated for 32bit and 64bit systems. -## Unit testing +## Testing a particular MIR pass If you are only testing the behavior of a particular mir-opt pass on some specific input (as is usually the case), you should add ``` -// unit-test: PassName +//@ test-mir-pass: PassName ``` to the top of the file. This makes sure that other passes don't run which means you'll get the input -you expected and your test won't break when other code changes. +you expected and your test won't break when other code changes. This also lets you test passes +that are disabled by default. ## Emit a diff of the mir for a specific optimization diff --git a/tests/mir-opt/array_index_is_temporary.rs b/tests/mir-opt/array_index_is_temporary.rs index 500b8b7f7c74..cd44c31d0004 100644 --- a/tests/mir-opt/array_index_is_temporary.rs +++ b/tests/mir-opt/array_index_is_temporary.rs @@ -1,4 +1,4 @@ -//@ unit-test: SimplifyCfg-pre-optimizations +//@ test-mir-pass: SimplifyCfg-pre-optimizations // EMIT_MIR_FOR_EACH_PANIC_STRATEGY // Retagging (from Stacked Borrows) relies on the array index being a fresh // temporary, so that side-effects cannot change it. diff --git a/tests/mir-opt/basic_assignment.rs b/tests/mir-opt/basic_assignment.rs index 0a23cddf620b..a9342c13e155 100644 --- a/tests/mir-opt/basic_assignment.rs +++ b/tests/mir-opt/basic_assignment.rs @@ -1,4 +1,4 @@ -//@ unit-test: ElaborateDrops +//@ test-mir-pass: ElaborateDrops //@ needs-unwind // this tests move up progration, which is not yet implemented diff --git a/tests/mir-opt/box_expr.rs b/tests/mir-opt/box_expr.rs index 8c016629770b..a2d3ab94db65 100644 --- a/tests/mir-opt/box_expr.rs +++ b/tests/mir-opt/box_expr.rs @@ -1,4 +1,4 @@ -//@ unit-test: ElaborateDrops +//@ test-mir-pass: ElaborateDrops //@ needs-unwind #![feature(rustc_attrs, stmt_expr_attributes)] diff --git a/tests/mir-opt/const_allocation.rs b/tests/mir-opt/const_allocation.rs index 038caaa0ae73..e65b2ed9a879 100644 --- a/tests/mir-opt/const_allocation.rs +++ b/tests/mir-opt/const_allocation.rs @@ -1,5 +1,5 @@ // skip-filecheck -//@ unit-test: GVN +//@ test-mir-pass: GVN //@ ignore-endian-big // EMIT_MIR_FOR_EACH_BIT_WIDTH static FOO: &[(Option, &[&str])] = diff --git a/tests/mir-opt/const_allocation2.rs b/tests/mir-opt/const_allocation2.rs index ba987c1c26b9..0681d4356dd8 100644 --- a/tests/mir-opt/const_allocation2.rs +++ b/tests/mir-opt/const_allocation2.rs @@ -1,5 +1,5 @@ // skip-filecheck -//@ unit-test: GVN +//@ test-mir-pass: GVN //@ ignore-endian-big // EMIT_MIR_FOR_EACH_BIT_WIDTH // EMIT_MIR const_allocation2.main.GVN.after.mir diff --git a/tests/mir-opt/const_allocation3.rs b/tests/mir-opt/const_allocation3.rs index 86ffdef24aba..46be3e1e36e8 100644 --- a/tests/mir-opt/const_allocation3.rs +++ b/tests/mir-opt/const_allocation3.rs @@ -1,5 +1,5 @@ // skip-filecheck -//@ unit-test: GVN +//@ test-mir-pass: GVN //@ ignore-endian-big // EMIT_MIR_FOR_EACH_BIT_WIDTH // EMIT_MIR const_allocation3.main.GVN.after.mir diff --git a/tests/mir-opt/const_debuginfo.rs b/tests/mir-opt/const_debuginfo.rs index 319bcf484111..b880d7e07a6e 100644 --- a/tests/mir-opt/const_debuginfo.rs +++ b/tests/mir-opt/const_debuginfo.rs @@ -1,4 +1,4 @@ -//@ unit-test: ConstDebugInfo +//@ test-mir-pass: ConstDebugInfo //@ compile-flags: -C overflow-checks=no -Zmir-enable-passes=+GVN struct Point { diff --git a/tests/mir-opt/const_prop/address_of_pair.rs b/tests/mir-opt/const_prop/address_of_pair.rs index c6bd57669903..6d0c0f8ad52a 100644 --- a/tests/mir-opt/const_prop/address_of_pair.rs +++ b/tests/mir-opt/const_prop/address_of_pair.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN // EMIT_MIR address_of_pair.fn0.GVN.diff pub fn fn0() -> bool { diff --git a/tests/mir-opt/const_prop/aggregate.rs b/tests/mir-opt/const_prop/aggregate.rs index 2db477077728..8f8f92bbba19 100644 --- a/tests/mir-opt/const_prop/aggregate.rs +++ b/tests/mir-opt/const_prop/aggregate.rs @@ -1,5 +1,5 @@ // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: GVN +//@ test-mir-pass: GVN //@ compile-flags: -O // EMIT_MIR aggregate.main.GVN.diff diff --git a/tests/mir-opt/const_prop/array_index.rs b/tests/mir-opt/const_prop/array_index.rs index 2a9ca5f95e65..aff72268223b 100644 --- a/tests/mir-opt/const_prop/array_index.rs +++ b/tests/mir-opt/const_prop/array_index.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN // EMIT_MIR_FOR_EACH_PANIC_STRATEGY // EMIT_MIR_FOR_EACH_BIT_WIDTH diff --git a/tests/mir-opt/const_prop/bad_op_div_by_zero.rs b/tests/mir-opt/const_prop/bad_op_div_by_zero.rs index c411d3b59ab2..6c576718ac87 100644 --- a/tests/mir-opt/const_prop/bad_op_div_by_zero.rs +++ b/tests/mir-opt/const_prop/bad_op_div_by_zero.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN // EMIT_MIR_FOR_EACH_PANIC_STRATEGY // EMIT_MIR bad_op_div_by_zero.main.GVN.diff diff --git a/tests/mir-opt/const_prop/bad_op_mod_by_zero.rs b/tests/mir-opt/const_prop/bad_op_mod_by_zero.rs index aa09c1639b35..b7623dc23da1 100644 --- a/tests/mir-opt/const_prop/bad_op_mod_by_zero.rs +++ b/tests/mir-opt/const_prop/bad_op_mod_by_zero.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN // EMIT_MIR_FOR_EACH_PANIC_STRATEGY // EMIT_MIR bad_op_mod_by_zero.main.GVN.diff diff --git a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.rs b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.rs index 25d513e21327..0f8d278535dc 100644 --- a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.rs +++ b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN // EMIT_MIR_FOR_EACH_PANIC_STRATEGY // EMIT_MIR_FOR_EACH_BIT_WIDTH diff --git a/tests/mir-opt/const_prop/boolean_identities.rs b/tests/mir-opt/const_prop/boolean_identities.rs index cbc106aa41ca..f23749312f23 100644 --- a/tests/mir-opt/const_prop/boolean_identities.rs +++ b/tests/mir-opt/const_prop/boolean_identities.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN // EMIT_MIR boolean_identities.test.GVN.diff pub fn test(x: bool, y: bool) -> bool { diff --git a/tests/mir-opt/const_prop/boxes.rs b/tests/mir-opt/const_prop/boxes.rs index 859491cf3614..7813352261e5 100644 --- a/tests/mir-opt/const_prop/boxes.rs +++ b/tests/mir-opt/const_prop/boxes.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN //@ compile-flags: -O // EMIT_MIR_FOR_EACH_PANIC_STRATEGY diff --git a/tests/mir-opt/const_prop/cast.rs b/tests/mir-opt/const_prop/cast.rs index ad95515b41b5..dce2e38fa917 100644 --- a/tests/mir-opt/const_prop/cast.rs +++ b/tests/mir-opt/const_prop/cast.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN // EMIT_MIR cast.main.GVN.diff fn main() { diff --git a/tests/mir-opt/const_prop/checked_add.rs b/tests/mir-opt/const_prop/checked_add.rs index 6f43e6abdc1e..0560b0495731 100644 --- a/tests/mir-opt/const_prop/checked_add.rs +++ b/tests/mir-opt/const_prop/checked_add.rs @@ -1,5 +1,5 @@ // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: GVN +//@ test-mir-pass: GVN //@ compile-flags: -C overflow-checks=on // EMIT_MIR checked_add.main.GVN.diff diff --git a/tests/mir-opt/const_prop/control_flow_simplification.rs b/tests/mir-opt/const_prop/control_flow_simplification.rs index eb336827dc29..39b5f2898306 100644 --- a/tests/mir-opt/const_prop/control_flow_simplification.rs +++ b/tests/mir-opt/const_prop/control_flow_simplification.rs @@ -1,6 +1,6 @@ // skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: GVN +//@ test-mir-pass: GVN //@ compile-flags: -Zmir-opt-level=1 trait NeedsDrop: Sized { diff --git a/tests/mir-opt/const_prop/discriminant.rs b/tests/mir-opt/const_prop/discriminant.rs index 51542afa4bc3..aa169eb4e369 100644 --- a/tests/mir-opt/const_prop/discriminant.rs +++ b/tests/mir-opt/const_prop/discriminant.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN // FIXME(wesleywiser): Ideally, we could const-prop away all of this and just be left with // `let x = 42` but that doesn't work because const-prop doesn't support `Operand::Indirect` diff --git a/tests/mir-opt/const_prop/indirect.rs b/tests/mir-opt/const_prop/indirect.rs index 5c469c5d844a..ca53e2b2f2b0 100644 --- a/tests/mir-opt/const_prop/indirect.rs +++ b/tests/mir-opt/const_prop/indirect.rs @@ -1,5 +1,5 @@ // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: GVN +//@ test-mir-pass: GVN //@ compile-flags: -C overflow-checks=on // EMIT_MIR indirect.main.GVN.diff diff --git a/tests/mir-opt/const_prop/indirect_mutation.rs b/tests/mir-opt/const_prop/indirect_mutation.rs index b2a9d5db367a..32ff8f142b1e 100644 --- a/tests/mir-opt/const_prop/indirect_mutation.rs +++ b/tests/mir-opt/const_prop/indirect_mutation.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN // Check that we do not propagate past an indirect mutation. #![feature(raw_ref_op)] diff --git a/tests/mir-opt/const_prop/inherit_overflow.rs b/tests/mir-opt/const_prop/inherit_overflow.rs index e71a05ce760b..c37f3fdb8647 100644 --- a/tests/mir-opt/const_prop/inherit_overflow.rs +++ b/tests/mir-opt/const_prop/inherit_overflow.rs @@ -1,5 +1,5 @@ // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: GVN +//@ test-mir-pass: GVN //@ compile-flags: -Zmir-enable-passes=+Inline // After inlining, this will contain a `CheckedBinaryOp`. diff --git a/tests/mir-opt/const_prop/invalid_constant.rs b/tests/mir-opt/const_prop/invalid_constant.rs index 1df82f2ee79d..afd8746af5fb 100644 --- a/tests/mir-opt/const_prop/invalid_constant.rs +++ b/tests/mir-opt/const_prop/invalid_constant.rs @@ -1,5 +1,5 @@ // skip-filecheck -//@ unit-test: GVN +//@ test-mir-pass: GVN //@ compile-flags: -Zmir-enable-passes=+RemoveZsts // Verify that we can pretty print invalid constants. diff --git a/tests/mir-opt/const_prop/issue_66971.rs b/tests/mir-opt/const_prop/issue_66971.rs index 30a9d62d499e..03f34969bf3e 100644 --- a/tests/mir-opt/const_prop/issue_66971.rs +++ b/tests/mir-opt/const_prop/issue_66971.rs @@ -1,5 +1,5 @@ // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: GVN +//@ test-mir-pass: GVN // Due to a bug in propagating scalar pairs the assertion below used to fail. In the expected // outputs below, after GVN this is how _2 would look like with the bug: diff --git a/tests/mir-opt/const_prop/issue_67019.rs b/tests/mir-opt/const_prop/issue_67019.rs index bf788b924ce1..e13c74429164 100644 --- a/tests/mir-opt/const_prop/issue_67019.rs +++ b/tests/mir-opt/const_prop/issue_67019.rs @@ -1,5 +1,5 @@ // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: GVN +//@ test-mir-pass: GVN // This used to ICE in const-prop diff --git a/tests/mir-opt/const_prop/large_array_index.rs b/tests/mir-opt/const_prop/large_array_index.rs index 1cefc85676ff..afba73f6a17f 100644 --- a/tests/mir-opt/const_prop/large_array_index.rs +++ b/tests/mir-opt/const_prop/large_array_index.rs @@ -1,5 +1,5 @@ // skip-filecheck -//@ unit-test: GVN +//@ test-mir-pass: GVN // EMIT_MIR_FOR_EACH_PANIC_STRATEGY // EMIT_MIR_FOR_EACH_BIT_WIDTH diff --git a/tests/mir-opt/const_prop/mult_by_zero.rs b/tests/mir-opt/const_prop/mult_by_zero.rs index d79f3e851615..7943c74c9ee7 100644 --- a/tests/mir-opt/const_prop/mult_by_zero.rs +++ b/tests/mir-opt/const_prop/mult_by_zero.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN // EMIT_MIR mult_by_zero.test.GVN.diff fn test(x: i32) -> i32 { diff --git a/tests/mir-opt/const_prop/mutable_variable.rs b/tests/mir-opt/const_prop/mutable_variable.rs index 4445bd224802..9698fba6a11a 100644 --- a/tests/mir-opt/const_prop/mutable_variable.rs +++ b/tests/mir-opt/const_prop/mutable_variable.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN // EMIT_MIR mutable_variable.main.GVN.diff fn main() { diff --git a/tests/mir-opt/const_prop/mutable_variable_aggregate.rs b/tests/mir-opt/const_prop/mutable_variable_aggregate.rs index c2b2731b2a6c..7de647ed9c37 100644 --- a/tests/mir-opt/const_prop/mutable_variable_aggregate.rs +++ b/tests/mir-opt/const_prop/mutable_variable_aggregate.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN // EMIT_MIR mutable_variable_aggregate.main.GVN.diff fn main() { diff --git a/tests/mir-opt/const_prop/mutable_variable_aggregate_mut_ref.rs b/tests/mir-opt/const_prop/mutable_variable_aggregate_mut_ref.rs index c9f09f878fe5..5656c0e7a686 100644 --- a/tests/mir-opt/const_prop/mutable_variable_aggregate_mut_ref.rs +++ b/tests/mir-opt/const_prop/mutable_variable_aggregate_mut_ref.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN // EMIT_MIR mutable_variable_aggregate_mut_ref.main.GVN.diff fn main() { diff --git a/tests/mir-opt/const_prop/mutable_variable_aggregate_partial_read.rs b/tests/mir-opt/const_prop/mutable_variable_aggregate_partial_read.rs index 5b7804b11647..6f99e6be246f 100644 --- a/tests/mir-opt/const_prop/mutable_variable_aggregate_partial_read.rs +++ b/tests/mir-opt/const_prop/mutable_variable_aggregate_partial_read.rs @@ -1,5 +1,5 @@ // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: GVN +//@ test-mir-pass: GVN // EMIT_MIR mutable_variable_aggregate_partial_read.main.GVN.diff fn main() { diff --git a/tests/mir-opt/const_prop/mutable_variable_no_prop.rs b/tests/mir-opt/const_prop/mutable_variable_no_prop.rs index 9ea2e78d8b20..8289832f81ea 100644 --- a/tests/mir-opt/const_prop/mutable_variable_no_prop.rs +++ b/tests/mir-opt/const_prop/mutable_variable_no_prop.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN // Verify that we do not propagate the contents of this mutable static. static mut STATIC: u32 = 0x42424242; diff --git a/tests/mir-opt/const_prop/mutable_variable_unprop_assign.rs b/tests/mir-opt/const_prop/mutable_variable_unprop_assign.rs index 39ac1fa3c946..cc92949128f7 100644 --- a/tests/mir-opt/const_prop/mutable_variable_unprop_assign.rs +++ b/tests/mir-opt/const_prop/mutable_variable_unprop_assign.rs @@ -1,5 +1,5 @@ // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: GVN +//@ test-mir-pass: GVN // EMIT_MIR mutable_variable_unprop_assign.main.GVN.diff fn main() { diff --git a/tests/mir-opt/const_prop/offset_of.rs b/tests/mir-opt/const_prop/offset_of.rs index 7d258f2e3628..105cbfb53dd0 100644 --- a/tests/mir-opt/const_prop/offset_of.rs +++ b/tests/mir-opt/const_prop/offset_of.rs @@ -1,5 +1,5 @@ // skip-filecheck -//@ unit-test: GVN +//@ test-mir-pass: GVN // EMIT_MIR_FOR_EACH_PANIC_STRATEGY #![feature(offset_of_enum, offset_of_nested)] diff --git a/tests/mir-opt/const_prop/overwrite_with_const_with_params.rs b/tests/mir-opt/const_prop/overwrite_with_const_with_params.rs index 535870fdf88e..a43558223fe1 100644 --- a/tests/mir-opt/const_prop/overwrite_with_const_with_params.rs +++ b/tests/mir-opt/const_prop/overwrite_with_const_with_params.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN //@ compile-flags: -O // Regression test for https://github.com/rust-lang/rust/issues/118328 diff --git a/tests/mir-opt/const_prop/pointer_expose_provenance.rs b/tests/mir-opt/const_prop/pointer_expose_provenance.rs index 840a4d65f3d7..f148a5b6542e 100644 --- a/tests/mir-opt/const_prop/pointer_expose_provenance.rs +++ b/tests/mir-opt/const_prop/pointer_expose_provenance.rs @@ -1,5 +1,5 @@ // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: GVN +//@ test-mir-pass: GVN #[inline(never)] fn read(_: usize) { } diff --git a/tests/mir-opt/const_prop/read_immutable_static.rs b/tests/mir-opt/const_prop/read_immutable_static.rs index ec2dbf6485a8..05fec2f3303b 100644 --- a/tests/mir-opt/const_prop/read_immutable_static.rs +++ b/tests/mir-opt/const_prop/read_immutable_static.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN static FOO: u8 = 2; diff --git a/tests/mir-opt/const_prop/ref_deref.rs b/tests/mir-opt/const_prop/ref_deref.rs index 20c1fba52099..aef36323cc03 100644 --- a/tests/mir-opt/const_prop/ref_deref.rs +++ b/tests/mir-opt/const_prop/ref_deref.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN // EMIT_MIR ref_deref.main.GVN.diff fn main() { diff --git a/tests/mir-opt/const_prop/ref_deref_project.rs b/tests/mir-opt/const_prop/ref_deref_project.rs index 59e7f1a50b80..5a48a887f93d 100644 --- a/tests/mir-opt/const_prop/ref_deref_project.rs +++ b/tests/mir-opt/const_prop/ref_deref_project.rs @@ -1,5 +1,5 @@ // This does not currently propagate (#67862) -//@ unit-test: GVN +//@ test-mir-pass: GVN // EMIT_MIR ref_deref_project.main.GVN.diff fn main() { diff --git a/tests/mir-opt/const_prop/reify_fn_ptr.rs b/tests/mir-opt/const_prop/reify_fn_ptr.rs index 55dca24f3d2e..ffce4e97f5de 100644 --- a/tests/mir-opt/const_prop/reify_fn_ptr.rs +++ b/tests/mir-opt/const_prop/reify_fn_ptr.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN // EMIT_MIR reify_fn_ptr.main.GVN.diff fn main() { diff --git a/tests/mir-opt/const_prop/repeat.rs b/tests/mir-opt/const_prop/repeat.rs index d881462b877e..d77819138951 100644 --- a/tests/mir-opt/const_prop/repeat.rs +++ b/tests/mir-opt/const_prop/repeat.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN // EMIT_MIR_FOR_EACH_PANIC_STRATEGY // EMIT_MIR_FOR_EACH_BIT_WIDTH diff --git a/tests/mir-opt/const_prop/return_place.rs b/tests/mir-opt/const_prop/return_place.rs index fea28c93dc3d..e7eea11ae492 100644 --- a/tests/mir-opt/const_prop/return_place.rs +++ b/tests/mir-opt/const_prop/return_place.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN // EMIT_MIR_FOR_EACH_PANIC_STRATEGY //@ compile-flags: -C overflow-checks=on diff --git a/tests/mir-opt/const_prop/scalar_literal_propagation.rs b/tests/mir-opt/const_prop/scalar_literal_propagation.rs index e07774683506..9d02f24e76bd 100644 --- a/tests/mir-opt/const_prop/scalar_literal_propagation.rs +++ b/tests/mir-opt/const_prop/scalar_literal_propagation.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN // EMIT_MIR_FOR_EACH_PANIC_STRATEGY // EMIT_MIR scalar_literal_propagation.main.GVN.diff diff --git a/tests/mir-opt/const_prop/slice_len.rs b/tests/mir-opt/const_prop/slice_len.rs index 4a48f92ec2b4..63cdbf01b3e8 100644 --- a/tests/mir-opt/const_prop/slice_len.rs +++ b/tests/mir-opt/const_prop/slice_len.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN //@ compile-flags: -Zmir-enable-passes=+InstSimplify // EMIT_MIR_FOR_EACH_PANIC_STRATEGY // EMIT_MIR_FOR_EACH_BIT_WIDTH diff --git a/tests/mir-opt/const_prop/switch_int.rs b/tests/mir-opt/const_prop/switch_int.rs index a176bf144389..114380e316de 100644 --- a/tests/mir-opt/const_prop/switch_int.rs +++ b/tests/mir-opt/const_prop/switch_int.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN //@ compile-flags: -Zmir-enable-passes=+SimplifyConstCondition-after-const-prop // EMIT_MIR_FOR_EACH_PANIC_STRATEGY diff --git a/tests/mir-opt/const_prop/transmute.rs b/tests/mir-opt/const_prop/transmute.rs index 5f2d76711599..9cbf8928753b 100644 --- a/tests/mir-opt/const_prop/transmute.rs +++ b/tests/mir-opt/const_prop/transmute.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN //@ compile-flags: -O --crate-type=lib //@ ignore-endian-big // EMIT_MIR_FOR_EACH_BIT_WIDTH diff --git a/tests/mir-opt/const_prop/tuple_literal_propagation.rs b/tests/mir-opt/const_prop/tuple_literal_propagation.rs index 5992bb151d3e..582411c7b59e 100644 --- a/tests/mir-opt/const_prop/tuple_literal_propagation.rs +++ b/tests/mir-opt/const_prop/tuple_literal_propagation.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN // EMIT_MIR_FOR_EACH_PANIC_STRATEGY // EMIT_MIR tuple_literal_propagation.main.GVN.diff diff --git a/tests/mir-opt/const_prop/while_let_loops.rs b/tests/mir-opt/const_prop/while_let_loops.rs index 6a421da0807d..cb2c8362e148 100644 --- a/tests/mir-opt/const_prop/while_let_loops.rs +++ b/tests/mir-opt/const_prop/while_let_loops.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN // EMIT_MIR while_let_loops.change_loop_body.GVN.diff pub fn change_loop_body() { diff --git a/tests/mir-opt/copy-prop/borrowed_local.rs b/tests/mir-opt/copy-prop/borrowed_local.rs index 24b8e45532cf..74ac6281a895 100644 --- a/tests/mir-opt/copy-prop/borrowed_local.rs +++ b/tests/mir-opt/copy-prop/borrowed_local.rs @@ -1,6 +1,6 @@ // skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: CopyProp +//@ test-mir-pass: CopyProp #![feature(custom_mir, core_intrinsics)] #![allow(unused_assignments)] diff --git a/tests/mir-opt/copy-prop/branch.rs b/tests/mir-opt/copy-prop/branch.rs index 0944bb3d59aa..fc9b8dc41b16 100644 --- a/tests/mir-opt/copy-prop/branch.rs +++ b/tests/mir-opt/copy-prop/branch.rs @@ -1,7 +1,7 @@ // skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY //! Tests that we bail out when there are multiple assignments to the same local. -//@ unit-test: CopyProp +//@ test-mir-pass: CopyProp fn val() -> i32 { 1 } diff --git a/tests/mir-opt/copy-prop/calls.rs b/tests/mir-opt/copy-prop/calls.rs index 7d123e64950c..8937c0d2ecb2 100644 --- a/tests/mir-opt/copy-prop/calls.rs +++ b/tests/mir-opt/copy-prop/calls.rs @@ -1,6 +1,6 @@ // skip-filecheck // Check that CopyProp does propagate return values of call terminators. -//@ unit-test: CopyProp +//@ test-mir-pass: CopyProp //@ needs-unwind #![feature(custom_mir, core_intrinsics)] diff --git a/tests/mir-opt/copy-prop/copy_propagation_arg.rs b/tests/mir-opt/copy-prop/copy_propagation_arg.rs index 3516d8f7f62f..e062e1e97288 100644 --- a/tests/mir-opt/copy-prop/copy_propagation_arg.rs +++ b/tests/mir-opt/copy-prop/copy_propagation_arg.rs @@ -2,7 +2,7 @@ // EMIT_MIR_FOR_EACH_PANIC_STRATEGY // Check that CopyProp does not propagate an assignment to a function argument // (doing so can break usages of the original argument value) -//@ unit-test: CopyProp +//@ test-mir-pass: CopyProp fn dummy(x: u8) -> u8 { x } diff --git a/tests/mir-opt/copy-prop/custom_move_arg.rs b/tests/mir-opt/copy-prop/custom_move_arg.rs index 3577ed2a4a18..a82d4618e68c 100644 --- a/tests/mir-opt/copy-prop/custom_move_arg.rs +++ b/tests/mir-opt/copy-prop/custom_move_arg.rs @@ -1,6 +1,6 @@ // skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: CopyProp +//@ test-mir-pass: CopyProp #![feature(custom_mir, core_intrinsics)] #![allow(unused_assignments)] diff --git a/tests/mir-opt/copy-prop/cycle.rs b/tests/mir-opt/copy-prop/cycle.rs index ed97e86f83ac..1c0c9eae7fea 100644 --- a/tests/mir-opt/copy-prop/cycle.rs +++ b/tests/mir-opt/copy-prop/cycle.rs @@ -1,7 +1,7 @@ // skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY //! Tests that cyclic assignments don't hang CopyProp, and result in reasonable code. -//@ unit-test: CopyProp +//@ test-mir-pass: CopyProp fn val() -> i32 { 1 } diff --git a/tests/mir-opt/copy-prop/dead_stores_79191.rs b/tests/mir-opt/copy-prop/dead_stores_79191.rs index f6e0eac6c2c9..24420e19fa80 100644 --- a/tests/mir-opt/copy-prop/dead_stores_79191.rs +++ b/tests/mir-opt/copy-prop/dead_stores_79191.rs @@ -1,6 +1,6 @@ // skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: CopyProp +//@ test-mir-pass: CopyProp fn id(x: T) -> T { x diff --git a/tests/mir-opt/copy-prop/dead_stores_better.rs b/tests/mir-opt/copy-prop/dead_stores_better.rs index fdf42876909a..4b1874294016 100644 --- a/tests/mir-opt/copy-prop/dead_stores_better.rs +++ b/tests/mir-opt/copy-prop/dead_stores_better.rs @@ -3,7 +3,7 @@ // This is a copy of the `dead_stores_79191` test, except that we turn on DSE. This demonstrates // that that pass enables this one to do more optimizations. -//@ unit-test: CopyProp +//@ test-mir-pass: CopyProp //@ compile-flags: -Zmir-enable-passes=+DeadStoreElimination fn id(x: T) -> T { diff --git a/tests/mir-opt/copy-prop/issue_107511.rs b/tests/mir-opt/copy-prop/issue_107511.rs index d9bd08b1bcf1..5e8fc8df42e2 100644 --- a/tests/mir-opt/copy-prop/issue_107511.rs +++ b/tests/mir-opt/copy-prop/issue_107511.rs @@ -1,6 +1,6 @@ // skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: CopyProp +//@ test-mir-pass: CopyProp // EMIT_MIR issue_107511.main.CopyProp.diff fn main() { diff --git a/tests/mir-opt/copy-prop/move_arg.rs b/tests/mir-opt/copy-prop/move_arg.rs index 85ced0f6c0d5..498340534324 100644 --- a/tests/mir-opt/copy-prop/move_arg.rs +++ b/tests/mir-opt/copy-prop/move_arg.rs @@ -1,7 +1,7 @@ // skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY // Test that we do not move multiple times from the same local. -//@ unit-test: CopyProp +//@ test-mir-pass: CopyProp // EMIT_MIR move_arg.f.CopyProp.diff pub fn f(a: T) { diff --git a/tests/mir-opt/copy-prop/move_projection.rs b/tests/mir-opt/copy-prop/move_projection.rs index d68ffad78bcb..231e4082e33b 100644 --- a/tests/mir-opt/copy-prop/move_projection.rs +++ b/tests/mir-opt/copy-prop/move_projection.rs @@ -1,6 +1,6 @@ // skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: CopyProp +//@ test-mir-pass: CopyProp #![feature(custom_mir, core_intrinsics)] #![allow(unused_assignments)] diff --git a/tests/mir-opt/copy-prop/mutate_through_pointer.rs b/tests/mir-opt/copy-prop/mutate_through_pointer.rs index 610f5401084a..14ca513c6927 100644 --- a/tests/mir-opt/copy-prop/mutate_through_pointer.rs +++ b/tests/mir-opt/copy-prop/mutate_through_pointer.rs @@ -1,5 +1,5 @@ // skip-filecheck -//@ unit-test: CopyProp +//@ test-mir-pass: CopyProp // // This attempts to mutate `a` via a pointer derived from `addr_of!(a)`. That is UB // according to Miri. However, the decision to make this UB - and to allow diff --git a/tests/mir-opt/copy-prop/non_dominate.rs b/tests/mir-opt/copy-prop/non_dominate.rs index d8b42b7f96e5..34e7eabc81a2 100644 --- a/tests/mir-opt/copy-prop/non_dominate.rs +++ b/tests/mir-opt/copy-prop/non_dominate.rs @@ -1,5 +1,5 @@ // skip-filecheck -//@ unit-test: CopyProp +//@ test-mir-pass: CopyProp #![feature(custom_mir, core_intrinsics)] #![allow(unused_assignments)] diff --git a/tests/mir-opt/copy-prop/partial_init.rs b/tests/mir-opt/copy-prop/partial_init.rs index 46390556418d..88e94988181d 100644 --- a/tests/mir-opt/copy-prop/partial_init.rs +++ b/tests/mir-opt/copy-prop/partial_init.rs @@ -1,5 +1,5 @@ // skip-filecheck -//@ unit-test: CopyProp +//@ test-mir-pass: CopyProp // Verify that we do not ICE on partial initializations. #![feature(custom_mir, core_intrinsics)] diff --git a/tests/mir-opt/copy-prop/reborrow.rs b/tests/mir-opt/copy-prop/reborrow.rs index 7d02fb328eef..2f1720556cde 100644 --- a/tests/mir-opt/copy-prop/reborrow.rs +++ b/tests/mir-opt/copy-prop/reborrow.rs @@ -1,7 +1,7 @@ // skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY // Check that CopyProp considers reborrows as not mutating the pointer. -//@ unit-test: CopyProp +//@ test-mir-pass: CopyProp #![feature(raw_ref_op)] diff --git a/tests/mir-opt/coverage/branch_match_arms.main.InstrumentCoverage.diff b/tests/mir-opt/coverage/branch_match_arms.main.InstrumentCoverage.diff new file mode 100644 index 000000000000..e60f71f47b1e --- /dev/null +++ b/tests/mir-opt/coverage/branch_match_arms.main.InstrumentCoverage.diff @@ -0,0 +1,138 @@ +- // MIR for `main` before InstrumentCoverage ++ // MIR for `main` after InstrumentCoverage + + fn main() -> () { + let mut _0: (); + let mut _1: Enum; + let mut _2: isize; + let _3: u32; + let mut _4: u32; + let _5: u32; + let mut _6: u32; + let _7: u32; + let mut _8: u32; + let _9: u32; + let mut _10: u32; + scope 1 { + debug d => _3; + } + scope 2 { + debug c => _5; + } + scope 3 { + debug b => _7; + } + scope 4 { + debug a => _9; + } + ++ coverage ExpressionId(0) => Expression { lhs: Counter(1), op: Add, rhs: Counter(2) }; ++ coverage ExpressionId(1) => Expression { lhs: Expression(0), op: Add, rhs: Counter(3) }; ++ coverage ExpressionId(2) => Expression { lhs: Counter(0), op: Subtract, rhs: Expression(1) }; ++ coverage ExpressionId(3) => Expression { lhs: Counter(3), op: Add, rhs: Counter(2) }; ++ coverage ExpressionId(4) => Expression { lhs: Expression(3), op: Add, rhs: Counter(1) }; ++ coverage ExpressionId(5) => Expression { lhs: Expression(4), op: Add, rhs: Expression(2) }; ++ coverage Code(Counter(0)) => $DIR/branch_match_arms.rs:14:1 - 15:21; ++ coverage Code(Counter(3)) => $DIR/branch_match_arms.rs:16:17 - 16:33; ++ coverage Code(Counter(2)) => $DIR/branch_match_arms.rs:17:17 - 17:33; ++ coverage Code(Counter(1)) => $DIR/branch_match_arms.rs:18:17 - 18:33; ++ coverage Code(Expression(2)) => $DIR/branch_match_arms.rs:19:17 - 19:33; ++ coverage Code(Expression(5)) => $DIR/branch_match_arms.rs:21:1 - 21:2; ++ + bb0: { ++ Coverage::CounterIncrement(0); + StorageLive(_1); + _1 = Enum::A(const 0_u32); + PlaceMention(_1); + _2 = discriminant(_1); + switchInt(move _2) -> [0: bb5, 1: bb4, 2: bb3, 3: bb2, otherwise: bb1]; + } + + bb1: { + FakeRead(ForMatchedPlace(None), _1); + unreachable; + } + + bb2: { ++ Coverage::CounterIncrement(3); + falseEdge -> [real: bb6, imaginary: bb3]; + } + + bb3: { ++ Coverage::CounterIncrement(2); + falseEdge -> [real: bb8, imaginary: bb4]; + } + + bb4: { ++ Coverage::CounterIncrement(1); + falseEdge -> [real: bb10, imaginary: bb5]; + } + + bb5: { ++ Coverage::ExpressionUsed(2); + StorageLive(_9); + _9 = ((_1 as A).0: u32); + StorageLive(_10); + _10 = _9; + _0 = consume(move _10) -> [return: bb12, unwind: bb14]; + } + + bb6: { + StorageLive(_3); + _3 = ((_1 as D).0: u32); + StorageLive(_4); + _4 = _3; + _0 = consume(move _4) -> [return: bb7, unwind: bb14]; + } + + bb7: { + StorageDead(_4); + StorageDead(_3); + goto -> bb13; + } + + bb8: { + StorageLive(_5); + _5 = ((_1 as C).0: u32); + StorageLive(_6); + _6 = _5; + _0 = consume(move _6) -> [return: bb9, unwind: bb14]; + } + + bb9: { + StorageDead(_6); + StorageDead(_5); + goto -> bb13; + } + + bb10: { + StorageLive(_7); + _7 = ((_1 as B).0: u32); + StorageLive(_8); + _8 = _7; + _0 = consume(move _8) -> [return: bb11, unwind: bb14]; + } + + bb11: { + StorageDead(_8); + StorageDead(_7); + goto -> bb13; + } + + bb12: { + StorageDead(_10); + StorageDead(_9); + goto -> bb13; + } + + bb13: { ++ Coverage::ExpressionUsed(5); + StorageDead(_1); + return; + } + + bb14 (cleanup): { + resume; + } + } + diff --git a/tests/mir-opt/coverage/branch_match_arms.rs b/tests/mir-opt/coverage/branch_match_arms.rs new file mode 100644 index 000000000000..18764b38d6e3 --- /dev/null +++ b/tests/mir-opt/coverage/branch_match_arms.rs @@ -0,0 +1,27 @@ +#![feature(coverage_attribute)] +//@ test-mir-pass: InstrumentCoverage +//@ compile-flags: -Cinstrument-coverage -Zno-profiler-runtime -Zcoverage-options=branch +// skip-filecheck + +enum Enum { + A(u32), + B(u32), + C(u32), + D(u32), +} + +// EMIT_MIR branch_match_arms.main.InstrumentCoverage.diff +fn main() { + match Enum::A(0) { + Enum::D(d) => consume(d), + Enum::C(c) => consume(c), + Enum::B(b) => consume(b), + Enum::A(a) => consume(a), + } +} + +#[inline(never)] +#[coverage(off)] +fn consume(x: u32) { + core::hint::black_box(x); +} diff --git a/tests/mir-opt/instrument_coverage.bar.InstrumentCoverage.diff b/tests/mir-opt/coverage/instrument_coverage.bar.InstrumentCoverage.diff similarity index 100% rename from tests/mir-opt/instrument_coverage.bar.InstrumentCoverage.diff rename to tests/mir-opt/coverage/instrument_coverage.bar.InstrumentCoverage.diff diff --git a/tests/mir-opt/instrument_coverage.main.InstrumentCoverage.diff b/tests/mir-opt/coverage/instrument_coverage.main.InstrumentCoverage.diff similarity index 100% rename from tests/mir-opt/instrument_coverage.main.InstrumentCoverage.diff rename to tests/mir-opt/coverage/instrument_coverage.main.InstrumentCoverage.diff diff --git a/tests/mir-opt/instrument_coverage.rs b/tests/mir-opt/coverage/instrument_coverage.rs similarity index 95% rename from tests/mir-opt/instrument_coverage.rs rename to tests/mir-opt/coverage/instrument_coverage.rs index ae63990f253d..beb88b607f97 100644 --- a/tests/mir-opt/instrument_coverage.rs +++ b/tests/mir-opt/coverage/instrument_coverage.rs @@ -2,7 +2,7 @@ // The Coverage::CounterIncrement statements are later converted into LLVM // instrprof.increment intrinsics, during codegen. -//@ unit-test: InstrumentCoverage +//@ test-mir-pass: InstrumentCoverage //@ compile-flags: -Cinstrument-coverage -Zno-profiler-runtime // EMIT_MIR instrument_coverage.main.InstrumentCoverage.diff diff --git a/tests/mir-opt/instrument_coverage_cleanup.main.CleanupPostBorrowck.diff b/tests/mir-opt/coverage/instrument_coverage_cleanup.main.CleanupPostBorrowck.diff similarity index 100% rename from tests/mir-opt/instrument_coverage_cleanup.main.CleanupPostBorrowck.diff rename to tests/mir-opt/coverage/instrument_coverage_cleanup.main.CleanupPostBorrowck.diff diff --git a/tests/mir-opt/instrument_coverage_cleanup.main.InstrumentCoverage.diff b/tests/mir-opt/coverage/instrument_coverage_cleanup.main.InstrumentCoverage.diff similarity index 100% rename from tests/mir-opt/instrument_coverage_cleanup.main.InstrumentCoverage.diff rename to tests/mir-opt/coverage/instrument_coverage_cleanup.main.InstrumentCoverage.diff diff --git a/tests/mir-opt/instrument_coverage_cleanup.rs b/tests/mir-opt/coverage/instrument_coverage_cleanup.rs similarity index 95% rename from tests/mir-opt/instrument_coverage_cleanup.rs rename to tests/mir-opt/coverage/instrument_coverage_cleanup.rs index 7db76339e551..acc544a28af6 100644 --- a/tests/mir-opt/instrument_coverage_cleanup.rs +++ b/tests/mir-opt/coverage/instrument_coverage_cleanup.rs @@ -5,7 +5,7 @@ // Removed statement kinds: BlockMarker, SpanMarker // Retained statement kinds: CounterIncrement, ExpressionUsed -//@ unit-test: InstrumentCoverage +//@ test-mir-pass: InstrumentCoverage //@ compile-flags: -Cinstrument-coverage -Zcoverage-options=branch -Zno-profiler-runtime // EMIT_MIR instrument_coverage_cleanup.main.InstrumentCoverage.diff diff --git a/tests/mir-opt/dataflow-const-prop/array_index.rs b/tests/mir-opt/dataflow-const-prop/array_index.rs index df8baf77add4..daf9c7729c60 100644 --- a/tests/mir-opt/dataflow-const-prop/array_index.rs +++ b/tests/mir-opt/dataflow-const-prop/array_index.rs @@ -1,5 +1,5 @@ // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: DataflowConstProp +//@ test-mir-pass: DataflowConstProp // EMIT_MIR_FOR_EACH_BIT_WIDTH // EMIT_MIR array_index.main.DataflowConstProp.diff diff --git a/tests/mir-opt/dataflow-const-prop/boolean_identities.rs b/tests/mir-opt/dataflow-const-prop/boolean_identities.rs index e2b7dbc096b2..11faecec6b12 100644 --- a/tests/mir-opt/dataflow-const-prop/boolean_identities.rs +++ b/tests/mir-opt/dataflow-const-prop/boolean_identities.rs @@ -1,4 +1,4 @@ -//@ unit-test: DataflowConstProp +//@ test-mir-pass: DataflowConstProp // EMIT_MIR boolean_identities.test.DataflowConstProp.diff diff --git a/tests/mir-opt/dataflow-const-prop/cast.rs b/tests/mir-opt/dataflow-const-prop/cast.rs index bd6141eedd16..a70cc8ee6a22 100644 --- a/tests/mir-opt/dataflow-const-prop/cast.rs +++ b/tests/mir-opt/dataflow-const-prop/cast.rs @@ -1,4 +1,4 @@ -//@ unit-test: DataflowConstProp +//@ test-mir-pass: DataflowConstProp // EMIT_MIR cast.main.DataflowConstProp.diff diff --git a/tests/mir-opt/dataflow-const-prop/checked.rs b/tests/mir-opt/dataflow-const-prop/checked.rs index d3d0938168b6..a73693464f95 100644 --- a/tests/mir-opt/dataflow-const-prop/checked.rs +++ b/tests/mir-opt/dataflow-const-prop/checked.rs @@ -1,4 +1,4 @@ -//@ unit-test: DataflowConstProp +//@ test-mir-pass: DataflowConstProp //@ compile-flags: -Coverflow-checks=on // EMIT_MIR_FOR_EACH_PANIC_STRATEGY diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.rs b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.rs index 617501217cf4..3a0cbac328cb 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.rs +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.rs @@ -1,4 +1,4 @@ -//@ unit-test: DataflowConstProp +//@ test-mir-pass: DataflowConstProp //@ compile-flags: -Zmir-enable-passes=+GVN,+Inline // EMIT_MIR_FOR_EACH_BIT_WIDTH // EMIT_MIR_FOR_EACH_PANIC_STRATEGY diff --git a/tests/mir-opt/dataflow-const-prop/enum.rs b/tests/mir-opt/dataflow-const-prop/enum.rs index 82752750eb17..5c52f92cd8f1 100644 --- a/tests/mir-opt/dataflow-const-prop/enum.rs +++ b/tests/mir-opt/dataflow-const-prop/enum.rs @@ -1,4 +1,4 @@ -//@ unit-test: DataflowConstProp +//@ test-mir-pass: DataflowConstProp // EMIT_MIR_FOR_EACH_BIT_WIDTH #![feature(custom_mir, core_intrinsics, rustc_attrs)] diff --git a/tests/mir-opt/dataflow-const-prop/if.rs b/tests/mir-opt/dataflow-const-prop/if.rs index 7df3bb9c42eb..8cd8b2c2bec1 100644 --- a/tests/mir-opt/dataflow-const-prop/if.rs +++ b/tests/mir-opt/dataflow-const-prop/if.rs @@ -1,4 +1,4 @@ -//@ unit-test: DataflowConstProp +//@ test-mir-pass: DataflowConstProp // EMIT_MIR if.main.DataflowConstProp.diff // CHECK-LABEL: fn main( diff --git a/tests/mir-opt/dataflow-const-prop/inherit_overflow.rs b/tests/mir-opt/dataflow-const-prop/inherit_overflow.rs index d0063a8e7e49..7ac59befc8ae 100644 --- a/tests/mir-opt/dataflow-const-prop/inherit_overflow.rs +++ b/tests/mir-opt/dataflow-const-prop/inherit_overflow.rs @@ -1,5 +1,5 @@ // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: DataflowConstProp +//@ test-mir-pass: DataflowConstProp //@ compile-flags: -Zmir-enable-passes=+Inline // EMIT_MIR inherit_overflow.main.DataflowConstProp.diff diff --git a/tests/mir-opt/dataflow-const-prop/issue_81605.rs b/tests/mir-opt/dataflow-const-prop/issue_81605.rs index 9231bb22c4ce..e7960e3fba11 100644 --- a/tests/mir-opt/dataflow-const-prop/issue_81605.rs +++ b/tests/mir-opt/dataflow-const-prop/issue_81605.rs @@ -1,4 +1,4 @@ -//@ unit-test: DataflowConstProp +//@ test-mir-pass: DataflowConstProp // EMIT_MIR issue_81605.f.DataflowConstProp.diff diff --git a/tests/mir-opt/dataflow-const-prop/large_array_index.rs b/tests/mir-opt/dataflow-const-prop/large_array_index.rs index 3a4159ab105f..e74fd88d002c 100644 --- a/tests/mir-opt/dataflow-const-prop/large_array_index.rs +++ b/tests/mir-opt/dataflow-const-prop/large_array_index.rs @@ -1,4 +1,4 @@ -//@ unit-test: DataflowConstProp +//@ test-mir-pass: DataflowConstProp // EMIT_MIR_FOR_EACH_PANIC_STRATEGY // EMIT_MIR_FOR_EACH_BIT_WIDTH diff --git a/tests/mir-opt/dataflow-const-prop/mult_by_zero.rs b/tests/mir-opt/dataflow-const-prop/mult_by_zero.rs index b15fba29bdf8..3cd0b715a521 100644 --- a/tests/mir-opt/dataflow-const-prop/mult_by_zero.rs +++ b/tests/mir-opt/dataflow-const-prop/mult_by_zero.rs @@ -1,4 +1,4 @@ -//@ unit-test: DataflowConstProp +//@ test-mir-pass: DataflowConstProp // EMIT_MIR mult_by_zero.test.DataflowConstProp.diff // CHECK-LABEL: fn test( diff --git a/tests/mir-opt/dataflow-const-prop/offset_of.rs b/tests/mir-opt/dataflow-const-prop/offset_of.rs index 867890dcf253..cd4e1f6990db 100644 --- a/tests/mir-opt/dataflow-const-prop/offset_of.rs +++ b/tests/mir-opt/dataflow-const-prop/offset_of.rs @@ -1,4 +1,4 @@ -//@ unit-test: DataflowConstProp +//@ test-mir-pass: DataflowConstProp // EMIT_MIR_FOR_EACH_PANIC_STRATEGY #![feature(offset_of_nested)] diff --git a/tests/mir-opt/dataflow-const-prop/ref_without_sb.rs b/tests/mir-opt/dataflow-const-prop/ref_without_sb.rs index aa669fffd446..399de921a590 100644 --- a/tests/mir-opt/dataflow-const-prop/ref_without_sb.rs +++ b/tests/mir-opt/dataflow-const-prop/ref_without_sb.rs @@ -1,5 +1,5 @@ // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: DataflowConstProp +//@ test-mir-pass: DataflowConstProp #[inline(never)] fn escape(x: &T) {} diff --git a/tests/mir-opt/dataflow-const-prop/repeat.rs b/tests/mir-opt/dataflow-const-prop/repeat.rs index bebedbb94642..e32c0d0877de 100644 --- a/tests/mir-opt/dataflow-const-prop/repeat.rs +++ b/tests/mir-opt/dataflow-const-prop/repeat.rs @@ -1,4 +1,4 @@ -//@ unit-test: DataflowConstProp +//@ test-mir-pass: DataflowConstProp // EMIT_MIR_FOR_EACH_PANIC_STRATEGY // EMIT_MIR_FOR_EACH_BIT_WIDTH diff --git a/tests/mir-opt/dataflow-const-prop/repr_transparent.rs b/tests/mir-opt/dataflow-const-prop/repr_transparent.rs index ace38364ee3f..6152724c98f6 100644 --- a/tests/mir-opt/dataflow-const-prop/repr_transparent.rs +++ b/tests/mir-opt/dataflow-const-prop/repr_transparent.rs @@ -1,4 +1,4 @@ -//@ unit-test: DataflowConstProp +//@ test-mir-pass: DataflowConstProp // The struct has scalar ABI, but is not a scalar type. // Make sure that we handle this correctly. diff --git a/tests/mir-opt/dataflow-const-prop/self_assign.rs b/tests/mir-opt/dataflow-const-prop/self_assign.rs index 4171d2991aea..f5897bfe37b6 100644 --- a/tests/mir-opt/dataflow-const-prop/self_assign.rs +++ b/tests/mir-opt/dataflow-const-prop/self_assign.rs @@ -1,4 +1,4 @@ -//@ unit-test: DataflowConstProp +//@ test-mir-pass: DataflowConstProp // EMIT_MIR self_assign.main.DataflowConstProp.diff diff --git a/tests/mir-opt/dataflow-const-prop/self_assign_add.rs b/tests/mir-opt/dataflow-const-prop/self_assign_add.rs index d958025c7077..11fe3849a1ec 100644 --- a/tests/mir-opt/dataflow-const-prop/self_assign_add.rs +++ b/tests/mir-opt/dataflow-const-prop/self_assign_add.rs @@ -1,4 +1,4 @@ -//@ unit-test: DataflowConstProp +//@ test-mir-pass: DataflowConstProp // EMIT_MIR self_assign_add.main.DataflowConstProp.diff diff --git a/tests/mir-opt/dataflow-const-prop/sibling_ptr.rs b/tests/mir-opt/dataflow-const-prop/sibling_ptr.rs index ad24c5855b8b..be7f311cdc10 100644 --- a/tests/mir-opt/dataflow-const-prop/sibling_ptr.rs +++ b/tests/mir-opt/dataflow-const-prop/sibling_ptr.rs @@ -6,7 +6,7 @@ // used to modify `x.1` - if it did not, then it might incorrectly assume that it // can infer the value of `x.1` at the end of this function. -//@ unit-test: DataflowConstProp +//@ test-mir-pass: DataflowConstProp // EMIT_MIR sibling_ptr.main.DataflowConstProp.diff diff --git a/tests/mir-opt/dataflow-const-prop/slice_len.rs b/tests/mir-opt/dataflow-const-prop/slice_len.rs index 08707779e2c8..5d9733f498c9 100644 --- a/tests/mir-opt/dataflow-const-prop/slice_len.rs +++ b/tests/mir-opt/dataflow-const-prop/slice_len.rs @@ -1,5 +1,5 @@ // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: DataflowConstProp +//@ test-mir-pass: DataflowConstProp //@ compile-flags: -Zmir-enable-passes=+InstSimplify // EMIT_MIR_FOR_EACH_BIT_WIDTH diff --git a/tests/mir-opt/dataflow-const-prop/struct.rs b/tests/mir-opt/dataflow-const-prop/struct.rs index 0180e978a09f..eed782c9036b 100644 --- a/tests/mir-opt/dataflow-const-prop/struct.rs +++ b/tests/mir-opt/dataflow-const-prop/struct.rs @@ -1,4 +1,4 @@ -//@ unit-test: DataflowConstProp +//@ test-mir-pass: DataflowConstProp // EMIT_MIR_FOR_EACH_BIT_WIDTH #[derive(Copy, Clone)] diff --git a/tests/mir-opt/dataflow-const-prop/terminator.rs b/tests/mir-opt/dataflow-const-prop/terminator.rs index d33f3216933a..aac5d11d3d48 100644 --- a/tests/mir-opt/dataflow-const-prop/terminator.rs +++ b/tests/mir-opt/dataflow-const-prop/terminator.rs @@ -1,5 +1,5 @@ // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: DataflowConstProp +//@ test-mir-pass: DataflowConstProp fn foo(n: i32) {} diff --git a/tests/mir-opt/dataflow-const-prop/transmute.rs b/tests/mir-opt/dataflow-const-prop/transmute.rs index 7cf0dad5e483..e7f93f421cf8 100644 --- a/tests/mir-opt/dataflow-const-prop/transmute.rs +++ b/tests/mir-opt/dataflow-const-prop/transmute.rs @@ -1,4 +1,4 @@ -//@ unit-test: DataflowConstProp +//@ test-mir-pass: DataflowConstProp //@ compile-flags: -O --crate-type=lib //@ ignore-endian-big // EMIT_MIR_FOR_EACH_BIT_WIDTH diff --git a/tests/mir-opt/dataflow-const-prop/tuple.rs b/tests/mir-opt/dataflow-const-prop/tuple.rs index 5d7c38970f6c..d624e21f21ac 100644 --- a/tests/mir-opt/dataflow-const-prop/tuple.rs +++ b/tests/mir-opt/dataflow-const-prop/tuple.rs @@ -1,4 +1,4 @@ -//@ unit-test: DataflowConstProp +//@ test-mir-pass: DataflowConstProp // EMIT_MIR_FOR_EACH_BIT_WIDTH // EMIT_MIR tuple.main.DataflowConstProp.diff diff --git a/tests/mir-opt/dead-store-elimination/call_arg_copy.rs b/tests/mir-opt/dead-store-elimination/call_arg_copy.rs index 2ce1e9023a76..edb35061cc65 100644 --- a/tests/mir-opt/dead-store-elimination/call_arg_copy.rs +++ b/tests/mir-opt/dead-store-elimination/call_arg_copy.rs @@ -1,5 +1,5 @@ // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: DeadStoreElimination-final +//@ test-mir-pass: DeadStoreElimination-final //@ compile-flags: -Zmir-enable-passes=+CopyProp #![feature(core_intrinsics)] diff --git a/tests/mir-opt/dead-store-elimination/cycle.rs b/tests/mir-opt/dead-store-elimination/cycle.rs index ddbc89a7ae2c..795d57d36f52 100644 --- a/tests/mir-opt/dead-store-elimination/cycle.rs +++ b/tests/mir-opt/dead-store-elimination/cycle.rs @@ -2,7 +2,7 @@ // report that *all* of these stores are live. // //@ needs-unwind -//@ unit-test: DeadStoreElimination-initial +//@ test-mir-pass: DeadStoreElimination-initial #![feature(core_intrinsics, custom_mir)] use std::intrinsics::mir::*; diff --git a/tests/mir-opt/dead-store-elimination/place_mention.rs b/tests/mir-opt/dead-store-elimination/place_mention.rs index d276f6fa0259..5e4a286a2087 100644 --- a/tests/mir-opt/dead-store-elimination/place_mention.rs +++ b/tests/mir-opt/dead-store-elimination/place_mention.rs @@ -1,7 +1,7 @@ // Verify that we account for the `PlaceMention` statement as a use of the tuple, // and don't remove it as a dead store. // -//@ unit-test: DeadStoreElimination-initial +//@ test-mir-pass: DeadStoreElimination-initial //@ compile-flags: -Zmir-keep-place-mention // EMIT_MIR place_mention.main.DeadStoreElimination-initial.diff diff --git a/tests/mir-opt/dead-store-elimination/provenance_soundness.rs b/tests/mir-opt/dead-store-elimination/provenance_soundness.rs index 20517a00489a..b2523684a09e 100644 --- a/tests/mir-opt/dead-store-elimination/provenance_soundness.rs +++ b/tests/mir-opt/dead-store-elimination/provenance_soundness.rs @@ -1,5 +1,5 @@ // Test that we don't remove pointer to int casts or retags -//@ unit-test: DeadStoreElimination-initial +//@ test-mir-pass: DeadStoreElimination-initial //@ compile-flags: -Zmir-emit-retag // EMIT_MIR provenance_soundness.pointer_to_int.DeadStoreElimination-initial.diff diff --git a/tests/mir-opt/deduplicate_blocks.rs b/tests/mir-opt/deduplicate_blocks.rs index 7979fdfe768c..3a164cb09a09 100644 --- a/tests/mir-opt/deduplicate_blocks.rs +++ b/tests/mir-opt/deduplicate_blocks.rs @@ -1,6 +1,6 @@ // skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: DeduplicateBlocks +//@ test-mir-pass: DeduplicateBlocks // EMIT_MIR deduplicate_blocks.is_line_doc_comment_2.DeduplicateBlocks.diff pub const fn is_line_doc_comment_2(s: &str) -> bool { diff --git a/tests/mir-opt/derefer_complex_case.rs b/tests/mir-opt/derefer_complex_case.rs index bdaf83fcb5bf..b1fa2c8733ef 100644 --- a/tests/mir-opt/derefer_complex_case.rs +++ b/tests/mir-opt/derefer_complex_case.rs @@ -1,5 +1,5 @@ // skip-filecheck -//@ unit-test: Derefer +//@ test-mir-pass: Derefer // EMIT_MIR derefer_complex_case.main.Derefer.diff // EMIT_MIR_FOR_EACH_PANIC_STRATEGY diff --git a/tests/mir-opt/derefer_inline_test.rs b/tests/mir-opt/derefer_inline_test.rs index 89de514a2808..7f9272bdec85 100644 --- a/tests/mir-opt/derefer_inline_test.rs +++ b/tests/mir-opt/derefer_inline_test.rs @@ -1,5 +1,5 @@ // skip-filecheck -//@ unit-test: Derefer +//@ test-mir-pass: Derefer // EMIT_MIR derefer_inline_test.main.Derefer.diff // EMIT_MIR_FOR_EACH_PANIC_STRATEGY diff --git a/tests/mir-opt/derefer_terminator_test.rs b/tests/mir-opt/derefer_terminator_test.rs index e225db5bbe8a..5de6a61eaf2c 100644 --- a/tests/mir-opt/derefer_terminator_test.rs +++ b/tests/mir-opt/derefer_terminator_test.rs @@ -1,5 +1,5 @@ // skip-filecheck -//@ unit-test: Derefer +//@ test-mir-pass: Derefer // EMIT_MIR derefer_terminator_test.main.Derefer.diff // EMIT_MIR_FOR_EACH_PANIC_STRATEGY diff --git a/tests/mir-opt/derefer_test.rs b/tests/mir-opt/derefer_test.rs index e30a286805fe..3ca2144e4fc4 100644 --- a/tests/mir-opt/derefer_test.rs +++ b/tests/mir-opt/derefer_test.rs @@ -1,5 +1,5 @@ // skip-filecheck -//@ unit-test: Derefer +//@ test-mir-pass: Derefer // EMIT_MIR derefer_test.main.Derefer.diff fn main() { let mut a = (42,43); diff --git a/tests/mir-opt/derefer_test_multiple.rs b/tests/mir-opt/derefer_test_multiple.rs index 4efc735b22eb..145a19ee6a3c 100644 --- a/tests/mir-opt/derefer_test_multiple.rs +++ b/tests/mir-opt/derefer_test_multiple.rs @@ -1,5 +1,5 @@ // skip-filecheck -//@ unit-test: Derefer +//@ test-mir-pass: Derefer // EMIT_MIR derefer_test_multiple.main.Derefer.diff fn main () { let mut a = (42, 43); diff --git a/tests/mir-opt/dest-prop/branch.rs b/tests/mir-opt/dest-prop/branch.rs index cd5513072857..481d4130c7b5 100644 --- a/tests/mir-opt/dest-prop/branch.rs +++ b/tests/mir-opt/dest-prop/branch.rs @@ -1,7 +1,7 @@ // skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY //! Tests that assignment in both branches of an `if` are eliminated. -//@ unit-test: DestinationPropagation +//@ test-mir-pass: DestinationPropagation fn val() -> i32 { 1 } diff --git a/tests/mir-opt/dest-prop/copy_propagation_arg.rs b/tests/mir-opt/dest-prop/copy_propagation_arg.rs index f84b5fde8d88..db4969924ffb 100644 --- a/tests/mir-opt/dest-prop/copy_propagation_arg.rs +++ b/tests/mir-opt/dest-prop/copy_propagation_arg.rs @@ -2,7 +2,7 @@ // EMIT_MIR_FOR_EACH_PANIC_STRATEGY // Check that DestinationPropagation does not propagate an assignment to a function argument // (doing so can break usages of the original argument value) -//@ unit-test: DestinationPropagation +//@ test-mir-pass: DestinationPropagation fn dummy(x: u8) -> u8 { x } diff --git a/tests/mir-opt/dest-prop/cycle.rs b/tests/mir-opt/dest-prop/cycle.rs index e6663956d782..e414daf20f2d 100644 --- a/tests/mir-opt/dest-prop/cycle.rs +++ b/tests/mir-opt/dest-prop/cycle.rs @@ -1,7 +1,7 @@ // skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY //! Tests that cyclic assignments don't hang DestinationPropagation, and result in reasonable code. -//@ unit-test: DestinationPropagation +//@ test-mir-pass: DestinationPropagation fn val() -> i32 { 1 } diff --git a/tests/mir-opt/dest-prop/dead_stores_79191.rs b/tests/mir-opt/dest-prop/dead_stores_79191.rs index b3e370966d05..5c218a328f58 100644 --- a/tests/mir-opt/dest-prop/dead_stores_79191.rs +++ b/tests/mir-opt/dest-prop/dead_stores_79191.rs @@ -1,6 +1,6 @@ // skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: DestinationPropagation +//@ test-mir-pass: DestinationPropagation fn id(x: T) -> T { x diff --git a/tests/mir-opt/dest-prop/dead_stores_better.rs b/tests/mir-opt/dest-prop/dead_stores_better.rs index c241d71594ba..06445dc87031 100644 --- a/tests/mir-opt/dest-prop/dead_stores_better.rs +++ b/tests/mir-opt/dest-prop/dead_stores_better.rs @@ -3,7 +3,7 @@ // This is a copy of the `dead_stores_79191` test, except that we turn on DSE. This demonstrates // that that pass enables this one to do more optimizations. -//@ unit-test: DestinationPropagation +//@ test-mir-pass: DestinationPropagation //@ compile-flags: -Zmir-enable-passes=+DeadStoreElimination fn id(x: T) -> T { diff --git a/tests/mir-opt/dest-prop/simple.rs b/tests/mir-opt/dest-prop/simple.rs index 4aa6b6a4876e..8e5d6340e566 100644 --- a/tests/mir-opt/dest-prop/simple.rs +++ b/tests/mir-opt/dest-prop/simple.rs @@ -1,7 +1,7 @@ // skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY //! Copy of `nrvo-simple.rs`, to ensure that full dest-prop handles it too. -//@ unit-test: DestinationPropagation +//@ test-mir-pass: DestinationPropagation // EMIT_MIR simple.nrvo.DestinationPropagation.diff fn nrvo(init: fn(&mut [u8; 1024])) -> [u8; 1024] { let mut buf = [0; 1024]; diff --git a/tests/mir-opt/dont_inline_type_id.rs b/tests/mir-opt/dont_inline_type_id.rs index ae72eb11735e..ab748940ac43 100644 --- a/tests/mir-opt/dont_inline_type_id.rs +++ b/tests/mir-opt/dont_inline_type_id.rs @@ -1,5 +1,5 @@ // skip-filecheck -//@ unit-test: Inline +//@ test-mir-pass: Inline //@ compile-flags: --crate-type=lib -C panic=abort use std::any::Any; diff --git a/tests/mir-opt/early_otherwise_branch.rs b/tests/mir-opt/early_otherwise_branch.rs index bfeb1f7bbc61..b047c50df97f 100644 --- a/tests/mir-opt/early_otherwise_branch.rs +++ b/tests/mir-opt/early_otherwise_branch.rs @@ -1,4 +1,4 @@ -//@ unit-test: EarlyOtherwiseBranch +//@ test-mir-pass: EarlyOtherwiseBranch //@ compile-flags: -Zmir-enable-passes=+UnreachableEnumBranching enum Option2 { diff --git a/tests/mir-opt/early_otherwise_branch_3_element_tuple.rs b/tests/mir-opt/early_otherwise_branch_3_element_tuple.rs index 2d215621bbd9..d2a3e1f59ff6 100644 --- a/tests/mir-opt/early_otherwise_branch_3_element_tuple.rs +++ b/tests/mir-opt/early_otherwise_branch_3_element_tuple.rs @@ -1,4 +1,4 @@ -//@ unit-test: EarlyOtherwiseBranch +//@ test-mir-pass: EarlyOtherwiseBranch //@ compile-flags: -Zmir-enable-passes=+UnreachableEnumBranching enum Option2 { diff --git a/tests/mir-opt/early_otherwise_branch_68867.rs b/tests/mir-opt/early_otherwise_branch_68867.rs index 59bc19ceeccc..789b5ebab807 100644 --- a/tests/mir-opt/early_otherwise_branch_68867.rs +++ b/tests/mir-opt/early_otherwise_branch_68867.rs @@ -1,4 +1,4 @@ -//@ unit-test: EarlyOtherwiseBranch +//@ test-mir-pass: EarlyOtherwiseBranch //@ compile-flags: -Zmir-enable-passes=+UnreachableEnumBranching // FIXME: This test was broken by the derefer change. diff --git a/tests/mir-opt/early_otherwise_branch_noopt.rs b/tests/mir-opt/early_otherwise_branch_noopt.rs index 6b48393e6b94..307c6e579c99 100644 --- a/tests/mir-opt/early_otherwise_branch_noopt.rs +++ b/tests/mir-opt/early_otherwise_branch_noopt.rs @@ -1,4 +1,4 @@ -//@ unit-test: EarlyOtherwiseBranch +//@ test-mir-pass: EarlyOtherwiseBranch //@ compile-flags: -Zmir-enable-passes=+UnreachableEnumBranching // must not optimize as it does not follow the pattern of diff --git a/tests/mir-opt/early_otherwise_branch_soundness.rs b/tests/mir-opt/early_otherwise_branch_soundness.rs index 74a2af884c0b..a22be312a9b9 100644 --- a/tests/mir-opt/early_otherwise_branch_soundness.rs +++ b/tests/mir-opt/early_otherwise_branch_soundness.rs @@ -1,4 +1,4 @@ -//@ unit-test: EarlyOtherwiseBranch +//@ test-mir-pass: EarlyOtherwiseBranch //@ compile-flags: -Zmir-enable-passes=+UnreachableEnumBranching // Tests various cases that the `early_otherwise_branch` opt should *not* optimize diff --git a/tests/mir-opt/enum_opt.rs b/tests/mir-opt/enum_opt.rs index c5b3e61a4cb2..cacc7301f122 100644 --- a/tests/mir-opt/enum_opt.rs +++ b/tests/mir-opt/enum_opt.rs @@ -1,5 +1,5 @@ // skip-filecheck -//@ unit-test: EnumSizeOpt +//@ test-mir-pass: EnumSizeOpt // EMIT_MIR_FOR_EACH_BIT_WIDTH //@ compile-flags: -Zunsound-mir-opts diff --git a/tests/mir-opt/gvn.rs b/tests/mir-opt/gvn.rs index 6f4d1e35585b..0484710f00e7 100644 --- a/tests/mir-opt/gvn.rs +++ b/tests/mir-opt/gvn.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN // EMIT_MIR_FOR_EACH_PANIC_STRATEGY //@ only-64bit diff --git a/tests/mir-opt/gvn_copy_moves.rs b/tests/mir-opt/gvn_copy_moves.rs index 9d83a19e4a58..1812de16d69f 100644 --- a/tests/mir-opt/gvn_copy_moves.rs +++ b/tests/mir-opt/gvn_copy_moves.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN #![feature(custom_mir, core_intrinsics)] extern crate core; diff --git a/tests/mir-opt/gvn_uninhabited.rs b/tests/mir-opt/gvn_uninhabited.rs index 5f9df7953c8e..015949c5d20b 100644 --- a/tests/mir-opt/gvn_uninhabited.rs +++ b/tests/mir-opt/gvn_uninhabited.rs @@ -1,4 +1,4 @@ -//@ unit-test: GVN +//@ test-mir-pass: GVN //@ compile-flags: -O // EMIT_MIR_FOR_EACH_PANIC_STRATEGY // skip-filecheck diff --git a/tests/mir-opt/if_condition_int.rs b/tests/mir-opt/if_condition_int.rs index 2f3f64330456..4cc2c2b90210 100644 --- a/tests/mir-opt/if_condition_int.rs +++ b/tests/mir-opt/if_condition_int.rs @@ -1,5 +1,5 @@ // skip-filecheck -//@ unit-test: SimplifyComparisonIntegral +//@ test-mir-pass: SimplifyComparisonIntegral // EMIT_MIR if_condition_int.opt_u32.SimplifyComparisonIntegral.diff // EMIT_MIR if_condition_int.opt_negative.SimplifyComparisonIntegral.diff // EMIT_MIR if_condition_int.opt_char.SimplifyComparisonIntegral.diff diff --git a/tests/mir-opt/inline/indirect_destination.rs b/tests/mir-opt/inline/indirect_destination.rs index 337f617e7036..4246eef08f70 100644 --- a/tests/mir-opt/inline/indirect_destination.rs +++ b/tests/mir-opt/inline/indirect_destination.rs @@ -1,6 +1,6 @@ // Test for inlining with an indirect destination place. // -//@ unit-test: Inline +//@ test-mir-pass: Inline //@ edition: 2021 //@ needs-unwind #![crate_type = "lib"] diff --git a/tests/mir-opt/inline/inline_box_fn.rs b/tests/mir-opt/inline/inline_box_fn.rs index 3e006016f8c3..bb2da3ac5157 100644 --- a/tests/mir-opt/inline/inline_box_fn.rs +++ b/tests/mir-opt/inline/inline_box_fn.rs @@ -1,5 +1,5 @@ // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: Inline +//@ test-mir-pass: Inline //@ compile-flags: --crate-type=lib // EMIT_MIR inline_box_fn.call.Inline.diff diff --git a/tests/mir-opt/inline/unit_test.rs b/tests/mir-opt/inline/unit_test.rs index f6c3d6a58de0..bebe69384612 100644 --- a/tests/mir-opt/inline/unit_test.rs +++ b/tests/mir-opt/inline/unit_test.rs @@ -1,5 +1,5 @@ // Check that `-Zmir-enable-passes=+Inline` does not ICE because of stolen MIR. -//@ unit-test: Inline +//@ test-mir-pass: Inline // skip-filecheck #![crate_type = "lib"] diff --git a/tests/mir-opt/inline_coroutine_body.rs b/tests/mir-opt/inline_coroutine_body.rs index be73bc49de59..4326ff8a11b3 100644 --- a/tests/mir-opt/inline_coroutine_body.rs +++ b/tests/mir-opt/inline_coroutine_body.rs @@ -1,6 +1,6 @@ // EMIT_MIR_FOR_EACH_PANIC_STRATEGY // skip-filecheck -//@ unit-test: Inline +//@ test-mir-pass: Inline //@ edition: 2021 //@ compile-flags: -Zinline-mir-hint-threshold=10000 -Zinline-mir-threshold=10000 --crate-type=lib diff --git a/tests/mir-opt/inline_generically_if_sized.rs b/tests/mir-opt/inline_generically_if_sized.rs index 794ce3dabbca..e4fc94ec4362 100644 --- a/tests/mir-opt/inline_generically_if_sized.rs +++ b/tests/mir-opt/inline_generically_if_sized.rs @@ -1,5 +1,5 @@ // skip-filecheck -//@ unit-test: Inline +//@ test-mir-pass: Inline //@ compile-flags: --crate-type=lib -C panic=abort trait Foo { diff --git a/tests/mir-opt/instsimplify/bool_compare.rs b/tests/mir-opt/instsimplify/bool_compare.rs index 47984edd669f..d1d903f9ef2c 100644 --- a/tests/mir-opt/instsimplify/bool_compare.rs +++ b/tests/mir-opt/instsimplify/bool_compare.rs @@ -1,4 +1,4 @@ -//@ unit-test: InstSimplify +//@ test-mir-pass: InstSimplify // EMIT_MIR bool_compare.eq_true.InstSimplify.diff fn eq_true(x: bool) -> u32 { diff --git a/tests/mir-opt/instsimplify/casts.rs b/tests/mir-opt/instsimplify/casts.rs index adcf325e3f5c..a7786fa570f1 100644 --- a/tests/mir-opt/instsimplify/casts.rs +++ b/tests/mir-opt/instsimplify/casts.rs @@ -1,6 +1,7 @@ -//@ unit-test: InstSimplify +//@ test-mir-pass: InstSimplify //@ compile-flags: -Zinline-mir #![crate_type = "lib"] +#![feature(core_intrinsics)] #[inline(always)] fn generic_cast(x: *const T) -> *const U { @@ -23,3 +24,11 @@ pub fn roundtrip(x: *const u8) -> *const u8 { // CHECK: _2 = move _3 as *const u8 (PointerCoercion(MutToConstPointer)); x as *mut u8 as *const u8 } + +// EMIT_MIR casts.roundtrip.InstSimplify.diff +pub fn cast_thin_via_aggregate(x: *const u8) -> *const () { + // CHECK-LABEL: fn cast_thin_via_aggregate( + // CHECK: _2 = _1; + // CHECK: _0 = move _2 as *const () (PtrToPtr); + std::intrinsics::aggregate_raw_ptr(x, ()) +} diff --git a/tests/mir-opt/instsimplify/combine_array_len.rs b/tests/mir-opt/instsimplify/combine_array_len.rs index 4b4054a7a2d6..86455e8b52d5 100644 --- a/tests/mir-opt/instsimplify/combine_array_len.rs +++ b/tests/mir-opt/instsimplify/combine_array_len.rs @@ -1,5 +1,5 @@ // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: InstSimplify +//@ test-mir-pass: InstSimplify // EMIT_MIR combine_array_len.norm2.InstSimplify.diff fn norm2(x: [f32; 2]) -> f32 { diff --git a/tests/mir-opt/instsimplify/combine_clone_of_primitives.rs b/tests/mir-opt/instsimplify/combine_clone_of_primitives.rs index d0c85595dbcf..7b1f3d14f4fd 100644 --- a/tests/mir-opt/instsimplify/combine_clone_of_primitives.rs +++ b/tests/mir-opt/instsimplify/combine_clone_of_primitives.rs @@ -1,4 +1,4 @@ -//@ unit-test: InstSimplify +//@ test-mir-pass: InstSimplify // EMIT_MIR_FOR_EACH_PANIC_STRATEGY // EMIT_MIR combine_clone_of_primitives.{impl#0}-clone.InstSimplify.diff diff --git a/tests/mir-opt/instsimplify/combine_transmutes.rs b/tests/mir-opt/instsimplify/combine_transmutes.rs index 3707ee17690d..0be7466001fe 100644 --- a/tests/mir-opt/instsimplify/combine_transmutes.rs +++ b/tests/mir-opt/instsimplify/combine_transmutes.rs @@ -1,9 +1,8 @@ -//@ unit-test: InstSimplify +//@ test-mir-pass: InstSimplify //@ compile-flags: -C panic=abort #![crate_type = "lib"] #![feature(core_intrinsics)] #![feature(custom_mir)] -#![feature(generic_nonzero)] use std::intrinsics::mir::*; use std::mem::{MaybeUninit, ManuallyDrop, transmute}; diff --git a/tests/mir-opt/instsimplify/duplicate_switch_targets.rs b/tests/mir-opt/instsimplify/duplicate_switch_targets.rs index fd09d632a4f0..454728249b1a 100644 --- a/tests/mir-opt/instsimplify/duplicate_switch_targets.rs +++ b/tests/mir-opt/instsimplify/duplicate_switch_targets.rs @@ -1,4 +1,4 @@ -//@ unit-test: InstSimplify +//@ test-mir-pass: InstSimplify #![feature(custom_mir, core_intrinsics)] #![crate_type = "lib"] diff --git a/tests/mir-opt/instsimplify/intrinsic_asserts.rs b/tests/mir-opt/instsimplify/intrinsic_asserts.rs index c14b1ac5a218..c031c9781626 100644 --- a/tests/mir-opt/instsimplify/intrinsic_asserts.rs +++ b/tests/mir-opt/instsimplify/intrinsic_asserts.rs @@ -1,4 +1,4 @@ -//@ unit-test: InstSimplify +//@ test-mir-pass: InstSimplify #![crate_type = "lib"] #![feature(core_intrinsics)] diff --git a/tests/mir-opt/instsimplify/ub_check.rs b/tests/mir-opt/instsimplify/ub_check.rs index fc568abcd601..5f13f5ba059b 100644 --- a/tests/mir-opt/instsimplify/ub_check.rs +++ b/tests/mir-opt/instsimplify/ub_check.rs @@ -1,4 +1,4 @@ -//@ unit-test: InstSimplify +//@ test-mir-pass: InstSimplify //@ compile-flags: -Cdebug-assertions=no -Zinline-mir // EMIT_MIR ub_check.unwrap_unchecked.InstSimplify.diff diff --git a/tests/mir-opt/issue_101973.rs b/tests/mir-opt/issue_101973.rs index c40eaa1f2a9b..84a36f1374e8 100644 --- a/tests/mir-opt/issue_101973.rs +++ b/tests/mir-opt/issue_101973.rs @@ -1,7 +1,7 @@ // skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY //@ compile-flags: -O -C debug-assertions=on -// This needs inlining followed by GVN to reproduce, so we cannot use "unit-test". +// This needs inlining followed by GVN to reproduce, so we cannot use "test-mir-pass". #[inline] pub fn imm8(x: u32) -> u32 { diff --git a/tests/mir-opt/jump_threading.rs b/tests/mir-opt/jump_threading.rs index eedb26ad41a5..57f4e4a2654f 100644 --- a/tests/mir-opt/jump_threading.rs +++ b/tests/mir-opt/jump_threading.rs @@ -1,4 +1,4 @@ -//@ unit-test: JumpThreading +//@ test-mir-pass: JumpThreading //@ compile-flags: -Zmir-enable-passes=+Inline // EMIT_MIR_FOR_EACH_PANIC_STRATEGY diff --git a/tests/mir-opt/lower_array_len.rs b/tests/mir-opt/lower_array_len.rs index 7fcea75aaaf4..1c30c4c89b96 100644 --- a/tests/mir-opt/lower_array_len.rs +++ b/tests/mir-opt/lower_array_len.rs @@ -1,5 +1,5 @@ // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: NormalizeArrayLen +//@ test-mir-pass: NormalizeArrayLen //@ compile-flags: -Zmir-enable-passes=+LowerSliceLenCalls // EMIT_MIR lower_array_len.array_bound.NormalizeArrayLen.diff diff --git a/tests/mir-opt/lower_intrinsics.make_pointers.LowerIntrinsics.panic-abort.diff b/tests/mir-opt/lower_intrinsics.make_pointers.LowerIntrinsics.panic-abort.diff new file mode 100644 index 000000000000..02934d4c01e6 --- /dev/null +++ b/tests/mir-opt/lower_intrinsics.make_pointers.LowerIntrinsics.panic-abort.diff @@ -0,0 +1,95 @@ +- // MIR for `make_pointers` before LowerIntrinsics ++ // MIR for `make_pointers` after LowerIntrinsics + + fn make_pointers(_1: *const u8, _2: *mut (), _3: usize) -> () { + debug a => _1; + debug b => _2; + debug n => _3; + let mut _0: (); + let _4: *const i32; + let mut _5: *const u8; + let mut _6: (); + let mut _8: *mut (); + let mut _9: (); + let mut _11: *const u8; + let mut _12: usize; + let mut _14: *mut (); + let mut _15: usize; + scope 1 { + debug _thin_const => _4; + let _7: *mut u8; + scope 2 { + debug _thin_mut => _7; + let _10: *const [u16]; + scope 3 { + debug _slice_const => _10; + let _13: *mut [u64]; + scope 4 { + debug _slice_mut => _13; + } + } + } + } + + bb0: { + StorageLive(_4); + StorageLive(_5); + _5 = _1; + StorageLive(_6); + _6 = (); +- _4 = aggregate_raw_ptr::<*const i32, *const u8, ()>(move _5, move _6) -> [return: bb1, unwind unreachable]; ++ _4 = *const i32 from (move _5, move _6); ++ goto -> bb1; + } + + bb1: { + StorageDead(_6); + StorageDead(_5); + StorageLive(_7); + StorageLive(_8); + _8 = _2; + StorageLive(_9); + _9 = (); +- _7 = aggregate_raw_ptr::<*mut u8, *mut (), ()>(move _8, move _9) -> [return: bb2, unwind unreachable]; ++ _7 = *mut u8 from (move _8, move _9); ++ goto -> bb2; + } + + bb2: { + StorageDead(_9); + StorageDead(_8); + StorageLive(_10); + StorageLive(_11); + _11 = _1; + StorageLive(_12); + _12 = _3; +- _10 = aggregate_raw_ptr::<*const [u16], *const u8, usize>(move _11, move _12) -> [return: bb3, unwind unreachable]; ++ _10 = *const [u16] from (move _11, move _12); ++ goto -> bb3; + } + + bb3: { + StorageDead(_12); + StorageDead(_11); + StorageLive(_13); + StorageLive(_14); + _14 = _2; + StorageLive(_15); + _15 = _3; +- _13 = aggregate_raw_ptr::<*mut [u64], *mut (), usize>(move _14, move _15) -> [return: bb4, unwind unreachable]; ++ _13 = *mut [u64] from (move _14, move _15); ++ goto -> bb4; + } + + bb4: { + StorageDead(_15); + StorageDead(_14); + _0 = const (); + StorageDead(_13); + StorageDead(_10); + StorageDead(_7); + StorageDead(_4); + return; + } + } + diff --git a/tests/mir-opt/lower_intrinsics.make_pointers.LowerIntrinsics.panic-unwind.diff b/tests/mir-opt/lower_intrinsics.make_pointers.LowerIntrinsics.panic-unwind.diff new file mode 100644 index 000000000000..02934d4c01e6 --- /dev/null +++ b/tests/mir-opt/lower_intrinsics.make_pointers.LowerIntrinsics.panic-unwind.diff @@ -0,0 +1,95 @@ +- // MIR for `make_pointers` before LowerIntrinsics ++ // MIR for `make_pointers` after LowerIntrinsics + + fn make_pointers(_1: *const u8, _2: *mut (), _3: usize) -> () { + debug a => _1; + debug b => _2; + debug n => _3; + let mut _0: (); + let _4: *const i32; + let mut _5: *const u8; + let mut _6: (); + let mut _8: *mut (); + let mut _9: (); + let mut _11: *const u8; + let mut _12: usize; + let mut _14: *mut (); + let mut _15: usize; + scope 1 { + debug _thin_const => _4; + let _7: *mut u8; + scope 2 { + debug _thin_mut => _7; + let _10: *const [u16]; + scope 3 { + debug _slice_const => _10; + let _13: *mut [u64]; + scope 4 { + debug _slice_mut => _13; + } + } + } + } + + bb0: { + StorageLive(_4); + StorageLive(_5); + _5 = _1; + StorageLive(_6); + _6 = (); +- _4 = aggregate_raw_ptr::<*const i32, *const u8, ()>(move _5, move _6) -> [return: bb1, unwind unreachable]; ++ _4 = *const i32 from (move _5, move _6); ++ goto -> bb1; + } + + bb1: { + StorageDead(_6); + StorageDead(_5); + StorageLive(_7); + StorageLive(_8); + _8 = _2; + StorageLive(_9); + _9 = (); +- _7 = aggregate_raw_ptr::<*mut u8, *mut (), ()>(move _8, move _9) -> [return: bb2, unwind unreachable]; ++ _7 = *mut u8 from (move _8, move _9); ++ goto -> bb2; + } + + bb2: { + StorageDead(_9); + StorageDead(_8); + StorageLive(_10); + StorageLive(_11); + _11 = _1; + StorageLive(_12); + _12 = _3; +- _10 = aggregate_raw_ptr::<*const [u16], *const u8, usize>(move _11, move _12) -> [return: bb3, unwind unreachable]; ++ _10 = *const [u16] from (move _11, move _12); ++ goto -> bb3; + } + + bb3: { + StorageDead(_12); + StorageDead(_11); + StorageLive(_13); + StorageLive(_14); + _14 = _2; + StorageLive(_15); + _15 = _3; +- _13 = aggregate_raw_ptr::<*mut [u64], *mut (), usize>(move _14, move _15) -> [return: bb4, unwind unreachable]; ++ _13 = *mut [u64] from (move _14, move _15); ++ goto -> bb4; + } + + bb4: { + StorageDead(_15); + StorageDead(_14); + _0 = const (); + StorageDead(_13); + StorageDead(_10); + StorageDead(_7); + StorageDead(_4); + return; + } + } + diff --git a/tests/mir-opt/lower_intrinsics.rs b/tests/mir-opt/lower_intrinsics.rs index 08366417d7cb..12e526ab0741 100644 --- a/tests/mir-opt/lower_intrinsics.rs +++ b/tests/mir-opt/lower_intrinsics.rs @@ -1,4 +1,4 @@ -//@ unit-test: LowerIntrinsics +//@ test-mir-pass: LowerIntrinsics // EMIT_MIR_FOR_EACH_PANIC_STRATEGY #![feature(core_intrinsics, intrinsics, rustc_attrs)] @@ -248,3 +248,13 @@ pub fn three_way_compare_signed(a: i16, b: i16) { pub fn three_way_compare_unsigned(a: u32, b: u32) { let _x = core::intrinsics::three_way_compare(a, b); } + +// EMIT_MIR lower_intrinsics.make_pointers.LowerIntrinsics.diff +pub fn make_pointers(a: *const u8, b: *mut (), n: usize) { + use std::intrinsics::aggregate_raw_ptr; + + let _thin_const: *const i32 = aggregate_raw_ptr(a, ()); + let _thin_mut: *mut u8 = aggregate_raw_ptr(b, ()); + let _slice_const: *const [u16] = aggregate_raw_ptr(a, n); + let _slice_mut: *mut [u64] = aggregate_raw_ptr(b, n); +} diff --git a/tests/mir-opt/lower_slice_len.rs b/tests/mir-opt/lower_slice_len.rs index 38d5e984cee7..b82094dc18f9 100644 --- a/tests/mir-opt/lower_slice_len.rs +++ b/tests/mir-opt/lower_slice_len.rs @@ -1,4 +1,4 @@ -//@ unit-test: LowerSliceLenCalls +//@ test-mir-pass: LowerSliceLenCalls // EMIT_MIR_FOR_EACH_PANIC_STRATEGY // EMIT_MIR lower_slice_len.bound.LowerSliceLenCalls.diff diff --git a/tests/mir-opt/matches_reduce_branches.match_i128_u128.MatchBranchSimplification.diff b/tests/mir-opt/matches_reduce_branches.match_i128_u128.MatchBranchSimplification.diff index 31ce51dc6de2..1f20349fdece 100644 --- a/tests/mir-opt/matches_reduce_branches.match_i128_u128.MatchBranchSimplification.diff +++ b/tests/mir-opt/matches_reduce_branches.match_i128_u128.MatchBranchSimplification.diff @@ -5,42 +5,37 @@ debug i => _1; let mut _0: u128; let mut _2: i128; -+ let mut _3: i128; bb0: { _2 = discriminant(_1); -- switchInt(move _2) -> [1: bb3, 2: bb4, 3: bb5, 340282366920938463463374607431768211455: bb2, otherwise: bb1]; -- } -- -- bb1: { -- unreachable; -- } -- -- bb2: { -- _0 = const core::num::::MAX; -- goto -> bb6; -- } -- -- bb3: { -- _0 = const 1_u128; -- goto -> bb6; -- } -- -- bb4: { -- _0 = const 2_u128; -- goto -> bb6; -- } -- -- bb5: { -- _0 = const 3_u128; -- goto -> bb6; -- } -- -- bb6: { -+ StorageLive(_3); -+ _3 = move _2; -+ _0 = _3 as u128 (IntToInt); -+ StorageDead(_3); + switchInt(move _2) -> [1: bb3, 2: bb4, 3: bb5, 340282366920938463463374607431768211455: bb2, otherwise: bb1]; + } + + bb1: { + unreachable; + } + + bb2: { + _0 = const core::num::::MAX; + goto -> bb6; + } + + bb3: { + _0 = const 1_u128; + goto -> bb6; + } + + bb4: { + _0 = const 2_u128; + goto -> bb6; + } + + bb5: { + _0 = const 3_u128; + goto -> bb6; + } + + bb6: { return; } } diff --git a/tests/mir-opt/matches_reduce_branches.match_i16_i8.MatchBranchSimplification.diff b/tests/mir-opt/matches_reduce_branches.match_i16_i8.MatchBranchSimplification.diff index e1b537b1b719..4b435310916a 100644 --- a/tests/mir-opt/matches_reduce_branches.match_i16_i8.MatchBranchSimplification.diff +++ b/tests/mir-opt/matches_reduce_branches.match_i16_i8.MatchBranchSimplification.diff @@ -5,37 +5,32 @@ debug i => _1; let mut _0: i8; let mut _2: i16; -+ let mut _3: i16; bb0: { _2 = discriminant(_1); -- switchInt(move _2) -> [65535: bb3, 2: bb4, 65533: bb2, otherwise: bb1]; -- } -- -- bb1: { -- unreachable; -- } -- -- bb2: { -- _0 = const -3_i8; -- goto -> bb5; -- } -- -- bb3: { -- _0 = const -1_i8; -- goto -> bb5; -- } -- -- bb4: { -- _0 = const 2_i8; -- goto -> bb5; -- } -- -- bb5: { -+ StorageLive(_3); -+ _3 = move _2; -+ _0 = _3 as i8 (IntToInt); -+ StorageDead(_3); + switchInt(move _2) -> [65535: bb3, 2: bb4, 65533: bb2, otherwise: bb1]; + } + + bb1: { + unreachable; + } + + bb2: { + _0 = const -3_i8; + goto -> bb5; + } + + bb3: { + _0 = const -1_i8; + goto -> bb5; + } + + bb4: { + _0 = const 2_i8; + goto -> bb5; + } + + bb5: { return; } } diff --git a/tests/mir-opt/matches_reduce_branches.match_i8_i16.MatchBranchSimplification.diff b/tests/mir-opt/matches_reduce_branches.match_i8_i16.MatchBranchSimplification.diff index cabc5a44cd81..8a390736add1 100644 --- a/tests/mir-opt/matches_reduce_branches.match_i8_i16.MatchBranchSimplification.diff +++ b/tests/mir-opt/matches_reduce_branches.match_i8_i16.MatchBranchSimplification.diff @@ -5,37 +5,32 @@ debug i => _1; let mut _0: i16; let mut _2: i8; -+ let mut _3: i8; bb0: { _2 = discriminant(_1); -- switchInt(move _2) -> [255: bb3, 2: bb4, 253: bb2, otherwise: bb1]; -- } -- -- bb1: { -- unreachable; -- } -- -- bb2: { -- _0 = const -3_i16; -- goto -> bb5; -- } -- -- bb3: { -- _0 = const -1_i16; -- goto -> bb5; -- } -- -- bb4: { -- _0 = const 2_i16; -- goto -> bb5; -- } -- -- bb5: { -+ StorageLive(_3); -+ _3 = move _2; -+ _0 = _3 as i16 (IntToInt); -+ StorageDead(_3); + switchInt(move _2) -> [255: bb3, 2: bb4, 253: bb2, otherwise: bb1]; + } + + bb1: { + unreachable; + } + + bb2: { + _0 = const -3_i16; + goto -> bb5; + } + + bb3: { + _0 = const -1_i16; + goto -> bb5; + } + + bb4: { + _0 = const 2_i16; + goto -> bb5; + } + + bb5: { return; } } diff --git a/tests/mir-opt/matches_reduce_branches.match_u8_i16.MatchBranchSimplification.diff b/tests/mir-opt/matches_reduce_branches.match_u8_i16.MatchBranchSimplification.diff index 9ee01a87a913..72ad60956ab7 100644 --- a/tests/mir-opt/matches_reduce_branches.match_u8_i16.MatchBranchSimplification.diff +++ b/tests/mir-opt/matches_reduce_branches.match_u8_i16.MatchBranchSimplification.diff @@ -5,32 +5,27 @@ debug i => _1; let mut _0: i16; let mut _2: u8; -+ let mut _3: u8; bb0: { _2 = discriminant(_1); -- switchInt(move _2) -> [1: bb3, 2: bb2, otherwise: bb1]; -- } -- -- bb1: { -- unreachable; -- } -- -- bb2: { -- _0 = const 2_i16; -- goto -> bb4; -- } -- -- bb3: { -- _0 = const 1_i16; -- goto -> bb4; -- } -- -- bb4: { -+ StorageLive(_3); -+ _3 = move _2; -+ _0 = _3 as i16 (IntToInt); -+ StorageDead(_3); + switchInt(move _2) -> [1: bb3, 2: bb2, otherwise: bb1]; + } + + bb1: { + unreachable; + } + + bb2: { + _0 = const 2_i16; + goto -> bb4; + } + + bb3: { + _0 = const 1_i16; + goto -> bb4; + } + + bb4: { return; } } diff --git a/tests/mir-opt/matches_reduce_branches.match_u8_u16.MatchBranchSimplification.diff b/tests/mir-opt/matches_reduce_branches.match_u8_u16.MatchBranchSimplification.diff index aa9fcc60a3e1..043fdb197a3a 100644 --- a/tests/mir-opt/matches_reduce_branches.match_u8_u16.MatchBranchSimplification.diff +++ b/tests/mir-opt/matches_reduce_branches.match_u8_u16.MatchBranchSimplification.diff @@ -5,37 +5,32 @@ debug i => _1; let mut _0: u16; let mut _2: u8; -+ let mut _3: u8; bb0: { _2 = discriminant(_1); -- switchInt(move _2) -> [1: bb3, 2: bb4, 5: bb2, otherwise: bb1]; -- } -- -- bb1: { -- unreachable; -- } -- -- bb2: { -- _0 = const 5_u16; -- goto -> bb5; -- } -- -- bb3: { -- _0 = const 1_u16; -- goto -> bb5; -- } -- -- bb4: { -- _0 = const 2_u16; -- goto -> bb5; -- } -- -- bb5: { -+ StorageLive(_3); -+ _3 = move _2; -+ _0 = _3 as u16 (IntToInt); -+ StorageDead(_3); + switchInt(move _2) -> [1: bb3, 2: bb4, 5: bb2, otherwise: bb1]; + } + + bb1: { + unreachable; + } + + bb2: { + _0 = const 5_u16; + goto -> bb5; + } + + bb3: { + _0 = const 1_u16; + goto -> bb5; + } + + bb4: { + _0 = const 2_u16; + goto -> bb5; + } + + bb5: { return; } } diff --git a/tests/mir-opt/matches_reduce_branches.rs b/tests/mir-opt/matches_reduce_branches.rs index ca3e5f747d10..fa466220b653 100644 --- a/tests/mir-opt/matches_reduce_branches.rs +++ b/tests/mir-opt/matches_reduce_branches.rs @@ -1,4 +1,4 @@ -//@ unit-test: MatchBranchSimplification +//@ test-mir-pass: MatchBranchSimplification #![feature(repr128)] #![feature(core_intrinsics)] @@ -75,9 +75,7 @@ enum EnumAu8 { // EMIT_MIR matches_reduce_branches.match_u8_i16.MatchBranchSimplification.diff fn match_u8_i16(i: EnumAu8) -> i16 { // CHECK-LABEL: fn match_u8_i16( - // CHECK-NOT: switchInt - // CHECK: _0 = _3 as i16 (IntToInt); - // CHECH: return + // CHECK: switchInt match i { EnumAu8::A => 1, EnumAu8::B => 2, @@ -146,9 +144,7 @@ enum EnumBu8 { // EMIT_MIR matches_reduce_branches.match_u8_u16.MatchBranchSimplification.diff fn match_u8_u16(i: EnumBu8) -> u16 { // CHECK-LABEL: fn match_u8_u16( - // CHECK-NOT: switchInt - // CHECK: _0 = _3 as u16 (IntToInt); - // CHECH: return + // CHECK: switchInt match i { EnumBu8::A => 1, EnumBu8::B => 2, @@ -204,9 +200,7 @@ enum EnumAi8 { // EMIT_MIR matches_reduce_branches.match_i8_i16.MatchBranchSimplification.diff fn match_i8_i16(i: EnumAi8) -> i16 { // CHECK-LABEL: fn match_i8_i16( - // CHECK-NOT: switchInt - // CHECK: _0 = _3 as i16 (IntToInt); - // CHECH: return + // CHECK: switchInt match i { EnumAi8::A => -1, EnumAi8::B => 2, @@ -235,9 +229,7 @@ enum EnumAi16 { // EMIT_MIR matches_reduce_branches.match_i16_i8.MatchBranchSimplification.diff fn match_i16_i8(i: EnumAi16) -> i8 { // CHECK-LABEL: fn match_i16_i8( - // CHECK-NOT: switchInt - // CHECK: _0 = _3 as i8 (IntToInt); - // CHECH: return + // CHECK: switchInt match i { EnumAi16::A => -1, EnumAi16::B => 2, @@ -256,9 +248,7 @@ enum EnumAi128 { // EMIT_MIR matches_reduce_branches.match_i128_u128.MatchBranchSimplification.diff fn match_i128_u128(i: EnumAi128) -> u128 { // CHECK-LABEL: fn match_i128_u128( - // CHECK-NOT: switchInt - // CHECK: _0 = _3 as u128 (IntToInt); - // CHECH: return + // CHECK: switchInt match i { EnumAi128::A => 1, EnumAi128::B => 2, diff --git a/tests/mir-opt/matches_u8.exhaustive_match.MatchBranchSimplification.diff b/tests/mir-opt/matches_u8.exhaustive_match.MatchBranchSimplification.diff index 11a18f58e3a1..157f9c98353e 100644 --- a/tests/mir-opt/matches_u8.exhaustive_match.MatchBranchSimplification.diff +++ b/tests/mir-opt/matches_u8.exhaustive_match.MatchBranchSimplification.diff @@ -5,32 +5,27 @@ debug e => _1; let mut _0: u8; let mut _2: isize; -+ let mut _3: isize; bb0: { _2 = discriminant(_1); -- switchInt(move _2) -> [0: bb3, 1: bb2, otherwise: bb1]; -- } -- -- bb1: { -- unreachable; -- } -- -- bb2: { -- _0 = const 1_u8; -- goto -> bb4; -- } -- -- bb3: { -- _0 = const 0_u8; -- goto -> bb4; -- } -- -- bb4: { -+ StorageLive(_3); -+ _3 = move _2; -+ _0 = _3 as u8 (IntToInt); -+ StorageDead(_3); + switchInt(move _2) -> [0: bb3, 1: bb2, otherwise: bb1]; + } + + bb1: { + unreachable; + } + + bb2: { + _0 = const 1_u8; + goto -> bb4; + } + + bb3: { + _0 = const 0_u8; + goto -> bb4; + } + + bb4: { return; } } diff --git a/tests/mir-opt/matches_u8.exhaustive_match_i8.MatchBranchSimplification.diff b/tests/mir-opt/matches_u8.exhaustive_match_i8.MatchBranchSimplification.diff index 809badc41ba7..19083771fd95 100644 --- a/tests/mir-opt/matches_u8.exhaustive_match_i8.MatchBranchSimplification.diff +++ b/tests/mir-opt/matches_u8.exhaustive_match_i8.MatchBranchSimplification.diff @@ -5,32 +5,27 @@ debug e => _1; let mut _0: i8; let mut _2: isize; -+ let mut _3: isize; bb0: { _2 = discriminant(_1); -- switchInt(move _2) -> [0: bb3, 1: bb2, otherwise: bb1]; -- } -- -- bb1: { -- unreachable; -- } -- -- bb2: { -- _0 = const 1_i8; -- goto -> bb4; -- } -- -- bb3: { -- _0 = const 0_i8; -- goto -> bb4; -- } -- -- bb4: { -+ StorageLive(_3); -+ _3 = move _2; -+ _0 = _3 as i8 (IntToInt); -+ StorageDead(_3); + switchInt(move _2) -> [0: bb3, 1: bb2, otherwise: bb1]; + } + + bb1: { + unreachable; + } + + bb2: { + _0 = const 1_i8; + goto -> bb4; + } + + bb3: { + _0 = const 0_i8; + goto -> bb4; + } + + bb4: { return; } } diff --git a/tests/mir-opt/matches_u8.rs b/tests/mir-opt/matches_u8.rs index e855c913226a..f0be82d02571 100644 --- a/tests/mir-opt/matches_u8.rs +++ b/tests/mir-opt/matches_u8.rs @@ -1,5 +1,5 @@ // skip-filecheck -//@ unit-test: MatchBranchSimplification +//@ test-mir-pass: MatchBranchSimplification // EMIT_MIR matches_u8.exhaustive_match.MatchBranchSimplification.diff diff --git a/tests/mir-opt/nrvo_miscompile_111005.rs b/tests/mir-opt/nrvo_miscompile_111005.rs index 3087c98d0528..18814b0678fe 100644 --- a/tests/mir-opt/nrvo_miscompile_111005.rs +++ b/tests/mir-opt/nrvo_miscompile_111005.rs @@ -1,7 +1,7 @@ // skip-filecheck // This is a miscompilation, #111005 to track -//@ unit-test: RenameReturnPlace +//@ test-mir-pass: RenameReturnPlace #![feature(custom_mir, core_intrinsics)] extern crate core; diff --git a/tests/mir-opt/nrvo_simple.rs b/tests/mir-opt/nrvo_simple.rs index adb787a09fb6..5d2894a704a5 100644 --- a/tests/mir-opt/nrvo_simple.rs +++ b/tests/mir-opt/nrvo_simple.rs @@ -1,6 +1,6 @@ // skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: RenameReturnPlace +//@ test-mir-pass: RenameReturnPlace // EMIT_MIR nrvo_simple.nrvo.RenameReturnPlace.diff fn nrvo(init: fn(&mut [u8; 1024])) -> [u8; 1024] { diff --git a/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.panic-abort.mir index 845673601b2f..af0a0efc2a66 100644 --- a/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.panic-abort.mir @@ -5,60 +5,33 @@ fn checked_shl(_1: u32, _2: u32) -> Option { debug rhs => _2; let mut _0: std::option::Option; scope 1 (inlined core::num::::checked_shl) { - debug self => _1; - debug rhs => _2; - let mut _6: bool; - scope 2 { - debug a => _4; - debug b => _5; - } - scope 3 (inlined core::num::::overflowing_shl) { - debug self => _1; - debug rhs => _2; - let mut _4: u32; - let mut _5: bool; - scope 4 (inlined core::num::::wrapping_shl) { - debug self => _1; - debug rhs => _2; - let mut _3: u32; - scope 5 (inlined core::num::::unchecked_shl) { - debug self => _1; - debug rhs => _3; - } - } + let mut _3: bool; + let mut _4: u32; + scope 2 (inlined core::num::::unchecked_shl) { } } bb0: { - StorageLive(_4); - StorageLive(_5); StorageLive(_3); - _3 = BitAnd(_2, const 31_u32); - _4 = ShlUnchecked(_1, _3); - StorageDead(_3); - _5 = Ge(_2, const core::num::::BITS); - StorageLive(_6); - _6 = unlikely(move _5) -> [return: bb1, unwind unreachable]; + _3 = Lt(_2, const core::num::::BITS); + switchInt(move _3) -> [0: bb1, otherwise: bb2]; } bb1: { - switchInt(move _6) -> [0: bb2, otherwise: bb3]; + _0 = const Option::::None; + goto -> bb3; } bb2: { - _0 = Option::::Some(_4); - goto -> bb4; + StorageLive(_4); + _4 = ShlUnchecked(_1, _2); + _0 = Option::::Some(move _4); + StorageDead(_4); + goto -> bb3; } bb3: { - _0 = const Option::::None; - goto -> bb4; - } - - bb4: { - StorageDead(_6); - StorageDead(_5); - StorageDead(_4); + StorageDead(_3); return; } } diff --git a/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.panic-unwind.mir index 845673601b2f..af0a0efc2a66 100644 --- a/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/checked_ops.checked_shl.PreCodegen.after.panic-unwind.mir @@ -5,60 +5,33 @@ fn checked_shl(_1: u32, _2: u32) -> Option { debug rhs => _2; let mut _0: std::option::Option; scope 1 (inlined core::num::::checked_shl) { - debug self => _1; - debug rhs => _2; - let mut _6: bool; - scope 2 { - debug a => _4; - debug b => _5; - } - scope 3 (inlined core::num::::overflowing_shl) { - debug self => _1; - debug rhs => _2; - let mut _4: u32; - let mut _5: bool; - scope 4 (inlined core::num::::wrapping_shl) { - debug self => _1; - debug rhs => _2; - let mut _3: u32; - scope 5 (inlined core::num::::unchecked_shl) { - debug self => _1; - debug rhs => _3; - } - } + let mut _3: bool; + let mut _4: u32; + scope 2 (inlined core::num::::unchecked_shl) { } } bb0: { - StorageLive(_4); - StorageLive(_5); StorageLive(_3); - _3 = BitAnd(_2, const 31_u32); - _4 = ShlUnchecked(_1, _3); - StorageDead(_3); - _5 = Ge(_2, const core::num::::BITS); - StorageLive(_6); - _6 = unlikely(move _5) -> [return: bb1, unwind unreachable]; + _3 = Lt(_2, const core::num::::BITS); + switchInt(move _3) -> [0: bb1, otherwise: bb2]; } bb1: { - switchInt(move _6) -> [0: bb2, otherwise: bb3]; + _0 = const Option::::None; + goto -> bb3; } bb2: { - _0 = Option::::Some(_4); - goto -> bb4; + StorageLive(_4); + _4 = ShlUnchecked(_1, _2); + _0 = Option::::Some(move _4); + StorageDead(_4); + goto -> bb3; } bb3: { - _0 = const Option::::None; - goto -> bb4; - } - - bb4: { - StorageDead(_6); - StorageDead(_5); - StorageDead(_4); + StorageDead(_3); return; } } diff --git a/tests/mir-opt/pre-codegen/checked_ops.rs b/tests/mir-opt/pre-codegen/checked_ops.rs index 8dd5c4b495ec..56f8e3f83384 100644 --- a/tests/mir-opt/pre-codegen/checked_ops.rs +++ b/tests/mir-opt/pre-codegen/checked_ops.rs @@ -1,5 +1,5 @@ // skip-filecheck -//@ compile-flags: -O -Zmir-opt-level=2 -Cdebuginfo=2 +//@ compile-flags: -O -Zmir-opt-level=2 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY #![crate_type = "lib"] diff --git a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-abort.mir new file mode 100644 index 000000000000..db0c84bd560f --- /dev/null +++ b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-abort.mir @@ -0,0 +1,44 @@ +// MIR for `demo_byte_add_fat` after PreCodegen + +fn demo_byte_add_fat(_1: *const [u32], _2: usize) -> *const [u32] { + debug p => _1; + debug n => _2; + let mut _0: *const [u32]; + scope 1 (inlined std::ptr::const_ptr::::byte_add) { + let mut _3: *const u8; + let mut _4: *const u8; + scope 2 (inlined std::ptr::const_ptr::::cast::) { + } + scope 3 (inlined std::ptr::const_ptr::::add) { + } + scope 4 (inlined std::ptr::const_ptr::::with_metadata_of::<[u32]>) { + let mut _5: *const (); + let mut _7: usize; + scope 5 (inlined std::ptr::metadata::<[u32]>) { + let mut _6: std::ptr::metadata::PtrRepr<[u32]>; + } + scope 6 (inlined std::ptr::from_raw_parts::<[u32]>) { + } + } + } + + bb0: { + StorageLive(_4); + StorageLive(_3); + _3 = _1 as *const u8 (PtrToPtr); + _4 = Offset(_3, _2); + StorageDead(_3); + StorageLive(_5); + _5 = _4 as *const () (PtrToPtr); + StorageLive(_7); + StorageLive(_6); + _6 = std::ptr::metadata::PtrRepr::<[u32]> { const_ptr: _1 }; + _7 = ((_6.2: std::ptr::metadata::PtrComponents<[u32]>).1: usize); + StorageDead(_6); + _0 = *const [u32] from (_5, _7); + StorageDead(_7); + StorageDead(_5); + StorageDead(_4); + return; + } +} diff --git a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-unwind.mir new file mode 100644 index 000000000000..db0c84bd560f --- /dev/null +++ b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-unwind.mir @@ -0,0 +1,44 @@ +// MIR for `demo_byte_add_fat` after PreCodegen + +fn demo_byte_add_fat(_1: *const [u32], _2: usize) -> *const [u32] { + debug p => _1; + debug n => _2; + let mut _0: *const [u32]; + scope 1 (inlined std::ptr::const_ptr::::byte_add) { + let mut _3: *const u8; + let mut _4: *const u8; + scope 2 (inlined std::ptr::const_ptr::::cast::) { + } + scope 3 (inlined std::ptr::const_ptr::::add) { + } + scope 4 (inlined std::ptr::const_ptr::::with_metadata_of::<[u32]>) { + let mut _5: *const (); + let mut _7: usize; + scope 5 (inlined std::ptr::metadata::<[u32]>) { + let mut _6: std::ptr::metadata::PtrRepr<[u32]>; + } + scope 6 (inlined std::ptr::from_raw_parts::<[u32]>) { + } + } + } + + bb0: { + StorageLive(_4); + StorageLive(_3); + _3 = _1 as *const u8 (PtrToPtr); + _4 = Offset(_3, _2); + StorageDead(_3); + StorageLive(_5); + _5 = _4 as *const () (PtrToPtr); + StorageLive(_7); + StorageLive(_6); + _6 = std::ptr::metadata::PtrRepr::<[u32]> { const_ptr: _1 }; + _7 = ((_6.2: std::ptr::metadata::PtrComponents<[u32]>).1: usize); + StorageDead(_6); + _0 = *const [u32] from (_5, _7); + StorageDead(_7); + StorageDead(_5); + StorageDead(_4); + return; + } +} diff --git a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-abort.mir new file mode 100644 index 000000000000..766bd29ef41c --- /dev/null +++ b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-abort.mir @@ -0,0 +1,30 @@ +// MIR for `demo_byte_add_thin` after PreCodegen + +fn demo_byte_add_thin(_1: *const u32, _2: usize) -> *const u32 { + debug p => _1; + debug n => _2; + let mut _0: *const u32; + scope 1 (inlined std::ptr::const_ptr::::byte_add) { + let mut _3: *const u8; + let mut _4: *const u8; + scope 2 (inlined std::ptr::const_ptr::::cast::) { + } + scope 3 (inlined std::ptr::const_ptr::::add) { + } + scope 4 (inlined std::ptr::const_ptr::::with_metadata_of::) { + scope 5 (inlined std::ptr::metadata::) { + } + scope 6 (inlined std::ptr::from_raw_parts::) { + } + } + } + + bb0: { + StorageLive(_3); + _3 = _1 as *const u8 (PtrToPtr); + _4 = Offset(_3, _2); + StorageDead(_3); + _0 = _4 as *const u32 (PtrToPtr); + return; + } +} diff --git a/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-unwind.mir new file mode 100644 index 000000000000..766bd29ef41c --- /dev/null +++ b/tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-unwind.mir @@ -0,0 +1,30 @@ +// MIR for `demo_byte_add_thin` after PreCodegen + +fn demo_byte_add_thin(_1: *const u32, _2: usize) -> *const u32 { + debug p => _1; + debug n => _2; + let mut _0: *const u32; + scope 1 (inlined std::ptr::const_ptr::::byte_add) { + let mut _3: *const u8; + let mut _4: *const u8; + scope 2 (inlined std::ptr::const_ptr::::cast::) { + } + scope 3 (inlined std::ptr::const_ptr::::add) { + } + scope 4 (inlined std::ptr::const_ptr::::with_metadata_of::) { + scope 5 (inlined std::ptr::metadata::) { + } + scope 6 (inlined std::ptr::from_raw_parts::) { + } + } + } + + bb0: { + StorageLive(_3); + _3 = _1 as *const u8 (PtrToPtr); + _4 = Offset(_3, _2); + StorageDead(_3); + _0 = _4 as *const u32 (PtrToPtr); + return; + } +} diff --git a/tests/mir-opt/pre-codegen/ptr_offset.rs b/tests/mir-opt/pre-codegen/ptr_offset.rs new file mode 100644 index 000000000000..88ee00296a07 --- /dev/null +++ b/tests/mir-opt/pre-codegen/ptr_offset.rs @@ -0,0 +1,16 @@ +// skip-filecheck +//@ compile-flags: -O -C debuginfo=0 -Zmir-opt-level=2 -Zinline-mir +//@ ignore-debug: precondition checks are under cfg(debug_assertions) +// EMIT_MIR_FOR_EACH_PANIC_STRATEGY + +#![crate_type = "lib"] + +// EMIT_MIR ptr_offset.demo_byte_add_thin.PreCodegen.after.mir +pub unsafe fn demo_byte_add_thin(p: *const u32, n: usize) -> *const u32 { + p.byte_add(n) +} + +// EMIT_MIR ptr_offset.demo_byte_add_fat.PreCodegen.after.mir +pub unsafe fn demo_byte_add_fat(p: *const [u32], n: usize) -> *const [u32] { + p.byte_add(n) +} diff --git a/tests/mir-opt/pre-codegen/slice_index.rs b/tests/mir-opt/pre-codegen/slice_index.rs index c9dd72d8be2a..04bbbff57b34 100644 --- a/tests/mir-opt/pre-codegen/slice_index.rs +++ b/tests/mir-opt/pre-codegen/slice_index.rs @@ -3,6 +3,7 @@ // EMIT_MIR_FOR_EACH_PANIC_STRATEGY #![crate_type = "lib"] +#![feature(slice_ptr_get)] use std::ops::Range; @@ -25,3 +26,11 @@ pub fn slice_index_range(slice: &[u32], index: Range) -> &[u32] { pub unsafe fn slice_get_unchecked_mut_range(slice: &mut [u32], index: Range) -> &mut [u32] { slice.get_unchecked_mut(index) } + +// EMIT_MIR slice_index.slice_ptr_get_unchecked_range.PreCodegen.after.mir +pub unsafe fn slice_ptr_get_unchecked_range( + slice: *const [u32], + index: Range, +) -> *const [u32] { + slice.get_unchecked(index) +} diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_ptr_get_unchecked_range.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_index.slice_ptr_get_unchecked_range.PreCodegen.after.panic-abort.mir new file mode 100644 index 000000000000..018ff6c357d1 --- /dev/null +++ b/tests/mir-opt/pre-codegen/slice_index.slice_ptr_get_unchecked_range.PreCodegen.after.panic-abort.mir @@ -0,0 +1,52 @@ +// MIR for `slice_ptr_get_unchecked_range` after PreCodegen + +fn slice_ptr_get_unchecked_range(_1: *const [u32], _2: std::ops::Range) -> *const [u32] { + debug slice => _1; + debug index => _2; + let mut _0: *const [u32]; + let mut _3: usize; + let mut _4: usize; + scope 1 (inlined std::ptr::const_ptr::::get_unchecked::>) { + scope 2 (inlined as SliceIndex<[u32]>>::get_unchecked) { + let _5: usize; + let mut _6: *const u32; + let mut _7: *const u32; + scope 3 { + scope 6 (inlined std::ptr::const_ptr::::as_ptr) { + } + scope 7 (inlined std::ptr::const_ptr::::add) { + } + scope 8 (inlined slice_from_raw_parts::) { + let mut _8: *const (); + scope 9 (inlined std::ptr::const_ptr::::cast::<()>) { + } + scope 10 (inlined std::ptr::from_raw_parts::<[u32]>) { + } + } + } + scope 4 (inlined std::ptr::const_ptr::::len) { + scope 5 (inlined std::ptr::metadata::<[u32]>) { + } + } + } + } + + bb0: { + _3 = move (_2.0: usize); + _4 = move (_2.1: usize); + StorageLive(_5); + _5 = SubUnchecked(_4, _3); + StorageLive(_7); + StorageLive(_6); + _6 = _1 as *const u32 (PtrToPtr); + _7 = Offset(_6, _3); + StorageDead(_6); + StorageLive(_8); + _8 = _7 as *const () (PtrToPtr); + _0 = *const [u32] from (_8, _5); + StorageDead(_8); + StorageDead(_7); + StorageDead(_5); + return; + } +} diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_ptr_get_unchecked_range.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_index.slice_ptr_get_unchecked_range.PreCodegen.after.panic-unwind.mir new file mode 100644 index 000000000000..018ff6c357d1 --- /dev/null +++ b/tests/mir-opt/pre-codegen/slice_index.slice_ptr_get_unchecked_range.PreCodegen.after.panic-unwind.mir @@ -0,0 +1,52 @@ +// MIR for `slice_ptr_get_unchecked_range` after PreCodegen + +fn slice_ptr_get_unchecked_range(_1: *const [u32], _2: std::ops::Range) -> *const [u32] { + debug slice => _1; + debug index => _2; + let mut _0: *const [u32]; + let mut _3: usize; + let mut _4: usize; + scope 1 (inlined std::ptr::const_ptr::::get_unchecked::>) { + scope 2 (inlined as SliceIndex<[u32]>>::get_unchecked) { + let _5: usize; + let mut _6: *const u32; + let mut _7: *const u32; + scope 3 { + scope 6 (inlined std::ptr::const_ptr::::as_ptr) { + } + scope 7 (inlined std::ptr::const_ptr::::add) { + } + scope 8 (inlined slice_from_raw_parts::) { + let mut _8: *const (); + scope 9 (inlined std::ptr::const_ptr::::cast::<()>) { + } + scope 10 (inlined std::ptr::from_raw_parts::<[u32]>) { + } + } + } + scope 4 (inlined std::ptr::const_ptr::::len) { + scope 5 (inlined std::ptr::metadata::<[u32]>) { + } + } + } + } + + bb0: { + _3 = move (_2.0: usize); + _4 = move (_2.1: usize); + StorageLive(_5); + _5 = SubUnchecked(_4, _3); + StorageLive(_7); + StorageLive(_6); + _6 = _1 as *const u32 (PtrToPtr); + _7 = Offset(_6, _3); + StorageDead(_6); + StorageLive(_8); + _8 = _7 as *const () (PtrToPtr); + _0 = *const [u32] from (_8, _5); + StorageDead(_8); + StorageDead(_7); + StorageDead(_5); + return; + } +} diff --git a/tests/mir-opt/pre-codegen/vec_deref.rs b/tests/mir-opt/pre-codegen/vec_deref.rs new file mode 100644 index 000000000000..3476e1760c0a --- /dev/null +++ b/tests/mir-opt/pre-codegen/vec_deref.rs @@ -0,0 +1,13 @@ +// skip-filecheck +//@ compile-flags: -O -Zmir-opt-level=2 -Cdebuginfo=2 +// EMIT_MIR_FOR_EACH_PANIC_STRATEGY + +#![crate_type = "lib"] + +// Added after it stopped inlining in a nightly; see +// + +// EMIT_MIR vec_deref.vec_deref_to_slice.PreCodegen.after.mir +pub fn vec_deref_to_slice(v: &Vec) -> &[u8] { + v +} diff --git a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir new file mode 100644 index 000000000000..18728d543ad8 --- /dev/null +++ b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir @@ -0,0 +1,72 @@ +// MIR for `vec_deref_to_slice` after PreCodegen + +fn vec_deref_to_slice(_1: &Vec) -> &[u8] { + debug v => _1; + let mut _0: &[u8]; + scope 1 (inlined as Deref>::deref) { + debug self => _1; + let mut _4: *const u8; + let mut _5: usize; + scope 2 (inlined Vec::::as_ptr) { + debug self => _1; + let mut _2: &alloc::raw_vec::RawVec; + scope 3 (inlined alloc::raw_vec::RawVec::::ptr) { + debug self => _2; + let mut _3: std::ptr::NonNull; + scope 4 (inlined Unique::::as_ptr) { + debug ((self: Unique).0: std::ptr::NonNull) => _3; + debug ((self: Unique).1: std::marker::PhantomData) => const PhantomData::; + scope 5 (inlined NonNull::::as_ptr) { + debug self => _3; + } + } + } + } + scope 6 (inlined std::slice::from_raw_parts::<'_, u8>) { + debug data => _4; + debug len => _5; + let _7: *const [u8]; + scope 7 (inlined core::ub_checks::check_language_ub) { + scope 8 (inlined core::ub_checks::check_language_ub::runtime) { + } + } + scope 9 (inlined std::mem::size_of::) { + } + scope 10 (inlined align_of::) { + } + scope 11 (inlined slice_from_raw_parts::) { + debug data => _4; + debug len => _5; + let mut _6: *const (); + scope 12 (inlined std::ptr::const_ptr::::cast::<()>) { + debug self => _4; + } + scope 13 (inlined std::ptr::from_raw_parts::<[u8]>) { + debug data_pointer => _6; + debug metadata => _5; + } + } + } + } + + bb0: { + StorageLive(_4); + StorageLive(_2); + _2 = &((*_1).0: alloc::raw_vec::RawVec); + StorageLive(_3); + _3 = ((((*_1).0: alloc::raw_vec::RawVec).0: std::ptr::Unique).0: std::ptr::NonNull); + _4 = (_3.0: *const u8); + StorageDead(_3); + StorageDead(_2); + StorageLive(_5); + _5 = ((*_1).1: usize); + StorageLive(_6); + _6 = _4 as *const () (PtrToPtr); + _7 = *const [u8] from (_6, _5); + StorageDead(_6); + StorageDead(_5); + StorageDead(_4); + _0 = &(*_7); + return; + } +} diff --git a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir new file mode 100644 index 000000000000..18728d543ad8 --- /dev/null +++ b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir @@ -0,0 +1,72 @@ +// MIR for `vec_deref_to_slice` after PreCodegen + +fn vec_deref_to_slice(_1: &Vec) -> &[u8] { + debug v => _1; + let mut _0: &[u8]; + scope 1 (inlined as Deref>::deref) { + debug self => _1; + let mut _4: *const u8; + let mut _5: usize; + scope 2 (inlined Vec::::as_ptr) { + debug self => _1; + let mut _2: &alloc::raw_vec::RawVec; + scope 3 (inlined alloc::raw_vec::RawVec::::ptr) { + debug self => _2; + let mut _3: std::ptr::NonNull; + scope 4 (inlined Unique::::as_ptr) { + debug ((self: Unique).0: std::ptr::NonNull) => _3; + debug ((self: Unique).1: std::marker::PhantomData) => const PhantomData::; + scope 5 (inlined NonNull::::as_ptr) { + debug self => _3; + } + } + } + } + scope 6 (inlined std::slice::from_raw_parts::<'_, u8>) { + debug data => _4; + debug len => _5; + let _7: *const [u8]; + scope 7 (inlined core::ub_checks::check_language_ub) { + scope 8 (inlined core::ub_checks::check_language_ub::runtime) { + } + } + scope 9 (inlined std::mem::size_of::) { + } + scope 10 (inlined align_of::) { + } + scope 11 (inlined slice_from_raw_parts::) { + debug data => _4; + debug len => _5; + let mut _6: *const (); + scope 12 (inlined std::ptr::const_ptr::::cast::<()>) { + debug self => _4; + } + scope 13 (inlined std::ptr::from_raw_parts::<[u8]>) { + debug data_pointer => _6; + debug metadata => _5; + } + } + } + } + + bb0: { + StorageLive(_4); + StorageLive(_2); + _2 = &((*_1).0: alloc::raw_vec::RawVec); + StorageLive(_3); + _3 = ((((*_1).0: alloc::raw_vec::RawVec).0: std::ptr::Unique).0: std::ptr::NonNull); + _4 = (_3.0: *const u8); + StorageDead(_3); + StorageDead(_2); + StorageLive(_5); + _5 = ((*_1).1: usize); + StorageLive(_6); + _6 = _4 as *const () (PtrToPtr); + _7 = *const [u8] from (_6, _5); + StorageDead(_6); + StorageDead(_5); + StorageDead(_4); + _0 = &(*_7); + return; + } +} diff --git a/tests/mir-opt/reference_prop.rs b/tests/mir-opt/reference_prop.rs index 70587dff0b54..2dda771ba7d7 100644 --- a/tests/mir-opt/reference_prop.rs +++ b/tests/mir-opt/reference_prop.rs @@ -1,5 +1,5 @@ //@ compile-flags: -Zlint-mir=no -//@ unit-test: ReferencePropagation +//@ test-mir-pass: ReferencePropagation //@ needs-unwind #![feature(raw_ref_op)] diff --git a/tests/mir-opt/remove_storage_markers.rs b/tests/mir-opt/remove_storage_markers.rs index c53c38750452..4a928b774527 100644 --- a/tests/mir-opt/remove_storage_markers.rs +++ b/tests/mir-opt/remove_storage_markers.rs @@ -1,5 +1,5 @@ // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: RemoveStorageMarkers +//@ test-mir-pass: RemoveStorageMarkers // Checks that storage markers are removed at opt-level=0. // diff --git a/tests/mir-opt/retag.rs b/tests/mir-opt/retag.rs index 17b3c10abeb1..43d74aa5726f 100644 --- a/tests/mir-opt/retag.rs +++ b/tests/mir-opt/retag.rs @@ -1,5 +1,5 @@ // skip-filecheck -//@ unit-test: AddRetag +//@ test-mir-pass: AddRetag // EMIT_MIR_FOR_EACH_PANIC_STRATEGY // ignore-tidy-linelength //@ compile-flags: -Z mir-emit-retag -Z mir-opt-level=0 -Z span_free_formats diff --git a/tests/mir-opt/set_no_discriminant.rs b/tests/mir-opt/set_no_discriminant.rs index 995bd11a1a92..0c29d1faf02a 100644 --- a/tests/mir-opt/set_no_discriminant.rs +++ b/tests/mir-opt/set_no_discriminant.rs @@ -1,6 +1,6 @@ // `SetDiscriminant` does not actually write anything if the chosen variant is the untagged variant // of a niche encoding. Verify that we do not thread over this case. -//@ unit-test: JumpThreading +//@ test-mir-pass: JumpThreading #![feature(custom_mir)] #![feature(core_intrinsics)] diff --git a/tests/mir-opt/simplify_dead_blocks.rs b/tests/mir-opt/simplify_dead_blocks.rs index d4de85622d41..686eac582364 100644 --- a/tests/mir-opt/simplify_dead_blocks.rs +++ b/tests/mir-opt/simplify_dead_blocks.rs @@ -1,4 +1,4 @@ -//@ unit-test: SimplifyCfg-after-unreachable-enum-branching +//@ test-mir-pass: SimplifyCfg-after-unreachable-enum-branching #![feature(custom_mir, core_intrinsics)] #![crate_type = "lib"] diff --git a/tests/mir-opt/simplify_locals.rs b/tests/mir-opt/simplify_locals.rs index 756679e77e39..f57611111cfc 100644 --- a/tests/mir-opt/simplify_locals.rs +++ b/tests/mir-opt/simplify_locals.rs @@ -1,5 +1,5 @@ // skip-filecheck -//@ unit-test: SimplifyLocals-before-const-prop +//@ test-mir-pass: SimplifyLocals-before-const-prop #![feature(thread_local)] diff --git a/tests/mir-opt/simplify_locals_removes_unused_consts.rs b/tests/mir-opt/simplify_locals_removes_unused_consts.rs index 3a461647e362..70d1555e786b 100644 --- a/tests/mir-opt/simplify_locals_removes_unused_consts.rs +++ b/tests/mir-opt/simplify_locals_removes_unused_consts.rs @@ -1,6 +1,6 @@ // skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ unit-test: SimplifyLocals-before-const-prop +//@ test-mir-pass: SimplifyLocals-before-const-prop //@ compile-flags: -C overflow-checks=no fn use_zst(_: ((), ())) {} diff --git a/tests/mir-opt/simplify_locals_removes_unused_discriminant_reads.rs b/tests/mir-opt/simplify_locals_removes_unused_discriminant_reads.rs index 52afb4f2c527..6257f5ee795a 100644 --- a/tests/mir-opt/simplify_locals_removes_unused_discriminant_reads.rs +++ b/tests/mir-opt/simplify_locals_removes_unused_discriminant_reads.rs @@ -1,5 +1,5 @@ // skip-filecheck -//@ unit-test: SimplifyLocals-before-const-prop +//@ test-mir-pass: SimplifyLocals-before-const-prop fn map(x: Option>) -> Option> { match x { diff --git a/tests/mir-opt/sroa/lifetimes.rs b/tests/mir-opt/sroa/lifetimes.rs index 3f5c99404d86..6c18dbaf5a23 100644 --- a/tests/mir-opt/sroa/lifetimes.rs +++ b/tests/mir-opt/sroa/lifetimes.rs @@ -1,4 +1,4 @@ -//@ unit-test: ScalarReplacementOfAggregates +//@ test-mir-pass: ScalarReplacementOfAggregates //@ compile-flags: -Cpanic=abort //@ no-prefer-dynamic diff --git a/tests/mir-opt/sroa/structs.rs b/tests/mir-opt/sroa/structs.rs index cbe4b9895303..a177dbf71cf1 100644 --- a/tests/mir-opt/sroa/structs.rs +++ b/tests/mir-opt/sroa/structs.rs @@ -1,4 +1,4 @@ -//@ unit-test: ScalarReplacementOfAggregates +//@ test-mir-pass: ScalarReplacementOfAggregates //@ compile-flags: -Cpanic=abort //@ no-prefer-dynamic diff --git a/tests/mir-opt/unreachable.rs b/tests/mir-opt/unreachable.rs index b07b8230faf3..5838b35a553a 100644 --- a/tests/mir-opt/unreachable.rs +++ b/tests/mir-opt/unreachable.rs @@ -1,4 +1,4 @@ -//@ unit-test: UnreachablePropagation +//@ test-mir-pass: UnreachablePropagation // EMIT_MIR_FOR_EACH_PANIC_STRATEGY enum Empty {} diff --git a/tests/mir-opt/unreachable_diverging.rs b/tests/mir-opt/unreachable_diverging.rs index b7e0f6eff9bf..695e3c4e8fc8 100644 --- a/tests/mir-opt/unreachable_diverging.rs +++ b/tests/mir-opt/unreachable_diverging.rs @@ -1,4 +1,4 @@ -//@ unit-test: UnreachablePropagation +//@ test-mir-pass: UnreachablePropagation // EMIT_MIR_FOR_EACH_PANIC_STRATEGY pub enum Empty {} diff --git a/tests/mir-opt/unreachable_enum_branching.rs b/tests/mir-opt/unreachable_enum_branching.rs index 156b23657b7a..6005dc546dc8 100644 --- a/tests/mir-opt/unreachable_enum_branching.rs +++ b/tests/mir-opt/unreachable_enum_branching.rs @@ -1,4 +1,4 @@ -//@ unit-test: UnreachableEnumBranching +//@ test-mir-pass: UnreachableEnumBranching // EMIT_MIR_FOR_EACH_PANIC_STRATEGY enum Empty {} diff --git a/tests/run-make/non-unicode-in-incremental-dir/foo.rs b/tests/run-make/non-unicode-in-incremental-dir/foo.rs new file mode 100644 index 000000000000..f328e4d9d04c --- /dev/null +++ b/tests/run-make/non-unicode-in-incremental-dir/foo.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/tests/run-make/non-unicode-in-incremental-dir/rmake.rs b/tests/run-make/non-unicode-in-incremental-dir/rmake.rs new file mode 100644 index 000000000000..129e424f27a4 --- /dev/null +++ b/tests/run-make/non-unicode-in-incremental-dir/rmake.rs @@ -0,0 +1,26 @@ +extern crate run_make_support; + +use run_make_support::{rustc, tmp_dir}; + +fn main() { + #[cfg(unix)] + let non_unicode: &std::ffi::OsStr = std::os::unix::ffi::OsStrExt::from_bytes(&[0xFF]); + #[cfg(windows)] + let non_unicode: std::ffi::OsString = std::os::windows::ffi::OsStringExt::from_wide(&[0xD800]); + match std::fs::create_dir(tmp_dir().join(&non_unicode)) { + // If an error occurs, check if creating a directory with a valid Unicode name would + // succeed. + Err(e) if std::fs::create_dir(tmp_dir().join("valid_unicode")).is_ok() => { + // Filesystem doesn't appear support non-Unicode paths. + return; + } + Err(e) => panic!("error creating non-Unicode directory: {e}"), + _ => {} + } + let incr_dir = tmp_dir().join("incr-dir"); + rustc().input("foo.rs").incremental(&incr_dir).run(); + for crate_dir in std::fs::read_dir(&incr_dir).unwrap() { + std::fs::create_dir(crate_dir.unwrap().path().join(&non_unicode)).unwrap(); + } + rustc().input("foo.rs").incremental(&incr_dir).run(); +} diff --git a/tests/ui/abi/compatibility.rs b/tests/ui/abi/compatibility.rs index 3ee4542810c6..373d1cce1d73 100644 --- a/tests/ui/abi/compatibility.rs +++ b/tests/ui/abi/compatibility.rs @@ -64,7 +64,6 @@ [csky] needs-llvm-components: csky */ #![feature(rustc_attrs, unsized_fn_params, transparent_unions)] -#![cfg_attr(host, feature(generic_nonzero))] #![cfg_attr(not(host), feature(no_core, lang_items), no_std, no_core)] #![allow(unused, improper_ctypes_definitions, internal_features)] diff --git a/tests/ui/issues/issue-19129-1.rs b/tests/ui/associated-types/issue-19129-1.rs similarity index 100% rename from tests/ui/issues/issue-19129-1.rs rename to tests/ui/associated-types/issue-19129-1.rs diff --git a/tests/ui/issues/issue-19129-2.rs b/tests/ui/associated-types/issue-19129-2.rs similarity index 100% rename from tests/ui/issues/issue-19129-2.rs rename to tests/ui/associated-types/issue-19129-2.rs diff --git a/tests/ui/issues/issue-20763-1.rs b/tests/ui/associated-types/issue-20763-1.rs similarity index 100% rename from tests/ui/issues/issue-20763-1.rs rename to tests/ui/associated-types/issue-20763-1.rs diff --git a/tests/ui/issues/issue-20763-2.rs b/tests/ui/associated-types/issue-20763-2.rs similarity index 100% rename from tests/ui/issues/issue-20763-2.rs rename to tests/ui/associated-types/issue-20763-2.rs diff --git a/tests/ui/async-await/async-drop.rs b/tests/ui/async-await/async-drop.rs new file mode 100644 index 000000000000..6d02dcebc0b6 --- /dev/null +++ b/tests/ui/async-await/async-drop.rs @@ -0,0 +1,197 @@ +//@ run-pass +//@ check-run-results + +#![feature(async_drop, impl_trait_in_assoc_type, noop_waker, async_closure)] +#![allow(incomplete_features, dead_code)] + +//@ edition: 2021 + +// FIXME(zetanumbers): consider AsyncDestruct::async_drop cleanup tests +use core::future::{async_drop_in_place, AsyncDrop, Future}; +use core::hint::black_box; +use core::mem::{self, ManuallyDrop}; +use core::pin::{pin, Pin}; +use core::task::{Context, Poll, Waker}; + +async fn test_async_drop(x: T) { + let mut x = mem::MaybeUninit::new(x); + let dtor = pin!(unsafe { async_drop_in_place(x.as_mut_ptr()) }); + test_idempotency(dtor).await; +} + +fn test_idempotency(mut x: Pin<&mut T>) -> impl Future + '_ +where + T: Future, +{ + core::future::poll_fn(move |cx| { + assert_eq!(x.as_mut().poll(cx), Poll::Ready(())); + assert_eq!(x.as_mut().poll(cx), Poll::Ready(())); + Poll::Ready(()) + }) +} + +fn main() { + let waker = Waker::noop(); + let mut cx = Context::from_waker(&waker); + + let i = 13; + let fut = pin!(async { + test_async_drop(Int(0)).await; + test_async_drop(AsyncInt(0)).await; + test_async_drop([AsyncInt(1), AsyncInt(2)]).await; + test_async_drop((AsyncInt(3), AsyncInt(4))).await; + test_async_drop(5).await; + let j = 42; + test_async_drop(&i).await; + test_async_drop(&j).await; + test_async_drop(AsyncStruct { b: AsyncInt(8), a: AsyncInt(7), i: 6 }).await; + test_async_drop(ManuallyDrop::new(AsyncInt(9))).await; + + let foo = AsyncInt(10); + test_async_drop(AsyncReference { foo: &foo }).await; + + let foo = AsyncInt(11); + test_async_drop(|| { + black_box(foo); + let foo = AsyncInt(10); + foo + }).await; + + test_async_drop(AsyncEnum::A(AsyncInt(12))).await; + test_async_drop(AsyncEnum::B(SyncInt(13))).await; + + test_async_drop(SyncInt(14)).await; + test_async_drop(SyncThenAsync { + i: 15, + a: AsyncInt(16), + b: SyncInt(17), + c: AsyncInt(18), + }).await; + + let async_drop_fut = pin!(core::future::async_drop(AsyncInt(19))); + test_idempotency(async_drop_fut).await; + + let foo = AsyncInt(20); + test_async_drop(async || { + black_box(foo); + let foo = AsyncInt(19); + // Await point there, but this is async closure so it's fine + black_box(core::future::ready(())).await; + foo + }).await; + + test_async_drop(AsyncUnion { signed: 21 }).await; + }); + let res = fut.poll(&mut cx); + assert_eq!(res, Poll::Ready(())); +} + +struct AsyncInt(i32); + +impl AsyncDrop for AsyncInt { + type Dropper<'a> = impl Future; + + fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> { + async move { + println!("AsyncInt::Dropper::poll: {}", self.0); + } + } +} + +struct SyncInt(i32); + +impl Drop for SyncInt { + fn drop(&mut self) { + println!("SyncInt::drop: {}", self.0); + } +} + +struct SyncThenAsync { + i: i32, + a: AsyncInt, + b: SyncInt, + c: AsyncInt, +} + +impl Drop for SyncThenAsync { + fn drop(&mut self) { + println!("SyncThenAsync::drop: {}", self.i); + } +} + +struct AsyncReference<'a> { + foo: &'a AsyncInt, +} + +impl AsyncDrop for AsyncReference<'_> { + type Dropper<'a> = impl Future where Self: 'a; + + fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> { + async move { + println!("AsyncReference::Dropper::poll: {}", self.foo.0); + } + } +} + +struct Int(i32); + +struct AsyncStruct { + i: i32, + a: AsyncInt, + b: AsyncInt, +} + +impl AsyncDrop for AsyncStruct { + type Dropper<'a> = impl Future; + + fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> { + async move { + println!("AsyncStruct::Dropper::poll: {}", self.i); + } + } +} + +enum AsyncEnum { + A(AsyncInt), + B(SyncInt), +} + +impl AsyncDrop for AsyncEnum { + type Dropper<'a> = impl Future; + + fn async_drop(mut self: Pin<&mut Self>) -> Self::Dropper<'_> { + async move { + let new_self = match &*self { + AsyncEnum::A(foo) => { + println!("AsyncEnum(A)::Dropper::poll: {}", foo.0); + AsyncEnum::B(SyncInt(foo.0)) + } + AsyncEnum::B(foo) => { + println!("AsyncEnum(B)::Dropper::poll: {}", foo.0); + AsyncEnum::A(AsyncInt(foo.0)) + } + }; + mem::forget(mem::replace(&mut *self, new_self)); + } + } +} + +// FIXME(zetanumbers): Disallow types with `AsyncDrop` in unions +union AsyncUnion { + signed: i32, + unsigned: u32, +} + +impl AsyncDrop for AsyncUnion { + type Dropper<'a> = impl Future; + + fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> { + async move { + println!( + "AsyncUnion::Dropper::poll: {}, {}", + unsafe { self.signed }, + unsafe { self.unsigned }, + ); + } + } +} diff --git a/tests/ui/async-await/async-drop.run.stdout b/tests/ui/async-await/async-drop.run.stdout new file mode 100644 index 000000000000..9cae4331caf9 --- /dev/null +++ b/tests/ui/async-await/async-drop.run.stdout @@ -0,0 +1,22 @@ +AsyncInt::Dropper::poll: 0 +AsyncInt::Dropper::poll: 1 +AsyncInt::Dropper::poll: 2 +AsyncInt::Dropper::poll: 3 +AsyncInt::Dropper::poll: 4 +AsyncStruct::Dropper::poll: 6 +AsyncInt::Dropper::poll: 7 +AsyncInt::Dropper::poll: 8 +AsyncReference::Dropper::poll: 10 +AsyncInt::Dropper::poll: 11 +AsyncEnum(A)::Dropper::poll: 12 +SyncInt::drop: 12 +AsyncEnum(B)::Dropper::poll: 13 +AsyncInt::Dropper::poll: 13 +SyncInt::drop: 14 +SyncThenAsync::drop: 15 +AsyncInt::Dropper::poll: 16 +SyncInt::drop: 17 +AsyncInt::Dropper::poll: 18 +AsyncInt::Dropper::poll: 19 +AsyncInt::Dropper::poll: 20 +AsyncUnion::Dropper::poll: 21, 21 diff --git a/tests/ui/issues/issue-40402-ref-hints/issue-40402-1.rs b/tests/ui/binding/issue-40402-1.rs similarity index 100% rename from tests/ui/issues/issue-40402-ref-hints/issue-40402-1.rs rename to tests/ui/binding/issue-40402-1.rs diff --git a/tests/ui/issues/issue-40402-ref-hints/issue-40402-1.stderr b/tests/ui/binding/issue-40402-1.stderr similarity index 100% rename from tests/ui/issues/issue-40402-ref-hints/issue-40402-1.stderr rename to tests/ui/binding/issue-40402-1.stderr diff --git a/tests/ui/issues/issue-40402-ref-hints/issue-40402-2.rs b/tests/ui/binding/issue-40402-2.rs similarity index 100% rename from tests/ui/issues/issue-40402-ref-hints/issue-40402-2.rs rename to tests/ui/binding/issue-40402-2.rs diff --git a/tests/ui/issues/issue-40402-ref-hints/issue-40402-2.stderr b/tests/ui/binding/issue-40402-2.stderr similarity index 100% rename from tests/ui/issues/issue-40402-ref-hints/issue-40402-2.stderr rename to tests/ui/binding/issue-40402-2.stderr diff --git a/tests/ui/borrowck/non-ADT-struct-pattern-box-pattern-ice-121463.rs b/tests/ui/borrowck/non-ADT-struct-pattern-box-pattern-ice-121463.rs new file mode 100644 index 000000000000..cf927e34fb41 --- /dev/null +++ b/tests/ui/borrowck/non-ADT-struct-pattern-box-pattern-ice-121463.rs @@ -0,0 +1,12 @@ +// issue rust-lang/rust#121463 +// ICE non-ADT in struct pattern +#![feature(box_patterns)] + +fn main() { + let mut a = E::StructVar { boxed: Box::new(5_i32) }; + //~^ ERROR failed to resolve: use of undeclared type `E` + match a { + E::StructVar { box boxed } => { } + //~^ ERROR failed to resolve: use of undeclared type `E` + } +} diff --git a/tests/ui/borrowck/non-ADT-struct-pattern-box-pattern-ice-121463.stderr b/tests/ui/borrowck/non-ADT-struct-pattern-box-pattern-ice-121463.stderr new file mode 100644 index 000000000000..349546606a57 --- /dev/null +++ b/tests/ui/borrowck/non-ADT-struct-pattern-box-pattern-ice-121463.stderr @@ -0,0 +1,21 @@ +error[E0433]: failed to resolve: use of undeclared type `E` + --> $DIR/non-ADT-struct-pattern-box-pattern-ice-121463.rs:6:17 + | +LL | let mut a = E::StructVar { boxed: Box::new(5_i32) }; + | ^ + | | + | use of undeclared type `E` + | help: a trait with a similar name exists: `Eq` + +error[E0433]: failed to resolve: use of undeclared type `E` + --> $DIR/non-ADT-struct-pattern-box-pattern-ice-121463.rs:9:9 + | +LL | E::StructVar { box boxed } => { } + | ^ + | | + | use of undeclared type `E` + | help: a trait with a similar name exists: `Eq` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0433`. diff --git a/tests/ui/check-cfg/mix.stderr b/tests/ui/check-cfg/mix.stderr index feb4f21b0ca4..557fdcbf38db 100644 --- a/tests/ui/check-cfg/mix.stderr +++ b/tests/ui/check-cfg/mix.stderr @@ -251,7 +251,7 @@ warning: unexpected `cfg` condition value: `zebra` LL | cfg!(target_feature = "zebra"); | ^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_feature` are: `10e60`, `2e3`, `3e3r1`, `3e3r2`, `3e3r3`, `3e7`, `7e10`, `a`, `aclass`, `adx`, `aes`, `altivec`, `alu32`, `atomics`, `avx`, `avx2`, `avx512bf16`, `avx512bitalg`, `avx512bw`, `avx512cd`, `avx512dq`, `avx512er`, `avx512f`, `avx512fp16`, `avx512ifma`, `avx512pf`, `avx512vbmi`, `avx512vbmi2`, `avx512vl`, `avx512vnni`, `avx512vp2intersect`, `avx512vpopcntdq`, `bf16`, `bmi1`, `bmi2` and 187 more + = note: expected values for `target_feature` are: `10e60`, `2e3`, `3e3r1`, `3e3r2`, `3e3r3`, `3e7`, `7e10`, `a`, `aclass`, `adx`, `aes`, `altivec`, `alu32`, `atomics`, `avx`, `avx2`, `avx512bf16`, `avx512bitalg`, `avx512bw`, `avx512cd`, `avx512dq`, `avx512er`, `avx512f`, `avx512fp16`, `avx512ifma`, `avx512pf`, `avx512vbmi`, `avx512vbmi2`, `avx512vl`, `avx512vnni`, `avx512vp2intersect`, `avx512vpopcntdq`, `bf16`, `bmi1`, `bmi2` and 188 more = note: see for more information about checking conditional configuration warning: 27 warnings emitted diff --git a/tests/ui/check-cfg/well-known-values.stderr b/tests/ui/check-cfg/well-known-values.stderr index dceb6f4c0306..1863032c3865 100644 --- a/tests/ui/check-cfg/well-known-values.stderr +++ b/tests/ui/check-cfg/well-known-values.stderr @@ -154,7 +154,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | target_feature = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_feature` are: `10e60`, `2e3`, `3e3r1`, `3e3r2`, `3e3r3`, `3e7`, `7e10`, `a`, `aclass`, `adx`, `aes`, `altivec`, `alu32`, `atomics`, `avx`, `avx2`, `avx512bf16`, `avx512bitalg`, `avx512bw`, `avx512cd`, `avx512dq`, `avx512er`, `avx512f`, `avx512fp16`, `avx512ifma`, `avx512pf`, `avx512vbmi`, `avx512vbmi2`, `avx512vl`, `avx512vnni`, `avx512vp2intersect`, `avx512vpopcntdq`, `bf16`, `bmi1`, `bmi2`, `bti`, `bulk-memory`, `c`, `cache`, `cmpxchg16b`, `crc`, `crt-static`, `d`, `d32`, `dit`, `doloop`, `dotprod`, `dpb`, `dpb2`, `dsp`, `dsp1e2`, `dspe60`, `e`, `e1`, `e2`, `edsp`, `elrw`, `ermsb`, `exception-handling`, `f`, `f16c`, `f32mm`, `f64mm`, `fcma`, `fdivdu`, `fhm`, `flagm`, `float1e2`, `float1e3`, `float3e4`, `float7e60`, `floate1`, `fma`, `fp-armv8`, `fp16`, `fp64`, `fpuv2_df`, `fpuv2_sf`, `fpuv3_df`, `fpuv3_hf`, `fpuv3_hi`, `fpuv3_sf`, `frecipe`, `frintts`, `fxsr`, `gfni`, `hard-float`, `hard-float-abi`, `hard-tp`, `high-registers`, `hvx`, `hvx-length128b`, `hwdiv`, `i8mm`, `jsconv`, `lahfsahf`, `lasx`, `lbt`, `lor`, `lse`, `lsx`, `lvz`, `lzcnt`, `m`, `mclass`, `movbe`, `mp`, `mp1e2`, `msa`, `mte`, `multivalue`, `mutable-globals`, `neon`, `nontrapping-fptoint`, `nvic`, `paca`, `pacg`, `pan`, `pclmulqdq`, `pmuv3`, `popcnt`, `power10-vector`, `power8-altivec`, `power8-vector`, `power9-altivec`, `power9-vector`, `prfchw`, `rand`, `ras`, `rclass`, `rcpc`, `rcpc2`, `rdm`, `rdrand`, `rdseed`, `reference-types`, `relax`, `relaxed-simd`, `rtm`, `sb`, `sha`, `sha2`, `sha3`, `sign-ext`, `simd128`, `sm4`, `spe`, `ssbs`, `sse`, `sse2`, `sse3`, `sse4.1`, `sse4.2`, `sse4a`, `ssse3`, `sve`, `sve2`, `sve2-aes`, `sve2-bitperm`, `sve2-sha3`, `sve2-sm4`, `tbm`, `thumb-mode`, `thumb2`, `tme`, `trust`, `trustzone`, `ual`, `unaligned-scalar-mem`, `v`, `v5te`, `v6`, `v6k`, `v6t2`, `v7`, `v8`, `v8.1a`, `v8.2a`, `v8.3a`, `v8.4a`, `v8.5a`, `v8.6a`, `v8.7a`, `vaes`, `vdsp2e60f`, `vdspv1`, `vdspv2`, `vfp2`, `vfp3`, `vfp4`, `vh`, `virt`, `virtualization`, `vpclmulqdq`, `vsx`, `xsave`, `xsavec`, `xsaveopt`, `xsaves`, `zba`, `zbb`, `zbc`, `zbkb`, `zbkc`, `zbkx`, `zbs`, `zdinx`, `zfh`, `zfhmin`, `zfinx`, `zhinx`, `zhinxmin`, `zk`, `zkn`, `zknd`, `zkne`, `zknh`, `zkr`, `zks`, `zksed`, `zksh`, `zkt` + = note: expected values for `target_feature` are: `10e60`, `2e3`, `3e3r1`, `3e3r2`, `3e3r3`, `3e7`, `7e10`, `a`, `aclass`, `adx`, `aes`, `altivec`, `alu32`, `atomics`, `avx`, `avx2`, `avx512bf16`, `avx512bitalg`, `avx512bw`, `avx512cd`, `avx512dq`, `avx512er`, `avx512f`, `avx512fp16`, `avx512ifma`, `avx512pf`, `avx512vbmi`, `avx512vbmi2`, `avx512vl`, `avx512vnni`, `avx512vp2intersect`, `avx512vpopcntdq`, `bf16`, `bmi1`, `bmi2`, `bti`, `bulk-memory`, `c`, `cache`, `cmpxchg16b`, `crc`, `crt-static`, `d`, `d32`, `dit`, `doloop`, `dotprod`, `dpb`, `dpb2`, `dsp`, `dsp1e2`, `dspe60`, `e`, `e1`, `e2`, `edsp`, `elrw`, `ermsb`, `exception-handling`, `extended-const`, `f`, `f16c`, `f32mm`, `f64mm`, `fcma`, `fdivdu`, `fhm`, `flagm`, `float1e2`, `float1e3`, `float3e4`, `float7e60`, `floate1`, `fma`, `fp-armv8`, `fp16`, `fp64`, `fpuv2_df`, `fpuv2_sf`, `fpuv3_df`, `fpuv3_hf`, `fpuv3_hi`, `fpuv3_sf`, `frecipe`, `frintts`, `fxsr`, `gfni`, `hard-float`, `hard-float-abi`, `hard-tp`, `high-registers`, `hvx`, `hvx-length128b`, `hwdiv`, `i8mm`, `jsconv`, `lahfsahf`, `lasx`, `lbt`, `lor`, `lse`, `lsx`, `lvz`, `lzcnt`, `m`, `mclass`, `movbe`, `mp`, `mp1e2`, `msa`, `mte`, `multivalue`, `mutable-globals`, `neon`, `nontrapping-fptoint`, `nvic`, `paca`, `pacg`, `pan`, `pclmulqdq`, `pmuv3`, `popcnt`, `power10-vector`, `power8-altivec`, `power8-vector`, `power9-altivec`, `power9-vector`, `prfchw`, `rand`, `ras`, `rclass`, `rcpc`, `rcpc2`, `rdm`, `rdrand`, `rdseed`, `reference-types`, `relax`, `relaxed-simd`, `rtm`, `sb`, `sha`, `sha2`, `sha3`, `sign-ext`, `simd128`, `sm4`, `spe`, `ssbs`, `sse`, `sse2`, `sse3`, `sse4.1`, `sse4.2`, `sse4a`, `ssse3`, `sve`, `sve2`, `sve2-aes`, `sve2-bitperm`, `sve2-sha3`, `sve2-sm4`, `tbm`, `thumb-mode`, `thumb2`, `tme`, `trust`, `trustzone`, `ual`, `unaligned-scalar-mem`, `v`, `v5te`, `v6`, `v6k`, `v6t2`, `v7`, `v8`, `v8.1a`, `v8.2a`, `v8.3a`, `v8.4a`, `v8.5a`, `v8.6a`, `v8.7a`, `vaes`, `vdsp2e60f`, `vdspv1`, `vdspv2`, `vfp2`, `vfp3`, `vfp4`, `vh`, `virt`, `virtualization`, `vpclmulqdq`, `vsx`, `xsave`, `xsavec`, `xsaveopt`, `xsaves`, `zba`, `zbb`, `zbc`, `zbkb`, `zbkc`, `zbkx`, `zbs`, `zdinx`, `zfh`, `zfhmin`, `zfinx`, `zhinx`, `zhinxmin`, `zk`, `zkn`, `zknd`, `zkne`, `zknh`, `zkr`, `zks`, `zksed`, `zksh`, `zkt` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` diff --git a/tests/ui/issues/issue-22864-1.rs b/tests/ui/closures/issue-22864-1.rs similarity index 100% rename from tests/ui/issues/issue-22864-1.rs rename to tests/ui/closures/issue-22864-1.rs diff --git a/tests/ui/issues/issue-22864-2.rs b/tests/ui/closures/issue-22864-2.rs similarity index 100% rename from tests/ui/issues/issue-22864-2.rs rename to tests/ui/closures/issue-22864-2.rs diff --git a/tests/ui/issues/issue-5239-1.rs b/tests/ui/closures/issue-5239-1.rs similarity index 100% rename from tests/ui/issues/issue-5239-1.rs rename to tests/ui/closures/issue-5239-1.rs diff --git a/tests/ui/issues/issue-5239-1.stderr b/tests/ui/closures/issue-5239-1.stderr similarity index 100% rename from tests/ui/issues/issue-5239-1.stderr rename to tests/ui/closures/issue-5239-1.stderr diff --git a/tests/ui/issues/issue-5239-2.rs b/tests/ui/closures/issue-5239-2.rs similarity index 100% rename from tests/ui/issues/issue-5239-2.rs rename to tests/ui/closures/issue-5239-2.rs diff --git a/tests/ui/issues/issue-32122-deref-coercions-composition/issue-32122-1.fixed b/tests/ui/coercion/issue-32122-1.fixed similarity index 100% rename from tests/ui/issues/issue-32122-deref-coercions-composition/issue-32122-1.fixed rename to tests/ui/coercion/issue-32122-1.fixed diff --git a/tests/ui/issues/issue-32122-deref-coercions-composition/issue-32122-1.rs b/tests/ui/coercion/issue-32122-1.rs similarity index 100% rename from tests/ui/issues/issue-32122-deref-coercions-composition/issue-32122-1.rs rename to tests/ui/coercion/issue-32122-1.rs diff --git a/tests/ui/issues/issue-32122-deref-coercions-composition/issue-32122-1.stderr b/tests/ui/coercion/issue-32122-1.stderr similarity index 100% rename from tests/ui/issues/issue-32122-deref-coercions-composition/issue-32122-1.stderr rename to tests/ui/coercion/issue-32122-1.stderr diff --git a/tests/ui/issues/issue-32122-deref-coercions-composition/issue-32122-2.fixed b/tests/ui/coercion/issue-32122-2.fixed similarity index 100% rename from tests/ui/issues/issue-32122-deref-coercions-composition/issue-32122-2.fixed rename to tests/ui/coercion/issue-32122-2.fixed diff --git a/tests/ui/issues/issue-32122-deref-coercions-composition/issue-32122-2.rs b/tests/ui/coercion/issue-32122-2.rs similarity index 100% rename from tests/ui/issues/issue-32122-deref-coercions-composition/issue-32122-2.rs rename to tests/ui/coercion/issue-32122-2.rs diff --git a/tests/ui/issues/issue-32122-deref-coercions-composition/issue-32122-2.stderr b/tests/ui/coercion/issue-32122-2.stderr similarity index 100% rename from tests/ui/issues/issue-32122-deref-coercions-composition/issue-32122-2.stderr rename to tests/ui/coercion/issue-32122-2.stderr diff --git a/tests/ui/const-generics/adt_const_params/index-oob-ice-83993.rs b/tests/ui/const-generics/adt_const_params/index-oob-ice-83993.rs new file mode 100644 index 000000000000..0d1f023d565f --- /dev/null +++ b/tests/ui/const-generics/adt_const_params/index-oob-ice-83993.rs @@ -0,0 +1,18 @@ +// issue: rust-lang/rust/#83993 + +#![feature(adt_const_params)] +//~^ WARN the feature `adt_const_params` is incomplete and may not be safe to use and/or cause compiler crashes +fn bug<'a>() +where + for<'b> [(); { + let x: &'b (); + //~^ ERROR generic parameters may not be used in const operations + 0 + }]: +{} + +fn bad() where for<'b> [();{let _:&'b (); 0}]: Sized { } +//~^ ERROR generic parameters may not be used in const operations +fn good() where for<'b> [();{0}]: Sized { } + +pub fn main() {} diff --git a/tests/ui/const-generics/adt_const_params/index-oob-ice-83993.stderr b/tests/ui/const-generics/adt_const_params/index-oob-ice-83993.stderr new file mode 100644 index 000000000000..a49dfc319167 --- /dev/null +++ b/tests/ui/const-generics/adt_const_params/index-oob-ice-83993.stderr @@ -0,0 +1,29 @@ +error: generic parameters may not be used in const operations + --> $DIR/index-oob-ice-83993.rs:8:17 + | +LL | let x: &'b (); + | ^^ cannot perform const operation using `'b` + | + = note: lifetime parameters may not be used in const expressions + = help: add `#![feature(generic_const_exprs)]` to allow generic const expressions + +error: generic parameters may not be used in const operations + --> $DIR/index-oob-ice-83993.rs:14:36 + | +LL | fn bad() where for<'b> [();{let _:&'b (); 0}]: Sized { } + | ^^ cannot perform const operation using `'b` + | + = note: lifetime parameters may not be used in const expressions + = help: add `#![feature(generic_const_exprs)]` to allow generic const expressions + +warning: the feature `adt_const_params` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/index-oob-ice-83993.rs:3:12 + | +LL | #![feature(adt_const_params)] + | ^^^^^^^^^^^^^^^^ + | + = note: see issue #95174 for more information + = note: `#[warn(incomplete_features)]` on by default + +error: aborting due to 2 previous errors; 1 warning emitted + diff --git a/tests/ui/const-generics/generic_const_exprs/cannot-convert-refree-ice-114463.rs b/tests/ui/const-generics/generic_const_exprs/cannot-convert-refree-ice-114463.rs new file mode 100644 index 000000000000..2ce998e9fd9c --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/cannot-convert-refree-ice-114463.rs @@ -0,0 +1,10 @@ +// issue: rust-lang/rust#114463 +// ICE cannot convert `ReFree ..` to a region vid +#![feature(generic_const_exprs)] +//~^ WARN the feature `generic_const_exprs` is incomplete and may not be safe to use and/or cause compiler crashes +fn bug<'a>() { + [(); (|_: &'a u8| (), 0).1]; + //~^ ERROR cannot capture late-bound lifetime in constant +} + +pub fn main() {} diff --git a/tests/ui/const-generics/generic_const_exprs/cannot-convert-refree-ice-114463.stderr b/tests/ui/const-generics/generic_const_exprs/cannot-convert-refree-ice-114463.stderr new file mode 100644 index 000000000000..e4845405ec81 --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/cannot-convert-refree-ice-114463.stderr @@ -0,0 +1,19 @@ +warning: the feature `generic_const_exprs` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/cannot-convert-refree-ice-114463.rs:3:12 + | +LL | #![feature(generic_const_exprs)] + | ^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #76560 for more information + = note: `#[warn(incomplete_features)]` on by default + +error: cannot capture late-bound lifetime in constant + --> $DIR/cannot-convert-refree-ice-114463.rs:6:16 + | +LL | fn bug<'a>() { + | -- lifetime defined here +LL | [(); (|_: &'a u8| (), 0).1]; + | ^^ + +error: aborting due to 1 previous error; 1 warning emitted + diff --git a/tests/ui/consts/const-eval/raw-bytes.rs b/tests/ui/consts/const-eval/raw-bytes.rs index e5dfd5ca2939..2fbf135c9975 100644 --- a/tests/ui/consts/const-eval/raw-bytes.rs +++ b/tests/ui/consts/const-eval/raw-bytes.rs @@ -3,7 +3,7 @@ // ignore-tidy-linelength //@ normalize-stderr-test "╾─*ALLOC[0-9]+(\+[a-z0-9]+)?()?─*╼" -> "╾ALLOC_ID$1╼" #![allow(invalid_value)] -#![feature(generic_nonzero, never_type, rustc_attrs, ptr_metadata, slice_from_ptr_range, const_slice_from_ptr_range)] +#![feature(never_type, rustc_attrs, ptr_metadata, slice_from_ptr_range, const_slice_from_ptr_range)] use std::mem; use std::alloc::Layout; diff --git a/tests/ui/consts/const-eval/ub-nonnull.rs b/tests/ui/consts/const-eval/ub-nonnull.rs index 76bd5248ffd8..10d304436f80 100644 --- a/tests/ui/consts/const-eval/ub-nonnull.rs +++ b/tests/ui/consts/const-eval/ub-nonnull.rs @@ -2,7 +2,7 @@ //@ normalize-stderr-test "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)" //@ normalize-stderr-test "([0-9a-f][0-9a-f] |╾─*ALLOC[0-9]+(\+[a-z0-9]+)?─*╼ )+ *│.*" -> "HEX_DUMP" #![allow(invalid_value)] // make sure we cannot allow away the errors tested here -#![feature(generic_nonzero, rustc_attrs, ptr_metadata)] +#![feature(rustc_attrs, ptr_metadata)] use std::mem; use std::ptr::NonNull; diff --git a/tests/ui/consts/const-eval/valid-const.rs b/tests/ui/consts/const-eval/valid-const.rs index 15d3e8834565..777484c6b092 100644 --- a/tests/ui/consts/const-eval/valid-const.rs +++ b/tests/ui/consts/const-eval/valid-const.rs @@ -1,7 +1,6 @@ //@ check-pass // // Some constants that *are* valid -#![feature(generic_nonzero)] use std::mem; use std::ptr::NonNull; diff --git a/tests/ui/consts/const_refs_to_static-ice-121413.rs b/tests/ui/consts/const_refs_to_static-ice-121413.rs new file mode 100644 index 000000000000..8a24fb799b6b --- /dev/null +++ b/tests/ui/consts/const_refs_to_static-ice-121413.rs @@ -0,0 +1,16 @@ +// ICE: ImmTy { imm: Scalar(alloc1), ty: *const dyn Sync } input to a fat-to-thin cast (*const dyn Sync -> *const usize +// or with -Zextra-const-ub-checks: expected wide pointer extra data (e.g. slice length or trait object vtable) +// issue: rust-lang/rust#121413 +//@ compile-flags: -Zextra-const-ub-checks +// ignore-tidy-linelength +#![feature(const_refs_to_static)] +const REF_INTERIOR_MUT: &usize = { + static FOO: Sync = AtomicUsize::new(0); + //~^ ERROR failed to resolve: use of undeclared type `AtomicUsize` + //~| WARN trait objects without an explicit `dyn` are deprecated + //~| ERROR the size for values of type `(dyn Sync + 'static)` cannot be known at compilation time + //~| ERROR the size for values of type `(dyn Sync + 'static)` cannot be known at compilation time + //~| WARN this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + unsafe { &*(&FOO as *const _ as *const usize) } +}; +pub fn main() {} diff --git a/tests/ui/consts/const_refs_to_static-ice-121413.stderr b/tests/ui/consts/const_refs_to_static-ice-121413.stderr new file mode 100644 index 000000000000..c977c698a92e --- /dev/null +++ b/tests/ui/consts/const_refs_to_static-ice-121413.stderr @@ -0,0 +1,46 @@ +error[E0433]: failed to resolve: use of undeclared type `AtomicUsize` + --> $DIR/const_refs_to_static-ice-121413.rs:8:24 + | +LL | static FOO: Sync = AtomicUsize::new(0); + | ^^^^^^^^^^^ use of undeclared type `AtomicUsize` + | +help: consider importing this struct + | +LL + use std::sync::atomic::AtomicUsize; + | + +warning: trait objects without an explicit `dyn` are deprecated + --> $DIR/const_refs_to_static-ice-121413.rs:8:17 + | +LL | static FOO: Sync = AtomicUsize::new(0); + | ^^^^ + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + = note: for more information, see + = note: `#[warn(bare_trait_objects)]` on by default +help: if this is an object-safe trait, use `dyn` + | +LL | static FOO: dyn Sync = AtomicUsize::new(0); + | +++ + +error[E0277]: the size for values of type `(dyn Sync + 'static)` cannot be known at compilation time + --> $DIR/const_refs_to_static-ice-121413.rs:8:17 + | +LL | static FOO: Sync = AtomicUsize::new(0); + | ^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `(dyn Sync + 'static)` + +error[E0277]: the size for values of type `(dyn Sync + 'static)` cannot be known at compilation time + --> $DIR/const_refs_to_static-ice-121413.rs:8:24 + | +LL | static FOO: Sync = AtomicUsize::new(0); + | ^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `(dyn Sync + 'static)` + = note: constant expressions must have a statically known size + +error: aborting due to 3 previous errors; 1 warning emitted + +Some errors have detailed explanations: E0277, E0433. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/issues/issue-19244-1.rs b/tests/ui/consts/issue-19244-1.rs similarity index 100% rename from tests/ui/issues/issue-19244-1.rs rename to tests/ui/consts/issue-19244-1.rs diff --git a/tests/ui/issues/issue-19244-1.stderr b/tests/ui/consts/issue-19244-1.stderr similarity index 100% rename from tests/ui/issues/issue-19244-1.stderr rename to tests/ui/consts/issue-19244-1.stderr diff --git a/tests/ui/issues/issue-19244-2.rs b/tests/ui/consts/issue-19244-2.rs similarity index 100% rename from tests/ui/issues/issue-19244-2.rs rename to tests/ui/consts/issue-19244-2.rs diff --git a/tests/ui/issues/issue-19244-2.stderr b/tests/ui/consts/issue-19244-2.stderr similarity index 100% rename from tests/ui/issues/issue-19244-2.stderr rename to tests/ui/consts/issue-19244-2.stderr diff --git a/tests/ui/consts/tuple-struct-constructors.rs b/tests/ui/consts/tuple-struct-constructors.rs index d2f25aeec9b1..e645b5740750 100644 --- a/tests/ui/consts/tuple-struct-constructors.rs +++ b/tests/ui/consts/tuple-struct-constructors.rs @@ -1,7 +1,6 @@ //@ run-pass // // https://github.com/rust-lang/rust/issues/41898 -#![feature(generic_nonzero)] use std::num::NonZero; diff --git a/tests/ui/debuginfo/msvc-strip-debuginfo.rs b/tests/ui/debuginfo/msvc-strip-debuginfo.rs new file mode 100644 index 000000000000..d5f516eefe2a --- /dev/null +++ b/tests/ui/debuginfo/msvc-strip-debuginfo.rs @@ -0,0 +1,26 @@ +//@ compile-flags: -C strip=debuginfo +//@ only-msvc +//@ run-pass + +use std::path::Path; + +pub fn is_related_pdb>(path: &P, exe: &P) -> bool { + let (exe, path) = (exe.as_ref(), path.as_ref()); + + path.extension() + .map(|x| x.to_ascii_lowercase()) + .is_some_and(|x| x == "pdb") + && path.file_stem() == exe.file_stem() +} + +pub fn main() { + let curr_exe = std::env::current_exe().unwrap(); + let curr_dir = curr_exe.parent().unwrap(); + + let entries = std::fs::read_dir(curr_dir).unwrap(); + + assert!(entries + .map_while(|x| x.ok()) + .find(|x| is_related_pdb(&x.path(), &curr_exe)) + .is_some()); +} diff --git a/tests/ui/debuginfo/msvc-strip-symbols.rs b/tests/ui/debuginfo/msvc-strip-symbols.rs new file mode 100644 index 000000000000..198c9496cb49 --- /dev/null +++ b/tests/ui/debuginfo/msvc-strip-symbols.rs @@ -0,0 +1,26 @@ +//@ compile-flags: -C strip=symbols +//@ only-msvc +//@ run-pass + +use std::path::Path; + +pub fn is_related_pdb>(path: &P, exe: &P) -> bool { + let (exe, path) = (exe.as_ref(), path.as_ref()); + + path.extension() + .map(|x| x.to_ascii_lowercase()) + .is_some_and(|x| x == "pdb") + && path.file_stem() == exe.file_stem() +} + +pub fn main() { + let curr_exe = std::env::current_exe().unwrap(); + let curr_dir = curr_exe.parent().unwrap(); + + let entries = std::fs::read_dir(curr_dir).unwrap(); + + assert!(entries + .map_while(|x| x.ok()) + .find(|x| is_related_pdb(&x.path(), &curr_exe)) + .is_some()); +} diff --git a/tests/ui/issues/issue-71676-suggest-deref/issue-71676-1.fixed b/tests/ui/deref-patterns/issue-71676-1.fixed similarity index 100% rename from tests/ui/issues/issue-71676-suggest-deref/issue-71676-1.fixed rename to tests/ui/deref-patterns/issue-71676-1.fixed diff --git a/tests/ui/issues/issue-71676-suggest-deref/issue-71676-1.rs b/tests/ui/deref-patterns/issue-71676-1.rs similarity index 100% rename from tests/ui/issues/issue-71676-suggest-deref/issue-71676-1.rs rename to tests/ui/deref-patterns/issue-71676-1.rs diff --git a/tests/ui/issues/issue-71676-suggest-deref/issue-71676-1.stderr b/tests/ui/deref-patterns/issue-71676-1.stderr similarity index 100% rename from tests/ui/issues/issue-71676-suggest-deref/issue-71676-1.stderr rename to tests/ui/deref-patterns/issue-71676-1.stderr diff --git a/tests/ui/issues/issue-71676-suggest-deref/issue-71676-2.rs b/tests/ui/deref-patterns/issue-71676-2.rs similarity index 100% rename from tests/ui/issues/issue-71676-suggest-deref/issue-71676-2.rs rename to tests/ui/deref-patterns/issue-71676-2.rs diff --git a/tests/ui/issues/issue-71676-suggest-deref/issue-71676-2.stderr b/tests/ui/deref-patterns/issue-71676-2.stderr similarity index 100% rename from tests/ui/issues/issue-71676-suggest-deref/issue-71676-2.stderr rename to tests/ui/deref-patterns/issue-71676-2.stderr diff --git a/tests/ui/issues/auxiliary/issue-19340-1.rs b/tests/ui/enum/auxiliary/issue-19340-1.rs similarity index 100% rename from tests/ui/issues/auxiliary/issue-19340-1.rs rename to tests/ui/enum/auxiliary/issue-19340-1.rs diff --git a/tests/ui/issues/issue-19340-1.rs b/tests/ui/enum/issue-19340-1.rs similarity index 100% rename from tests/ui/issues/issue-19340-1.rs rename to tests/ui/enum/issue-19340-1.rs diff --git a/tests/ui/issues/issue-19340-2.rs b/tests/ui/enum/issue-19340-2.rs similarity index 100% rename from tests/ui/issues/issue-19340-2.rs rename to tests/ui/enum/issue-19340-2.rs diff --git a/tests/ui/issues/issue-23304-1.rs b/tests/ui/enum/issue-23304-1.rs similarity index 100% rename from tests/ui/issues/issue-23304-1.rs rename to tests/ui/enum/issue-23304-1.rs diff --git a/tests/ui/issues/issue-23304-2.rs b/tests/ui/enum/issue-23304-2.rs similarity index 100% rename from tests/ui/issues/issue-23304-2.rs rename to tests/ui/enum/issue-23304-2.rs diff --git a/tests/ui/issues/issue-22933-1.rs b/tests/ui/expr/issue-22933-1.rs similarity index 100% rename from tests/ui/issues/issue-22933-1.rs rename to tests/ui/expr/issue-22933-1.rs diff --git a/tests/ui/issues/issue-22933-2.rs b/tests/ui/expr/issue-22933-2.rs similarity index 100% rename from tests/ui/issues/issue-22933-2.rs rename to tests/ui/expr/issue-22933-2.rs diff --git a/tests/ui/issues/issue-22933-2.stderr b/tests/ui/expr/issue-22933-2.stderr similarity index 100% rename from tests/ui/issues/issue-22933-2.stderr rename to tests/ui/expr/issue-22933-2.stderr diff --git a/tests/ui/impl-trait/in-trait/nested-rpitit-bounds.rs b/tests/ui/impl-trait/in-trait/nested-rpitit-bounds.rs index 3a173efb32db..10c2a8112434 100644 --- a/tests/ui/impl-trait/in-trait/nested-rpitit-bounds.rs +++ b/tests/ui/impl-trait/in-trait/nested-rpitit-bounds.rs @@ -6,6 +6,10 @@ trait Foo { fn foo() -> impl Deref> { &&() } + + fn bar() -> impl Deref> { + &Some(()) + } } fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/capture-parent-arg.rs b/tests/ui/impl-trait/precise-capturing/capture-parent-arg.rs new file mode 100644 index 000000000000..35b28d0e6fb5 --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/capture-parent-arg.rs @@ -0,0 +1,38 @@ +#![feature(precise_capturing)] +//~^ WARN the feature `precise_capturing` is incomplete + +trait Tr { + type Assoc; +} + +struct W<'a>(&'a ()); + +impl Tr for W<'_> { + type Assoc = (); +} + +// The normal way of capturing `'a`... +impl<'a> W<'a> { + fn good1() -> impl use<'a> Into< as Tr>::Assoc> {} +} + +// This ensures that we don't error when we capture the *parent* copy of `'a`, +// since the opaque captures that rather than the duplicated `'a` lifetime +// synthesized from mentioning `'a` directly in the bounds. +impl<'a> W<'a> { + fn good2() -> impl use<'a> Into<::Assoc> {} +} + +// The normal way of capturing `'a`... but not mentioned in the bounds. +impl<'a> W<'a> { + fn bad1() -> impl use<> Into< as Tr>::Assoc> {} + //~^ ERROR `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list +} + +// But also make sure that we error here... +impl<'a> W<'a> { + fn bad2() -> impl use<> Into<::Assoc> {} + //~^ ERROR `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list +} + +fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/capture-parent-arg.stderr b/tests/ui/impl-trait/precise-capturing/capture-parent-arg.stderr new file mode 100644 index 000000000000..13aaa9997073 --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/capture-parent-arg.stderr @@ -0,0 +1,27 @@ +warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/capture-parent-arg.rs:1:12 + | +LL | #![feature(precise_capturing)] + | ^^^^^^^^^^^^^^^^^ + | + = note: see issue #123432 for more information + = note: `#[warn(incomplete_features)]` on by default + +error: `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list + --> $DIR/capture-parent-arg.rs:28:37 + | +LL | impl<'a> W<'a> { + | -- this lifetime parameter is captured +LL | fn bad1() -> impl use<> Into< as Tr>::Assoc> {} + | -------------------^^---------------- lifetime captured due to being mentioned in the bounds of the `impl Trait` + +error: `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list + --> $DIR/capture-parent-arg.rs:34:18 + | +LL | impl<'a> W<'a> { + | -- this lifetime parameter is captured +LL | fn bad2() -> impl use<> Into<::Assoc> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime captured due to being mentioned in the bounds of the `impl Trait` + +error: aborting due to 2 previous errors; 1 warning emitted + diff --git a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-const.stderr b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-const.stderr index 9c99f2b711ea..3f78e7c56b6e 100644 --- a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-const.stderr +++ b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-const.stderr @@ -7,11 +7,13 @@ LL | #![feature(precise_capturing)] = note: see issue #123432 for more information = note: `#[warn(incomplete_features)]` on by default -error: `impl Trait` must mention all const parameters in scope - --> $DIR/forgot-to-capture-const.rs:4:13 +error: `impl Trait` must mention all const parameters in scope in `use<...>` + --> $DIR/forgot-to-capture-const.rs:4:34 | LL | fn constant() -> impl use<> Sized {} - | ^^^^^^^^^^^^^^ ---------------- const parameter is implicitly captured by this `impl Trait` + | -------------- ^^^^^^^^^^^^^^^^ + | | + | const parameter is implicitly captured by this `impl Trait` | = note: currently, all const parameters are required to be mentioned in the precise captures list diff --git a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.rs b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.rs index 6eaff01183d4..d359ea5e26df 100644 --- a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.rs +++ b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.rs @@ -5,8 +5,8 @@ fn type_param() -> impl use<> Sized {} //~^ ERROR `impl Trait` must mention all type parameters in scope trait Foo { -//~^ ERROR `impl Trait` must mention all type parameters in scope fn bar() -> impl use<> Sized; + //~^ ERROR `impl Trait` must mention the `Self` type of the trait } fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.stderr b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.stderr index a8eb4547dcdd..26994d0bdbf1 100644 --- a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.stderr +++ b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.stderr @@ -7,22 +7,23 @@ LL | #![feature(precise_capturing)] = note: see issue #123432 for more information = note: `#[warn(incomplete_features)]` on by default -error: `impl Trait` must mention all type parameters in scope - --> $DIR/forgot-to-capture-type.rs:4:15 +error: `impl Trait` must mention all type parameters in scope in `use<...>` + --> $DIR/forgot-to-capture-type.rs:4:23 | LL | fn type_param() -> impl use<> Sized {} - | ^ ---------------- type parameter is implicitly captured by this `impl Trait` + | - ^^^^^^^^^^^^^^^^ + | | + | type parameter is implicitly captured by this `impl Trait` | = note: currently, all type parameters are required to be mentioned in the precise captures list -error: `impl Trait` must mention all type parameters in scope - --> $DIR/forgot-to-capture-type.rs:7:1 +error: `impl Trait` must mention the `Self` type of the trait in `use<...>` + --> $DIR/forgot-to-capture-type.rs:8:17 | LL | trait Foo { - | ^^^^^^^^^ -LL | + | --------- `Self` type parameter is implicitly captured by this `impl Trait` LL | fn bar() -> impl use<> Sized; - | ---------------- type parameter is implicitly captured by this `impl Trait` + | ^^^^^^^^^^^^^^^^ | = note: currently, all type parameters are required to be mentioned in the precise captures list diff --git a/tests/ui/inference/hint-closure-signature-119266.rs b/tests/ui/inference/hint-closure-signature-119266.rs new file mode 100644 index 000000000000..35be600fd6ab --- /dev/null +++ b/tests/ui/inference/hint-closure-signature-119266.rs @@ -0,0 +1,11 @@ +fn main() { + let x = |a: u8, b: (usize, u32), c: fn() -> char| -> String { "I love beans.".to_string() }; + //~^ NOTE: the found closure + + let x: fn(i32) = x; + //~^ ERROR: 5:22: 5:23: mismatched types [E0308] + //~| NOTE: incorrect number of function parameters + //~| NOTE: expected due to this + //~| NOTE: expected fn pointer `fn(i32)` + //~| NOTE: closure has signature: `fn(u8, (usize, u32), fn() -> char) -> String` +} diff --git a/tests/ui/inference/hint-closure-signature-119266.stderr b/tests/ui/inference/hint-closure-signature-119266.stderr new file mode 100644 index 000000000000..f0b957906afb --- /dev/null +++ b/tests/ui/inference/hint-closure-signature-119266.stderr @@ -0,0 +1,18 @@ +error[E0308]: mismatched types + --> $DIR/hint-closure-signature-119266.rs:5:22 + | +LL | let x = |a: u8, b: (usize, u32), c: fn() -> char| -> String { "I love beans.".to_string() }; + | --------------------------------------------------- the found closure +... +LL | let x: fn(i32) = x; + | ------- ^ incorrect number of function parameters + | | + | expected due to this + | + = note: expected fn pointer `fn(i32)` + found closure `{closure@$DIR/hint-closure-signature-119266.rs:2:13: 2:64}` + = note: closure has signature: `fn(u8, (usize, u32), fn() -> char) -> String` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/instrument-coverage/coverage-options.bad.stderr b/tests/ui/instrument-coverage/coverage-options.bad.stderr index ca82dc5bdb93..f6e5421f8784 100644 --- a/tests/ui/instrument-coverage/coverage-options.bad.stderr +++ b/tests/ui/instrument-coverage/coverage-options.bad.stderr @@ -1,2 +1,2 @@ -error: incorrect value `bad` for unstable option `coverage-options` - `branch` or `no-branch` was expected +error: incorrect value `bad` for unstable option `coverage-options` - either `no-branch`, `branch` or `mcdc` was expected diff --git a/tests/ui/instrument-coverage/coverage-options.rs b/tests/ui/instrument-coverage/coverage-options.rs index a62e0554f769..50c01ed29b5e 100644 --- a/tests/ui/instrument-coverage/coverage-options.rs +++ b/tests/ui/instrument-coverage/coverage-options.rs @@ -8,7 +8,13 @@ //@ [no-branch] check-pass //@ [no-branch] compile-flags: -Zcoverage-options=no-branch +//@ [mcdc] check-pass +//@ [mcdc] compile-flags: -Zcoverage-options=mcdc + //@ [bad] check-fail //@ [bad] compile-flags: -Zcoverage-options=bad +//@ [conflict] check-fail +//@ [conflict] compile-flags: -Zcoverage-options=no-branch,mcdc + fn main() {} diff --git a/tests/ui/intrinsics/panic-uninitialized-zeroed.rs b/tests/ui/intrinsics/panic-uninitialized-zeroed.rs index b1ac7528d584..67b9832d6014 100644 --- a/tests/ui/intrinsics/panic-uninitialized-zeroed.rs +++ b/tests/ui/intrinsics/panic-uninitialized-zeroed.rs @@ -7,7 +7,6 @@ // // This test checks panic emitted from `mem::{uninitialized,zeroed}`. #![allow(deprecated, invalid_value)] -#![feature(generic_nonzero)] #![feature(never_type)] use std::{ diff --git a/tests/ui/issues/issue-64593.rs b/tests/ui/issues/issue-64593.rs index 091c3a2f316d..e28b9577347b 100644 --- a/tests/ui/issues/issue-64593.rs +++ b/tests/ui/issues/issue-64593.rs @@ -1,6 +1,5 @@ //@ check-pass #![deny(improper_ctypes)] -#![feature(generic_nonzero)] pub struct Error(std::num::NonZero); diff --git a/tests/ui/layout/unsafe-cell-hides-niche.rs b/tests/ui/layout/unsafe-cell-hides-niche.rs index 568eb819be2e..fe51c9925e80 100644 --- a/tests/ui/layout/unsafe-cell-hides-niche.rs +++ b/tests/ui/layout/unsafe-cell-hides-niche.rs @@ -6,7 +6,6 @@ //@ check-pass //@ compile-flags: --crate-type=lib //@ only-x86 -#![feature(generic_nonzero)] #![feature(repr_simd)] use std::cell::{UnsafeCell, RefCell, Cell}; diff --git a/tests/ui/layout/zero-sized-array-enum-niche.rs b/tests/ui/layout/zero-sized-array-enum-niche.rs index 058f59234878..0c37c0f010e2 100644 --- a/tests/ui/layout/zero-sized-array-enum-niche.rs +++ b/tests/ui/layout/zero-sized-array-enum-niche.rs @@ -1,6 +1,5 @@ //@ normalize-stderr-test "pref: Align\([1-8] bytes\)" -> "pref: $$PREF_ALIGN" #![crate_type = "lib"] -#![feature(generic_nonzero)] #![feature(rustc_attrs)] // Various tests around the behavior of zero-sized arrays and diff --git a/tests/ui/layout/zero-sized-array-enum-niche.stderr b/tests/ui/layout/zero-sized-array-enum-niche.stderr index af049125de46..ee34cfdfb0db 100644 --- a/tests/ui/layout/zero-sized-array-enum-niche.stderr +++ b/tests/ui/layout/zero-sized-array-enum-niche.stderr @@ -98,7 +98,7 @@ error: layout_of(Result<[u32; 0], bool>) = Layout { max_repr_align: None, unadjusted_abi_align: Align(4 bytes), } - --> $DIR/zero-sized-array-enum-niche.rs:14:1 + --> $DIR/zero-sized-array-enum-niche.rs:13:1 | LL | type AlignedResult = Result<[u32; 0], bool>; | ^^^^^^^^^^^^^^^^^^ @@ -227,7 +227,7 @@ error: layout_of(MultipleAlignments) = Layout { max_repr_align: None, unadjusted_abi_align: Align(4 bytes), } - --> $DIR/zero-sized-array-enum-niche.rs:22:1 + --> $DIR/zero-sized-array-enum-niche.rs:21:1 | LL | enum MultipleAlignments { | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -332,7 +332,7 @@ error: layout_of(Result<[u32; 0], Packed>>) = Layout { max_repr_align: None, unadjusted_abi_align: Align(4 bytes), } - --> $DIR/zero-sized-array-enum-niche.rs:38:1 + --> $DIR/zero-sized-array-enum-niche.rs:37:1 | LL | type NicheLosesToTagged = Result<[u32; 0], Packed>>; | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -441,7 +441,7 @@ error: layout_of(Result<[u32; 0], Packed>) = Layout { max_repr_align: None, unadjusted_abi_align: Align(4 bytes), } - --> $DIR/zero-sized-array-enum-niche.rs:45:1 + --> $DIR/zero-sized-array-enum-niche.rs:44:1 | LL | type NicheWinsOverTagged = Result<[u32; 0], Packed>; | ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/lint/clashing-extern-fn.rs b/tests/ui/lint/clashing-extern-fn.rs index cb63af0ea422..728dfabb393e 100644 --- a/tests/ui/lint/clashing-extern-fn.rs +++ b/tests/ui/lint/clashing-extern-fn.rs @@ -2,7 +2,6 @@ //@ aux-build:external_extern_fn.rs #![crate_type = "lib"] #![warn(clashing_extern_declarations)] -#![feature(generic_nonzero)] mod redeclared_different_signature { mod a { diff --git a/tests/ui/lint/clashing-extern-fn.stderr b/tests/ui/lint/clashing-extern-fn.stderr index 86ee789aeb22..43c8cdead9f4 100644 --- a/tests/ui/lint/clashing-extern-fn.stderr +++ b/tests/ui/lint/clashing-extern-fn.stderr @@ -1,5 +1,5 @@ warning: `extern` block uses type `Option`, which is not FFI-safe - --> $DIR/clashing-extern-fn.rs:430:55 + --> $DIR/clashing-extern-fn.rs:429:55 | LL | fn hidden_niche_transparent_no_niche() -> Option; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -9,7 +9,7 @@ LL | fn hidden_niche_transparent_no_niche() -> Option>>`, which is not FFI-safe - --> $DIR/clashing-extern-fn.rs:434:46 + --> $DIR/clashing-extern-fn.rs:433:46 | LL | fn hidden_niche_unsafe_cell() -> Option>>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -18,7 +18,7 @@ LL | fn hidden_niche_unsafe_cell() -> Option $DIR/clashing-extern-fn.rs:15:13 + --> $DIR/clashing-extern-fn.rs:14:13 | LL | fn clash(x: u8); | --------------- `clash` previously declared here @@ -35,7 +35,7 @@ LL | #![warn(clashing_extern_declarations)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: `extern_link_name` redeclared with a different signature - --> $DIR/clashing-extern-fn.rs:53:9 + --> $DIR/clashing-extern-fn.rs:52:9 | LL | #[link_name = "extern_link_name"] | --------------------------------- `extern_link_name` previously declared here @@ -47,7 +47,7 @@ LL | fn extern_link_name(x: u32); found `unsafe extern "C" fn(u32)` warning: `some_other_extern_link_name` redeclares `some_other_new_name` with a different signature - --> $DIR/clashing-extern-fn.rs:56:9 + --> $DIR/clashing-extern-fn.rs:55:9 | LL | fn some_other_new_name(x: i16); | ------------------------------ `some_other_new_name` previously declared here @@ -59,7 +59,7 @@ LL | #[link_name = "some_other_new_name"] found `unsafe extern "C" fn(u32)` warning: `other_both_names_different` redeclares `link_name_same` with a different signature - --> $DIR/clashing-extern-fn.rs:60:9 + --> $DIR/clashing-extern-fn.rs:59:9 | LL | #[link_name = "link_name_same"] | ------------------------------- `link_name_same` previously declared here @@ -71,7 +71,7 @@ LL | #[link_name = "link_name_same"] found `unsafe extern "C" fn(u32)` warning: `different_mod` redeclared with a different signature - --> $DIR/clashing-extern-fn.rs:73:9 + --> $DIR/clashing-extern-fn.rs:72:9 | LL | fn different_mod(x: u8); | ----------------------- `different_mod` previously declared here @@ -83,7 +83,7 @@ LL | fn different_mod(x: u64); found `unsafe extern "C" fn(u64)` warning: `variadic_decl` redeclared with a different signature - --> $DIR/clashing-extern-fn.rs:83:9 + --> $DIR/clashing-extern-fn.rs:82:9 | LL | fn variadic_decl(x: u8, ...); | ---------------------------- `variadic_decl` previously declared here @@ -95,7 +95,7 @@ LL | fn variadic_decl(x: u8); found `unsafe extern "C" fn(u8)` warning: `weigh_banana` redeclared with a different signature - --> $DIR/clashing-extern-fn.rs:143:13 + --> $DIR/clashing-extern-fn.rs:142:13 | LL | fn weigh_banana(count: *const Banana) -> u64; | -------------------------------------------- `weigh_banana` previously declared here @@ -107,7 +107,7 @@ LL | fn weigh_banana(count: *const Banana) -> u64; found `unsafe extern "C" fn(*const three::Banana) -> u64` warning: `draw_point` redeclared with a different signature - --> $DIR/clashing-extern-fn.rs:172:13 + --> $DIR/clashing-extern-fn.rs:171:13 | LL | fn draw_point(p: Point); | ----------------------- `draw_point` previously declared here @@ -119,7 +119,7 @@ LL | fn draw_point(p: Point); found `unsafe extern "C" fn(sameish_members::b::Point)` warning: `origin` redeclared with a different signature - --> $DIR/clashing-extern-fn.rs:198:13 + --> $DIR/clashing-extern-fn.rs:197:13 | LL | fn origin() -> Point3; | --------------------- `origin` previously declared here @@ -131,7 +131,7 @@ LL | fn origin() -> Point3; found `unsafe extern "C" fn() -> same_sized_members_clash::b::Point3` warning: `transparent_incorrect` redeclared with a different signature - --> $DIR/clashing-extern-fn.rs:221:13 + --> $DIR/clashing-extern-fn.rs:220:13 | LL | fn transparent_incorrect() -> T; | ------------------------------- `transparent_incorrect` previously declared here @@ -143,7 +143,7 @@ LL | fn transparent_incorrect() -> isize; found `unsafe extern "C" fn() -> isize` warning: `missing_return_type` redeclared with a different signature - --> $DIR/clashing-extern-fn.rs:260:13 + --> $DIR/clashing-extern-fn.rs:259:13 | LL | fn missing_return_type() -> usize; | --------------------------------- `missing_return_type` previously declared here @@ -155,7 +155,7 @@ LL | fn missing_return_type(); found `unsafe extern "C" fn()` warning: `non_zero_usize` redeclared with a different signature - --> $DIR/clashing-extern-fn.rs:278:13 + --> $DIR/clashing-extern-fn.rs:277:13 | LL | fn non_zero_usize() -> core::num::NonZero; | ------------------------------------------------ `non_zero_usize` previously declared here @@ -167,7 +167,7 @@ LL | fn non_zero_usize() -> usize; found `unsafe extern "C" fn() -> usize` warning: `non_null_ptr` redeclared with a different signature - --> $DIR/clashing-extern-fn.rs:280:13 + --> $DIR/clashing-extern-fn.rs:279:13 | LL | fn non_null_ptr() -> core::ptr::NonNull; | ---------------------------------------------- `non_null_ptr` previously declared here @@ -179,7 +179,7 @@ LL | fn non_null_ptr() -> *const usize; found `unsafe extern "C" fn() -> *const usize` warning: `option_non_zero_usize_incorrect` redeclared with a different signature - --> $DIR/clashing-extern-fn.rs:374:13 + --> $DIR/clashing-extern-fn.rs:373:13 | LL | fn option_non_zero_usize_incorrect() -> usize; | --------------------------------------------- `option_non_zero_usize_incorrect` previously declared here @@ -191,7 +191,7 @@ LL | fn option_non_zero_usize_incorrect() -> isize; found `unsafe extern "C" fn() -> isize` warning: `option_non_null_ptr_incorrect` redeclared with a different signature - --> $DIR/clashing-extern-fn.rs:376:13 + --> $DIR/clashing-extern-fn.rs:375:13 | LL | fn option_non_null_ptr_incorrect() -> *const usize; | -------------------------------------------------- `option_non_null_ptr_incorrect` previously declared here @@ -203,7 +203,7 @@ LL | fn option_non_null_ptr_incorrect() -> *const isize; found `unsafe extern "C" fn() -> *const isize` warning: `hidden_niche_transparent_no_niche` redeclared with a different signature - --> $DIR/clashing-extern-fn.rs:430:13 + --> $DIR/clashing-extern-fn.rs:429:13 | LL | fn hidden_niche_transparent_no_niche() -> usize; | ----------------------------------------------- `hidden_niche_transparent_no_niche` previously declared here @@ -215,7 +215,7 @@ LL | fn hidden_niche_transparent_no_niche() -> Option Option` warning: `hidden_niche_unsafe_cell` redeclared with a different signature - --> $DIR/clashing-extern-fn.rs:434:13 + --> $DIR/clashing-extern-fn.rs:433:13 | LL | fn hidden_niche_unsafe_cell() -> usize; | -------------------------------------- `hidden_niche_unsafe_cell` previously declared here diff --git a/tests/ui/lint/invalid_value.rs b/tests/ui/lint/invalid_value.rs index 1d2f23aaaf6f..29e8e6cfef6d 100644 --- a/tests/ui/lint/invalid_value.rs +++ b/tests/ui/lint/invalid_value.rs @@ -2,7 +2,7 @@ // in a lint. #![allow(deprecated)] #![deny(invalid_value)] -#![feature(generic_nonzero, never_type, rustc_attrs)] +#![feature(never_type, rustc_attrs)] use std::mem::{self, MaybeUninit}; use std::ptr::NonNull; diff --git a/tests/ui/lint/lint-ctypes-enum.rs b/tests/ui/lint/lint-ctypes-enum.rs index 3157b6e240ae..c60290f8553a 100644 --- a/tests/ui/lint/lint-ctypes-enum.rs +++ b/tests/ui/lint/lint-ctypes-enum.rs @@ -1,6 +1,5 @@ #![allow(dead_code)] #![deny(improper_ctypes)] -#![feature(generic_nonzero)] #![feature(ptr_internals)] #![feature(transparent_unions)] diff --git a/tests/ui/lint/lint-ctypes-enum.stderr b/tests/ui/lint/lint-ctypes-enum.stderr index 48be3eb5a563..103fda8d4025 100644 --- a/tests/ui/lint/lint-ctypes-enum.stderr +++ b/tests/ui/lint/lint-ctypes-enum.stderr @@ -1,5 +1,5 @@ error: `extern` block uses type `U`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:61:13 + --> $DIR/lint-ctypes-enum.rs:60:13 | LL | fn uf(x: U); | ^ not FFI-safe @@ -7,7 +7,7 @@ LL | fn uf(x: U); = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum = note: enum has no representation hint note: the type is defined here - --> $DIR/lint-ctypes-enum.rs:10:1 + --> $DIR/lint-ctypes-enum.rs:9:1 | LL | enum U { | ^^^^^^ @@ -18,7 +18,7 @@ LL | #![deny(improper_ctypes)] | ^^^^^^^^^^^^^^^ error: `extern` block uses type `B`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:62:13 + --> $DIR/lint-ctypes-enum.rs:61:13 | LL | fn bf(x: B); | ^ not FFI-safe @@ -26,13 +26,13 @@ LL | fn bf(x: B); = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum = note: enum has no representation hint note: the type is defined here - --> $DIR/lint-ctypes-enum.rs:13:1 + --> $DIR/lint-ctypes-enum.rs:12:1 | LL | enum B { | ^^^^^^ error: `extern` block uses type `T`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:63:13 + --> $DIR/lint-ctypes-enum.rs:62:13 | LL | fn tf(x: T); | ^ not FFI-safe @@ -40,13 +40,13 @@ LL | fn tf(x: T); = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum = note: enum has no representation hint note: the type is defined here - --> $DIR/lint-ctypes-enum.rs:17:1 + --> $DIR/lint-ctypes-enum.rs:16:1 | LL | enum T { | ^^^^^^ error: `extern` block uses type `u128`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:75:23 + --> $DIR/lint-ctypes-enum.rs:74:23 | LL | fn nonzero_u128(x: Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -54,7 +54,7 @@ LL | fn nonzero_u128(x: Option>); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `i128`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:82:23 + --> $DIR/lint-ctypes-enum.rs:81:23 | LL | fn nonzero_i128(x: Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -62,7 +62,7 @@ LL | fn nonzero_i128(x: Option>); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `Option>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:87:28 + --> $DIR/lint-ctypes-enum.rs:86:28 | LL | fn transparent_union(x: Option>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -71,7 +71,7 @@ LL | fn transparent_union(x: Option>>); = note: enum has no representation hint error: `extern` block uses type `Option>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:89:20 + --> $DIR/lint-ctypes-enum.rs:88:20 | LL | fn repr_rust(x: Option>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -80,7 +80,7 @@ LL | fn repr_rust(x: Option>>); = note: enum has no representation hint error: `extern` block uses type `Result<(), NonZero>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:90:20 + --> $DIR/lint-ctypes-enum.rs:89:20 | LL | fn no_result(x: Result<(), num::NonZero>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe diff --git a/tests/ui/lint/non-local-defs/trait-solver-overflow-123573.rs b/tests/ui/lint/non-local-defs/trait-solver-overflow-123573.rs new file mode 100644 index 000000000000..4291426e046a --- /dev/null +++ b/tests/ui/lint/non-local-defs/trait-solver-overflow-123573.rs @@ -0,0 +1,14 @@ +//@ check-pass +//@ edition:2021 + +// https://github.com/rust-lang/rust/issues/123573#issue-2229428739 + +pub trait Test {} + +impl<'a, T: 'a> Test for &[T] where &'a T: Test {} + +fn main() { + struct Local {} + impl Test for &Local {} + //~^ WARN non-local `impl` definition +} diff --git a/tests/ui/lint/non-local-defs/trait-solver-overflow-123573.stderr b/tests/ui/lint/non-local-defs/trait-solver-overflow-123573.stderr new file mode 100644 index 000000000000..9a8ab810835c --- /dev/null +++ b/tests/ui/lint/non-local-defs/trait-solver-overflow-123573.stderr @@ -0,0 +1,14 @@ +warning: non-local `impl` definition, they should be avoided as they go against expectation + --> $DIR/trait-solver-overflow-123573.rs:12:5 + | +LL | impl Test for &Local {} + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: move this `impl` block outside the of the current function `main` + = note: an `impl` definition is non-local if it is nested inside an item and may impact type checking outside of that item. This can be the case if neither the trait or the self type are at the same nesting level as the `impl` + = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type + = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue + = note: `#[warn(non_local_definitions)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/issues/issue-11692-1.rs b/tests/ui/macros/issue-11692-1.rs similarity index 100% rename from tests/ui/issues/issue-11692-1.rs rename to tests/ui/macros/issue-11692-1.rs diff --git a/tests/ui/issues/issue-11692-1.stderr b/tests/ui/macros/issue-11692-1.stderr similarity index 100% rename from tests/ui/issues/issue-11692-1.stderr rename to tests/ui/macros/issue-11692-1.stderr diff --git a/tests/ui/issues/issue-11692-2.rs b/tests/ui/macros/issue-11692-2.rs similarity index 100% rename from tests/ui/issues/issue-11692-2.rs rename to tests/ui/macros/issue-11692-2.rs diff --git a/tests/ui/issues/issue-11692-2.stderr b/tests/ui/macros/issue-11692-2.stderr similarity index 100% rename from tests/ui/issues/issue-11692-2.stderr rename to tests/ui/macros/issue-11692-2.stderr diff --git a/tests/ui/issues/issue-57362-1.rs b/tests/ui/nll/issue-57362-1.rs similarity index 100% rename from tests/ui/issues/issue-57362-1.rs rename to tests/ui/nll/issue-57362-1.rs diff --git a/tests/ui/issues/issue-57362-1.stderr b/tests/ui/nll/issue-57362-1.stderr similarity index 100% rename from tests/ui/issues/issue-57362-1.stderr rename to tests/ui/nll/issue-57362-1.stderr diff --git a/tests/ui/issues/issue-57362-2.rs b/tests/ui/nll/issue-57362-2.rs similarity index 100% rename from tests/ui/issues/issue-57362-2.rs rename to tests/ui/nll/issue-57362-2.rs diff --git a/tests/ui/issues/issue-57362-2.stderr b/tests/ui/nll/issue-57362-2.stderr similarity index 100% rename from tests/ui/issues/issue-57362-2.stderr rename to tests/ui/nll/issue-57362-2.stderr diff --git a/tests/ui/numbers-arithmetic/overflowing-neg-nonzero.rs b/tests/ui/numbers-arithmetic/overflowing-neg-nonzero.rs index bda5cef979ec..8aa0d04e500c 100644 --- a/tests/ui/numbers-arithmetic/overflowing-neg-nonzero.rs +++ b/tests/ui/numbers-arithmetic/overflowing-neg-nonzero.rs @@ -3,7 +3,6 @@ //@ ignore-emscripten no processes //@ compile-flags: -C debug-assertions #![allow(arithmetic_overflow)] -#![feature(generic_nonzero)] use std::num::NonZero; diff --git a/tests/ui/numbers-arithmetic/saturating-float-casts-wasm.stderr b/tests/ui/numbers-arithmetic/saturating-float-casts-wasm.stderr deleted file mode 100644 index 475b41388d3b..000000000000 --- a/tests/ui/numbers-arithmetic/saturating-float-casts-wasm.stderr +++ /dev/null @@ -1,6 +0,0 @@ -warning: unstable feature specified for `-Ctarget-feature`: `nontrapping-fptoint` - | - = note: this feature is not stably supported; its behavior can change in the future - -warning: 1 warning emitted - diff --git a/tests/ui/issues/issue-12187-1.rs b/tests/ui/parser/issue-12187-1.rs similarity index 100% rename from tests/ui/issues/issue-12187-1.rs rename to tests/ui/parser/issue-12187-1.rs diff --git a/tests/ui/issues/issue-12187-1.stderr b/tests/ui/parser/issue-12187-1.stderr similarity index 100% rename from tests/ui/issues/issue-12187-1.stderr rename to tests/ui/parser/issue-12187-1.stderr diff --git a/tests/ui/issues/issue-12187-2.rs b/tests/ui/parser/issue-12187-2.rs similarity index 100% rename from tests/ui/issues/issue-12187-2.rs rename to tests/ui/parser/issue-12187-2.rs diff --git a/tests/ui/issues/issue-12187-2.stderr b/tests/ui/parser/issue-12187-2.stderr similarity index 100% rename from tests/ui/issues/issue-12187-2.stderr rename to tests/ui/parser/issue-12187-2.stderr diff --git a/tests/ui/print_type_sizes/niche-filling.rs b/tests/ui/print_type_sizes/niche-filling.rs index 07da1cff27a7..5dda0da84582 100644 --- a/tests/ui/print_type_sizes/niche-filling.rs +++ b/tests/ui/print_type_sizes/niche-filling.rs @@ -15,7 +15,6 @@ // ^-- needed because `--pass check` does not emit the output needed. // FIXME: consider using an attribute instead of side-effects. #![allow(dead_code)] -#![feature(generic_nonzero)] #![feature(rustc_attrs)] use std::num::NonZero; diff --git a/tests/ui/issues/issue-23122-1.rs b/tests/ui/recursion/issue-23122-1.rs similarity index 100% rename from tests/ui/issues/issue-23122-1.rs rename to tests/ui/recursion/issue-23122-1.rs diff --git a/tests/ui/issues/issue-23122-1.stderr b/tests/ui/recursion/issue-23122-1.stderr similarity index 100% rename from tests/ui/issues/issue-23122-1.stderr rename to tests/ui/recursion/issue-23122-1.stderr diff --git a/tests/ui/issues/issue-23122-2.rs b/tests/ui/recursion/issue-23122-2.rs similarity index 100% rename from tests/ui/issues/issue-23122-2.rs rename to tests/ui/recursion/issue-23122-2.rs diff --git a/tests/ui/issues/issue-23122-2.stderr b/tests/ui/recursion/issue-23122-2.stderr similarity index 100% rename from tests/ui/issues/issue-23122-2.stderr rename to tests/ui/recursion/issue-23122-2.stderr diff --git a/tests/ui/issues/issue-3214.rs b/tests/ui/resolve/issue-3214.rs similarity index 100% rename from tests/ui/issues/issue-3214.rs rename to tests/ui/resolve/issue-3214.rs diff --git a/tests/ui/issues/issue-3214.stderr b/tests/ui/resolve/issue-3214.stderr similarity index 100% rename from tests/ui/issues/issue-3214.stderr rename to tests/ui/resolve/issue-3214.stderr diff --git a/tests/ui/structs-enums/enum-null-pointer-opt.rs b/tests/ui/structs-enums/enum-null-pointer-opt.rs index a8418943ba4f..7a9e1b3c1e49 100644 --- a/tests/ui/structs-enums/enum-null-pointer-opt.rs +++ b/tests/ui/structs-enums/enum-null-pointer-opt.rs @@ -1,5 +1,4 @@ //@ run-pass -#![feature(generic_nonzero)] #![feature(transparent_unions)] use std::mem::size_of; diff --git a/tests/ui/structs-enums/enum-null-pointer-opt.stderr b/tests/ui/structs-enums/enum-null-pointer-opt.stderr index fca62bd1c801..64e93ffaffd6 100644 --- a/tests/ui/structs-enums/enum-null-pointer-opt.stderr +++ b/tests/ui/structs-enums/enum-null-pointer-opt.stderr @@ -1,5 +1,5 @@ warning: method `dummy` is never used - --> $DIR/enum-null-pointer-opt.rs:11:18 + --> $DIR/enum-null-pointer-opt.rs:10:18 | LL | trait Trait { fn dummy(&self) { } } | ----- ^^^^^ diff --git a/tests/ui/structs-enums/type-sizes.rs b/tests/ui/structs-enums/type-sizes.rs index 50491d5ef3eb..9c933a9ef1c4 100644 --- a/tests/ui/structs-enums/type-sizes.rs +++ b/tests/ui/structs-enums/type-sizes.rs @@ -2,7 +2,6 @@ #![allow(non_camel_case_types)] #![allow(dead_code)] -#![feature(generic_nonzero)] #![feature(never_type)] #![feature(pointer_is_aligned_to)] #![feature(strict_provenance)] diff --git a/tests/ui/suggestions/core-std-import-order-issue-83564.rs b/tests/ui/suggestions/core-std-import-order-issue-83564.rs index 62b9b246cc81..6f2bdd7a38af 100644 --- a/tests/ui/suggestions/core-std-import-order-issue-83564.rs +++ b/tests/ui/suggestions/core-std-import-order-issue-83564.rs @@ -2,7 +2,6 @@ // // This is a regression test for #83564. // For some reason, Rust 2018 or higher is required to reproduce the bug. -#![feature(generic_nonzero)] fn main() { //~^ HELP consider importing one of these items diff --git a/tests/ui/suggestions/core-std-import-order-issue-83564.stderr b/tests/ui/suggestions/core-std-import-order-issue-83564.stderr index 56e10b9340c0..8665cc6d87cf 100644 --- a/tests/ui/suggestions/core-std-import-order-issue-83564.stderr +++ b/tests/ui/suggestions/core-std-import-order-issue-83564.stderr @@ -1,5 +1,5 @@ error[E0433]: failed to resolve: use of undeclared type `NonZero` - --> $DIR/core-std-import-order-issue-83564.rs:9:14 + --> $DIR/core-std-import-order-issue-83564.rs:8:14 | LL | let _x = NonZero::new(5u32).unwrap(); | ^^^^^^^ use of undeclared type `NonZero` diff --git a/tests/ui/traits/next-solver/specialization-transmute.rs b/tests/ui/traits/next-solver/specialization-transmute.rs index 17c55fb4d49d..caa3bfc552eb 100644 --- a/tests/ui/traits/next-solver/specialization-transmute.rs +++ b/tests/ui/traits/next-solver/specialization-transmute.rs @@ -1,6 +1,5 @@ //@ compile-flags: -Znext-solver //~^ ERROR cannot normalize `::Id: '_` -#![feature(generic_nonzero)] #![feature(specialization)] //~^ WARN the feature `specialization` is incomplete diff --git a/tests/ui/traits/next-solver/specialization-transmute.stderr b/tests/ui/traits/next-solver/specialization-transmute.stderr index 65e337003252..76ae08fdb7a7 100644 --- a/tests/ui/traits/next-solver/specialization-transmute.stderr +++ b/tests/ui/traits/next-solver/specialization-transmute.stderr @@ -1,5 +1,5 @@ warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/specialization-transmute.rs:4:12 + --> $DIR/specialization-transmute.rs:3:12 | LL | #![feature(specialization)] | ^^^^^^^^^^^^^^ @@ -11,31 +11,31 @@ LL | #![feature(specialization)] error: cannot normalize `::Id: '_` error[E0284]: type annotations needed: cannot satisfy `::Id == _` - --> $DIR/specialization-transmute.rs:16:23 + --> $DIR/specialization-transmute.rs:15:23 | LL | fn intu(&self) -> &Self::Id { | ^^^^^^^^^ cannot satisfy `::Id == _` error[E0284]: type annotations needed: cannot satisfy `T <: ::Id` - --> $DIR/specialization-transmute.rs:18:9 + --> $DIR/specialization-transmute.rs:17:9 | LL | self | ^^^^ cannot satisfy `T <: ::Id` error[E0284]: type annotations needed: cannot satisfy `::Id == Option>` - --> $DIR/specialization-transmute.rs:29:13 + --> $DIR/specialization-transmute.rs:28:13 | LL | let s = transmute::>>(0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `::Id == Option>` | note: required by a bound in `transmute` - --> $DIR/specialization-transmute.rs:22:25 + --> $DIR/specialization-transmute.rs:21:25 | LL | fn transmute, U: Copy>(t: T) -> U { | ^^^^^^ required by this bound in `transmute` error[E0282]: type annotations needed - --> $DIR/specialization-transmute.rs:14:23 + --> $DIR/specialization-transmute.rs:13:23 | LL | default type Id = T; | ^ cannot infer type for associated type `::Id` diff --git a/tests/ui/issues/issue-7607-1.rs b/tests/ui/type/issue-7607-1.rs similarity index 100% rename from tests/ui/issues/issue-7607-1.rs rename to tests/ui/type/issue-7607-1.rs diff --git a/tests/ui/issues/issue-7607-1.stderr b/tests/ui/type/issue-7607-1.stderr similarity index 100% rename from tests/ui/issues/issue-7607-1.stderr rename to tests/ui/type/issue-7607-1.stderr diff --git a/tests/ui/issues/issue-7607-2.rs b/tests/ui/type/issue-7607-2.rs similarity index 100% rename from tests/ui/issues/issue-7607-2.rs rename to tests/ui/type/issue-7607-2.rs diff --git a/triagebot.toml b/triagebot.toml index 66b8f44d6889..903569bf4454 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -851,7 +851,6 @@ compiler-team-contributors = [ "@nnethercote", "@fmease", "@fee1-dead", - "@BoxyUwU", "@jieyouxu", ] compiler = [