Merge from rustc
This commit is contained in:
commit
2eb7791a1b
994 changed files with 9961 additions and 8374 deletions
|
|
@ -1 +1 @@
|
|||
Subproject commit a6c604d1b8a2f2a8ff1f3ba6092f9fda42f4b7e9
|
||||
Subproject commit 0e93c5bf6a1d5ee7bc2af63d1afb16cd28793601
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
///
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)) => {
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
|
|
|||
|
|
@ -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()),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
||||
|
|
|
|||
156
src/tools/compiletest/src/executor.rs
Normal file
156
src/tools/compiletest/src/executor.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)?;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
36
src/tools/run-make-support/src/linker.rs
Normal file
36
src/tools/run-make-support/src/linker.rs
Normal 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()))
|
||||
}
|
||||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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" }
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue