unused_io_amount captures Ok(_)s

Partial rewrite of `unused_io_account` to lint over Ok(_).

Moved the check to `check_block` to simplify context checking for
expressions and allow us to check only some expressions.

For match (expr, arms) we emit a lint for io ops used on `expr` when an
arm is `Ok(_)`. Also considers the cases when there are guards in the
arms. It also captures `if let Ok(_) = ...` cases.

For `Ok(_)` it emits a note indicating where the value is ignored.

changelog: False Negatives [`unused_io_amount`]: Extended
`unused_io_amount` to catch `Ok(_)`s in `If let` and match exprs.
This commit is contained in:
Quinn Sinclair 2023-12-21 13:26:46 +02:00
parent a71211d0b5
commit f73879e096
3 changed files with 406 additions and 126 deletions

View file

@ -1,9 +1,10 @@
use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
use clippy_utils::{is_trait_method, is_try, match_trait_method, paths};
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::{is_res_lang_ctor, is_trait_method, match_trait_method, paths};
use hir::{ExprKind, PatKind};
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass;
use rustc_span::sym;
use rustc_span::{sym, Span};
declare_clippy_lint! {
/// ### What it does
@ -45,126 +46,219 @@ declare_clippy_lint! {
declare_lint_pass!(UnusedIoAmount => [UNUSED_IO_AMOUNT]);
impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount {
fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) {
let (hir::StmtKind::Semi(expr) | hir::StmtKind::Expr(expr)) = s.kind else {
return;
};
#[derive(Copy, Clone)]
enum IoOp {
AsyncWrite(bool),
AsyncRead(bool),
SyncRead(bool),
SyncWrite(bool),
}
match expr.kind {
hir::ExprKind::Match(res, _, _) if is_try(cx, expr).is_some() => {
if let hir::ExprKind::Call(func, [ref arg_0, ..]) = res.kind {
if matches!(
func.kind,
hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::TryTraitBranch, ..))
) {
check_map_error(cx, arg_0, expr);
}
} else {
check_map_error(cx, res, expr);
}
},
hir::ExprKind::MethodCall(path, arg_0, ..) => match path.ident.as_str() {
"expect" | "unwrap" | "unwrap_or" | "unwrap_or_else" | "is_ok" | "is_err" => {
check_map_error(cx, arg_0, expr);
},
_ => (),
},
_ => (),
impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount {
/// We perform the check on the block level.
/// If we want to catch match and if expressions that act as returns of the block
/// we need to check them at `check_expr` or `check_block` as they are not stmts
/// but we can't check them at `check_expr` because we need the broader context
/// because we should do this only for the final expression of the block, and not for
/// `StmtKind::Local` which binds values => the io amount is used.
///
/// To check for unused io amount in stmts, we only consider `StmtKind::Semi`.
/// `StmtKind::Local` is not considered because it binds values => the io amount is used.
/// `StmtKind::Expr` is not considered because requires unit type => the io amount is used.
/// `StmtKind::Item` is not considered because it's not an expression.
///
/// We then check the individual expressions via `check_expr`. We use the same logic for
/// semi expressions and the final expression as we need to check match and if expressions
/// for binding of the io amount to `Ok(_)`.
///
/// We explicitly check for the match source to be Normal as it needs special logic
/// to consider the arms, and we want to avoid breaking the logic for situations where things
/// get desugared to match.
fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'tcx>) {
for stmt in block.stmts {
if let hir::StmtKind::Semi(exp) = stmt.kind {
check_expr(cx, exp);
}
}
if let Some(exp) = block.expr
&& matches!(exp.kind, hir::ExprKind::If(_, _, _) | hir::ExprKind::Match(_, _, _))
{
check_expr(cx, exp);
}
}
}
fn check_expr<'a>(cx: &LateContext<'a>, expr: &'a hir::Expr<'a>) {
match expr.kind {
hir::ExprKind::If(cond, _, _)
if let ExprKind::Let(hir::Let { pat, init, .. }) = cond.kind
&& pattern_is_ignored_ok(cx, pat)
&& let Some(op) = should_lint(cx, init) =>
{
emit_lint(cx, cond.span, op, &[pat.span]);
},
hir::ExprKind::Match(expr, arms, hir::MatchSource::Normal) if let Some(op) = should_lint(cx, expr) => {
let found_arms: Vec<_> = arms
.iter()
.filter_map(|arm| {
if pattern_is_ignored_ok(cx, arm.pat) {
Some(arm.span)
} else {
None
}
})
.collect();
if !found_arms.is_empty() {
emit_lint(cx, expr.span, op, found_arms.as_slice());
}
},
_ if let Some(op) = should_lint(cx, expr) => {
emit_lint(cx, expr.span, op, &[]);
},
_ => {},
};
}
fn should_lint<'a>(cx: &LateContext<'a>, mut inner: &'a hir::Expr<'a>) -> Option<IoOp> {
inner = unpack_match(inner);
inner = unpack_try(inner);
inner = unpack_call_chain(inner);
inner = unpack_await(inner);
// we type-check it to get whether it's a read/write or their vectorized forms
// and keep only the ones that are produce io amount
check_io_mode(cx, inner)
}
fn pattern_is_ignored_ok(cx: &LateContext<'_>, pat: &hir::Pat<'_>) -> bool {
// the if checks whether we are in a result Ok( ) pattern
// and the return checks whether it is unhandled
if let PatKind::TupleStruct(ref path, inner_pat, ddp) = pat.kind
// we check against Result::Ok to avoid linting on Err(_) or something else.
&& is_res_lang_ctor(cx, cx.qpath_res(path, pat.hir_id), hir::LangItem::ResultOk)
{
return match (inner_pat, ddp.as_opt_usize()) {
// Ok(_) pattern
([inner_pat], None) if matches!(inner_pat.kind, PatKind::Wild) => true,
// Ok(..) pattern
([], Some(0)) => true,
_ => false,
};
}
false
}
fn unpack_call_chain<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> {
while let hir::ExprKind::MethodCall(path, receiver, ..) = expr.kind {
if matches!(
path.ident.as_str(),
"unwrap" | "expect" | "unwrap_or" | "unwrap_or_else" | "ok" | "is_ok" | "is_err" | "or_else" | "or"
) {
expr = receiver;
} else {
break;
}
}
expr
}
fn unpack_try<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> {
while let hir::ExprKind::Call(func, [ref arg_0, ..]) = expr.kind
&& matches!(
func.kind,
hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::TryTraitBranch, ..))
)
{
expr = arg_0;
}
expr
}
fn unpack_match<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> {
while let hir::ExprKind::Match(res, _, _) = expr.kind {
expr = res;
}
expr
}
/// If `expr` is an (e).await, return the inner expression "e" that's being
/// waited on. Otherwise return None.
fn try_remove_await<'a>(expr: &'a hir::Expr<'a>) -> Option<&hir::Expr<'a>> {
fn unpack_await<'a>(expr: &'a hir::Expr<'a>) -> &hir::Expr<'a> {
if let hir::ExprKind::Match(expr, _, hir::MatchSource::AwaitDesugar) = expr.kind {
if let hir::ExprKind::Call(func, [ref arg_0, ..]) = expr.kind {
if matches!(
func.kind,
hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::IntoFutureIntoFuture, ..))
) {
return Some(arg_0);
return arg_0;
}
}
}
None
expr
}
fn check_map_error(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>) {
let mut call = call;
while let hir::ExprKind::MethodCall(path, receiver, ..) = call.kind {
if matches!(path.ident.as_str(), "or" | "or_else" | "ok") {
call = receiver;
} else {
break;
/// Check whether the current expr is a function call for an IO operation
fn check_io_mode(cx: &LateContext<'_>, call: &hir::Expr<'_>) -> Option<IoOp> {
let hir::ExprKind::MethodCall(path, ..) = call.kind else {
return None;
};
let vectorized = match path.ident.as_str() {
"write_vectored" | "read_vectored" => true,
"write" | "read" => false,
_ => {
return None;
},
};
match (
is_trait_method(cx, call, sym::IoRead),
is_trait_method(cx, call, sym::IoWrite),
match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCREADEXT)
|| match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCREADEXT),
match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCWRITEEXT)
|| match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCWRITEEXT),
) {
(true, _, _, _) => Some(IoOp::SyncRead(vectorized)),
(_, true, _, _) => Some(IoOp::SyncWrite(vectorized)),
(_, _, true, _) => Some(IoOp::AsyncRead(vectorized)),
(_, _, _, true) => Some(IoOp::AsyncWrite(vectorized)),
_ => None,
}
}
fn emit_lint(cx: &LateContext<'_>, span: Span, op: IoOp, wild_cards: &[Span]) {
let (msg, help) = match op {
IoOp::AsyncRead(false) => (
"read amount is not handled",
Some("use `AsyncReadExt::read_exact` instead, or handle partial reads"),
),
IoOp::SyncRead(false) => (
"read amount is not handled",
Some("use `Read::read_exact` instead, or handle partial reads"),
),
IoOp::SyncWrite(false) => (
"written amount is not handled",
Some("use `Write::write_all` instead, or handle partial writes"),
),
IoOp::AsyncWrite(false) => (
"written amount is not handled",
Some("use `AsyncWriteExt::write_all` instead, or handle partial writes"),
),
IoOp::SyncRead(true) | IoOp::AsyncRead(true) => ("read amount is not handled", None),
IoOp::SyncWrite(true) | IoOp::AsyncWrite(true) => ("written amount is not handled", None),
};
span_lint_and_then(cx, UNUSED_IO_AMOUNT, span, msg, |diag| {
if let Some(help_str) = help {
diag.help(help_str);
}
}
if let Some(call) = try_remove_await(call) {
check_method_call(cx, call, expr, true);
} else {
check_method_call(cx, call, expr, false);
}
}
fn check_method_call(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>, is_await: bool) {
if let hir::ExprKind::MethodCall(path, ..) = call.kind {
let symbol = path.ident.as_str();
let read_trait = if is_await {
match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCREADEXT)
|| match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCREADEXT)
} else {
is_trait_method(cx, call, sym::IoRead)
};
let write_trait = if is_await {
match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCWRITEEXT)
|| match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCWRITEEXT)
} else {
is_trait_method(cx, call, sym::IoWrite)
};
match (read_trait, write_trait, symbol, is_await) {
(true, _, "read", false) => span_lint_and_help(
cx,
UNUSED_IO_AMOUNT,
expr.span,
"read amount is not handled",
None,
"use `Read::read_exact` instead, or handle partial reads",
),
(true, _, "read", true) => span_lint_and_help(
cx,
UNUSED_IO_AMOUNT,
expr.span,
"read amount is not handled",
None,
"use `AsyncReadExt::read_exact` instead, or handle partial reads",
),
(true, _, "read_vectored", _) => {
span_lint(cx, UNUSED_IO_AMOUNT, expr.span, "read amount is not handled");
},
(_, true, "write", false) => span_lint_and_help(
cx,
UNUSED_IO_AMOUNT,
expr.span,
"written amount is not handled",
None,
"use `Write::write_all` instead, or handle partial writes",
),
(_, true, "write", true) => span_lint_and_help(
cx,
UNUSED_IO_AMOUNT,
expr.span,
"written amount is not handled",
None,
"use `AsyncWriteExt::write_all` instead, or handle partial writes",
),
(_, true, "write_vectored", _) => {
span_lint(cx, UNUSED_IO_AMOUNT, expr.span, "written amount is not handled");
},
_ => (),
for span in wild_cards {
diag.span_note(
*span,
"the result is consumed here, but the amount of I/O bytes remains unhandled",
);
}
}
});
}

View file

@ -1,4 +1,5 @@
#![allow(dead_code, clippy::needless_pass_by_ref_mut)]
#![allow(clippy::redundant_pattern_matching)]
#![warn(clippy::unused_io_amount)]
extern crate futures;
@ -142,4 +143,90 @@ async fn undetected_bad_async_write<W: AsyncWrite + Unpin>(w: &mut W) {
future.await.unwrap();
}
fn match_okay_underscore<T: io::Read + io::Write>(s: &mut T) {
match s.write(b"test") {
//~^ ERROR: written amount is not handled
Ok(_) => todo!(),
//~^ NOTE: the result is consumed here, but the amount of I/O bytes remains unhandled
Err(_) => todo!(),
};
let mut buf = [0u8; 4];
match s.read(&mut buf) {
//~^ ERROR: read amount is not handled
Ok(_) => todo!(),
//~^ NOTE: the result is consumed here, but the amount of I/O bytes remains unhandled
Err(_) => todo!(),
}
}
fn match_okay_underscore_read_expr<T: io::Read + io::Write>(s: &mut T) {
match s.read(&mut [0u8; 4]) {
//~^ ERROR: read amount is not handled
Ok(_) => todo!(),
//~^ NOTE: the result is consumed here, but the amount of I/O bytes remains unhandled
Err(_) => todo!(),
}
}
fn match_okay_underscore_write_expr<T: io::Read + io::Write>(s: &mut T) {
match s.write(b"test") {
//~^ ERROR: written amount is not handled
Ok(_) => todo!(),
//~^ NOTE: the result is consumed here, but the amount of I/O bytes remains unhandled
Err(_) => todo!(),
}
}
fn returned_value_should_not_lint<T: io::Read + io::Write>(s: &mut T) -> Result<usize, std::io::Error> {
s.write(b"test")
}
fn if_okay_underscore_read_expr<T: io::Read + io::Write>(s: &mut T) {
if let Ok(_) = s.read(&mut [0u8; 4]) {
//~^ ERROR: read amount is not handled
todo!()
}
}
fn if_okay_underscore_write_expr<T: io::Read + io::Write>(s: &mut T) {
if let Ok(_) = s.write(b"test") {
//~^ ERROR: written amount is not handled
todo!()
}
}
fn if_okay_dots_write_expr<T: io::Read + io::Write>(s: &mut T) {
if let Ok(..) = s.write(b"test") {
//~^ ERROR: written amount is not handled
todo!()
}
}
fn if_okay_underscore_write_expr_true_negative<T: io::Read + io::Write>(s: &mut T) {
if let Ok(bound) = s.write(b"test") {
todo!()
}
}
fn match_okay_underscore_true_neg<T: io::Read + io::Write>(s: &mut T) {
match s.write(b"test") {
Ok(bound) => todo!(),
Err(_) => todo!(),
};
}
fn true_negative<T: io::Read + io::Write>(s: &mut T) {
let mut buf = [0u8; 4];
let read_amount = s.read(&mut buf).unwrap();
}
fn on_return_should_not_raise<T: io::Read + io::Write>(s: &mut T) -> io::Result<usize> {
/// this is bad code because it goes around the problem of handling the read amount
/// by returning it, which makes it impossible to know this is a resonpose from the
/// correct account.
let mut buf = [0u8; 4];
s.read(&mut buf)
}
fn main() {}

View file

@ -1,5 +1,5 @@
error: written amount is not handled
--> $DIR/unused_io_amount.rs:9:5
--> $DIR/unused_io_amount.rs:10:5
|
LL | s.write(b"test")?;
| ^^^^^^^^^^^^^^^^^
@ -9,7 +9,7 @@ LL | s.write(b"test")?;
= help: to override `-D warnings` add `#[allow(clippy::unused_io_amount)]`
error: read amount is not handled
--> $DIR/unused_io_amount.rs:12:5
--> $DIR/unused_io_amount.rs:13:5
|
LL | s.read(&mut buf)?;
| ^^^^^^^^^^^^^^^^^
@ -17,7 +17,7 @@ LL | s.read(&mut buf)?;
= help: use `Read::read_exact` instead, or handle partial reads
error: written amount is not handled
--> $DIR/unused_io_amount.rs:18:5
--> $DIR/unused_io_amount.rs:19:5
|
LL | s.write(b"test").unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^
@ -25,7 +25,7 @@ LL | s.write(b"test").unwrap();
= help: use `Write::write_all` instead, or handle partial writes
error: read amount is not handled
--> $DIR/unused_io_amount.rs:21:5
--> $DIR/unused_io_amount.rs:22:5
|
LL | s.read(&mut buf).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^
@ -33,19 +33,19 @@ LL | s.read(&mut buf).unwrap();
= help: use `Read::read_exact` instead, or handle partial reads
error: read amount is not handled
--> $DIR/unused_io_amount.rs:26:5
--> $DIR/unused_io_amount.rs:27:5
|
LL | s.read_vectored(&mut [io::IoSliceMut::new(&mut [])])?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: written amount is not handled
--> $DIR/unused_io_amount.rs:28:5
--> $DIR/unused_io_amount.rs:29:5
|
LL | s.write_vectored(&[io::IoSlice::new(&[])])?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: read amount is not handled
--> $DIR/unused_io_amount.rs:36:5
--> $DIR/unused_io_amount.rs:37:5
|
LL | reader.read(&mut result).ok()?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -53,7 +53,7 @@ LL | reader.read(&mut result).ok()?;
= help: use `Read::read_exact` instead, or handle partial reads
error: read amount is not handled
--> $DIR/unused_io_amount.rs:46:5
--> $DIR/unused_io_amount.rs:47:5
|
LL | reader.read(&mut result).or_else(|err| Err(err))?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -61,7 +61,7 @@ LL | reader.read(&mut result).or_else(|err| Err(err))?;
= help: use `Read::read_exact` instead, or handle partial reads
error: read amount is not handled
--> $DIR/unused_io_amount.rs:59:5
--> $DIR/unused_io_amount.rs:60:5
|
LL | reader.read(&mut result).or(Err(Error::Kind))?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -69,7 +69,7 @@ LL | reader.read(&mut result).or(Err(Error::Kind))?;
= help: use `Read::read_exact` instead, or handle partial reads
error: read amount is not handled
--> $DIR/unused_io_amount.rs:67:5
--> $DIR/unused_io_amount.rs:68:5
|
LL | / reader
LL | |
@ -82,7 +82,7 @@ LL | | .expect("error");
= help: use `Read::read_exact` instead, or handle partial reads
error: written amount is not handled
--> $DIR/unused_io_amount.rs:77:5
--> $DIR/unused_io_amount.rs:78:5
|
LL | s.write(b"ok").is_ok();
| ^^^^^^^^^^^^^^^^^^^^^^
@ -90,7 +90,7 @@ LL | s.write(b"ok").is_ok();
= help: use `Write::write_all` instead, or handle partial writes
error: written amount is not handled
--> $DIR/unused_io_amount.rs:79:5
--> $DIR/unused_io_amount.rs:80:5
|
LL | s.write(b"err").is_err();
| ^^^^^^^^^^^^^^^^^^^^^^^^
@ -98,7 +98,7 @@ LL | s.write(b"err").is_err();
= help: use `Write::write_all` instead, or handle partial writes
error: read amount is not handled
--> $DIR/unused_io_amount.rs:82:5
--> $DIR/unused_io_amount.rs:83:5
|
LL | s.read(&mut buf).is_ok();
| ^^^^^^^^^^^^^^^^^^^^^^^^
@ -106,7 +106,7 @@ LL | s.read(&mut buf).is_ok();
= help: use `Read::read_exact` instead, or handle partial reads
error: read amount is not handled
--> $DIR/unused_io_amount.rs:84:5
--> $DIR/unused_io_amount.rs:85:5
|
LL | s.read(&mut buf).is_err();
| ^^^^^^^^^^^^^^^^^^^^^^^^^
@ -114,7 +114,7 @@ LL | s.read(&mut buf).is_err();
= help: use `Read::read_exact` instead, or handle partial reads
error: written amount is not handled
--> $DIR/unused_io_amount.rs:89:5
--> $DIR/unused_io_amount.rs:90:5
|
LL | w.write(b"hello world").await.unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -122,7 +122,7 @@ LL | w.write(b"hello world").await.unwrap();
= help: use `AsyncWriteExt::write_all` instead, or handle partial writes
error: read amount is not handled
--> $DIR/unused_io_amount.rs:95:5
--> $DIR/unused_io_amount.rs:96:5
|
LL | r.read(&mut buf[..]).await.unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -130,7 +130,15 @@ LL | r.read(&mut buf[..]).await.unwrap();
= help: use `AsyncReadExt::read_exact` instead, or handle partial reads
error: written amount is not handled
--> $DIR/unused_io_amount.rs:109:9
--> $DIR/unused_io_amount.rs:104:5
|
LL | w.write(b"hello world");
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= help: use `AsyncWriteExt::write_all` instead, or handle partial writes
error: written amount is not handled
--> $DIR/unused_io_amount.rs:110:9
|
LL | w.write(b"hello world").await?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -138,7 +146,7 @@ LL | w.write(b"hello world").await?;
= help: use `AsyncWriteExt::write_all` instead, or handle partial writes
error: read amount is not handled
--> $DIR/unused_io_amount.rs:118:9
--> $DIR/unused_io_amount.rs:119:9
|
LL | r.read(&mut buf[..]).await.or(Err(Error::Kind))?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -146,7 +154,7 @@ LL | r.read(&mut buf[..]).await.or(Err(Error::Kind))?;
= help: use `AsyncReadExt::read_exact` instead, or handle partial reads
error: written amount is not handled
--> $DIR/unused_io_amount.rs:127:5
--> $DIR/unused_io_amount.rs:128:5
|
LL | w.write(b"hello world").await.unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -154,12 +162,103 @@ LL | w.write(b"hello world").await.unwrap();
= help: use `AsyncWriteExt::write_all` instead, or handle partial writes
error: read amount is not handled
--> $DIR/unused_io_amount.rs:133:5
--> $DIR/unused_io_amount.rs:134:5
|
LL | r.read(&mut buf[..]).await.unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: use `AsyncReadExt::read_exact` instead, or handle partial reads
error: aborting due to 20 previous errors
error: written amount is not handled
--> $DIR/unused_io_amount.rs:147:11
|
LL | match s.write(b"test") {
| ^^^^^^^^^^^^^^^^
|
= help: use `Write::write_all` instead, or handle partial writes
note: the result is consumed here, but the amount of I/O bytes remains unhandled
--> $DIR/unused_io_amount.rs:149:9
|
LL | Ok(_) => todo!(),
| ^^^^^^^^^^^^^^^^
error: read amount is not handled
--> $DIR/unused_io_amount.rs:155:11
|
LL | match s.read(&mut buf) {
| ^^^^^^^^^^^^^^^^
|
= help: use `Read::read_exact` instead, or handle partial reads
note: the result is consumed here, but the amount of I/O bytes remains unhandled
--> $DIR/unused_io_amount.rs:157:9
|
LL | Ok(_) => todo!(),
| ^^^^^^^^^^^^^^^^
error: read amount is not handled
--> $DIR/unused_io_amount.rs:164:11
|
LL | match s.read(&mut [0u8; 4]) {
| ^^^^^^^^^^^^^^^^^^^^^
|
= help: use `Read::read_exact` instead, or handle partial reads
note: the result is consumed here, but the amount of I/O bytes remains unhandled
--> $DIR/unused_io_amount.rs:166:9
|
LL | Ok(_) => todo!(),
| ^^^^^^^^^^^^^^^^
error: written amount is not handled
--> $DIR/unused_io_amount.rs:173:11
|
LL | match s.write(b"test") {
| ^^^^^^^^^^^^^^^^
|
= help: use `Write::write_all` instead, or handle partial writes
note: the result is consumed here, but the amount of I/O bytes remains unhandled
--> $DIR/unused_io_amount.rs:175:9
|
LL | Ok(_) => todo!(),
| ^^^^^^^^^^^^^^^^
error: read amount is not handled
--> $DIR/unused_io_amount.rs:186:8
|
LL | if let Ok(_) = s.read(&mut [0u8; 4]) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: use `Read::read_exact` instead, or handle partial reads
note: the result is consumed here, but the amount of I/O bytes remains unhandled
--> $DIR/unused_io_amount.rs:186:12
|
LL | if let Ok(_) = s.read(&mut [0u8; 4]) {
| ^^^^^
error: written amount is not handled
--> $DIR/unused_io_amount.rs:193:8
|
LL | if let Ok(_) = s.write(b"test") {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: use `Write::write_all` instead, or handle partial writes
note: the result is consumed here, but the amount of I/O bytes remains unhandled
--> $DIR/unused_io_amount.rs:193:12
|
LL | if let Ok(_) = s.write(b"test") {
| ^^^^^
error: written amount is not handled
--> $DIR/unused_io_amount.rs:200:8
|
LL | if let Ok(..) = s.write(b"test") {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: use `Write::write_all` instead, or handle partial writes
note: the result is consumed here, but the amount of I/O bytes remains unhandled
--> $DIR/unused_io_amount.rs:200:12
|
LL | if let Ok(..) = s.write(b"test") {
| ^^^^^^
error: aborting due to 28 previous errors