Skip to content

Commit 8fa406b

Browse files
committed
Auto merge of rust-lang#13069 - Jarcho:misc_small4, r=Manishearth
Misc refactorings part 4 And even more rearrangements to check the HIR tree before other checks. changelog: none
2 parents 87f8a5b + eda45aa commit 8fa406b

19 files changed

+104
-150
lines changed

clippy_lints/src/len_zero.rs

+8-11
Original file line numberDiff line numberDiff line change
@@ -121,11 +121,9 @@ declare_lint_pass!(LenZero => [LEN_ZERO, LEN_WITHOUT_IS_EMPTY, COMPARISON_TO_EMP
121121

122122
impl<'tcx> LateLintPass<'tcx> for LenZero {
123123
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
124-
if item.span.from_expansion() {
125-
return;
126-
}
127-
128-
if let ItemKind::Trait(_, _, _, _, trait_items) = item.kind {
124+
if let ItemKind::Trait(_, _, _, _, trait_items) = item.kind
125+
&& !item.span.from_expansion()
126+
{
129127
check_trait_items(cx, item, trait_items);
130128
}
131129
}
@@ -162,17 +160,14 @@ impl<'tcx> LateLintPass<'tcx> for LenZero {
162160
}
163161

164162
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
165-
if expr.span.from_expansion() {
166-
return;
167-
}
168-
169163
if let ExprKind::Let(lt) = expr.kind
170-
&& has_is_empty(cx, lt.init)
171164
&& match lt.pat.kind {
172165
PatKind::Slice([], None, []) => true,
173166
PatKind::Lit(lit) if is_empty_string(lit) => true,
174167
_ => false,
175168
}
169+
&& !expr.span.from_expansion()
170+
&& has_is_empty(cx, lt.init)
176171
{
177172
let mut applicability = Applicability::MachineApplicable;
178173

@@ -190,7 +185,9 @@ impl<'tcx> LateLintPass<'tcx> for LenZero {
190185
);
191186
}
192187

193-
if let ExprKind::Binary(Spanned { node: cmp, .. }, left, right) = expr.kind {
188+
if let ExprKind::Binary(Spanned { node: cmp, .. }, left, right) = expr.kind
189+
&& !expr.span.from_expansion()
190+
{
194191
// expr.span might contains parenthesis, see issue #10529
195192
let actual_span = span_without_enclosing_paren(cx, expr.span);
196193
match cmp {

clippy_lints/src/let_if_seq.rs

+3-5
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,10 @@ declare_lint_pass!(LetIfSeq => [USELESS_LET_IF_SEQ]);
5858

5959
impl<'tcx> LateLintPass<'tcx> for LetIfSeq {
6060
fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) {
61-
let mut it = block.stmts.iter().peekable();
62-
while let Some(stmt) = it.next() {
63-
if let Some(expr) = it.peek()
64-
&& let hir::StmtKind::Let(local) = stmt.kind
61+
for [stmt, next] in block.stmts.array_windows::<2>() {
62+
if let hir::StmtKind::Let(local) = stmt.kind
6563
&& let hir::PatKind::Binding(mode, canonical_id, ident, None) = local.pat.kind
66-
&& let hir::StmtKind::Expr(if_) = expr.kind
64+
&& let hir::StmtKind::Expr(if_) = next.kind
6765
&& let hir::ExprKind::If(
6866
hir::Expr {
6967
kind: hir::ExprKind::DropTemps(cond),

clippy_lints/src/let_underscore.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -139,9 +139,9 @@ const SYNC_GUARD_PATHS: [&[&str]; 3] = [
139139
impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
140140
fn check_local(&mut self, cx: &LateContext<'tcx>, local: &LetStmt<'tcx>) {
141141
if matches!(local.source, LocalSource::Normal)
142-
&& !in_external_macro(cx.tcx.sess, local.span)
143142
&& let PatKind::Wild = local.pat.kind
144143
&& let Some(init) = local.init
144+
&& !in_external_macro(cx.tcx.sess, local.span)
145145
{
146146
let init_ty = cx.typeck_results().expr_ty(init);
147147
let contains_sync_guard = init_ty.walk().any(|inner| match inner.unpack() {

clippy_lints/src/let_with_type_underscore.rs

+6-11
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use clippy_utils::diagnostics::span_lint_and_help;
2-
use clippy_utils::source::snippet;
2+
use clippy_utils::is_from_proc_macro;
33
use rustc_hir::{LetStmt, TyKind};
44
use rustc_lint::{LateContext, LateLintPass};
55
use rustc_middle::lint::in_external_macro;
@@ -25,19 +25,14 @@ declare_clippy_lint! {
2525
}
2626
declare_lint_pass!(UnderscoreTyped => [LET_WITH_TYPE_UNDERSCORE]);
2727

28-
impl LateLintPass<'_> for UnderscoreTyped {
29-
fn check_local(&mut self, cx: &LateContext<'_>, local: &LetStmt<'_>) {
30-
if !in_external_macro(cx.tcx.sess, local.span)
31-
&& let Some(ty) = local.ty // Ensure that it has a type defined
28+
impl<'tcx> LateLintPass<'tcx> for UnderscoreTyped {
29+
fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'_>) {
30+
if let Some(ty) = local.ty // Ensure that it has a type defined
3231
&& let TyKind::Infer = &ty.kind // that type is '_'
3332
&& local.span.eq_ctxt(ty.span)
33+
&& !in_external_macro(cx.tcx.sess, local.span)
34+
&& !is_from_proc_macro(cx, ty)
3435
{
35-
// NOTE: Using `is_from_proc_macro` on `init` will require that it's initialized,
36-
// this doesn't. Alternatively, `WithSearchPat` can be implemented for `Ty`
37-
if snippet(cx, ty.span, "_").trim() != "_" {
38-
return;
39-
}
40-
4136
span_lint_and_help(
4237
cx,
4338
LET_WITH_TYPE_UNDERSCORE,

clippy_lints/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#![feature(if_let_guard)]
77
#![feature(is_sorted)]
88
#![feature(iter_intersperse)]
9+
#![feature(iter_partition_in_place)]
910
#![feature(let_chains)]
1011
#![feature(never_type)]
1112
#![feature(rustc_private)]

clippy_lints/src/literal_representation.rs

+6-10
Original file line numberDiff line numberDiff line change
@@ -233,11 +233,9 @@ impl_lint_pass!(LiteralDigitGrouping => [
233233

234234
impl EarlyLintPass for LiteralDigitGrouping {
235235
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
236-
if in_external_macro(cx.sess(), expr.span) {
237-
return;
238-
}
239-
240-
if let ExprKind::Lit(lit) = expr.kind {
236+
if let ExprKind::Lit(lit) = expr.kind
237+
&& !in_external_macro(cx.sess(), expr.span)
238+
{
241239
self.check_lit(cx, lit, expr.span);
242240
}
243241
}
@@ -448,11 +446,9 @@ impl_lint_pass!(DecimalLiteralRepresentation => [DECIMAL_LITERAL_REPRESENTATION]
448446

449447
impl EarlyLintPass for DecimalLiteralRepresentation {
450448
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
451-
if in_external_macro(cx.sess(), expr.span) {
452-
return;
453-
}
454-
455-
if let ExprKind::Lit(lit) = expr.kind {
449+
if let ExprKind::Lit(lit) = expr.kind
450+
&& !in_external_macro(cx.sess(), expr.span)
451+
{
456452
self.check_lit(cx, lit, expr.span);
457453
}
458454
}

clippy_lints/src/manual_bits.rs

+1-4
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,10 @@ impl_lint_pass!(ManualBits => [MANUAL_BITS]);
5050

5151
impl<'tcx> LateLintPass<'tcx> for ManualBits {
5252
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
53-
if !self.msrv.meets(msrvs::MANUAL_BITS) {
54-
return;
55-
}
56-
5753
if let ExprKind::Binary(bin_op, left_expr, right_expr) = expr.kind
5854
&& let BinOpKind::Mul = &bin_op.node
5955
&& !in_external_macro(cx.sess(), expr.span)
56+
&& self.msrv.meets(msrvs::MANUAL_BITS)
6057
&& let ctxt = expr.span.ctxt()
6158
&& left_expr.span.ctxt() == ctxt
6259
&& right_expr.span.ctxt() == ctxt

clippy_lints/src/manual_float_methods.rs

+13-16
Original file line numberDiff line numberDiff line change
@@ -82,29 +82,26 @@ impl Variant {
8282

8383
impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods {
8484
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
85-
if !in_external_macro(cx.sess(), expr.span)
86-
&& (
87-
matches!(cx.tcx.constness(cx.tcx.hir().enclosing_body_owner(expr.hir_id)), Constness::NotConst)
88-
|| cx.tcx.features().declared(sym!(const_float_classify))
89-
) && let ExprKind::Binary(kind, lhs, rhs) = expr.kind
85+
if let ExprKind::Binary(kind, lhs, rhs) = expr.kind
9086
&& let ExprKind::Binary(lhs_kind, lhs_lhs, lhs_rhs) = lhs.kind
9187
&& let ExprKind::Binary(rhs_kind, rhs_lhs, rhs_rhs) = rhs.kind
9288
// Checking all possible scenarios using a function would be a hopeless task, as we have
9389
// 16 possible alignments of constants/operands. For now, let's use `partition`.
94-
&& let (operands, constants) = [lhs_lhs, lhs_rhs, rhs_lhs, rhs_rhs]
95-
.into_iter()
96-
.partition::<Vec<&Expr<'_>>, _>(|i| path_to_local(i).is_some())
97-
&& let [first, second] = &*operands
98-
&& let Some([const_1, const_2]) = constants
99-
.into_iter()
100-
.map(|i| constant(cx, cx.typeck_results(), i))
101-
.collect::<Option<Vec<_>>>()
102-
.as_deref()
90+
&& let mut exprs = [lhs_lhs, lhs_rhs, rhs_lhs, rhs_rhs]
91+
&& exprs.iter_mut().partition_in_place(|i| path_to_local(i).is_some()) == 2
92+
&& !in_external_macro(cx.sess(), expr.span)
93+
&& (
94+
matches!(cx.tcx.constness(cx.tcx.hir().enclosing_body_owner(expr.hir_id)), Constness::NotConst)
95+
|| cx.tcx.features().declared(sym!(const_float_classify))
96+
)
97+
&& let [first, second, const_1, const_2] = exprs
98+
&& let Some(const_1) = constant(cx, cx.typeck_results(), const_1)
99+
&& let Some(const_2) = constant(cx, cx.typeck_results(), const_2)
103100
&& path_to_local(first).is_some_and(|f| path_to_local(second).is_some_and(|s| f == s))
104101
// The actual infinity check, we also allow `NEG_INFINITY` before` INFINITY` just in
105102
// case somebody does that for some reason
106-
&& (is_infinity(const_1) && is_neg_infinity(const_2)
107-
|| is_neg_infinity(const_1) && is_infinity(const_2))
103+
&& (is_infinity(&const_1) && is_neg_infinity(&const_2)
104+
|| is_neg_infinity(&const_1) && is_infinity(&const_2))
108105
&& let Some(local_snippet) = snippet_opt(cx, first.span)
109106
{
110107
let variant = match (kind.node, lhs_kind.node, rhs_kind.node) {

clippy_lints/src/manual_let_else.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,14 @@ declare_clippy_lint! {
4949

5050
impl<'tcx> QuestionMark {
5151
pub(crate) fn check_manual_let_else(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) {
52-
if !self.msrv.meets(msrvs::LET_ELSE) || in_external_macro(cx.sess(), stmt.span) {
53-
return;
54-
}
55-
5652
if let StmtKind::Let(local) = stmt.kind
5753
&& let Some(init) = local.init
5854
&& local.els.is_none()
5955
&& local.ty.is_none()
6056
&& init.span.eq_ctxt(stmt.span)
6157
&& let Some(if_let_or_match) = IfLetOrMatch::parse(cx, init)
58+
&& self.msrv.meets(msrvs::LET_ELSE)
59+
&& !in_external_macro(cx.sess(), stmt.span)
6260
{
6361
match if_let_or_match {
6462
IfLetOrMatch::IfLet(if_let_expr, let_pat, if_then, if_else, ..) => {

clippy_lints/src/manual_main_separator_str.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,13 @@ impl_lint_pass!(ManualMainSeparatorStr => [MANUAL_MAIN_SEPARATOR_STR]);
4747

4848
impl LateLintPass<'_> for ManualMainSeparatorStr {
4949
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
50-
if self.msrv.meets(msrvs::PATH_MAIN_SEPARATOR_STR)
51-
&& let (target, _) = peel_hir_expr_refs(expr)
52-
&& is_trait_method(cx, target, sym::ToString)
53-
&& let ExprKind::MethodCall(path, receiver, &[], _) = target.kind
50+
let (target, _) = peel_hir_expr_refs(expr);
51+
if let ExprKind::MethodCall(path, receiver, &[], _) = target.kind
5452
&& path.ident.name == sym::to_string
5553
&& let ExprKind::Path(QPath::Resolved(None, path)) = receiver.kind
5654
&& let Res::Def(DefKind::Const, receiver_def_id) = path.res
55+
&& is_trait_method(cx, target, sym::ToString)
56+
&& self.msrv.meets(msrvs::PATH_MAIN_SEPARATOR_STR)
5757
&& match_def_path(cx, receiver_def_id, &paths::PATH_MAIN_SEPARATOR)
5858
&& let ty::Ref(_, ty, Mutability::Not) = cx.typeck_results().expr_ty_adjusted(expr).kind()
5959
&& ty.is_str()

clippy_lints/src/manual_non_exhaustive.rs

+5-9
Original file line numberDiff line numberDiff line change
@@ -97,19 +97,15 @@ impl_lint_pass!(ManualNonExhaustiveEnum => [MANUAL_NON_EXHAUSTIVE]);
9797

9898
impl EarlyLintPass for ManualNonExhaustiveStruct {
9999
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
100-
if !self.msrv.meets(msrvs::NON_EXHAUSTIVE) {
101-
return;
102-
}
103-
104-
if let ast::ItemKind::Struct(variant_data, _) = &item.kind {
105-
let (fields, delimiter) = match variant_data {
100+
if let ast::ItemKind::Struct(variant_data, _) = &item.kind
101+
&& let (fields, delimiter) = match variant_data {
106102
ast::VariantData::Struct { fields, .. } => (&**fields, '{'),
107103
ast::VariantData::Tuple(fields, _) => (&**fields, '('),
108104
ast::VariantData::Unit(_) => return,
109-
};
110-
if fields.len() <= 1 {
111-
return;
112105
}
106+
&& fields.len() > 1
107+
&& self.msrv.meets(msrvs::NON_EXHAUSTIVE)
108+
{
113109
let mut iter = fields.iter().filter_map(|f| match f.vis.kind {
114110
VisibilityKind::Public => None,
115111
VisibilityKind::Inherited => Some(Ok(f)),

clippy_lints/src/manual_range_patterns.rs

+1-4
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,11 @@ impl Num {
7676

7777
impl LateLintPass<'_> for ManualRangePatterns {
7878
fn check_pat(&mut self, cx: &LateContext<'_>, pat: &'_ rustc_hir::Pat<'_>) {
79-
if in_external_macro(cx.sess(), pat.span) {
80-
return;
81-
}
82-
8379
// a pattern like 1 | 2 seems fine, lint if there are at least 3 alternatives
8480
// or at least one range
8581
if let PatKind::Or(pats) = pat.kind
8682
&& (pats.len() >= 3 || pats.iter().any(|p| matches!(p.kind, PatKind::Range(..))))
83+
&& !in_external_macro(cx.sess(), pat.span)
8784
{
8885
let mut min = Num::dummy(i128::MAX);
8986
let mut max = Num::dummy(i128::MIN);

clippy_lints/src/manual_rem_euclid.rs

+22-27
Original file line numberDiff line numberDiff line change
@@ -48,35 +48,30 @@ impl_lint_pass!(ManualRemEuclid => [MANUAL_REM_EUCLID]);
4848

4949
impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid {
5050
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
51-
if !self.msrv.meets(msrvs::REM_EUCLID) {
52-
return;
53-
}
54-
55-
if in_constant(cx, expr.hir_id) && !self.msrv.meets(msrvs::REM_EUCLID_CONST) {
56-
return;
57-
}
58-
59-
if in_external_macro(cx.sess(), expr.span) {
60-
return;
61-
}
62-
6351
// (x % c + c) % c
64-
if let ExprKind::Binary(op1, expr1, right) = expr.kind
65-
&& op1.node == BinOpKind::Rem
52+
if let ExprKind::Binary(rem_op, rem_lhs, rem_rhs) = expr.kind
53+
&& rem_op.node == BinOpKind::Rem
54+
&& let ExprKind::Binary(add_op, add_lhs, add_rhs) = rem_lhs.kind
55+
&& add_op.node == BinOpKind::Add
6656
&& let ctxt = expr.span.ctxt()
67-
&& expr1.span.ctxt() == ctxt
68-
&& let Some(const1) = check_for_unsigned_int_constant(cx, right)
69-
&& let ExprKind::Binary(op2, left, right) = expr1.kind
70-
&& op2.node == BinOpKind::Add
71-
&& let Some((const2, expr2)) = check_for_either_unsigned_int_constant(cx, left, right)
72-
&& expr2.span.ctxt() == ctxt
73-
&& let ExprKind::Binary(op3, expr3, right) = expr2.kind
74-
&& op3.node == BinOpKind::Rem
75-
&& let Some(const3) = check_for_unsigned_int_constant(cx, right)
57+
&& rem_lhs.span.ctxt() == ctxt
58+
&& rem_rhs.span.ctxt() == ctxt
59+
&& add_lhs.span.ctxt() == ctxt
60+
&& add_rhs.span.ctxt() == ctxt
61+
&& !in_external_macro(cx.sess(), expr.span)
62+
&& self.msrv.meets(msrvs::REM_EUCLID)
63+
&& (self.msrv.meets(msrvs::REM_EUCLID_CONST) || !in_constant(cx, expr.hir_id))
64+
&& let Some(const1) = check_for_unsigned_int_constant(cx, rem_rhs)
65+
&& let Some((const2, add_other)) = check_for_either_unsigned_int_constant(cx, add_lhs, add_rhs)
66+
&& let ExprKind::Binary(rem2_op, rem2_lhs, rem2_rhs) = add_other.kind
67+
&& rem2_op.node == BinOpKind::Rem
68+
&& const1 == const2
69+
&& let Some(hir_id) = path_to_local(rem2_lhs)
70+
&& let Some(const3) = check_for_unsigned_int_constant(cx, rem2_rhs)
7671
// Also ensures the const is nonzero since zero can't be a divisor
77-
&& const1 == const2 && const2 == const3
78-
&& let Some(hir_id) = path_to_local(expr3)
79-
&& let Node::Pat(_) = cx.tcx.hir_node(hir_id)
72+
&& const2 == const3
73+
&& rem2_lhs.span.ctxt() == ctxt
74+
&& rem2_rhs.span.ctxt() == ctxt
8075
{
8176
// Apply only to params or locals with annotated types
8277
match cx.tcx.parent_hir_node(hir_id) {
@@ -91,7 +86,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid {
9186
};
9287

9388
let mut app = Applicability::MachineApplicable;
94-
let rem_of = snippet_with_context(cx, expr3.span, ctxt, "_", &mut app).0;
89+
let rem_of = snippet_with_context(cx, rem2_lhs.span, ctxt, "_", &mut app).0;
9590
span_lint_and_sugg(
9691
cx,
9792
MANUAL_REM_EUCLID,

clippy_lints/src/manual_retain.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,8 @@ impl_lint_pass!(ManualRetain => [MANUAL_RETAIN]);
7070
impl<'tcx> LateLintPass<'tcx> for ManualRetain {
7171
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
7272
if let Assign(left_expr, collect_expr, _) = &expr.kind
73-
&& let hir::ExprKind::MethodCall(seg, ..) = &collect_expr.kind
73+
&& let hir::ExprKind::MethodCall(seg, target_expr, [], _) = &collect_expr.kind
7474
&& seg.args.is_none()
75-
&& let hir::ExprKind::MethodCall(_, target_expr, [], _) = &collect_expr.kind
7675
&& let Some(collect_def_id) = cx.typeck_results().type_dependent_def_id(collect_expr.hir_id)
7776
&& cx.tcx.is_diagnostic_item(sym::iterator_collect_fn, collect_def_id)
7877
{

clippy_lints/src/manual_slice_size_calculation.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,11 @@ declare_lint_pass!(ManualSliceSizeCalculation => [MANUAL_SLICE_SIZE_CALCULATION]
4040

4141
impl<'tcx> LateLintPass<'tcx> for ManualSliceSizeCalculation {
4242
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
43-
// Does not apply inside const because size_of_val is not cost in stable.
44-
if !in_constant(cx, expr.hir_id)
45-
&& let ExprKind::Binary(ref op, left, right) = expr.kind
43+
if let ExprKind::Binary(ref op, left, right) = expr.kind
4644
&& BinOpKind::Mul == op.node
4745
&& !expr.span.from_expansion()
46+
// Does not apply inside const because size_of_val is not cost in stable.
47+
&& !in_constant(cx, expr.hir_id)
4848
&& let Some(receiver) = simplify(cx, left, right)
4949
{
5050
let ctxt = expr.span.ctxt();

clippy_lints/src/manual_strip.rs

+2-5
Original file line numberDiff line numberDiff line change
@@ -66,14 +66,11 @@ enum StripKind {
6666

6767
impl<'tcx> LateLintPass<'tcx> for ManualStrip {
6868
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
69-
if !self.msrv.meets(msrvs::STR_STRIP_PREFIX) {
70-
return;
71-
}
72-
7369
if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr)
7470
&& let ExprKind::MethodCall(_, target_arg, [pattern], _) = cond.kind
75-
&& let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(cond.hir_id)
7671
&& let ExprKind::Path(target_path) = &target_arg.kind
72+
&& self.msrv.meets(msrvs::STR_STRIP_PREFIX)
73+
&& let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(cond.hir_id)
7774
{
7875
let strip_kind = if match_def_path(cx, method_def_id, &paths::STR_STARTS_WITH) {
7976
StripKind::Prefix

0 commit comments

Comments
 (0)