diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 293e65ce872..2c176828c84 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 6eff70410cb..4cbdc9f256d 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 00000000000..d21cb82f83b --- /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 13f27c1c95c..10886aace53 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 fada69c4e6d..dd87a5c4dc3 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 ec0d4af599e..61cd085eef5 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 dbea317e7bb..a505924fabd 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 7fbf4c47ec8..a69b644c4dc 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 0600a105459..61fa8466674 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 8b0b9123ac7..efa4be7e15a 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 f07421431da..f428c4c7c0d 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 ad863903cee..8d52fd9d7a8 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 add77880716..5a7ddc4cd7f 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 160f361b9b5..4c2bdb2f5ec 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 a62dfe13204..9e85c2d88f9 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 2af28146a51..12a846a49ec 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 140566e8da9..085ce15d81f 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 83158f6f1d5..a10dc61967e 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 fad6f439441..85fcc4f4f40 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 b19f52182b6..cce3f0e6f2d 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 452398e6d82..64c13fa7c1f 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 6725a6d9e38..7823d4c249a 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 c3f26da8a79..b29034e991e 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 c0e27e86d50..908c4da71f1 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 50420aaec04..d27d42737cd 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 a499e4b980f..bf5592c828f 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 b82a9a909e6..2b799d6f5d3 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 5d345e788e9..a17e0da47a5 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 8d6b22a9fa9..e3363e89594 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 cb28bb4e8e4..36ef8fe7816 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 2a796ca5465..879476f890e 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 0ff78ebff99..06b5ee299b8 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 3881e240ced..a43a67d50d8 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 bd64621f077..fb4a76bf089 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 c2205815ba6..02291cc603e 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 520bf1d9f40..8a9b5fe6369 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 d340a08ee79..dd76862451c 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 c374f9762d6..66b86c9eb02 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 a64d3b9633f..dfd0b7c2945 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 8923137fdd8..48b9142b014 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 bd2454f6368..0b684fdf57b 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 d9e5289f632..b729fbe844a 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 02759064abd..d57015282de 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 46227e406a3..e74c4d06b5b 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 1462037c8c8..bf6ee044a0b 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 29c9f08a166..feef64bc6a5 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 008b75b4c9a..6a42f9b42c3 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 355fcf59495..81130d69151 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 31ceb234332..c6ffba59638 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 d0ce23dacb5..75df006a56f 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 9d53f0799ea..8d741ef4c1b 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 31c80c4d75b..086c3834410 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 4b06278330f..004c2c2e4f4 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 0998b463a88..e842f47f48c 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 6e11fd629e4..565bdc3af03 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 f133a2f5f73..7dd03407bd7 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 e52a5863fd0..aadaca18326 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 e30b6b203d7..123b32f4aea 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 582a1806688..04011fd4194 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 43e1318a75a..9eed7019782 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 15bd5c08965..7a91d7383e5 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 105f30f1db8..e3f58729fbd 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 97c3eb55638..db13bb9a3e8 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 506003ff7c0..abe99f3e95c 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 4f7b2f7cbe4..a6d525230b0 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 0d625ff0fae..4ae79399ef4 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 66130a8dde0..d52b1efce4b 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 e984f543701..a99196cb363 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 f8f59fbeab4..4002d0da790 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 d0a69c6fd2c..cc026e349d7 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 0bd009cd51d..e5450182bf2 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 90c68e7ddfc..14a77d4b37e 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 6084e8d7cab..0184ff54979 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 9af665cfb6f..42e0565db83 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 1de691f32a7..34440c60cf3 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 ab0043906b1..9e9ccd3dc2d 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 9730473c428..f46dceeeedf 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 26f10fdd333..9ddfb12bf76 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 d382d2c03c2..0b15c52c281 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 65715253647..f77ee63d02c 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 03ede886688..88f18b72085 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 adb0c9f1929..64f21d74b1c 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 8e8d78226c3..24832086b16 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 625d8f53939..7dcff458ced 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 99c7b616f1b..8c5f965108b 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 ff786d44d6a..fd768cc96ae 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 2744026a7c9..b8dbf8a759f 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 7e8920604c1..da63fcf23d9 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 a8a576e4efe..1411d9be223 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 fa6906bdd55..1c85a604053 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 00000000000..80eadb9abdc --- /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 36d623fd93e..a8fa6fe002d 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 5a92657cb40..23e07890bb6 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 e03052bcfed..bce29e2af09 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 3373835d813..914481d712e 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 534937003eb..2b3fa7f6cfa 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 6a959a99e5d..416f556f57d 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 5a9403e0a85..3e1d8f3828b 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 8ad64897e83..d5b22f841d2 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 c63af90a7f3..58e1394c090 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 8567591cbd9..d5108058948 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 b63c119eee0..2bc14b43234 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 61bbedf9eec..fd31c020f89 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 c9f66612590..7c021621103 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 112e44f674e..4abf991fba2 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 46bae1c1e98..2d3053ccee6 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 f68668a91e6..f1c3512315f 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 6b171af47de..1b507bb2a15 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 8b5c029428c..68b0db21141 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 ebf2a0d9621..c662ab23c53 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 e522339358a..ed76ea74f08 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 73e94da165f..f9e7ed9dcbb 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 db3794c1c40..9d3caaa01ab 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 fbff78304ac..8cd9f39d5d8 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 8f5a30c436d..88ebf8754d3 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 67865bfcaa3..116e17c7e43 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 c415d288b8f..40d206b92b8 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 10370c7898b..fc12fed3537 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 51b908881eb..cb95239e991 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 a8f9afb87dd..c1661fa63a8 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 a652bb78116..d7d31a88c9b 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 94c552199bc..e82d1f4813e 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 1ad05633d62..6f666406c22 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 a032a180fcf..394038926f6 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 dec04d7e421..88faf5a9c7d 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 1930be65bfb..b2e22d8715a 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 a34bce66496..b5175a8487f 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 bb61224b5ba..3807cd5d76c 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 2a447aafe72..05874ab6c4c 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 65ae4831839..a93b94867ce 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 00000000000..0eb8d7bb328 --- /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 0f77a2d8343..c3bd18e30aa 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 9406efd7ab2..92f1bd27408 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 35092ec51cd..3b126785728 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 95c03043e3e..7c1c6122efe 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 e34e9b7fff6..a92da88dd3f 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 9ebbb4ffe80..443401c5dba 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 62ea7abf652..b9d771ba359 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 ba6a243041c..c13763243f0 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 34dfa9e4c51..1325d90e4f3 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 ac808bec50e..81d5e5c949e 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 e8e23f2a7ec..bda1ee6f457 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 25a06f121cd..1226c8e2419 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 dd2ad9a58f6..1b3d6729663 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 e741149e7ce..7bd962fa260 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 2c82eda9a58..7b55c2bf8a8 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 c5743eda3e8..ffe8ffcc7f2 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 c8db028b651..1ceff2e506c 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 1293abddaf3..409ead0e284 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 241a443fab9..aa908f0499f 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 1343fdfd1df..fbe68f7e303 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 69cc61b30ef..4a73a9be88b 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 48e83b04ef4..203c7180001 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 48cc8633e93..4765a286e6b 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 206e62bb5e2..162c3fbd97a 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 7df81ba8c3e..c0257c1660e 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 f3c22061d25..f3a26e25938 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 d6e60d52d63..3cfd0240794 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 4cb67b7aa62..44fb1911fc6 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 927d72e8ccb..8235d4634b7 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 9e103a350e6..5bcaeed7faa 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 33323ec1e5d..01b5e99116f 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 50149d370d5..c20653b5dfa 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 78176665929..0eca20901b7 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 a22cbeacf01..38c48bd9570 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 fc9f9789bd6..9fc65a0a73a 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 767c0f69364..6a75f35c93a 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 7cb15fe5590..d4473e24039 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 7bc68b5aec1..c0683cdda1e 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 1980980b6d0..ca10a128b98 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 45b1d5a05f3..994f0bef0dc 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 deb782cad0c..f88c1b3ee82 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 27ab6eac03a..adef9ebd0e3 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 178df633cec..caec46366dd 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 2bb899a064f..2acce627359 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 99412848abb..23556e8bc5d 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 0b6d987f87d..663a2d8e17c 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 7e59b7f6f57..540b8671333 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 a40ee189001..928c9aa4dff 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 c5c718a892f..d7d20e57bd1 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 16e0c2ac185..462b76d03bd 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 cc86e3bab0c..2a950e669b9 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 5c459e5cd18..00552db4b01 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 00000000000..95471895fe7 --- /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 ae8dfadec73..0eff7ca5962 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 8875e226fd8..f874b2ed475 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 a3e8f6176a3..6a09ab3065f 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 00000000000..ae28da0bdc7 --- /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 3131aa4642c..d207d894cc5 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 eb3eb80e106..0c68e90acaa 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 55694913b13..5854fcc2865 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 b77a34bd463..07425fed36b 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 35bf2c27ff1..673ff12b5ce 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 185a3ba5dbd..bbd81e7437c 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 f7836f18cf9..2578b71a158 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 a6246fa3bd8..ed55bcf4f35 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 450573cc6c7..5e192d9aca1 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 392a5a11967..f3972346bb5 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 e4cdf965eb6..233a2670ed2 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 140c3aa0a64..3231f9fd3a4 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 00000000000..dfb001d8e39 --- /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 f5d7ce1c5f9..8aafbb3e399 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 2566124a037..aca780e33f0 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 db2cd01ce0b..0070d1f3ebc 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 d44d04e9bf8..0c0ac4c6036 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 2e19c9ff713..e1c0da9118d 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 fe618f6c819..f1d0ccc7d03 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 00000000000..f16206f3db6 --- /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 00000000000..9cae4331caf --- /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 00000000000..9cae4331caf --- /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 97c1b64494d..0d99d06bcdd 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 08ad10c2971..a10345a7060 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 de61b2389ae..b6cd4a795a8 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 00000000000..da65b034be3 --- /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 c7cf4479b33..a6e460134f2 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 d9343d2b963..f7e3ae51dfd 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 8c3f6f8468b..552f71f1518 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 27eb05cd4dc..240af7925cc 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 785ff9ceffa..2b64a07a5a9 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 454d6fc5384..9a365889e6a 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 62fb429a63f..a1ae15fcdda 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 091146a0010..8ba7018316a 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 4ee86954acd..5dfaaf77420 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 9f761c9ead1..c136dd13664 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 523ff6fc404..41c59ea0d93 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 fa7730f302e..d9eeffd7983 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 1a63e96bfa9..9b68797fbf7 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 da790f11516..e3d750d33ca 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 a7461b78af1..0fe73418e51 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 30d52d87f19..55ecabdc38e 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 b0872fcdc0e..73ce942c580 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 0cd4a5db8c3..bf728a71079 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 4638b377197..acc60e1d9e4 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 2b059d1f8dc..a60b9f9f3ab 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 585e93ce21e..610480736cc 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 f02163cbe44..4b5ef56d782 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 0c84057950b..cef2a3fb866 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 46898ce542d..88d4572196c 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 89c1b446081..163211fea52 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 23b10cfd8e6..8904aca9f28 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 ae8f028e488..0a6cd0fe9ed 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 4f308080156..ca05618aecd 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 f1540498f26..f8bf88d83cd 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 528038a9ccf..94681b42a9b 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 fd3e4e7a4db..4d6fe6db396 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 db3558a84e9..9dd44262ba9 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 5461c1c49a3..d7233a8923f 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 9a0b218e6d1..66465ce6005 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 959595afb57..711acfeb3d8 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 db8bbeccef8..338bd25ede3 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 0b69799e6bf..8f74bffc2b9 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 bf473740166..a83ee9824e2 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 cb118a36848..41acd3555eb 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 d1aebeff261..0bf01b0bc6a 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 705609ba68d..f09277a92e6 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 38eb3371e3c..ecbb1d4c60e 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 20b0da441da..a5a42c52af0 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 f45beb4c92b..c171dbc1700 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 cbca0e801d4..081b4d83a86 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 8740ae6797c..a357e850351 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 be3b50e1411..281386e1364 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 f8c03ee2886..060b5f36f29 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 32845ac2e36..a25498eff33 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 cfbbc9dd6c0..72928851f12 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 35d59679355..d011a62e77a 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 09a4d998ee1..1b354935a5b 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 afb89fe1e5b..b68fefc5150 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 532b650e8ff..7546369d8d4 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 ba64f5c8d7e..1727cec9893 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 25ccc84c13c..4d0516ead67 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 73b07df56f7..cd723494713 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 d4d669182f2..fee3dd3ada8 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 7e582c03efc..151f65cfbb8 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 be81915bb40..4ad00909e41 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 d6557c3a816..a384c9306ee 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 80f92eaf435..119de7f050e 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 50692674996..e8369caa771 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 80d5a0ae001..4355881d729 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 8565b60210b..c2d2047e6f9 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 f39404593e5..a9d28ebfef5 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 759af18c98b..dfcd322a39c 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 afd4d1f271d..42c7a840328 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 190722075a2..6d7ecd1e50b 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 1d74f9a4bb2..cb5f5b06aef 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 23c6b078b96..84f03d111f2 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 106056c2fc3..bcd94a611a9 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 9796009cb45..e792e159acf 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 d4d6f0b243f..434e4b5a0cf 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 c1b95bb1e25..24a32086f3d 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 62696d1a9a8..b90bccb48ed 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 00000000000..5459bd334c8 --- /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 65ce3e822c5..34326294d2e 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 ff051fa870f..db94a21a6d2 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 a90fe83857e..44307ffd75b 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 67fea772c79..7c2dc0e0c10 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 d67b259d2f5..0f0f13bbc80 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 00000000000..0fa46ef43a1 --- /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 8f0b8f861c2..0df5e913a57 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 a66e199a75b..937e78f8d7d 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 8bd5d179331..bc0c9b79c75 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 361ad821f4a..c6e243b31a7 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 e4678089462..2361d14aae7 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 995e3f48253..8b435f419c7 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 e667e2e0168..7d710f1e130 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 3718dff56e8..64a32dee3d7 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 97709728656..66f1bff7c1c 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 071e1b47179..9a6826a5c42 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 ec05f6d13d1..ce9a5f0dd29 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 72ca354365e..58e77b95c32 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 bd5c464c557..e97f1b86143 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 10c285a13fb..9d1f1cc09c6 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 4b6d54b5eab..919c1273e5a 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 270cf844c65..c3ced36a696 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 4d6809efbe1..ca937a03f82 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 a090b60413e..2e7e230e5ac 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 4b0961cbbeb..e2d629a02fc 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 813691540f9..568906a098e 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 2bc07211231..c1b7693a650 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 e20e0b67f4b..6f32ce76b22 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 822751c0e4c..95de3c88c8a 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 abedbff831a..3f0fc851344 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 08925fcdff5..6bbc8b380d6 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 dda38ce77e0..15eecd1b54a 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 815a4ba7fd7..9d8ba90b2ff 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 ad48d803895..431aa30e56f 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 6e8a6d020cc..57356152836 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 fef2aba3c61..01af864cdf5 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 79324bf3877..b6c6753755c 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 3fef16df25e..ca013da7099 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 e7346cbb992..a72f505eb85 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 5c7a463ccdc..e329023606a 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 7ba1194d675..5234d362c26 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 c2990fd76ea..901e41d27cd 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 1065d5899ab..05cdf430efb 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 c89722cc40d..c1a768833b9 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 48e84a7b25e..b6f90ec53b8 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 79d6fe36b56..31b0c8cdec5 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 4d5531ae307..f4bbaef7af3 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 2f2c0aa6ff5..cfad8bcc0b4 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 3170834d54f..78d4bfee2a1 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 3a853512660..d5de56312a3 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 c934db6b715..3230eeb5bd8 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 a261b1d4319..bbfe378200d 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 e3d12d87078..9c7d7af7b14 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 00000000000..26178544f9b --- /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 86c771c0008..c7ad025f6b0 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 051461243af..5b901f911dc 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 6ecfdc9f466..ef83420c523 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 c65219b28dc..0e040965261 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 2f3c7febc04..f92b39edb76 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 cd4894c1e8b..88a89a53a76 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 fd491644648..0ab16c38c87 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 00000000000..07f914fece0 --- /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 a8c732f3154..874d1c6cd38 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 c76c201d69e..6a0ae362d88 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 00000000000..07f914fece0 --- /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 d40eb26063d..fbd423c9eac 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 51c1b094f7b..ff7cf144aa8 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 af682904b19..b409bc1ce7a 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 00000000000..762e01c9177 --- /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 28696aa3277..7f3e35ca5db 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 512588cc8f8..fac6eb8ad3e 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 501b1fdc8c5..4f69b2b96f0 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 fc0b507b332..fd09dff503f 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 b8c5885108d..98c5a02dcdc 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 0ad19ca9f75..c2a2d6ed911 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 0ad19ca9f75..c2a2d6ed911 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 e2334dca875..c291ffcca7b 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 ccaba963ded..80c91365894 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 6d70124188d..cd3349899e9 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 815a98980b9..6a5f7b02624 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 fdd77199aa0..a1eea8839eb 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 757f2dd70ca..ead4d706e65 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 b2337300997..6964977840a 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 eeec13a14be..2f9394d0ee1 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 7475a8e6e6d..e956791d9df 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 a0a53f545c9..65a9a491493 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 7c4deac93f2..3d3f9440199 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 8516ffa0dfa..e9bca19af64 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 b5c4a4f435e..4b8c3d06ce4 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 77692ed3ae7..cf97d7d9d23 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 2731e845f35..7b385ca9d96 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 eac982f1b27..12f8e71c981 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 3e00222b752..991c10743f7 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 d8bb12528b9..d02f4612dc1 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 38df3235125..f37b25fb955 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 771a5599f6f..5d8a66cabc7 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 439b006977d..b87f02947bf 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 8bbe6ff3724..f04962a7a27 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 78da4487d4c..34439391333 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 223da9b5290..14238e2fed5 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 9df41e03c16..5ecd1b8a058 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 5983765eec7..659797d6d4a 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 3b5bd7f9e3b..fd31ab20416 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 75204c8ff60..cfafa40ce33 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 44abae3170f..7b8e6840fc9 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 47a2dd1ce0c..3b2d7df3fbe 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 922ec5a7752..f04f48e3bab 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 a7bbc088f9c..b060d8aab68 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 c065e7e2bde..bfa9cc0591a 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 fe807598873..f86683ee77a 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 1ff85a3ea45..edad551842d 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 1a8ff33b2ef..1dfde639869 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 559b0338608..204c0883b85 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 e11cabfe11e..4f3341f5150 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 dd223eeeba9..ed1d499f637 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 7ac21dec1a8..0dbea1d563e 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 c0e6416f4a3..e0acf44041b 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 295b716b4e9..829b4d5b0ff 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 cbda91f0a59..9f85f0107cc 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 c9109c72d0d..8ca7bc2d38a 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 1f396a1e97b..81fc56c961e 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 01f2af419ed..0e62de5febb 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 e9ab7a4320b..168ca9f1328 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 41d33c457ce..2445e4f1a32 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 18a56e2823a..28a9dadacef 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 ff18fee9bab..186f1b01da4 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 28738671790..682dcd7cc44 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 a3023c3195f..36615d11d85 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 1bb82cc191f..3a9ebafe87d 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 d750476f63c..420f4938e54 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 14715b57253..354b89fd490 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 fb8aee9c3b0..ed4894f9b9c 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 439daa358a8..f0d58efc01e 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 c8d785f83e8..89ed6a61579 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 7e34c361899..aafe4fb5b10 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 4cfdec2b5c5..45bb777d4d2 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 7eeb10d544a..d7d283c3eb8 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 2146df185d3..3cdb60dcb26 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 939b1819c7e..f1815082e2e 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 00000000000..a85843721c6 --- /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 00000000000..f3154279cf5 --- /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 00000000000..25ddbb2efaa --- /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 00000000000..592ed5b6dbc --- /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 00000000000..23d1507136f --- /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 00000000000..fc1a6530bc8 --- /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 00000000000..bfa1f5b7dd4 --- /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 00000000000..80c415fe07b --- /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 00000000000..fdd58c84794 --- /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 00000000000..e9cbf3f7086 --- /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 00000000000..c03db384e76 --- /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 00000000000..5e55ac2aa94 --- /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 00000000000..8c9b4bddbe8 --- /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 00000000000..46948207df3 --- /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 00000000000..7c193ec1bce --- /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 00000000000..a4e1c551890 --- /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 cfac14a22c4..0293ec0ec39 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 1ff72d34fbd..98bc31e8855 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 39a7b5aea12..e4b252f5565 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 500b8b7f7c7..cd44c31d000 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 0a23cddf620..a9342c13e15 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 8c016629770..a2d3ab94db6 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 038caaa0ae7..e65b2ed9a87 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 ba987c1c26b..0681d4356dd 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 86ffdef24ab..46be3e1e36e 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 319bcf48411..b880d7e07a6 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 c6bd5766990..6d0c0f8ad52 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 2db47707772..8f8f92bbba1 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 2a9ca5f95e6..aff72268223 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 c411d3b59ab..6c576718ac8 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 aa09c1639b3..b7623dc23da 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 25d513e2132..0f8d278535d 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 cbc106aa41c..f23749312f2 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 859491cf361..7813352261e 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 ad95515b41b..dce2e38fa91 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 6f43e6abdc1..0560b049573 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 eb336827dc2..39b5f289830 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 51542afa4bc..aa169eb4e36 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 5c469c5d844..ca53e2b2f2b 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 b2a9d5db367..32ff8f142b1 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 e71a05ce760..c37f3fdb864 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 1df82f2ee79..afd8746af5f 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 30a9d62d499..03f34969bf3 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 bf788b924ce..e13c7442916 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 1cefc85676f..afba73f6a17 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 d79f3e85161..7943c74c9ee 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 4445bd22480..9698fba6a11 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 c2b2731b2a6..7de647ed9c3 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 c9f09f878fe..5656c0e7a68 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 5b7804b1164..6f99e6be246 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 9ea2e78d8b2..8289832f81e 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 39ac1fa3c94..cc92949128f 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 7d258f2e362..105cbfb53dd 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 535870fdf88..a43558223fe 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 840a4d65f3d..f148a5b6542 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 ec2dbf6485a..05fec2f3303 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 20c1fba5209..aef36323cc0 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 59e7f1a50b8..5a48a887f93 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 55dca24f3d2..ffce4e97f5d 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 d881462b877..d7781913895 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 fea28c93dc3..e7eea11ae49 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 e0777468350..9d02f24e76b 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 4a48f92ec2b..63cdbf01b3e 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 a176bf14438..114380e316d 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 5f2d7671159..9cbf8928753 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 5992bb151d3..582411c7b59 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 6a421da0807..cb2c8362e14 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 24b8e45532c..74ac6281a89 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 0944bb3d59a..fc9b8dc41b1 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 7d123e64950..8937c0d2ecb 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 3516d8f7f62..e062e1e9728 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 3577ed2a4a1..a82d4618e68 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 ed97e86f83a..1c0c9eae7fe 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 f6e0eac6c2c..24420e19fa8 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 fdf42876909..4b187429401 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 d9bd08b1bcf..5e8fc8df42e 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 85ced0f6c0d..49834053432 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 d68ffad78bc..231e4082e33 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 610f5401084..14ca513c692 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 d8b42b7f96e..34e7eabc81a 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 46390556418..88e94988181 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 7d02fb328ee..2f1720556cd 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 00000000000..e60f71f47b1 --- /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 00000000000..18764b38d6e --- /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 ae63990f253..beb88b607f9 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 7db76339e55..acc544a28af 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 df8baf77add..daf9c7729c6 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 e2b7dbc096b..11faecec6b1 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 bd6141eedd1..a70cc8ee6a2 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 d3d0938168b..a73693464f9 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 617501217cf..3a0cbac328c 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 82752750eb1..5c52f92cd8f 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 7df3bb9c42e..8cd8b2c2bec 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 d0063a8e7e4..7ac59befc8a 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 9231bb22c4c..e7960e3fba1 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 3a4159ab105..e74fd88d002 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 b15fba29bdf..3cd0b715a52 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 867890dcf25..cd4e1f6990d 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 aa669fffd44..399de921a59 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 bebedbb9464..e32c0d0877d 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 ace38364ee3..6152724c98f 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 4171d2991ae..f5897bfe37b 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 d958025c707..11fe3849a1e 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 ad24c5855b8..be7f311cdc1 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 08707779e2c..5d9733f498c 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 0180e978a09..eed782c9036 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 d33f3216933..aac5d11d3d4 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 7cf0dad5e48..e7f93f421cf 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 5d7c38970f6..d624e21f21a 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 2ce1e9023a7..edb35061cc6 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 ddbc89a7ae2..795d57d36f5 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 d276f6fa025..5e4a286a208 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 20517a00489..b2523684a09 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 7979fdfe768..3a164cb09a0 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 bdaf83fcb5b..b1fa2c8733e 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 89de514a280..7f9272bdec8 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 e225db5bbe8..5de6a61eaf2 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 e30a286805f..3ca2144e4fc 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 4efc735b22e..145a19ee6a3 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 cd551307285..481d4130c7b 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 f84b5fde8d8..db4969924ff 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 e6663956d78..e414daf20f2 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 b3e370966d0..5c218a328f5 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 c241d71594b..06445dc8703 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 4aa6b6a4876..8e5d6340e56 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 ae72eb11735..ab748940ac4 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 bfeb1f7bbc6..b047c50df97 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 2d215621bbd..d2a3e1f59ff 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 59bc19ceecc..789b5ebab80 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 6b48393e6b9..307c6e579c9 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 74a2af884c0..a22be312a9b 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 c5b3e61a4cb..cacc7301f12 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 6f4d1e35585..0484710f00e 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 9d83a19e4a5..1812de16d69 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 5f9df7953c8..015949c5d20 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 2f3f6433045..4cc2c2b9021 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 337f617e703..4246eef08f7 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 3e006016f8c..bb2da3ac515 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 f6c3d6a58de..bebe6938461 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 be73bc49de5..4326ff8a11b 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 794ce3dabbc..e4fc94ec436 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 47984edd669..d1d903f9ef2 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 adcf325e3f5..a7786fa570f 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 4b4054a7a2d..86455e8b52d 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 d0c85595dbc..7b1f3d14f4f 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 3707ee17690..0be7466001f 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 fd09d632a4f..454728249b1 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 c14b1ac5a21..c031c978162 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 fc568abcd60..5f13f5ba059 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 c40eaa1f2a9..84a36f1374e 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 eedb26ad41a..57f4e4a2654 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 7fcea75aaaf..1c30c4c89b9 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 00000000000..02934d4c01e --- /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 00000000000..02934d4c01e --- /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 08366417d7c..12e526ab074 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 38d5e984cee..b82094dc18f 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 31ce51dc6de..1f20349fdec 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 e1b537b1b71..4b435310916 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 cabc5a44cd8..8a390736add 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 9ee01a87a91..72ad60956ab 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 aa9fcc60a3e..043fdb197a3 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 ca3e5f747d1..fa466220b65 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 11a18f58e3a..157f9c98353 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 809badc41ba..19083771fd9 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 e855c913226..f0be82d0257 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 3087c98d052..18814b0678f 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 adb787a09fb..5d2894a704a 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 845673601b2..af0a0efc2a6 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 845673601b2..af0a0efc2a6 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 8dd5c4b495e..56f8e3f8338 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 00000000000..db0c84bd560 --- /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 00000000000..db0c84bd560 --- /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 00000000000..766bd29ef41 --- /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 00000000000..766bd29ef41 --- /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 00000000000..88ee00296a0 --- /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 c9dd72d8be2..04bbbff57b3 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 00000000000..018ff6c357d --- /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 00000000000..018ff6c357d --- /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 00000000000..3476e1760c0 --- /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 00000000000..18728d543ad --- /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 00000000000..18728d543ad --- /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 70587dff0b5..2dda771ba7d 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 c53c3875045..4a928b77452 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 17b3c10abeb..43d74aa5726 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 995bd11a1a9..0c29d1faf02 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 d4de85622d4..686eac58236 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 756679e77e3..f57611111cf 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 3a461647e36..70d1555e786 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 52afb4f2c52..6257f5ee795 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 3f5c99404d8..6c18dbaf5a2 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 cbe4b989530..a177dbf71cf 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 b07b8230faf..5838b35a553 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 b7e0f6eff9b..695e3c4e8fc 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 156b23657b7..6005dc546dc 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 00000000000..f328e4d9d04 --- /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 00000000000..129e424f27a --- /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 3ee4542810c..373d1cce1d7 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 00000000000..6d02dcebc0b --- /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 00000000000..9cae4331caf --- /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 00000000000..cf927e34fb4 --- /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 00000000000..349546606a5 --- /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 feb4f21b0ca..557fdcbf38d 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 dceb6f4c030..1863032c386 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 00000000000..0d1f023d565 --- /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 00000000000..a49dfc31916 --- /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 00000000000..2ce998e9fd9 --- /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 00000000000..e4845405ec8 --- /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 e5dfd5ca293..2fbf135c997 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 76bd5248ffd..10d304436f8 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 15d3e883456..777484c6b09 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 00000000000..8a24fb799b6 --- /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 00000000000..c977c698a92 --- /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 d2f25aeec9b..e645b574075 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 00000000000..d5f516eefe2 --- /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 00000000000..198c9496cb4 --- /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 3a173efb32d..10c2a811243 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 00000000000..35b28d0e6fb --- /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 00000000000..13aaa999707 --- /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 9c99f2b711e..3f78e7c56b6 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 6eaff01183d..d359ea5e26d 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 a8eb4547dcd..26994d0bdbf 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 00000000000..35be600fd6a --- /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 00000000000..f0b957906af --- /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 ca82dc5bdb9..f6e5421f878 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 a62e0554f76..50c01ed29b5 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 b1ac7528d58..67b9832d601 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 091c3a2f316..e28b9577347 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 568eb819be2..fe51c9925e8 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 058f5923487..0c37c0f010e 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 af049125de4..ee34cfdfb0d 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 cb63af0ea42..728dfabb393 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 86ee789aeb2..43c8cdead9f 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 1d2f23aaaf6..29e8e6cfef6 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 3157b6e240a..c60290f8553 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 48be3eb5a56..103fda8d402 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 00000000000..4291426e046 --- /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 00000000000..9a8ab810835 --- /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 bda5cef979e..8aa0d04e500 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 475b41388d3..00000000000 --- 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 07da1cff27a..5dda0da8458 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 a8418943ba4..7a9e1b3c1e4 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 fca62bd1c80..64e93ffaffd 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 50491d5ef3e..9c933a9ef1c 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 62b9b246cc8..6f2bdd7a38a 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 56e10b9340c..8665cc6d87c 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 17c55fb4d49..caa3bfc552e 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 65e33700325..76ae08fdb7a 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 66b8f44d688..903569bf445 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -851,7 +851,6 @@ compiler-team-contributors = [ "@nnethercote", "@fmease", "@fee1-dead", - "@BoxyUwU", "@jieyouxu", ] compiler = [