Merge from rustc

This commit is contained in:
The Miri Cronjob Bot 2025-04-07 05:24:10 +00:00
commit 2eb7791a1b
994 changed files with 9961 additions and 8374 deletions

@ -1 +1 @@
Subproject commit a6c604d1b8a2f2a8ff1f3ba6092f9fda42f4b7e9
Subproject commit 0e93c5bf6a1d5ee7bc2af63d1afb16cd28793601

View file

@ -199,7 +199,7 @@ fn check_simplify_not(cx: &LateContext<'_>, msrv: Msrv, expr: &Expr<'_>) {
&& !expr.span.from_expansion()
&& !inner.span.from_expansion()
&& let Some(suggestion) = simplify_not(cx, msrv, inner)
&& cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, expr.hir_id).0 != Level::Allow
&& cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, expr.hir_id).level != Level::Allow
{
use clippy_utils::sugg::{Sugg, has_enclosing_paren};
let maybe_par = if let Some(sug) = Sugg::hir_opt(cx, inner) {
@ -605,7 +605,7 @@ impl<'tcx> NonminimalBoolVisitor<'_, 'tcx> {
}
}
let nonminimal_bool_lint = |mut suggestions: Vec<_>| {
if self.cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, e.hir_id).0 != Level::Allow {
if self.cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, e.hir_id).level != Level::Allow {
suggestions.sort();
span_lint_hir_and_then(
self.cx,

View file

@ -69,7 +69,7 @@ impl EarlyLintPass for DisallowedScriptIdents {
// Implementation is heavily inspired by the implementation of [`non_ascii_idents`] lint:
// https://github.com/rust-lang/rust/blob/master/compiler/rustc_lint/src/non_ascii_idents.rs
let check_disallowed_script_idents = cx.builder.lint_level(DISALLOWED_SCRIPT_IDENTS).0 != Level::Allow;
let check_disallowed_script_idents = cx.builder.lint_level(DISALLOWED_SCRIPT_IDENTS).level != Level::Allow;
if !check_disallowed_script_idents {
return;
}

View file

@ -2,6 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_help;
use rustc_ast::ast::{Crate, Inline, Item, ItemKind, ModKind};
use rustc_errors::MultiSpan;
use rustc_lint::{EarlyContext, EarlyLintPass, Level, LintContext};
use rustc_middle::lint::LevelAndSource;
use rustc_session::impl_lint_pass;
use rustc_span::{FileName, Span};
use std::collections::BTreeMap;
@ -45,11 +46,10 @@ declare_clippy_lint! {
"file loaded as module multiple times"
}
#[derive(PartialOrd, Ord, PartialEq, Eq)]
struct Modules {
local_path: PathBuf,
spans: Vec<Span>,
lint_levels: Vec<Level>,
lint_levels: Vec<LevelAndSource>,
}
#[derive(Default)]
@ -95,11 +95,11 @@ impl EarlyLintPass for DuplicateMod {
.iter()
.zip(lint_levels)
.filter_map(|(span, lvl)| {
if let Some(id) = lvl.get_expectation_id() {
if let Some(id) = lvl.lint_id {
cx.fulfill_expectation(id);
}
(!matches!(lvl, Level::Allow | Level::Expect(_))).then_some(*span)
(!matches!(lvl.level, Level::Allow | Level::Expect)).then_some(*span)
})
.collect();

View file

@ -1,7 +1,7 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::higher;
use clippy_utils::ty::is_type_lang_item;
use rustc_hir::{BinOpKind, Expr, ExprKind, LangItem, MatchSource};
use rustc_hir::{AssignOpKind, Expr, ExprKind, LangItem, MatchSource};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass;
use rustc_span::sym;
@ -77,7 +77,7 @@ impl<'tcx> LateLintPass<'tcx> for FormatPushString {
return;
}
},
ExprKind::AssignOp(op, left, arg) if op.node == BinOpKind::Add && is_string(cx, left) => arg,
ExprKind::AssignOp(op, left, arg) if op.node == AssignOpKind::AddAssign && is_string(cx, left) => arg,
_ => return,
};
if is_format(cx, arg) {

View file

@ -5,7 +5,7 @@ use clippy_utils::source::snippet_with_context;
use rustc_ast::ast::{LitIntType, LitKind};
use rustc_data_structures::packed::Pu128;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Block, Expr, ExprKind, Stmt, StmtKind};
use rustc_hir::{AssignOpKind, BinOpKind, Block, Expr, ExprKind, Stmt, StmtKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{IntTy, Ty, UintTy};
use rustc_session::declare_lint_pass;
@ -68,7 +68,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingAdd {
&& ex.span.ctxt() == ctxt
&& expr1.span.ctxt() == ctxt
&& clippy_utils::SpanlessEq::new(cx).eq_expr(l, target)
&& BinOpKind::Add == op1.node
&& AssignOpKind::AddAssign == op1.node
&& let ExprKind::Lit(lit) = value.kind
&& let LitKind::Int(Pu128(1), LitIntType::Unsuffixed) = lit.node
&& block.expr.is_none()

View file

@ -8,7 +8,7 @@ use clippy_utils::{
use rustc_ast::ast::LitKind;
use rustc_data_structures::packed::Pu128;
use rustc_errors::Applicability;
use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, QPath};
use rustc_hir::{AssignOpKind, BinOp, BinOpKind, Expr, ExprKind, QPath};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::impl_lint_pass;
use rustc_span::Span;
@ -366,7 +366,7 @@ fn subtracts_one<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a Exp
match peel_blocks_with_stmt(expr).kind {
ExprKind::AssignOp(ref op1, target, value) => {
// Check if literal being subtracted is one
(BinOpKind::Sub == op1.node && is_integer_literal(value, 1)).then_some(target)
(AssignOpKind::SubAssign == op1.node && is_integer_literal(value, 1)).then_some(target)
},
ExprKind::Assign(target, value, _) => {
if let ExprKind::Binary(ref op1, left1, right1) = value.kind

View file

@ -408,9 +408,9 @@ mod zombie_processes;
use clippy_config::{Conf, get_configuration_metadata, sanitize_explanation};
use clippy_utils::macros::FormatArgsStorage;
use utils::attr_collector::{AttrCollector, AttrStorage};
use rustc_data_structures::fx::FxHashSet;
use rustc_lint::{Lint, LintId};
use utils::attr_collector::{AttrCollector, AttrStorage};
/// Register all pre expansion lints
///

View file

@ -3,7 +3,9 @@ use clippy_utils::{get_parent_expr, is_integer_const, path_to_local, path_to_loc
use rustc_ast::ast::{LitIntType, LitKind};
use rustc_errors::Applicability;
use rustc_hir::intravisit::{Visitor, walk_expr, walk_local};
use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, HirId, HirIdMap, LetStmt, Mutability, PatKind};
use rustc_hir::{
AssignOpKind, BorrowKind, Expr, ExprKind, HirId, HirIdMap, LetStmt, Mutability, PatKind
};
use rustc_lint::LateContext;
use rustc_middle::hir::nested_filter;
use rustc_middle::ty::{self, Ty};
@ -58,7 +60,7 @@ impl<'tcx> Visitor<'tcx> for IncrementVisitor<'_, 'tcx> {
match parent.kind {
ExprKind::AssignOp(op, lhs, rhs) => {
if lhs.hir_id == expr.hir_id {
*state = if op.node == BinOpKind::Add
*state = if op.node == AssignOpKind::AddAssign
&& is_integer_const(self.cx, rhs, 1)
&& *state == IncrementVisitorVarState::Initial
&& self.depth == 0

View file

@ -153,9 +153,15 @@ impl LateLintPass<'_> for MacroUseImports {
[] | [_] => return,
[root, item] => {
if !check_dup.contains(&(*item).to_string()) {
used.entry(((*root).to_string(), span, hir_id))
.or_insert_with(Vec::new)
.push((*item).to_string());
used.entry((
(*root).to_string(),
span,
hir_id.local_id,
cx.tcx.def_path_hash(hir_id.owner.def_id.into()),
))
.or_insert_with(|| (vec![], hir_id))
.0
.push((*item).to_string());
check_dup.push((*item).to_string());
}
},
@ -171,15 +177,27 @@ impl LateLintPass<'_> for MacroUseImports {
}
})
.collect::<Vec<_>>();
used.entry(((*root).to_string(), span, hir_id))
.or_insert_with(Vec::new)
.push(filtered.join("::"));
used.entry((
(*root).to_string(),
span,
hir_id.local_id,
cx.tcx.def_path_hash(hir_id.owner.def_id.into()),
))
.or_insert_with(|| (vec![], hir_id))
.0
.push(filtered.join("::"));
check_dup.extend(filtered);
} else {
let rest = rest.to_vec();
used.entry(((*root).to_string(), span, hir_id))
.or_insert_with(Vec::new)
.push(rest.join("::"));
used.entry((
(*root).to_string(),
span,
hir_id.local_id,
cx.tcx.def_path_hash(hir_id.owner.def_id.into()),
))
.or_insert_with(|| (vec![], hir_id))
.0
.push(rest.join("::"));
check_dup.extend(rest.iter().map(ToString::to_string));
}
},
@ -190,7 +208,7 @@ impl LateLintPass<'_> for MacroUseImports {
// If mac_refs is not empty we have encountered an import we could not handle
// such as `std::prelude::v1::foo` or some other macro that expands to an import.
if self.mac_refs.is_empty() {
for ((root, span, hir_id), path) in used {
for ((root, span, ..), (path, hir_id)) in used {
let import = if let [single] = &path[..] {
format!("{root}::{single}")
} else {

View file

@ -10,7 +10,7 @@ use rustc_ast::{LitKind, RangeLimits};
use rustc_data_structures::packed::Pu128;
use rustc_data_structures::unhash::UnindexMap;
use rustc_errors::{Applicability, Diag};
use rustc_hir::{BinOp, Block, Body, Expr, ExprKind, UnOp};
use rustc_hir::{BinOpKind, Block, Body, Expr, ExprKind, UnOp};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass;
use rustc_span::source_map::Spanned;
@ -97,7 +97,7 @@ enum LengthComparison {
///
/// E.g. for `v.len() > 5` this returns `Some((LengthComparison::IntLessThanLength, 5, v.len()))`
fn len_comparison<'hir>(
bin_op: BinOp,
bin_op: BinOpKind,
left: &'hir Expr<'hir>,
right: &'hir Expr<'hir>,
) -> Option<(LengthComparison, usize, &'hir Expr<'hir>)> {
@ -112,7 +112,7 @@ fn len_comparison<'hir>(
// normalize comparison, `v.len() > 4` becomes `4 < v.len()`
// this simplifies the logic a bit
let (op, left, right) = normalize_comparison(bin_op.node, left, right)?;
let (op, left, right) = normalize_comparison(bin_op, left, right)?;
match (op, left.kind, right.kind) {
(Rel::Lt, int_lit_pat!(left), _) => Some((LengthComparison::IntLessThanLength, left as usize, right)),
(Rel::Lt, _, int_lit_pat!(right)) => Some((LengthComparison::LengthLessThanInt, right as usize, left)),
@ -138,7 +138,7 @@ fn assert_len_expr<'hir>(
&& let ExprKind::Unary(UnOp::Not, condition) = &cond.kind
&& let ExprKind::Binary(bin_op, left, right) = &condition.kind
&& let Some((cmp, asserted_len, slice_len)) = len_comparison(*bin_op, left, right)
&& let Some((cmp, asserted_len, slice_len)) = len_comparison(bin_op.node, left, right)
&& let ExprKind::MethodCall(method, recv, [], _) = &slice_len.kind
&& cx.typeck_results().expr_ty_adjusted(recv).peel_refs().is_slice()
&& method.ident.name == sym::len

View file

@ -261,10 +261,11 @@ fn check_expr<'tcx>(vis: &mut ReadVisitor<'_, 'tcx>, expr: &'tcx Expr<'_>) -> St
| ExprKind::Assign(..)
| ExprKind::Index(..)
| ExprKind::Repeat(_, _)
| ExprKind::Struct(_, _, _) => {
| ExprKind::Struct(_, _, _)
| ExprKind::AssignOp(_, _, _) => {
walk_expr(vis, expr);
},
ExprKind::Binary(op, _, _) | ExprKind::AssignOp(op, _, _) => {
ExprKind::Binary(op, _, _) => {
if op.node == BinOpKind::And || op.node == BinOpKind::Or {
// x && y and x || y always evaluate x first, so these are
// strictly sequenced.

View file

@ -73,8 +73,8 @@ impl_lint_pass!(ModStyle => [MOD_MODULE_FILES, SELF_NAMED_MODULE_FILES]);
impl EarlyLintPass for ModStyle {
fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) {
if cx.builder.lint_level(MOD_MODULE_FILES).0 == Level::Allow
&& cx.builder.lint_level(SELF_NAMED_MODULE_FILES).0 == Level::Allow
if cx.builder.lint_level(MOD_MODULE_FILES).level == Level::Allow
&& cx.builder.lint_level(SELF_NAMED_MODULE_FILES).level == Level::Allow
{
return;
}

View file

@ -335,9 +335,12 @@ impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects {
return;
}
match &expr.kind {
hir::ExprKind::AssignOp(op, lhs, rhs) | hir::ExprKind::Binary(op, lhs, rhs) => {
hir::ExprKind::Binary(op, lhs, rhs) => {
self.manage_bin_ops(cx, expr, op.node, lhs, rhs);
},
hir::ExprKind::AssignOp(op, lhs, rhs) => {
self.manage_bin_ops(cx, expr, op.node.into(), lhs, rhs);
},
hir::ExprKind::MethodCall(ps, receiver, args, _) => {
self.manage_method_call(args, cx, expr, ps, receiver);
},

View file

@ -913,9 +913,10 @@ impl<'tcx> LateLintPass<'tcx> for Operators {
);
},
ExprKind::AssignOp(op, lhs, rhs) => {
self.arithmetic_context.check_binary(cx, e, op.node, lhs, rhs);
misrefactored_assign_op::check(cx, e, op.node, lhs, rhs);
modulo_arithmetic::check(cx, e, op.node, lhs, rhs, false);
let bin_op = op.node.into();
self.arithmetic_context.check_binary(cx, e, bin_op, lhs, rhs);
misrefactored_assign_op::check(cx, e, bin_op, lhs, rhs);
modulo_arithmetic::check(cx, e, bin_op, lhs, rhs, false);
},
ExprKind::Assign(lhs, rhs, _) => {
assign_op_pattern::check(cx, e, lhs, rhs);

View file

@ -138,7 +138,7 @@ impl RawStrings {
);
},
);
if !matches!(cx.get_lint_level(NEEDLESS_RAW_STRINGS), rustc_lint::Allow) {
if !matches!(cx.get_lint_level(NEEDLESS_RAW_STRINGS).level, rustc_lint::Allow) {
return;
}
}

View file

@ -404,7 +404,7 @@ fn check_final_expr<'tcx>(
match cx.tcx.hir_attrs(expr.hir_id) {
[] => {},
[attr] => {
if matches!(Level::from_attr(attr), Some(Level::Expect(_)))
if matches!(Level::from_attr(attr), Some((Level::Expect, _)))
&& let metas = attr.meta_item_list()
&& let Some(lst) = metas
&& let [MetaItemInner::MetaItem(meta_item), ..] = lst.as_slice()

View file

@ -5,6 +5,7 @@ use core::ops::ControlFlow;
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass;
use rustc_span::Span;
declare_clippy_lint! {
/// ### What it does
@ -56,8 +57,27 @@ declare_lint_pass!(SuspiciousImpl => [SUSPICIOUS_ARITHMETIC_IMPL, SUSPICIOUS_OP_
impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
if let hir::ExprKind::Binary(binop, _, _) | hir::ExprKind::AssignOp(binop, ..) = expr.kind
&& let Some((binop_trait_lang, op_assign_trait_lang)) = binop_traits(binop.node)
match expr.kind {
hir::ExprKind::Binary(op, _, _) => {
self.check_expr_inner(cx, expr, op.node, op.span);
}
hir::ExprKind::AssignOp(op, _, _) => {
self.check_expr_inner(cx, expr, op.node.into(), op.span);
}
_ => {}
}
}
}
impl<'tcx> SuspiciousImpl {
fn check_expr_inner(
&mut self,
cx: &LateContext<'tcx>,
expr: &'tcx hir::Expr<'_>,
binop: hir::BinOpKind,
span: Span,
) {
if let Some((binop_trait_lang, op_assign_trait_lang)) = binop_traits(binop)
&& let Some(binop_trait_id) = cx.tcx.lang_items().get(binop_trait_lang)
&& let Some(op_assign_trait_id) = cx.tcx.lang_items().get(op_assign_trait_lang)
@ -82,10 +102,10 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl {
span_lint(
cx,
lint,
binop.span,
span,
format!(
"suspicious use of `{}` in `{}` impl",
binop.node.as_str(),
binop.as_str(),
cx.tcx.item_name(trait_id)
),
);

View file

@ -10,7 +10,7 @@ use rustc_data_structures::fx::FxIndexSet;
use rustc_hir::intravisit::{Visitor, walk_expr};
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Block, Expr, ExprKind, LetStmt, PatKind, QPath, Stmt, StmtKind};
use rustc_hir::{AssignOpKind, Block, Expr, ExprKind, LetStmt, PatKind, QPath, Stmt, StmtKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::ty;
use rustc_session::declare_lint_pass;
@ -307,7 +307,7 @@ fn extract_sides_of_xor_assign<'a, 'hir>(
if let StmtKind::Semi(expr) = stmt.kind
&& let ExprKind::AssignOp(
Spanned {
node: BinOpKind::BitXor,
node: AssignOpKind::BitXorAssign,
..
},
lhs,

View file

@ -15,7 +15,7 @@ use rustc_apfloat::ieee::{Half, Quad};
use rustc_ast::ast::{self, LitFloatType, LitKind};
use rustc_hir::def::{DefKind, Res};
use rustc_hir::{
BinOp, BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item, ItemKind, Node, PatExpr, PatExprKind, QPath, UnOp,
BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item, ItemKind, Node, PatExpr, PatExprKind, QPath, UnOp,
};
use rustc_lexer::tokenize;
use rustc_lint::LateContext;
@ -506,7 +506,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
UnOp::Deref => Some(if let Constant::Ref(r) = o { *r } else { o }),
}),
ExprKind::If(cond, then, ref otherwise) => self.ifthenelse(cond, then, *otherwise),
ExprKind::Binary(op, left, right) => self.binop(op, left, right),
ExprKind::Binary(op, left, right) => self.binop(op.node, left, right),
ExprKind::Call(callee, []) => {
// We only handle a few const functions for now.
if let ExprKind::Path(qpath) = &callee.kind
@ -744,7 +744,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
}
}
fn binop(&self, op: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> Option<Constant<'tcx>> {
fn binop(&self, op: BinOpKind, left: &Expr<'_>, right: &Expr<'_>) -> Option<Constant<'tcx>> {
let l = self.expr(left)?;
let r = self.expr(right);
match (l, r) {
@ -757,7 +757,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
// Using / or %, where the left-hand argument is the smallest integer of a signed integer type and
// the right-hand argument is -1 always panics, even with overflow-checks disabled
if let BinOpKind::Div | BinOpKind::Rem = op.node
if let BinOpKind::Div | BinOpKind::Rem = op
&& l == ty_min_value
&& r == -1
{
@ -765,7 +765,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
}
let zext = |n: i128| Constant::Int(unsext(self.tcx, n, ity));
match op.node {
match op {
// When +, * or binary - create a value greater than the maximum value, or less than
// the minimum value that can be stored, it panics.
BinOpKind::Add => l.checked_add(r).and_then(|n| ity.ensure_fits(n)).map(zext),
@ -792,7 +792,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
ty::Uint(ity) => {
let bits = ity.bits();
match op.node {
match op {
BinOpKind::Add => l.checked_add(r).and_then(|n| ity.ensure_fits(n)).map(Constant::Int),
BinOpKind::Sub => l.checked_sub(r).and_then(|n| ity.ensure_fits(n)).map(Constant::Int),
BinOpKind::Mul => l.checked_mul(r).and_then(|n| ity.ensure_fits(n)).map(Constant::Int),
@ -815,7 +815,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
_ => None,
},
// FIXME(f16_f128): add these types when binary operations are available on all platforms
(Constant::F32(l), Some(Constant::F32(r))) => match op.node {
(Constant::F32(l), Some(Constant::F32(r))) => match op {
BinOpKind::Add => Some(Constant::F32(l + r)),
BinOpKind::Sub => Some(Constant::F32(l - r)),
BinOpKind::Mul => Some(Constant::F32(l * r)),
@ -829,7 +829,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
BinOpKind::Gt => Some(Constant::Bool(l > r)),
_ => None,
},
(Constant::F64(l), Some(Constant::F64(r))) => match op.node {
(Constant::F64(l), Some(Constant::F64(r))) => match op {
BinOpKind::Add => Some(Constant::F64(l + r)),
BinOpKind::Sub => Some(Constant::F64(l - r)),
BinOpKind::Mul => Some(Constant::F64(l * r)),
@ -843,7 +843,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
BinOpKind::Gt => Some(Constant::Bool(l > r)),
_ => None,
},
(l, r) => match (op.node, l, r) {
(l, r) => match (op, l, r) {
(BinOpKind::And, Constant::Bool(false), _) => Some(Constant::Bool(false)),
(BinOpKind::Or, Constant::Bool(true), _) => Some(Constant::Bool(true)),
(BinOpKind::And, Constant::Bool(true), Some(r)) | (BinOpKind::Or, Constant::Bool(false), Some(r)) => {

View file

@ -114,6 +114,7 @@ use rustc_hir::{
use rustc_lexer::{TokenKind, tokenize};
use rustc_lint::{LateContext, Level, Lint, LintContext};
use rustc_middle::hir::place::PlaceBase;
use rustc_middle::lint::LevelAndSource;
use rustc_middle::mir::{AggregateKind, Operand, RETURN_PLACE, Rvalue, StatementKind, TerminatorKind};
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
use rustc_middle::ty::fast_reject::SimplifiedType;
@ -1976,14 +1977,14 @@ pub fn fulfill_or_allowed(cx: &LateContext<'_>, lint: &'static Lint, ids: impl I
let mut suppress_lint = false;
for id in ids {
let (level, _) = cx.tcx.lint_level_at_node(lint, id);
if let Some(expectation) = level.get_expectation_id() {
let LevelAndSource { level, lint_id, .. } = cx.tcx.lint_level_at_node(lint, id);
if let Some(expectation) = lint_id {
cx.fulfill_expectation(expectation);
}
match level {
Level::Allow | Level::Expect(_) => suppress_lint = true,
Level::Warn | Level::ForceWarn(_) | Level::Deny | Level::Forbid => {},
Level::Allow | Level::Expect => suppress_lint = true,
Level::Warn | Level::ForceWarn | Level::Deny | Level::Forbid => {},
}
}
@ -1998,7 +1999,7 @@ pub fn fulfill_or_allowed(cx: &LateContext<'_>, lint: &'static Lint, ids: impl I
/// make sure to use `span_lint_hir` functions to emit the lint. This ensures that
/// expectations at the checked nodes will be fulfilled.
pub fn is_lint_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool {
cx.tcx.lint_level_at_node(lint, id).0 == Level::Allow
cx.tcx.lint_level_at_node(lint, id).level == Level::Allow
}
pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> {

View file

@ -357,7 +357,7 @@ fn binop_to_string(op: AssocOp, lhs: &str, rhs: &str) -> String {
match op {
AssocOp::Binary(op) => format!("{lhs} {} {rhs}", op.as_str()),
AssocOp::Assign => format!("{lhs} = {rhs}"),
AssocOp::AssignOp(op) => format!("{lhs} {}= {rhs}", op.as_str()),
AssocOp::AssignOp(op) => format!("{lhs} {} {rhs}", op.as_str()),
AssocOp::Cast => format!("{lhs} as {rhs}"),
AssocOp::Range(limits) => format!("{lhs}{}{rhs}", limits.as_str()),
}

View file

@ -9,9 +9,9 @@ use std::{fmt, iter};
use build_helper::git::GitConfig;
use semver::Version;
use serde::de::{Deserialize, Deserializer, Error as _};
use test::{ColorConfig, OutputFormat};
pub use self::Mode::*;
use crate::executor::{ColorConfig, OutputFormat};
use crate::util::{PathBufExt, add_dylib_path};
macro_rules! string_enum {
@ -178,6 +178,10 @@ pub struct Config {
/// `true` to overwrite stderr/stdout files instead of complaining about changes in output.
pub bless: bool,
/// Stop as soon as possible after any test fails.
/// May run a few more tests before stopping, due to threading.
pub fail_fast: bool,
/// The library paths required for running the compiler.
pub compile_lib_path: PathBuf,

View file

@ -0,0 +1,156 @@
//! This module encapsulates all of the code that interacts directly with
//! libtest, to execute the collected tests.
//!
//! This will hopefully make it easier to migrate away from libtest someday.
use std::borrow::Cow;
use std::io;
use std::sync::Arc;
use crate::common::{Config, TestPaths};
/// Delegates to libtest to run the list of collected tests.
///
/// Returns `Ok(true)` if all tests passed, or `Ok(false)` if one or more tests failed.
pub(crate) fn execute_tests(config: &Config, tests: Vec<CollectedTest>) -> io::Result<bool> {
let opts = test_opts(config);
let tests = tests.into_iter().map(|t| t.into_libtest()).collect::<Vec<_>>();
test::run_tests_console(&opts, tests)
}
/// Information needed to create a `test::TestDescAndFn`.
pub(crate) struct CollectedTest {
pub(crate) desc: CollectedTestDesc,
pub(crate) config: Arc<Config>,
pub(crate) testpaths: TestPaths,
pub(crate) revision: Option<String>,
}
/// Information needed to create a `test::TestDesc`.
pub(crate) struct CollectedTestDesc {
pub(crate) name: String,
pub(crate) ignore: bool,
pub(crate) ignore_message: Option<Cow<'static, str>>,
pub(crate) should_panic: ShouldPanic,
}
impl CollectedTest {
fn into_libtest(self) -> test::TestDescAndFn {
let Self { desc, config, testpaths, revision } = self;
let CollectedTestDesc { name, ignore, ignore_message, should_panic } = desc;
// Libtest requires the ignore message to be a &'static str, so we might
// have to leak memory to create it. This is fine, as we only do so once
// per test, so the leak won't grow indefinitely.
let ignore_message = ignore_message.map(|msg| match msg {
Cow::Borrowed(s) => s,
Cow::Owned(s) => &*String::leak(s),
});
let desc = test::TestDesc {
name: test::DynTestName(name),
ignore,
ignore_message,
source_file: "",
start_line: 0,
start_col: 0,
end_line: 0,
end_col: 0,
should_panic: should_panic.to_libtest(),
compile_fail: false,
no_run: false,
test_type: test::TestType::Unknown,
};
// This closure is invoked when libtest returns control to compiletest
// to execute the test.
let testfn = test::DynTestFn(Box::new(move || {
crate::runtest::run(config, &testpaths, revision.as_deref());
Ok(())
}));
test::TestDescAndFn { desc, testfn }
}
}
/// Whether console output should be colored or not.
#[derive(Copy, Clone, Default, Debug)]
pub enum ColorConfig {
#[default]
AutoColor,
AlwaysColor,
NeverColor,
}
impl ColorConfig {
fn to_libtest(self) -> test::ColorConfig {
match self {
Self::AutoColor => test::ColorConfig::AutoColor,
Self::AlwaysColor => test::ColorConfig::AlwaysColor,
Self::NeverColor => test::ColorConfig::NeverColor,
}
}
}
/// Format of the test results output.
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub enum OutputFormat {
/// Verbose output
Pretty,
/// Quiet output
#[default]
Terse,
/// JSON output
Json,
}
impl OutputFormat {
fn to_libtest(self) -> test::OutputFormat {
match self {
Self::Pretty => test::OutputFormat::Pretty,
Self::Terse => test::OutputFormat::Terse,
Self::Json => test::OutputFormat::Json,
}
}
}
/// Whether test is expected to panic or not.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub(crate) enum ShouldPanic {
No,
Yes,
}
impl ShouldPanic {
fn to_libtest(self) -> test::ShouldPanic {
match self {
Self::No => test::ShouldPanic::No,
Self::Yes => test::ShouldPanic::Yes,
}
}
}
fn test_opts(config: &Config) -> test::TestOpts {
test::TestOpts {
exclude_should_panic: false,
filters: config.filters.clone(),
filter_exact: config.filter_exact,
run_ignored: if config.run_ignored { test::RunIgnored::Yes } else { test::RunIgnored::No },
format: config.format.to_libtest(),
logfile: config.logfile.clone(),
run_tests: true,
bench_benchmarks: true,
nocapture: config.nocapture,
color: config.color.to_libtest(),
shuffle: false,
shuffle_seed: None,
test_threads: None,
skip: config.skip.clone(),
list: false,
options: test::Options::new(),
time_options: None,
force_run_in_process: false,
fail_fast: config.fail_fast,
}
}

View file

@ -11,6 +11,7 @@ use tracing::*;
use crate::common::{Config, Debugger, FailMode, Mode, PassMode};
use crate::debuggers::{extract_cdb_version, extract_gdb_version};
use crate::executor::{CollectedTestDesc, ShouldPanic};
use crate::header::auxiliary::{AuxProps, parse_and_update_aux};
use crate::header::needs::CachedNeedsConditions;
use crate::util::static_regex;
@ -1355,15 +1356,15 @@ where
Some((min, max))
}
pub fn make_test_description<R: Read>(
pub(crate) fn make_test_description<R: Read>(
config: &Config,
cache: &HeadersCache,
name: test::TestName,
name: String,
path: &Path,
src: R,
test_revision: Option<&str>,
poisoned: &mut bool,
) -> test::TestDesc {
) -> CollectedTestDesc {
let mut ignore = false;
let mut ignore_message = None;
let mut should_fail = false;
@ -1387,10 +1388,7 @@ pub fn make_test_description<R: Read>(
match $e {
IgnoreDecision::Ignore { reason } => {
ignore = true;
// The ignore reason must be a &'static str, so we have to leak memory to
// create it. This is fine, as the header is parsed only at the start of
// compiletest so it won't grow indefinitely.
ignore_message = Some(&*Box::leak(Box::<str>::from(reason)));
ignore_message = Some(reason.into());
}
IgnoreDecision::Error { message } => {
eprintln!("error: {}:{line_number}: {message}", path.display());
@ -1431,25 +1429,12 @@ pub fn make_test_description<R: Read>(
// since we run the pretty printer across all tests by default.
// If desired, we could add a `should-fail-pretty` annotation.
let should_panic = match config.mode {
crate::common::Pretty => test::ShouldPanic::No,
_ if should_fail => test::ShouldPanic::Yes,
_ => test::ShouldPanic::No,
crate::common::Pretty => ShouldPanic::No,
_ if should_fail => ShouldPanic::Yes,
_ => ShouldPanic::No,
};
test::TestDesc {
name,
ignore,
ignore_message,
source_file: "",
start_line: 0,
start_col: 0,
end_line: 0,
end_col: 0,
should_panic,
compile_fail: false,
no_run: false,
test_type: test::TestType::Unknown,
}
CollectedTestDesc { name, ignore, ignore_message, should_panic }
}
fn ignore_cdb(config: &Config, line: &str) -> IgnoreDecision {

View file

@ -8,14 +8,15 @@ use super::{
parse_normalize_rule,
};
use crate::common::{Config, Debugger, Mode};
use crate::executor::{CollectedTestDesc, ShouldPanic};
fn make_test_description<R: Read>(
config: &Config,
name: test::TestName,
name: String,
path: &Path,
src: R,
revision: Option<&str>,
) -> test::TestDesc {
) -> CollectedTestDesc {
let cache = HeadersCache::load(config);
let mut poisoned = false;
let test = crate::header::make_test_description(
@ -233,7 +234,7 @@ fn parse_rs(config: &Config, contents: &str) -> EarlyProps {
}
fn check_ignore(config: &Config, contents: &str) -> bool {
let tn = test::DynTestName(String::new());
let tn = String::new();
let p = Path::new("a.rs");
let d = make_test_description(&config, tn, p, std::io::Cursor::new(contents), None);
d.ignore
@ -242,13 +243,13 @@ fn check_ignore(config: &Config, contents: &str) -> bool {
#[test]
fn should_fail() {
let config: Config = cfg().build();
let tn = test::DynTestName(String::new());
let tn = String::new();
let p = Path::new("a.rs");
let d = make_test_description(&config, tn.clone(), p, std::io::Cursor::new(""), None);
assert_eq!(d.should_panic, test::ShouldPanic::No);
assert_eq!(d.should_panic, ShouldPanic::No);
let d = make_test_description(&config, tn, p, std::io::Cursor::new("//@ should-fail"), None);
assert_eq!(d.should_panic, test::ShouldPanic::Yes);
assert_eq!(d.should_panic, ShouldPanic::Yes);
}
#[test]

View file

@ -12,6 +12,7 @@ pub mod common;
pub mod compute_diff;
mod debuggers;
pub mod errors;
mod executor;
pub mod header;
mod json;
mod raise_fd_limit;
@ -32,7 +33,6 @@ use std::{env, fs, vec};
use build_helper::git::{get_git_modified_files, get_git_untracked_files};
use getopts::Options;
use test::ColorConfig;
use tracing::*;
use walkdir::WalkDir;
@ -41,6 +41,7 @@ use crate::common::{
CompareMode, Config, Debugger, Mode, PassMode, TestPaths, UI_EXTENSIONS, expected_output_path,
output_base_dir, output_relative_path,
};
use crate::executor::{CollectedTest, ColorConfig, OutputFormat};
use crate::header::HeadersCache;
use crate::util::logv;
@ -50,6 +51,12 @@ use crate::util::logv;
/// some code here that inspects environment variables or even runs executables
/// (e.g. when discovering debugger versions).
pub fn parse_config(args: Vec<String>) -> Config {
if env::var("RUST_TEST_NOCAPTURE").is_ok() {
eprintln!(
"WARNING: RUST_TEST_NOCAPTURE is not supported. Use the `--no-capture` flag instead."
);
}
let mut opts = Options::new();
opts.reqopt("", "compile-lib-path", "path to host shared libraries", "PATH")
.reqopt("", "run-lib-path", "path to target shared libraries", "PATH")
@ -128,6 +135,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
"bless",
"overwrite stderr/stdout files instead of complaining about a mismatch",
)
.optflag("", "fail-fast", "stop as soon as possible after any test fails")
.optflag("", "quiet", "print one character per test instead of one line")
.optopt("", "color", "coloring: auto, always, never", "WHEN")
.optflag("", "json", "emit json output instead of plaintext output")
@ -319,6 +327,9 @@ pub fn parse_config(args: Vec<String>) -> Config {
Config {
bless: matches.opt_present("bless"),
fail_fast: matches.opt_present("fail-fast")
|| env::var_os("RUSTC_TEST_FAIL_FAST").is_some(),
compile_lib_path: make_absolute(opt_path(matches, "compile-lib-path")),
run_lib_path: make_absolute(opt_path(matches, "run-lib-path")),
rustc_path: opt_path(matches, "rustc-path"),
@ -392,9 +403,9 @@ pub fn parse_config(args: Vec<String>) -> Config {
verbose: matches.opt_present("verbose"),
format: match (matches.opt_present("quiet"), matches.opt_present("json")) {
(true, true) => panic!("--quiet and --json are incompatible"),
(true, false) => test::OutputFormat::Terse,
(false, true) => test::OutputFormat::Json,
(false, false) => test::OutputFormat::Pretty,
(true, false) => OutputFormat::Terse,
(false, true) => OutputFormat::Json,
(false, false) => OutputFormat::Pretty,
},
only_modified: matches.opt_present("only-modified"),
color,
@ -525,8 +536,6 @@ pub fn run_tests(config: Arc<Config>) {
// Let tests know which target they're running as
env::set_var("TARGET", &config.target);
let opts = test_opts(&config);
let mut configs = Vec::new();
if let Mode::DebugInfo = config.mode {
// Debugging emscripten code doesn't make sense today
@ -553,12 +562,12 @@ pub fn run_tests(config: Arc<Config>) {
tests.extend(collect_and_make_tests(c));
}
tests.sort_by(|a, b| a.desc.name.as_slice().cmp(&b.desc.name.as_slice()));
tests.sort_by(|a, b| Ord::cmp(&a.desc.name, &b.desc.name));
// Delegate to libtest to filter and run the big list of structures created
// during test discovery. When libtest decides to run a test, it will invoke
// the corresponding closure created by `make_test_closure`.
let res = test::run_tests_console(&opts, tests);
// during test discovery. When libtest decides to run a test, it will
// return control to compiletest by invoking a closure.
let res = crate::executor::execute_tests(&config, tests);
// Check the outcome reported by libtest.
match res {
@ -602,37 +611,6 @@ pub fn run_tests(config: Arc<Config>) {
}
}
pub fn test_opts(config: &Config) -> test::TestOpts {
if env::var("RUST_TEST_NOCAPTURE").is_ok() {
eprintln!(
"WARNING: RUST_TEST_NOCAPTURE is no longer used. \
Use the `--nocapture` flag instead."
);
}
test::TestOpts {
exclude_should_panic: false,
filters: config.filters.clone(),
filter_exact: config.filter_exact,
run_ignored: if config.run_ignored { test::RunIgnored::Yes } else { test::RunIgnored::No },
format: config.format,
logfile: config.logfile.clone(),
run_tests: true,
bench_benchmarks: true,
nocapture: config.nocapture,
color: config.color,
shuffle: false,
shuffle_seed: None,
test_threads: None,
skip: config.skip.clone(),
list: false,
options: test::Options::new(),
time_options: None,
force_run_in_process: false,
fail_fast: std::env::var_os("RUSTC_TEST_FAIL_FAST").is_some(),
}
}
/// Read-only context data used during test collection.
struct TestCollectorCx {
config: Arc<Config>,
@ -643,17 +621,17 @@ struct TestCollectorCx {
/// Mutable state used during test collection.
struct TestCollector {
tests: Vec<test::TestDescAndFn>,
tests: Vec<CollectedTest>,
found_path_stems: HashSet<PathBuf>,
poisoned: bool,
}
/// Creates libtest structures for every test/revision in the test suite directory.
/// Creates test structures for every test/revision in the test suite directory.
///
/// This always inspects _all_ test files in the suite (e.g. all 17k+ ui tests),
/// regardless of whether any filters/tests were specified on the command-line,
/// because filtering is handled later by libtest.
pub fn collect_and_make_tests(config: Arc<Config>) -> Vec<test::TestDescAndFn> {
pub(crate) fn collect_and_make_tests(config: Arc<Config>) -> Vec<CollectedTest> {
debug!("making tests from {}", config.src_test_suite_root.display());
let common_inputs_stamp = common_inputs_stamp(&config);
let modified_tests =
@ -882,7 +860,7 @@ fn make_test(cx: &TestCollectorCx, collector: &mut TestCollector, testpaths: &Te
};
// For each revision (or the sole dummy revision), create and append a
// `test::TestDescAndFn` that can be handed over to libtest.
// `CollectedTest` that can be handed over to the test executor.
collector.tests.extend(revisions.into_iter().map(|revision| {
// Create a test name and description to hand over to libtest.
let src_file = fs::File::open(&test_path).expect("open test file to parse ignores");
@ -905,13 +883,14 @@ fn make_test(cx: &TestCollectorCx, collector: &mut TestCollector, testpaths: &Te
if !cx.config.force_rerun && is_up_to_date(cx, testpaths, &early_props, revision) {
desc.ignore = true;
// Keep this in sync with the "up-to-date" message detected by bootstrap.
desc.ignore_message = Some("up-to-date");
desc.ignore_message = Some("up-to-date".into());
}
// Create the callback that will run this test/revision when libtest calls it.
let testfn = make_test_closure(Arc::clone(&cx.config), testpaths, revision);
let config = Arc::clone(&cx.config);
let testpaths = testpaths.clone();
let revision = revision.map(str::to_owned);
test::TestDescAndFn { desc, testfn }
CollectedTest { desc, config, testpaths, revision }
}));
}
@ -1043,11 +1022,7 @@ impl Stamp {
}
/// Creates a name for this test/revision that can be handed over to libtest.
fn make_test_name(
config: &Config,
testpaths: &TestPaths,
revision: Option<&str>,
) -> test::TestName {
fn make_test_name(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> String {
// Print the name of the file, relative to the sources root.
let path = testpaths.file.strip_prefix(&config.src_root).unwrap();
let debugger = match config.debugger {
@ -1059,32 +1034,14 @@ fn make_test_name(
None => String::new(),
};
test::DynTestName(format!(
format!(
"[{}{}{}] {}{}",
config.mode,
debugger,
mode_suffix,
path.display(),
revision.map_or("".to_string(), |rev| format!("#{}", rev))
))
}
/// Creates a callback for this test/revision that libtest will call when it
/// decides to actually run the underlying test.
fn make_test_closure(
config: Arc<Config>,
testpaths: &TestPaths,
revision: Option<&str>,
) -> test::TestFn {
let testpaths = testpaths.clone();
let revision = revision.map(str::to_owned);
// This callback is the link between compiletest's test discovery code,
// and the parts of compiletest that know how to run an individual test.
test::DynTestFn(Box::new(move || {
runtest::run(config, &testpaths, revision.as_deref());
Ok(())
}))
)
}
/// Checks that test discovery didn't find any tests whose name stem is a prefix

View file

@ -169,16 +169,9 @@ impl TestCx<'_> {
self.props.error_patterns
);
let check_patterns = should_run == WillExecute::No
&& (!self.props.error_patterns.is_empty()
|| !self.props.regex_error_patterns.is_empty());
if !explicit && self.config.compare_mode.is_none() {
let check_annotations = !check_patterns || !expected_errors.is_empty();
if check_annotations {
// "//~ERROR comments"
self.check_expected_errors(expected_errors, &proc_res);
}
// "//~ERROR comments"
self.check_expected_errors(expected_errors, &proc_res);
} else if explicit && !expected_errors.is_empty() {
let msg = format!(
"line {}: cannot combine `--error-format` with {} annotations; use `error-pattern` instead",
@ -188,7 +181,10 @@ impl TestCx<'_> {
self.fatal(&msg);
}
let output_to_check = self.get_output(&proc_res);
if check_patterns {
if should_run == WillExecute::No
&& (!self.props.error_patterns.is_empty()
|| !self.props.regex_error_patterns.is_empty())
{
// "// error-pattern" comments
self.check_all_error_patterns(&output_to_check, &proc_res, pm);
}

View file

@ -411,9 +411,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
};
let res = this.binary_op(op, &a, &b)?;
// `binary_op` already called `generate_nan` if needed.
// Apply a relative error of 16ULP to simulate non-deterministic precision loss
// Apply a relative error of 4ULP to simulate non-deterministic precision loss
// due to optimizations.
let res = apply_random_float_error_to_imm(this, res, 4 /* log2(16) */)?;
let res = apply_random_float_error_to_imm(this, res, 2 /* log2(4) */)?;
this.write_immediate(*res, dest)?;
}
@ -464,9 +464,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
if !float_finite(&res)? {
throw_ub_format!("`{intrinsic_name}` intrinsic produced non-finite value as result");
}
// Apply a relative error of 16ULP to simulate non-deterministic precision loss
// Apply a relative error of 4ULP to simulate non-deterministic precision loss
// due to optimizations.
let res = apply_random_float_error_to_imm(this, res, 4 /* log2(16) */)?;
let res = apply_random_float_error_to_imm(this, res, 2 /* log2(4) */)?;
this.write_immediate(*res, dest)?;
}

View file

@ -17,6 +17,7 @@ pub mod assertion_helpers;
pub mod diff;
pub mod env;
pub mod external_deps;
pub mod linker;
pub mod path_helpers;
pub mod run;
pub mod scoped_run;

View file

@ -0,0 +1,36 @@
use regex::Regex;
use crate::{Rustc, is_msvc};
/// Asserts that `rustc` uses LLD for linking when executed.
pub fn assert_rustc_uses_lld(rustc: &mut Rustc) {
let stderr = get_stderr_with_linker_messages(rustc);
assert!(
has_lld_version_in_logs(&stderr),
"LLD version should be present in rustc stderr:\n{stderr}"
);
}
/// Asserts that `rustc` doesn't use LLD for linking when executed.
pub fn assert_rustc_doesnt_use_lld(rustc: &mut Rustc) {
let stderr = get_stderr_with_linker_messages(rustc);
assert!(
!has_lld_version_in_logs(&stderr),
"LLD version should NOT be present in rustc stderr:\n{stderr}"
);
}
fn get_stderr_with_linker_messages(rustc: &mut Rustc) -> String {
// lld-link is used if msvc, otherwise a gnu-compatible lld is used.
let linker_version_flag = if is_msvc() { "--version" } else { "-Wl,-v" };
let output = rustc.arg("-Wlinker-messages").link_arg(linker_version_flag).run();
output.stderr_utf8()
}
fn has_lld_version_in_logs(stderr: &str) -> bool {
// Strip the `-Wlinker-messages` wrappers prefixing the linker output.
let stderr = Regex::new(r"warning: linker std(out|err):").unwrap().replace_all(&stderr, "");
let lld_version_re = Regex::new(r"^LLD [0-9]+\.[0-9]+\.[0-9]+").unwrap();
stderr.lines().any(|line| lld_version_re.is_match(line.trim()))
}

View file

@ -1270,6 +1270,7 @@ dependencies = [
"edition",
"expect-test",
"ra-ap-rustc_lexer",
"rustc-literal-escaper",
"stdx",
"tracing",
]
@ -1743,6 +1744,12 @@ version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152"
[[package]]
name = "rustc-literal-escaper"
version = "0.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0041b6238913c41fe704213a4a9329e2f685a156d1781998128b4149c230ad04"
[[package]]
name = "rustc-stable-hash"
version = "0.1.1"
@ -1978,10 +1985,10 @@ dependencies = [
"indexmap",
"itertools",
"parser",
"ra-ap-rustc_lexer",
"rayon",
"rowan",
"rustc-hash 2.0.0",
"rustc-literal-escaper",
"rustc_apfloat",
"smol_str",
"stdx",

View file

@ -136,6 +136,7 @@ pulldown-cmark-to-cmark = "10.0.4"
pulldown-cmark = { version = "0.9.0", default-features = false }
rayon = "1.8.0"
rustc-hash = "2.0.0"
rustc-literal-escaper = "0.0.2"
semver = "1.0.14"
serde = { version = "1.0.192" }
serde_derive = { version = "1.0.192" }

View file

@ -14,6 +14,7 @@ rust-version.workspace = true
[dependencies]
drop_bomb = "0.1.5"
ra-ap-rustc_lexer.workspace = true
rustc-literal-escaper.workspace = true
tracing = { workspace = true, optional = true }
edition.workspace = true

View file

@ -10,7 +10,7 @@
use std::ops;
use rustc_lexer::unescape::{EscapeError, Mode};
use rustc_literal_escaper::{EscapeError, Mode, unescape_byte, unescape_char, unescape_mixed, unescape_unicode};
use crate::{
Edition,
@ -282,7 +282,7 @@ impl<'a> Converter<'a> {
let text = &self.res.text[self.offset + 1..][..len - 1];
let i = text.rfind('\'').unwrap();
let text = &text[..i];
if let Err(e) = rustc_lexer::unescape::unescape_char(text) {
if let Err(e) = unescape_char(text) {
err = error_to_diagnostic_message(e, Mode::Char);
}
}
@ -295,7 +295,7 @@ impl<'a> Converter<'a> {
let text = &self.res.text[self.offset + 2..][..len - 2];
let i = text.rfind('\'').unwrap();
let text = &text[..i];
if let Err(e) = rustc_lexer::unescape::unescape_byte(text) {
if let Err(e) = unescape_byte(text) {
err = error_to_diagnostic_message(e, Mode::Byte);
}
}
@ -402,14 +402,14 @@ fn unescape_string_error_message(text: &str, mode: Mode) -> &'static str {
let mut error_message = "";
match mode {
Mode::CStr => {
rustc_lexer::unescape::unescape_mixed(text, mode, &mut |_, res| {
unescape_mixed(text, mode, &mut |_, res| {
if let Err(e) = res {
error_message = error_to_diagnostic_message(e, mode);
}
});
}
Mode::ByteStr | Mode::Str => {
rustc_lexer::unescape::unescape_unicode(text, mode, &mut |_, res| {
unescape_unicode(text, mode, &mut |_, res| {
if let Err(e) = res {
error_message = error_to_diagnostic_message(e, mode);
}

View file

@ -17,13 +17,12 @@ either.workspace = true
itertools.workspace = true
rowan = "=0.15.15"
rustc-hash.workspace = true
rustc-literal-escaper.workspace = true
indexmap.workspace = true
smol_str.workspace = true
triomphe.workspace = true
tracing.workspace = true
ra-ap-rustc_lexer.workspace = true
parser.workspace = true
stdx.workspace = true

View file

@ -2,7 +2,7 @@
use std::{borrow::Cow, num::ParseIntError};
use rustc_lexer::unescape::{
use rustc_literal_escaper::{
unescape_byte, unescape_char, unescape_mixed, unescape_unicode, EscapeError, MixedUnit, Mode,
};
use stdx::always;

View file

@ -19,13 +19,6 @@
//! [RFC]: <https://github.com/rust-lang/rfcs/pull/2256>
//! [Swift]: <https://github.com/apple/swift/blob/13d593df6f359d0cb2fc81cfaac273297c539455/lib/Syntax/README.md>
#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))]
#[cfg(not(feature = "in-rust-tree"))]
extern crate ra_ap_rustc_lexer as rustc_lexer;
#[cfg(feature = "in-rust-tree")]
extern crate rustc_lexer;
mod parsing;
mod ptr;
mod syntax_error;
@ -64,7 +57,7 @@ pub use rowan::{
api::Preorder, Direction, GreenNode, NodeOrToken, SyntaxText, TextRange, TextSize,
TokenAtOffset, WalkEvent,
};
pub use rustc_lexer::unescape;
pub use rustc_literal_escaper as unescape;
pub use smol_str::{format_smolstr, SmolStr, SmolStrBuilder, ToSmolStr};
/// `Parse` is the result of the parsing: a syntax tree and a collection of

View file

@ -5,7 +5,7 @@
mod block;
use rowan::Direction;
use rustc_lexer::unescape::{self, unescape_mixed, unescape_unicode, Mode};
use rustc_literal_escaper::{unescape_mixed, unescape_unicode, EscapeError, Mode};
use crate::{
algo,
@ -44,8 +44,8 @@ pub(crate) fn validate(root: &SyntaxNode, errors: &mut Vec<SyntaxError>) {
}
}
fn rustc_unescape_error_to_string(err: unescape::EscapeError) -> (&'static str, bool) {
use unescape::EscapeError as EE;
fn rustc_unescape_error_to_string(err: EscapeError) -> (&'static str, bool) {
use rustc_literal_escaper::EscapeError as EE;
#[rustfmt::skip]
let err_message = match err {
@ -127,7 +127,7 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) {
let text = token.text();
// FIXME: lift this lambda refactor to `fn` (https://github.com/rust-lang/rust-analyzer/pull/2834#discussion_r366199205)
let mut push_err = |prefix_len, off, err: unescape::EscapeError| {
let mut push_err = |prefix_len, off, err: EscapeError| {
let off = token.text_range().start() + TextSize::try_from(off + prefix_len).unwrap();
let (message, is_err) = rustc_unescape_error_to_string(err);
// FIXME: Emit lexer warnings

View file

@ -156,9 +156,9 @@ checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
[[package]]
name = "cc"
version = "1.2.17"
version = "1.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a"
checksum = "525046617d8376e3db1deffb079e91cef90a89fc3ca5c185bbf8c9ecdd15cd5c"
dependencies = [
"shlex",
]
@ -185,9 +185,9 @@ dependencies = [
[[package]]
name = "clap"
version = "4.5.32"
version = "4.5.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6088f3ae8c3608d19260cd7445411865a485688711b78b5be70d78cd96136f83"
checksum = "d8aa86934b44c19c50f87cc2790e19f54f7a67aedb64101c2e1a2e5ecfb73944"
dependencies = [
"clap_builder",
"clap_derive",
@ -195,9 +195,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.5.32"
version = "4.5.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22a7ef7f676155edfb82daa97f99441f3ebf4a58d5e32f295a56259f1b6facc8"
checksum = "2414dbb2dd0695280da6ea9261e327479e9d37b0630f6b53ba2a11c60c679fd9"
dependencies = [
"anstream",
"anstyle",
@ -275,9 +275,9 @@ dependencies = [
[[package]]
name = "darling"
version = "0.20.10"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989"
checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee"
dependencies = [
"darling_core",
"darling_macro",
@ -285,9 +285,9 @@ dependencies = [
[[package]]
name = "darling_core"
version = "0.20.10"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5"
checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e"
dependencies = [
"fnv",
"ident_case",
@ -299,9 +299,9 @@ dependencies = [
[[package]]
name = "darling_macro"
version = "0.20.10"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
dependencies = [
"darling_core",
"quote",
@ -413,9 +413,9 @@ dependencies = [
[[package]]
name = "env_logger"
version = "0.11.7"
version = "0.11.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3716d7a920fb4fac5d84e9d4bce8ceb321e9414b4409da61b07b75c1e3d0697"
checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f"
dependencies = [
"anstream",
"anstyle",
@ -432,9 +432,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "errno"
version = "0.3.10"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e"
dependencies = [
"libc",
"windows-sys",
@ -455,9 +455,9 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
[[package]]
name = "flate2"
version = "1.1.0"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc"
checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece"
dependencies = [
"crc32fast",
"miniz_oxide",
@ -584,14 +584,15 @@ dependencies = [
[[package]]
name = "iana-time-zone"
version = "0.1.61"
version = "0.1.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220"
checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"log",
"wasm-bindgen",
"windows-core",
]
@ -646,9 +647,9 @@ dependencies = [
[[package]]
name = "icu_locid_transform_data"
version = "1.5.0"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e"
checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d"
[[package]]
name = "icu_normalizer"
@ -670,9 +671,9 @@ dependencies = [
[[package]]
name = "icu_normalizer_data"
version = "1.5.0"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516"
checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7"
[[package]]
name = "icu_properties"
@ -691,9 +692,9 @@ dependencies = [
[[package]]
name = "icu_properties_data"
version = "1.5.0"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569"
checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2"
[[package]]
name = "icu_provider"
@ -752,9 +753,9 @@ dependencies = [
[[package]]
name = "indexmap"
version = "2.8.0"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058"
checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
dependencies = [
"equivalent",
"hashbrown",
@ -861,9 +862,9 @@ dependencies = [
[[package]]
name = "log"
version = "0.4.26"
version = "0.4.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e"
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
[[package]]
name = "mac"
@ -980,9 +981,9 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "miniz_oxide"
version = "0.8.5"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5"
checksum = "ff70ce3e48ae43fa075863cef62e8b43b71a4f2382229920e0df362592919430"
dependencies = [
"adler2",
]
@ -1028,9 +1029,9 @@ dependencies = [
[[package]]
name = "once_cell"
version = "1.21.1"
version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "onig"
@ -1103,9 +1104,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "pest"
version = "2.7.15"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc"
checksum = "198db74531d58c70a361c42201efde7e2591e976d518caf7662a47dc5720e7b6"
dependencies = [
"memchr",
"thiserror 2.0.12",
@ -1114,9 +1115,9 @@ dependencies = [
[[package]]
name = "pest_derive"
version = "2.7.15"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "816518421cfc6887a0d62bf441b6ffb4536fcc926395a69e1a85852d4363f57e"
checksum = "d725d9cfd79e87dccc9341a2ef39d1b6f6353d68c4b33c177febbe1a402c97c5"
dependencies = [
"pest",
"pest_generator",
@ -1124,9 +1125,9 @@ dependencies = [
[[package]]
name = "pest_generator"
version = "2.7.15"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d1396fd3a870fc7838768d171b4616d5c91f6cc25e377b673d714567d99377b"
checksum = "db7d01726be8ab66ab32f9df467ae8b1148906685bbe75c82d1e65d7f5b3f841"
dependencies = [
"pest",
"pest_meta",
@ -1137,9 +1138,9 @@ dependencies = [
[[package]]
name = "pest_meta"
version = "2.7.15"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1e58089ea25d717bfd31fb534e4f3afcc2cc569c70de3e239778991ea3b7dea"
checksum = "7f9f832470494906d1fca5329f8ab5791cc60beb230c74815dff541cbd2b5ca0"
dependencies = [
"once_cell",
"pest",
@ -1316,9 +1317,9 @@ checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
[[package]]
name = "redox_syscall"
version = "0.5.10"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1"
checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3"
dependencies = [
"bitflags 2.9.0",
]
@ -1366,9 +1367,9 @@ dependencies = [
[[package]]
name = "rustix"
version = "1.0.3"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e56a18552996ac8d29ecc3b190b4fdbb2d91ca4ec396de7bbffaf43f3d637e96"
checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf"
dependencies = [
"bitflags 2.9.0",
"errno",
@ -1476,9 +1477,9 @@ checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
[[package]]
name = "smallvec"
version = "1.14.0"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd"
checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9"
[[package]]
name = "stable_deref_trait"
@ -1488,9 +1489,9 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "string_cache"
version = "0.8.8"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "938d512196766101d333398efde81bc1f37b00cb42c2f8350e5df639f040bbbe"
checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f"
dependencies = [
"new_debug_unreachable",
"parking_lot",
@ -1879,11 +1880,37 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-core"
version = "0.52.0"
version = "0.61.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980"
dependencies = [
"windows-targets",
"windows-implement",
"windows-interface",
"windows-link",
"windows-result",
"windows-strings",
]
[[package]]
name = "windows-implement"
version = "0.60.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "windows-interface"
version = "0.59.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
@ -1892,6 +1919,24 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
[[package]]
name = "windows-result"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-strings"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-sys"
version = "0.59.0"

View file

@ -2058,7 +2058,7 @@ fn rewrite_assignment(
context: &RewriteContext<'_>,
lhs: &ast::Expr,
rhs: &ast::Expr,
op: Option<&ast::BinOp>,
op: Option<&ast::AssignOp>,
shape: Shape,
) -> RewriteResult {
let operator_str = match op {

View file

@ -361,6 +361,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
"regex-syntax",
"rustc-demangle",
"rustc-hash",
"rustc-literal-escaper",
"rustc-rayon",
"rustc-rayon-core",
"rustc-stable-hash",
@ -486,6 +487,7 @@ const PERMITTED_STDLIB_DEPENDENCIES: &[&str] = &[
"rand_core",
"rand_xorshift",
"rustc-demangle",
"rustc-literal-escaper",
"shlex",
"syn",
"unicode-ident",

View file

@ -1,11 +1,13 @@
//! Tidy check to ensure `#[test]` and `#[bench]` are not used directly inside `core`.
//! Tidy check to ensure `#[test]` and `#[bench]` are not used directly inside
//! `core` or `alloc`.
//!
//! `#![no_core]` libraries cannot be tested directly due to duplicating lang
//! items. All tests and benchmarks must be written externally in `core/{tests,benches}`.
//! `core` and `alloc` cannot be tested directly due to duplicating lang items.
//! All tests and benchmarks must be written externally in
//! `{coretests,alloctests}/{tests,benches}`.
//!
//! Outside of core tests and benchmarks should be outlined into separate files
//! named `tests.rs` or `benches.rs`, or directories named `tests` or `benches` unconfigured
//! during normal build.
//! Outside of `core` and `alloc`, tests and benchmarks should be outlined into
//! separate files named `tests.rs` or `benches.rs`, or directories named
//! `tests` or `benches` unconfigured during normal build.
use std::path::Path;
@ -14,40 +16,51 @@ use crate::walk::{filter_dirs, walk};
pub fn check(root_path: &Path, bad: &mut bool) {
let core = root_path.join("core");
let core_copy = core.clone();
let core_tests = core.join("tests");
let core_benches = core.join("benches");
let is_core = move |path: &Path| {
path.starts_with(&core)
&& !(path.starts_with(&core_tests) || path.starts_with(&core_benches))
};
let is_core = move |path: &Path| path.starts_with(&core);
let alloc = root_path.join("alloc");
let alloc_copy = alloc.clone();
let is_alloc = move |path: &Path| path.starts_with(&alloc);
let skip = move |path: &Path, is_dir| {
let file_name = path.file_name().unwrap_or_default();
if is_dir {
filter_dirs(path)
|| path.ends_with("src/doc")
|| (file_name == "tests" || file_name == "benches") && !is_core(path)
|| (file_name == "tests" || file_name == "benches")
&& !is_core(path)
&& !is_alloc(path)
} else {
let extension = path.extension().unwrap_or_default();
extension != "rs"
|| (file_name == "tests.rs" || file_name == "benches.rs") && !is_core(path)
// UI tests with different names
|| path.ends_with("src/thread/local/dynamic_tests.rs")
|| path.ends_with("src/sync/mpsc/sync_tests.rs")
|| (file_name == "tests.rs" || file_name == "benches.rs")
&& !is_core(path)
&& !is_alloc(path)
// Tests which use non-public internals and, as such, need to
// have the types in the same crate as the tests themselves. See
// the comment in alloctests/lib.rs.
|| path.ends_with("library/alloc/src/collections/btree/borrow/tests.rs")
|| path.ends_with("library/alloc/src/collections/btree/map/tests.rs")
|| path.ends_with("library/alloc/src/collections/btree/node/tests.rs")
|| path.ends_with("library/alloc/src/collections/btree/set/tests.rs")
|| path.ends_with("library/alloc/src/collections/linked_list/tests.rs")
|| path.ends_with("library/alloc/src/collections/vec_deque/tests.rs")
|| path.ends_with("library/alloc/src/raw_vec/tests.rs")
}
};
walk(root_path, skip, &mut |entry, contents| {
let path = entry.path();
let is_core = path.starts_with(&core_copy);
let is_alloc = path.starts_with(&alloc_copy);
for (i, line) in contents.lines().enumerate() {
let line = line.trim();
let is_test = || line.contains("#[test]") && !line.contains("`#[test]");
let is_bench = || line.contains("#[bench]") && !line.contains("`#[bench]");
if !line.starts_with("//") && (is_test() || is_bench()) {
let explanation = if is_core {
"core unit tests and benchmarks must be placed into \
`core/tests` or `core/benches`"
"`core` unit tests and benchmarks must be placed into `coretests`"
} else if is_alloc {
"`alloc` unit tests and benchmarks must be placed into `alloctests`"
} else {
"unit tests and benchmarks must be placed into \
separate files or directories named \

View file

@ -45,45 +45,78 @@ const fn bitset_search<
(word & (1 << (needle % 64) as u64)) != 0
}
fn decode_prefix_sum(short_offset_run_header: u32) -> u32 {
short_offset_run_header & ((1 << 21) - 1)
}
fn decode_length(short_offset_run_header: u32) -> usize {
(short_offset_run_header >> 21) as usize
#[repr(transparent)]
struct ShortOffsetRunHeader(u32);
impl ShortOffsetRunHeader {
const fn new(start_index: usize, prefix_sum: u32) -> Self {
assert!(start_index < (1 << 11));
assert!(prefix_sum < (1 << 21));
Self((start_index as u32) << 21 | prefix_sum)
}
#[inline]
const fn start_index(&self) -> usize {
(self.0 >> 21) as usize
}
#[inline]
const fn prefix_sum(&self) -> u32 {
self.0 & ((1 << 21) - 1)
}
}
/// # Safety
///
/// - The last element of `short_offset_runs` must be greater than `std::char::MAX`.
/// - The start indices of all elements in `short_offset_runs` must be less than `OFFSETS`.
#[inline(always)]
fn skip_search<const SOR: usize, const OFFSETS: usize>(
needle: u32,
short_offset_runs: &[u32; SOR],
unsafe fn skip_search<const SOR: usize, const OFFSETS: usize>(
needle: char,
short_offset_runs: &[ShortOffsetRunHeader; SOR],
offsets: &[u8; OFFSETS],
) -> bool {
// Note that this *cannot* be past the end of the array, as the last
// element is greater than std::char::MAX (the largest possible needle).
//
// So, we cannot have found it (i.e. Ok(idx) + 1 != length) and the correct
// location cannot be past it, so Err(idx) != length either.
//
// This means that we can avoid bounds checking for the accesses below, too.
let needle = needle as u32;
let last_idx =
match short_offset_runs.binary_search_by_key(&(needle << 11), |header| header << 11) {
match short_offset_runs.binary_search_by_key(&(needle << 11), |header| (header.0 << 11)) {
Ok(idx) => idx + 1,
Err(idx) => idx,
};
// SAFETY: `last_idx` *cannot* be past the end of the array, as the last
// element is greater than `std::char::MAX` (the largest possible needle)
// as guaranteed by the caller.
//
// So, we cannot have found it (i.e. `Ok(idx) => idx + 1 != length`) and the
// correct location cannot be past it, so `Err(idx) => idx != length` either.
//
// This means that we can avoid bounds checking for the accesses below, too.
//
// We need to use `intrinsics::assume` since the `panic_nounwind` contained
// in `hint::assert_unchecked` may not be optimized out.
unsafe { crate::intrinsics::assume(last_idx < SOR) };
let mut offset_idx = decode_length(short_offset_runs[last_idx]);
let mut offset_idx = short_offset_runs[last_idx].start_index();
let length = if let Some(next) = short_offset_runs.get(last_idx + 1) {
decode_length(*next) - offset_idx
(*next).start_index() - offset_idx
} else {
offsets.len() - offset_idx
};
let prev =
last_idx.checked_sub(1).map(|prev| decode_prefix_sum(short_offset_runs[prev])).unwrap_or(0);
last_idx.checked_sub(1).map(|prev| short_offset_runs[prev].prefix_sum()).unwrap_or(0);
let total = needle - prev;
let mut prefix_sum = 0;
for _ in 0..(length - 1) {
// SAFETY: It is guaranteed that `length <= OFFSETS - offset_idx`,
// so it follows that `length - 1 + offset_idx < OFFSETS`, therefore
// `offset_idx < OFFSETS` is always true in this loop.
//
// We need to use `intrinsics::assume` since the `panic_nounwind` contained
// in `hint::assert_unchecked` may not be optimized out.
unsafe { crate::intrinsics::assume(offset_idx < OFFSETS) };
let offset = offsets[offset_idx];
prefix_sum += offset as u32;
if prefix_sum > total {

View file

@ -1,26 +1,23 @@
use std::fmt::Write as _;
use std::fmt::{self, Write as _};
use std::ops::Range;
use crate::fmt_list;
use crate::raw_emitter::RawEmitter;
/// This will get packed into a single u32 before inserting into the data set.
#[derive(Debug, PartialEq)]
#[derive(PartialEq)]
struct ShortOffsetRunHeader {
/// Note, we only allow for 21 bits here.
prefix_sum: u32,
/// Note, we actually only allow for 11 bits here. This should be enough --
/// our largest sets are around ~1400 offsets long.
start_idx: u16,
start_index: u16,
/// Note, we only allow for 21 bits here.
prefix_sum: u32,
}
impl ShortOffsetRunHeader {
fn pack(&self) -> u32 {
assert!(self.start_idx < (1 << 11));
assert!(self.prefix_sum < (1 << 21));
(self.start_idx as u32) << 21 | self.prefix_sum
impl fmt::Debug for ShortOffsetRunHeader {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "ShortOffsetRunHeader::new({}, {})", self.start_index, self.prefix_sum)
}
}
@ -53,7 +50,7 @@ impl RawEmitter {
coded_offsets.push(offset);
} else {
short_offset_runs.push(ShortOffsetRunHeader {
start_idx: start.try_into().unwrap(),
start_index: start.try_into().unwrap(),
prefix_sum,
});
// This is just needed to maintain indices even/odd
@ -71,11 +68,12 @@ impl RawEmitter {
assert!(inserted);
}
writeln!(&mut self.file, "use super::ShortOffsetRunHeader;\n").unwrap();
writeln!(
&mut self.file,
"static SHORT_OFFSET_RUNS: [u32; {}] = [{}];",
"static SHORT_OFFSET_RUNS: [ShortOffsetRunHeader; {}] = [{}];",
short_offset_runs.len(),
fmt_list(short_offset_runs.iter().map(|v| v.pack()))
fmt_list(short_offset_runs.iter())
)
.unwrap();
self.bytes_used += 4 * short_offset_runs.len();
@ -104,15 +102,43 @@ impl RawEmitter {
writeln!(&mut self.file, " (c as u32) >= {first_code_point:#04x} && lookup_slow(c)")
.unwrap();
writeln!(&mut self.file, "}}").unwrap();
writeln!(&mut self.file).unwrap();
writeln!(&mut self.file, "#[inline(never)]").unwrap();
writeln!(&mut self.file, "fn lookup_slow(c: char) -> bool {{").unwrap();
} else {
writeln!(&mut self.file, "pub fn lookup(c: char) -> bool {{").unwrap();
}
writeln!(&mut self.file, " super::skip_search(",).unwrap();
writeln!(&mut self.file, " c as u32,").unwrap();
writeln!(&mut self.file, " &SHORT_OFFSET_RUNS,").unwrap();
writeln!(&mut self.file, " &OFFSETS,").unwrap();
writeln!(&mut self.file, " )").unwrap();
writeln!(&mut self.file, " const {{").unwrap();
writeln!(
&mut self.file,
" assert!(SHORT_OFFSET_RUNS.last().unwrap().0 > char::MAX as u32);",
)
.unwrap();
writeln!(&mut self.file, " let mut i = 0;").unwrap();
writeln!(&mut self.file, " while i < SHORT_OFFSET_RUNS.len() {{").unwrap();
writeln!(
&mut self.file,
" assert!(SHORT_OFFSET_RUNS[i].start_index() < OFFSETS.len());",
)
.unwrap();
writeln!(&mut self.file, " i += 1;").unwrap();
writeln!(&mut self.file, " }}").unwrap();
writeln!(&mut self.file, " }}").unwrap();
writeln!(
&mut self.file,
" // SAFETY: We just ensured the last element of `SHORT_OFFSET_RUNS` is greater than `std::char::MAX`",
)
.unwrap();
writeln!(
&mut self.file,
" // and the start indices of all elements in `SHORT_OFFSET_RUNS` are smaller than `OFFSETS.len()`.",
)
.unwrap();
writeln!(
&mut self.file,
" unsafe {{ super::skip_search(c, &SHORT_OFFSET_RUNS, &OFFSETS) }}"
)
.unwrap();
writeln!(&mut self.file, "}}").unwrap();
}
}