From 8876523af3144700711a476ece414e3e5d2fdcec Mon Sep 17 00:00:00 2001 From: "Heinz N. Gies" Date: Fri, 18 Oct 2019 21:09:42 +0200 Subject: [PATCH 01/14] Add lint for exit --- clippy_lints/src/exit.rs | 41 +++++++++++++++++++++++++++++++++ clippy_lints/src/lib.rs | 2 ++ clippy_lints/src/utils/paths.rs | 1 + tests/ui/exit.rs | 4 ++++ tests/ui/exit.stderr | 10 ++++++++ 5 files changed, 58 insertions(+) create mode 100644 clippy_lints/src/exit.rs create mode 100644 tests/ui/exit.rs create mode 100644 tests/ui/exit.stderr diff --git a/clippy_lints/src/exit.rs b/clippy_lints/src/exit.rs new file mode 100644 index 000000000000..222f84bd028e --- /dev/null +++ b/clippy_lints/src/exit.rs @@ -0,0 +1,41 @@ +use crate::utils::{match_def_path, paths, qpath_res, span_lint}; +use if_chain::if_chain; +use rustc::hir::{Expr, ExprKind}; +use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass}; +use rustc::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** `exit()` terminates the program and doesn't provide a + /// stack trace. + /// + /// **Why is this bad?** Ideally a program is terminated by finishing + /// the main function. + /// + /// **Known problems:** This can be valid code in main() to return + /// errors + /// + /// **Example:** + /// ```ignore + /// std::process::exit(0) + /// ``` + pub EXIT, + restriction, + "`std::process::exit` is called, terminating the program" +} + +declare_lint_pass!(Exit => [EXIT]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Exit { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) { + if_chain! { + if let ExprKind::Call(ref path_expr, ref _args) = e.kind; + if let ExprKind::Path(ref path) = path_expr.kind; + if let Some(def_id) = qpath_res(cx, path, path_expr.hir_id).opt_def_id(); + if match_def_path(cx, def_id, &paths::EXIT); + then { + span_lint(cx, EXIT, e.span, "usage of `process::exit`"); + } + + } + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 817575092cc5..49ccc7d8f482 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -188,6 +188,7 @@ pub mod escape; pub mod eta_reduction; pub mod eval_order_dependence; pub mod excessive_precision; +pub mod exit; pub mod explicit_write; pub mod fallible_impl_from; pub mod format; @@ -938,6 +939,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf store.register_early_pass(move || box enum_variants::EnumVariantNames::new(enum_variant_name_threshold)); store.register_late_pass(|| box unused_self::UnusedSelf); store.register_late_pass(|| box mutable_debug_assertion::DebugAssertWithMutCall); + reg.register_late_lint_pass(||box exit::Exit); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 9787517e5055..042ca4ee7fdc 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -27,6 +27,7 @@ pub const DROP: [&str; 3] = ["core", "mem", "drop"]; pub const DROP_TRAIT: [&str; 4] = ["core", "ops", "drop", "Drop"]; pub const DURATION: [&str; 3] = ["core", "time", "Duration"]; pub const EARLY_CONTEXT: [&str; 4] = ["rustc", "lint", "context", "EarlyContext"]; +pub const EXIT: [&str; 3] = ["std", "process", "exit"]; pub const FMT_ARGUMENTS_NEW_V1: [&str; 4] = ["core", "fmt", "Arguments", "new_v1"]; pub const FMT_ARGUMENTS_NEW_V1_FORMATTED: [&str; 4] = ["core", "fmt", "Arguments", "new_v1_formatted"]; pub const FMT_ARGUMENTV1_NEW: [&str; 4] = ["core", "fmt", "ArgumentV1", "new"]; diff --git a/tests/ui/exit.rs b/tests/ui/exit.rs new file mode 100644 index 000000000000..1850241c4768 --- /dev/null +++ b/tests/ui/exit.rs @@ -0,0 +1,4 @@ +#[warn(clippy::exit)] +fn main() { + std::process::exit(1); +} diff --git a/tests/ui/exit.stderr b/tests/ui/exit.stderr new file mode 100644 index 000000000000..4fd294395501 --- /dev/null +++ b/tests/ui/exit.stderr @@ -0,0 +1,10 @@ +error: usage of `process::exit` + --> $DIR/exit.rs:3:5 + | +LL | std::process::exit(1); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::exit` implied by `-D warnings` + +error: aborting due to previous error + From 54297f1cfd49668d14355cd06bcb002026050385 Mon Sep 17 00:00:00 2001 From: "Heinz N. Gies" Date: Fri, 18 Oct 2019 21:10:35 +0200 Subject: [PATCH 02/14] Update lints --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 1 + src/lintlist/mod.rs | 7 +++++++ 3 files changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b32914854fe..84a3bd491cb8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -996,6 +996,7 @@ Released 2018-09-13 [`erasing_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#erasing_op [`eval_order_dependence`]: https://rust-lang.github.io/rust-clippy/master/index.html#eval_order_dependence [`excessive_precision`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_precision +[`exit`]: https://rust-lang.github.io/rust-clippy/master/index.html#exit [`expect_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_fun_call [`expl_impl_clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#expl_impl_clone_on_copy [`explicit_counter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_counter_loop diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 49ccc7d8f482..2f87b1b8310a 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -946,6 +946,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf LintId::of(&arithmetic::INTEGER_ARITHMETIC), LintId::of(&dbg_macro::DBG_MACRO), LintId::of(&else_if_without_else::ELSE_IF_WITHOUT_ELSE), + LintId::of(&exit::EXIT), LintId::of(&implicit_return::IMPLICIT_RETURN), LintId::of(&indexing_slicing::INDEXING_SLICING), LintId::of(&inherent_impl::MULTIPLE_INHERENT_IMPL), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index a053cf8bef0c..3d47c5c3e415 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -490,6 +490,13 @@ pub const ALL_LINTS: [Lint; 332] = [ deprecation: None, module: "excessive_precision", }, + Lint { + name: "exit", + group: "restriction", + desc: "`std::process::exit` is called, terminating the program", + deprecation: None, + module: "exit", + }, Lint { name: "expect_fun_call", group: "perf", From 43821c9be5a0b90703abe1cf3ce46eb12961e752 Mon Sep 17 00:00:00 2001 From: "Heinz N. Gies" Date: Sat, 19 Oct 2019 14:31:02 +0200 Subject: [PATCH 03/14] Exclude main from exit lint --- clippy_lints/src/exit.rs | 29 +++++++++++++++++++++++++---- tests/ui/exit.rs | 8 ++++++++ tests/ui/exit.stderr | 2 +- 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/exit.rs b/clippy_lints/src/exit.rs index 222f84bd028e..9952ef3ea623 100644 --- a/clippy_lints/src/exit.rs +++ b/clippy_lints/src/exit.rs @@ -1,6 +1,6 @@ use crate::utils::{match_def_path, paths, qpath_res, span_lint}; use if_chain::if_chain; -use rustc::hir::{Expr, ExprKind}; +use rustc::hir::{Expr, ExprKind, Item, ItemKind, Node}; use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass}; use rustc::{declare_lint_pass, declare_tool_lint}; @@ -11,8 +11,7 @@ declare_clippy_lint! { /// **Why is this bad?** Ideally a program is terminated by finishing /// the main function. /// - /// **Known problems:** This can be valid code in main() to return - /// errors + /// **Known problems:** None. /// /// **Example:** /// ```ignore @@ -33,7 +32,29 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Exit { if let Some(def_id) = qpath_res(cx, path, path_expr.hir_id).opt_def_id(); if match_def_path(cx, def_id, &paths::EXIT); then { - span_lint(cx, EXIT, e.span, "usage of `process::exit`"); + let mut parent = cx.tcx.hir().get_parent_item(e.hir_id); + // We have to traverse the parents upwards until we find a function + // otherwise a exit in a let or if in main would still trigger this + loop{ + match cx.tcx.hir().find(parent) { + Some(Node::Item(Item{ident, kind: ItemKind::Fn(..), ..})) => { + // If we found a function we check it's name if it is + // `main` we emit a lint. + if ident.name.as_str() != "main" { + span_lint(cx, EXIT, e.span, "usage of `process::exit`"); + } + // We found any kind of function and can end our loop + break; + } + // If we found anything but a funciton we continue with the + // loop and go one parent up + Some(_) => { + cx.tcx.hir().get_parent_item(parent); + }, + // If we found nothing we break. + None => break, + } + } } } diff --git a/tests/ui/exit.rs b/tests/ui/exit.rs index 1850241c4768..bf8ea9bebf7e 100644 --- a/tests/ui/exit.rs +++ b/tests/ui/exit.rs @@ -1,4 +1,12 @@ #[warn(clippy::exit)] +fn not_main() { + std::process::exit(3); +} + fn main() { + if true { + std::process::exit(2); + }; + not_main(); std::process::exit(1); } diff --git a/tests/ui/exit.stderr b/tests/ui/exit.stderr index 4fd294395501..756cf8837491 100644 --- a/tests/ui/exit.stderr +++ b/tests/ui/exit.stderr @@ -1,7 +1,7 @@ error: usage of `process::exit` --> $DIR/exit.rs:3:5 | -LL | std::process::exit(1); +LL | std::process::exit(3); | ^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::exit` implied by `-D warnings` From 67e2c00dbcbb00f0d034440588365c6d225a8af4 Mon Sep 17 00:00:00 2001 From: "Heinz N. Gies" Date: Mon, 21 Oct 2019 22:55:01 +0200 Subject: [PATCH 04/14] Improve function checking --- clippy_lints/src/exit.rs | 7 ++++--- tests/ui/exit.rs | 8 ++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/exit.rs b/clippy_lints/src/exit.rs index 9952ef3ea623..23952efbc8d8 100644 --- a/clippy_lints/src/exit.rs +++ b/clippy_lints/src/exit.rs @@ -1,4 +1,4 @@ -use crate::utils::{match_def_path, paths, qpath_res, span_lint}; +use crate::utils::{is_entrypoint_fn, match_def_path, paths, qpath_res, span_lint}; use if_chain::if_chain; use rustc::hir::{Expr, ExprKind, Item, ItemKind, Node}; use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass}; @@ -40,7 +40,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Exit { Some(Node::Item(Item{ident, kind: ItemKind::Fn(..), ..})) => { // If we found a function we check it's name if it is // `main` we emit a lint. - if ident.name.as_str() != "main" { + let def_id = cx.tcx.hir().local_def_id(parent); + if !is_entrypoint_fn(cx, def_id) { span_lint(cx, EXIT, e.span, "usage of `process::exit`"); } // We found any kind of function and can end our loop @@ -49,7 +50,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Exit { // If we found anything but a funciton we continue with the // loop and go one parent up Some(_) => { - cx.tcx.hir().get_parent_item(parent); + parent = cx.tcx.hir().get_parent_item(parent); }, // If we found nothing we break. None => break, diff --git a/tests/ui/exit.rs b/tests/ui/exit.rs index bf8ea9bebf7e..0ad15faef77b 100644 --- a/tests/ui/exit.rs +++ b/tests/ui/exit.rs @@ -1,5 +1,12 @@ #[warn(clippy::exit)] + fn not_main() { + if true { + std::process::exit(4); + } +} + +fn also_not_main() { std::process::exit(3); } @@ -7,6 +14,7 @@ fn main() { if true { std::process::exit(2); }; + also_not_main(); not_main(); std::process::exit(1); } From 17141734362a1de17f6d848cdd123409b4e212e4 Mon Sep 17 00:00:00 2001 From: "Heinz N. Gies" Date: Thu, 24 Oct 2019 21:17:21 +0200 Subject: [PATCH 05/14] Simplify dentry point detection --- README.md | 2 +- clippy_lints/src/exit.rs | 28 ++++++---------------------- clippy_lints/src/lib.rs | 1 + src/lintlist/mod.rs | 2 +- 4 files changed, 9 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 87ef441eadd5..922dbcd11380 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. -[There are 332 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are 333 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you: diff --git a/clippy_lints/src/exit.rs b/clippy_lints/src/exit.rs index 23952efbc8d8..7220833b9f23 100644 --- a/clippy_lints/src/exit.rs +++ b/clippy_lints/src/exit.rs @@ -33,31 +33,15 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Exit { if match_def_path(cx, def_id, &paths::EXIT); then { let mut parent = cx.tcx.hir().get_parent_item(e.hir_id); - // We have to traverse the parents upwards until we find a function - // otherwise a exit in a let or if in main would still trigger this - loop{ - match cx.tcx.hir().find(parent) { - Some(Node::Item(Item{ident, kind: ItemKind::Fn(..), ..})) => { - // If we found a function we check it's name if it is - // `main` we emit a lint. - let def_id = cx.tcx.hir().local_def_id(parent); - if !is_entrypoint_fn(cx, def_id) { - span_lint(cx, EXIT, e.span, "usage of `process::exit`"); - } - // We found any kind of function and can end our loop - break; - } - // If we found anything but a funciton we continue with the - // loop and go one parent up - Some(_) => { - parent = cx.tcx.hir().get_parent_item(parent); - }, - // If we found nothing we break. - None => break, + if let Some(Node::Item(Item{ident, kind: ItemKind::Fn(..), ..})) = cx.tcx.hir().find(parent) { + // If the next item up is a function we check if it is an entry point + // and only then emit a linter warning + let def_id = cx.tcx.hir().local_def_id(parent); + if !is_entrypoint_fn(cx, def_id) { + span_lint(cx, EXIT, e.span, "usage of `process::exit`"); } } } - } } } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 2f87b1b8310a..4763e9e00111 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -498,6 +498,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf &eval_order_dependence::DIVERGING_SUB_EXPRESSION, &eval_order_dependence::EVAL_ORDER_DEPENDENCE, &excessive_precision::EXCESSIVE_PRECISION, + &exit::EXIT, &explicit_write::EXPLICIT_WRITE, &fallible_impl_from::FALLIBLE_IMPL_FROM, &format::USELESS_FORMAT, diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 3d47c5c3e415..55f86ec401ad 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -6,7 +6,7 @@ pub use lint::Lint; pub use lint::LINT_LEVELS; // begin lint list, do not remove this comment, it’s used in `update_lints` -pub const ALL_LINTS: [Lint; 332] = [ +pub const ALL_LINTS: [Lint; 333] = [ Lint { name: "absurd_extreme_comparisons", group: "correctness", From b5b6ab84a56c71dfc45082145b09b2adb7a66bbb Mon Sep 17 00:00:00 2001 From: "Heinz N. Gies" Date: Fri, 25 Oct 2019 10:16:25 +0200 Subject: [PATCH 06/14] Update clippy_lints/src/lib.rs Co-Authored-By: Philipp Krones --- clippy_lints/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 4763e9e00111..e9283b456f68 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -940,7 +940,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf store.register_early_pass(move || box enum_variants::EnumVariantNames::new(enum_variant_name_threshold)); store.register_late_pass(|| box unused_self::UnusedSelf); store.register_late_pass(|| box mutable_debug_assertion::DebugAssertWithMutCall); - reg.register_late_lint_pass(||box exit::Exit); + store.register_late_lint_pass(|| box exit::Exit); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), From 62483d74b66b42697de843658ed3455b1a93502e Mon Sep 17 00:00:00 2001 From: "Heinz N. Gies" Date: Fri, 25 Oct 2019 10:59:33 +0200 Subject: [PATCH 07/14] Update clippy_lints/src/lib.rs Co-Authored-By: Philipp Krones --- clippy_lints/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index e9283b456f68..dcec8778b769 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -940,7 +940,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf store.register_early_pass(move || box enum_variants::EnumVariantNames::new(enum_variant_name_threshold)); store.register_late_pass(|| box unused_self::UnusedSelf); store.register_late_pass(|| box mutable_debug_assertion::DebugAssertWithMutCall); - store.register_late_lint_pass(|| box exit::Exit); + store.register_late_pass(|| box exit::Exit); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), From 0bd85277318109a6b6566ef0da875d5c34c6e38b Mon Sep 17 00:00:00 2001 From: "Heinz N. Gies" Date: Tue, 29 Oct 2019 21:34:00 +0100 Subject: [PATCH 08/14] Update tests for exit --- tests/ui/{exit.rs => exit1.rs} | 5 ----- tests/ui/exit1.stderr | 10 ++++++++++ tests/ui/exit2.rs | 13 +++++++++++++ tests/ui/{exit.stderr => exit2.stderr} | 2 +- tests/ui/exit3.rs | 8 ++++++++ 5 files changed, 32 insertions(+), 6 deletions(-) rename tests/ui/{exit.rs => exit1.rs} (73%) create mode 100644 tests/ui/exit1.stderr create mode 100644 tests/ui/exit2.rs rename tests/ui/{exit.stderr => exit2.stderr} (89%) create mode 100644 tests/ui/exit3.rs diff --git a/tests/ui/exit.rs b/tests/ui/exit1.rs similarity index 73% rename from tests/ui/exit.rs rename to tests/ui/exit1.rs index 0ad15faef77b..4eac6eb74672 100644 --- a/tests/ui/exit.rs +++ b/tests/ui/exit1.rs @@ -6,15 +6,10 @@ fn not_main() { } } -fn also_not_main() { - std::process::exit(3); -} - fn main() { if true { std::process::exit(2); }; - also_not_main(); not_main(); std::process::exit(1); } diff --git a/tests/ui/exit1.stderr b/tests/ui/exit1.stderr new file mode 100644 index 000000000000..a8d3956aa27a --- /dev/null +++ b/tests/ui/exit1.stderr @@ -0,0 +1,10 @@ +error: usage of `process::exit` + --> $DIR/exit1.rs:5:9 + | +LL | std::process::exit(4); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::exit` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui/exit2.rs b/tests/ui/exit2.rs new file mode 100644 index 000000000000..4b693ed7083f --- /dev/null +++ b/tests/ui/exit2.rs @@ -0,0 +1,13 @@ +#[warn(clippy::exit)] + +fn also_not_main() { + std::process::exit(3); +} + +fn main() { + if true { + std::process::exit(2); + }; + also_not_main(); + std::process::exit(1); +} diff --git a/tests/ui/exit.stderr b/tests/ui/exit2.stderr similarity index 89% rename from tests/ui/exit.stderr rename to tests/ui/exit2.stderr index 756cf8837491..7263e156a9d2 100644 --- a/tests/ui/exit.stderr +++ b/tests/ui/exit2.stderr @@ -1,5 +1,5 @@ error: usage of `process::exit` - --> $DIR/exit.rs:3:5 + --> $DIR/exit2.rs:4:5 | LL | std::process::exit(3); | ^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/exit3.rs b/tests/ui/exit3.rs new file mode 100644 index 000000000000..9dc0e1015a4f --- /dev/null +++ b/tests/ui/exit3.rs @@ -0,0 +1,8 @@ +#[warn(clippy::exit)] + +fn main() { + if true { + std::process::exit(2); + }; + std::process::exit(1); +} From 94e35510aedb6248e3066300acb41efd2c8699c9 Mon Sep 17 00:00:00 2001 From: Evan Stoll Date: Wed, 30 Oct 2019 21:05:23 -0400 Subject: [PATCH 09/14] Fix #4748 - Deprecated lints don't expand - Move doc comments inside of declare_deprecated_lint macros so that they are picked up by lintlib.py --- clippy_lints/src/deprecated_lints.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index 97ae9c780888..ec349a23760c 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -4,85 +4,85 @@ macro_rules! declare_deprecated_lint { } } +declare_deprecated_lint! { /// **What it does:** Nothing. This lint has been deprecated. /// /// **Deprecation reason:** This used to check for `assert!(a == b)` and recommend /// replacement with `assert_eq!(a, b)`, but this is no longer needed after RFC 2011. -declare_deprecated_lint! { pub SHOULD_ASSERT_EQ, "`assert!()` will be more flexible with RFC 2011" } +declare_deprecated_lint! { /// **What it does:** Nothing. This lint has been deprecated. /// /// **Deprecation reason:** This used to check for `Vec::extend`, which was slower than /// `Vec::extend_from_slice`. Thanks to specialization, this is no longer true. -declare_deprecated_lint! { pub EXTEND_FROM_SLICE, "`.extend_from_slice(_)` is a faster way to extend a Vec by a slice" } +declare_deprecated_lint! { /// **What it does:** Nothing. This lint has been deprecated. /// /// **Deprecation reason:** `Range::step_by(0)` used to be linted since it's /// an infinite iterator, which is better expressed by `iter::repeat`, /// but the method has been removed for `Iterator::step_by` which panics /// if given a zero -declare_deprecated_lint! { pub RANGE_STEP_BY_ZERO, "`iterator.step_by(0)` panics nowadays" } +declare_deprecated_lint! { /// **What it does:** Nothing. This lint has been deprecated. /// /// **Deprecation reason:** This used to check for `Vec::as_slice`, which was unstable with good /// stable alternatives. `Vec::as_slice` has now been stabilized. -declare_deprecated_lint! { pub UNSTABLE_AS_SLICE, "`Vec::as_slice` has been stabilized in 1.7" } - +declare_deprecated_lint! { /// **What it does:** Nothing. This lint has been deprecated. /// /// **Deprecation reason:** This used to check for `Vec::as_mut_slice`, which was unstable with good /// stable alternatives. `Vec::as_mut_slice` has now been stabilized. -declare_deprecated_lint! { pub UNSTABLE_AS_MUT_SLICE, "`Vec::as_mut_slice` has been stabilized in 1.7" } +declare_deprecated_lint! { /// **What it does:** Nothing. This lint has been deprecated. /// /// **Deprecation reason:** This used to check for `.to_string()` method calls on values /// of type `&str`. This is not unidiomatic and with specialization coming, `to_string` could be /// specialized to be as efficient as `to_owned`. -declare_deprecated_lint! { pub STR_TO_STRING, "using `str::to_string` is common even today and specialization will likely happen soon" } +declare_deprecated_lint! { /// **What it does:** Nothing. This lint has been deprecated. /// /// **Deprecation reason:** This used to check for `.to_string()` method calls on values /// of type `String`. This is not unidiomatic and with specialization coming, `to_string` could be /// specialized to be as efficient as `clone`. -declare_deprecated_lint! { pub STRING_TO_STRING, "using `string::to_string` is common even today and specialization will likely happen soon" } +declare_deprecated_lint! { /// **What it does:** Nothing. This lint has been deprecated. /// /// **Deprecation reason:** This lint should never have applied to non-pointer types, as transmuting /// between non-pointer types of differing alignment is well-defined behavior (it's semantically /// equivalent to a memcpy). This lint has thus been refactored into two separate lints: /// cast_ptr_alignment and transmute_ptr_to_ptr. -declare_deprecated_lint! { pub MISALIGNED_TRANSMUTE, "this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr" } +declare_deprecated_lint! { /// **What it does:** Nothing. This lint has been deprecated. /// /// **Deprecation reason:** This lint is too subjective, not having a good reason for being in clippy. @@ -93,40 +93,40 @@ declare_deprecated_lint! { "using compound assignment operators (e.g., `+=`) is harmless" } +declare_deprecated_lint! { /// **What it does:** Nothing. This lint has been deprecated. /// /// **Deprecation reason:** The original rule will only lint for `if let`. After /// making it support to lint `match`, naming as `if let` is not suitable for it. /// So, this lint is deprecated. -declare_deprecated_lint! { pub IF_LET_REDUNDANT_PATTERN_MATCHING, "this lint has been changed to redundant_pattern_matching" } +declare_deprecated_lint! { /// **What it does:** Nothing. This lint has been deprecated. /// /// **Deprecation reason:** This lint used to suggest replacing `let mut vec = /// Vec::with_capacity(n); vec.set_len(n);` with `let vec = vec![0; n];`. The /// replacement has very different performance characteristics so the lint is /// deprecated. -declare_deprecated_lint! { pub UNSAFE_VECTOR_INITIALIZATION, "the replacement suggested by this lint had substantially different behavior" } +declare_deprecated_lint! { /// **What it does:** Nothing. This lint has been deprecated. /// /// **Deprecation reason:** This lint has been superseded by the warn-by-default /// `invalid_value` rustc lint. -declare_deprecated_lint! { pub INVALID_REF, "superseded by rustc lint `invalid_value`" } +declare_deprecated_lint! { /// **What it does:** Nothing. This lint has been deprecated. /// /// **Deprecation reason:** This lint has been superseded by #[must_use] in rustc. -declare_deprecated_lint! { pub UNUSED_COLLECT, "`collect` has been marked as #[must_use] in rustc and that covers all cases of this lint" } From 4e78547e6f65e2b5bdbdd46592f6882d6cd33628 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Thu, 31 Oct 2019 09:13:08 +0200 Subject: [PATCH 10/14] `DecimalLiteralRepresentation` simplification Remove recalculation of literal value. --- clippy_lints/src/literal_representation.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index badd2237073c..09ef000994f2 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -509,17 +509,12 @@ impl DecimalLiteralRepresentation { fn check_lit(self, cx: &EarlyContext<'_>, lit: &Lit) { // Lint integral literals. if_chain! { - if let LitKind::Int(..) = lit.kind; + if let LitKind::Int(val, _) = lit.kind; if let Some(src) = snippet_opt(cx, lit.span); if let Some(firstch) = src.chars().next(); if char::to_digit(firstch, 10).is_some(); let digit_info = DigitInfo::new(&src, false); if digit_info.radix == Radix::Decimal; - if let Ok(val) = digit_info.digits - .chars() - .filter(|&c| c != '_') - .collect::() - .parse::(); if val >= u128::from(self.threshold); then { let hex = format!("{:#X}", val); From 1acea2e4fc231e4dd6229b6e0869f29e79b40edc Mon Sep 17 00:00:00 2001 From: Evan Stoll Date: Sat, 2 Nov 2019 14:09:41 -0400 Subject: [PATCH 11/14] deprecated_lints: remove extraneous `declare_deprecated_lint` --- clippy_lints/src/deprecated_lints.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index ec349a23760c..5156749e8657 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -82,7 +82,6 @@ declare_deprecated_lint! { "this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr" } -declare_deprecated_lint! { /// **What it does:** Nothing. This lint has been deprecated. /// /// **Deprecation reason:** This lint is too subjective, not having a good reason for being in clippy. From 9a2c968f9ff9826fb46e8b37036f904f303e064b Mon Sep 17 00:00:00 2001 From: Evan Stoll Date: Sat, 2 Nov 2019 14:10:59 -0400 Subject: [PATCH 12/14] deprecated_lints: align doc comment indents with `pub LINT_NAME` --- clippy_lints/src/deprecated_lints.rs | 112 +++++++++++++-------------- 1 file changed, 56 insertions(+), 56 deletions(-) diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index 5156749e8657..a7640b8e8ced 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -5,79 +5,79 @@ macro_rules! declare_deprecated_lint { } declare_deprecated_lint! { -/// **What it does:** Nothing. This lint has been deprecated. -/// -/// **Deprecation reason:** This used to check for `assert!(a == b)` and recommend -/// replacement with `assert_eq!(a, b)`, but this is no longer needed after RFC 2011. + /// **What it does:** Nothing. This lint has been deprecated. + /// + /// **Deprecation reason:** This used to check for `assert!(a == b)` and recommend + /// replacement with `assert_eq!(a, b)`, but this is no longer needed after RFC 2011. pub SHOULD_ASSERT_EQ, "`assert!()` will be more flexible with RFC 2011" } declare_deprecated_lint! { -/// **What it does:** Nothing. This lint has been deprecated. -/// -/// **Deprecation reason:** This used to check for `Vec::extend`, which was slower than -/// `Vec::extend_from_slice`. Thanks to specialization, this is no longer true. + /// **What it does:** Nothing. This lint has been deprecated. + /// + /// **Deprecation reason:** This used to check for `Vec::extend`, which was slower than + /// `Vec::extend_from_slice`. Thanks to specialization, this is no longer true. pub EXTEND_FROM_SLICE, "`.extend_from_slice(_)` is a faster way to extend a Vec by a slice" } declare_deprecated_lint! { -/// **What it does:** Nothing. This lint has been deprecated. -/// -/// **Deprecation reason:** `Range::step_by(0)` used to be linted since it's -/// an infinite iterator, which is better expressed by `iter::repeat`, -/// but the method has been removed for `Iterator::step_by` which panics -/// if given a zero + /// **What it does:** Nothing. This lint has been deprecated. + /// + /// **Deprecation reason:** `Range::step_by(0)` used to be linted since it's + /// an infinite iterator, which is better expressed by `iter::repeat`, + /// but the method has been removed for `Iterator::step_by` which panics + /// if given a zero pub RANGE_STEP_BY_ZERO, "`iterator.step_by(0)` panics nowadays" } declare_deprecated_lint! { -/// **What it does:** Nothing. This lint has been deprecated. -/// -/// **Deprecation reason:** This used to check for `Vec::as_slice`, which was unstable with good -/// stable alternatives. `Vec::as_slice` has now been stabilized. + /// **What it does:** Nothing. This lint has been deprecated. + /// + /// **Deprecation reason:** This used to check for `Vec::as_slice`, which was unstable with good + /// stable alternatives. `Vec::as_slice` has now been stabilized. pub UNSTABLE_AS_SLICE, "`Vec::as_slice` has been stabilized in 1.7" } declare_deprecated_lint! { -/// **What it does:** Nothing. This lint has been deprecated. -/// -/// **Deprecation reason:** This used to check for `Vec::as_mut_slice`, which was unstable with good -/// stable alternatives. `Vec::as_mut_slice` has now been stabilized. + /// **What it does:** Nothing. This lint has been deprecated. + /// + /// **Deprecation reason:** This used to check for `Vec::as_mut_slice`, which was unstable with good + /// stable alternatives. `Vec::as_mut_slice` has now been stabilized. pub UNSTABLE_AS_MUT_SLICE, "`Vec::as_mut_slice` has been stabilized in 1.7" } declare_deprecated_lint! { -/// **What it does:** Nothing. This lint has been deprecated. -/// -/// **Deprecation reason:** This used to check for `.to_string()` method calls on values -/// of type `&str`. This is not unidiomatic and with specialization coming, `to_string` could be -/// specialized to be as efficient as `to_owned`. + /// **What it does:** Nothing. This lint has been deprecated. + /// + /// **Deprecation reason:** This used to check for `.to_string()` method calls on values + /// of type `&str`. This is not unidiomatic and with specialization coming, `to_string` could be + /// specialized to be as efficient as `to_owned`. pub STR_TO_STRING, "using `str::to_string` is common even today and specialization will likely happen soon" } declare_deprecated_lint! { -/// **What it does:** Nothing. This lint has been deprecated. -/// -/// **Deprecation reason:** This used to check for `.to_string()` method calls on values -/// of type `String`. This is not unidiomatic and with specialization coming, `to_string` could be -/// specialized to be as efficient as `clone`. + /// **What it does:** Nothing. This lint has been deprecated. + /// + /// **Deprecation reason:** This used to check for `.to_string()` method calls on values + /// of type `String`. This is not unidiomatic and with specialization coming, `to_string` could be + /// specialized to be as efficient as `clone`. pub STRING_TO_STRING, "using `string::to_string` is common even today and specialization will likely happen soon" } declare_deprecated_lint! { -/// **What it does:** Nothing. This lint has been deprecated. -/// -/// **Deprecation reason:** This lint should never have applied to non-pointer types, as transmuting -/// between non-pointer types of differing alignment is well-defined behavior (it's semantically -/// equivalent to a memcpy). This lint has thus been refactored into two separate lints: -/// cast_ptr_alignment and transmute_ptr_to_ptr. + /// **What it does:** Nothing. This lint has been deprecated. + /// + /// **Deprecation reason:** This lint should never have applied to non-pointer types, as transmuting + /// between non-pointer types of differing alignment is well-defined behavior (it's semantically + /// equivalent to a memcpy). This lint has thus been refactored into two separate lints: + /// cast_ptr_alignment and transmute_ptr_to_ptr. pub MISALIGNED_TRANSMUTE, "this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr" } @@ -93,39 +93,39 @@ declare_deprecated_lint! { } declare_deprecated_lint! { -/// **What it does:** Nothing. This lint has been deprecated. -/// -/// **Deprecation reason:** The original rule will only lint for `if let`. After -/// making it support to lint `match`, naming as `if let` is not suitable for it. -/// So, this lint is deprecated. + /// **What it does:** Nothing. This lint has been deprecated. + /// + /// **Deprecation reason:** The original rule will only lint for `if let`. After + /// making it support to lint `match`, naming as `if let` is not suitable for it. + /// So, this lint is deprecated. pub IF_LET_REDUNDANT_PATTERN_MATCHING, "this lint has been changed to redundant_pattern_matching" } declare_deprecated_lint! { -/// **What it does:** Nothing. This lint has been deprecated. -/// -/// **Deprecation reason:** This lint used to suggest replacing `let mut vec = -/// Vec::with_capacity(n); vec.set_len(n);` with `let vec = vec![0; n];`. The -/// replacement has very different performance characteristics so the lint is -/// deprecated. + /// **What it does:** Nothing. This lint has been deprecated. + /// + /// **Deprecation reason:** This lint used to suggest replacing `let mut vec = + /// Vec::with_capacity(n); vec.set_len(n);` with `let vec = vec![0; n];`. The + /// replacement has very different performance characteristics so the lint is + /// deprecated. pub UNSAFE_VECTOR_INITIALIZATION, "the replacement suggested by this lint had substantially different behavior" } declare_deprecated_lint! { -/// **What it does:** Nothing. This lint has been deprecated. -/// -/// **Deprecation reason:** This lint has been superseded by the warn-by-default -/// `invalid_value` rustc lint. + /// **What it does:** Nothing. This lint has been deprecated. + /// + /// **Deprecation reason:** This lint has been superseded by the warn-by-default + /// `invalid_value` rustc lint. pub INVALID_REF, "superseded by rustc lint `invalid_value`" } declare_deprecated_lint! { -/// **What it does:** Nothing. This lint has been deprecated. -/// -/// **Deprecation reason:** This lint has been superseded by #[must_use] in rustc. + /// **What it does:** Nothing. This lint has been deprecated. + /// + /// **Deprecation reason:** This lint has been superseded by #[must_use] in rustc. pub UNUSED_COLLECT, "`collect` has been marked as #[must_use] in rustc and that covers all cases of this lint" } From 8ca9c237a069dc12db8073923ec0a4a83d0a1efc Mon Sep 17 00:00:00 2001 From: Evan Stoll Date: Sat, 2 Nov 2019 14:19:25 -0400 Subject: [PATCH 13/14] deprecated_lints: re-fix ASSIGN_OPS lint doc-comment --- clippy_lints/src/deprecated_lints.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index a7640b8e8ced..729c494d1dcc 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -82,12 +82,12 @@ declare_deprecated_lint! { "this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr" } -/// **What it does:** Nothing. This lint has been deprecated. -/// -/// **Deprecation reason:** This lint is too subjective, not having a good reason for being in clippy. -/// Additionally, compound assignment operators may be overloaded separately from their non-assigning -/// counterparts, so this lint may suggest a change in behavior or the code may not compile. declare_deprecated_lint! { + /// **What it does:** Nothing. This lint has been deprecated. + /// + /// **Deprecation reason:** This lint is too subjective, not having a good reason for being in clippy. + /// Additionally, compound assignment operators may be overloaded separately from their non-assigning + /// counterparts, so this lint may suggest a change in behavior or the code may not compile. pub ASSIGN_OPS, "using compound assignment operators (e.g., `+=`) is harmless" } From 1329dc206adcce220f43466e9dcf91468931bcc8 Mon Sep 17 00:00:00 2001 From: Andy Russell Date: Sun, 3 Nov 2019 00:41:22 -0400 Subject: [PATCH 14/14] don't warn on CRLF in `with_newline` lints --- clippy_lints/src/lib.rs | 2 ++ clippy_lints/src/write.rs | 54 ++++++++++++++---------------- tests/ui/print_with_newline.rs | 6 ++++ tests/ui/print_with_newline.stderr | 24 ++++++++++++- tests/ui/write_with_newline.rs | 6 ++++ tests/ui/write_with_newline.stderr | 24 ++++++++++++- 6 files changed, 85 insertions(+), 31 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 817575092cc5..97a3149392dc 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -29,6 +29,8 @@ extern crate rustc_errors; #[allow(unused_extern_crates)] extern crate rustc_index; #[allow(unused_extern_crates)] +extern crate rustc_lexer; +#[allow(unused_extern_crates)] extern crate rustc_mir; #[allow(unused_extern_crates)] extern crate rustc_target; diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 989d2f374a84..aa255472ac07 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -1,8 +1,11 @@ +use std::borrow::Cow; +use std::ops::Range; + use crate::utils::{snippet_with_applicability, span_lint, span_lint_and_sugg, span_lint_and_then}; use rustc::lint::{EarlyContext, EarlyLintPass, LintArray, LintPass}; use rustc::{declare_lint_pass, declare_tool_lint}; use rustc_errors::Applicability; -use std::borrow::Cow; +use rustc_lexer::unescape::{self, EscapeError}; use syntax::ast::*; use syntax::parse::{parser, token}; use syntax::tokenstream::TokenStream; @@ -201,7 +204,7 @@ impl EarlyLintPass for Write { } else if mac.path == sym!(print) { span_lint(cx, PRINT_STDOUT, mac.span, "use of `print!`"); if let (Some(fmt_str), _) = check_tts(cx, &mac.tts, false) { - if check_newlines(&fmt_str) { + if check_newlines(&fmt_str.contents, fmt_str.style) { span_lint_and_then( cx, PRINT_WITH_NEWLINE, @@ -222,7 +225,7 @@ impl EarlyLintPass for Write { } } else if mac.path == sym!(write) { if let (Some(fmt_str), _) = check_tts(cx, &mac.tts, true) { - if check_newlines(&fmt_str) { + if check_newlines(&fmt_str.contents, fmt_str.style) { span_lint_and_then( cx, WRITE_WITH_NEWLINE, @@ -440,38 +443,31 @@ fn check_tts<'a>(cx: &EarlyContext<'a>, tts: &TokenStream, is_write: bool) -> (O } } -/// Checks if the format string constains a single newline that terminates it. +/// Checks if the format string contains a single newline that terminates it. /// /// Literal and escaped newlines are both checked (only literal for raw strings). -fn check_newlines(fmt_str: &FmtStr) -> bool { - let s = &fmt_str.contents; +fn check_newlines(contents: &str, style: StrStyle) -> bool { + let mut has_internal_newline = false; + let mut last_was_cr = false; + let mut should_lint = false; - if s.ends_with('\n') { - return true; - } else if let StrStyle::Raw(_) = fmt_str.style { - return false; - } - - if s.len() < 2 { - return false; - } + let mut cb = |r: Range, c: Result| { + let c = c.unwrap(); - let bytes = s.as_bytes(); - if bytes[bytes.len() - 2] != b'\\' || bytes[bytes.len() - 1] != b'n' { - return false; - } - - let mut escaping = false; - for (index, &byte) in bytes.iter().enumerate() { - if escaping { - if byte == b'n' { - return index == bytes.len() - 1; + if r.end == contents.len() && c == '\n' && !last_was_cr && !has_internal_newline { + should_lint = true; + } else { + last_was_cr = c == '\r'; + if c == '\n' { + has_internal_newline = true; } - escaping = false; - } else if byte == b'\\' { - escaping = true; } + }; + + match style { + StrStyle::Cooked => unescape::unescape_str(contents, &mut cb), + StrStyle::Raw(_) => unescape::unescape_raw_str(contents, &mut cb), } - false + should_lint } diff --git a/tests/ui/print_with_newline.rs b/tests/ui/print_with_newline.rs index 111a29faa41e..3f710540e903 100644 --- a/tests/ui/print_with_newline.rs +++ b/tests/ui/print_with_newline.rs @@ -42,4 +42,10 @@ fn main() { r" " ); + + // Don't warn on CRLF (#4208) + print!("\r\n"); + print!("foo\r\n"); + print!("\\r\n"); //~ ERROR + print!("foo\rbar\n") // ~ ERROR } diff --git a/tests/ui/print_with_newline.stderr b/tests/ui/print_with_newline.stderr index c9e37eab6e1b..05fe88915d6e 100644 --- a/tests/ui/print_with_newline.stderr +++ b/tests/ui/print_with_newline.stderr @@ -84,5 +84,27 @@ LL | println!( LL | r"" | -error: aborting due to 7 previous errors +error: using `print!()` with a format string that ends in a single newline + --> $DIR/print_with_newline.rs:49:5 + | +LL | print!("/r/n"); //~ ERROR + | ^^^^^^^^^^^^^^^ + | +help: use `println!` instead + | +LL | println!("/r"); //~ ERROR + | ^^^^^^^ -- + +error: using `print!()` with a format string that ends in a single newline + --> $DIR/print_with_newline.rs:50:5 + | +LL | print!("foo/rbar/n") // ~ ERROR + | ^^^^^^^^^^^^^^^^^^^^ + | +help: use `println!` instead + | +LL | println!("foo/rbar") // ~ ERROR + | ^^^^^^^ -- + +error: aborting due to 9 previous errors diff --git a/tests/ui/write_with_newline.rs b/tests/ui/write_with_newline.rs index f0b13a698871..64ed849a251b 100644 --- a/tests/ui/write_with_newline.rs +++ b/tests/ui/write_with_newline.rs @@ -49,4 +49,10 @@ fn main() { r" " ); + + // Don't warn on CRLF (#4208) + write!(&mut v, "\r\n"); + write!(&mut v, "foo\r\n"); + write!(&mut v, "\\r\n"); //~ ERROR + write!(&mut v, "foo\rbar\n"); //~ ERROR } diff --git a/tests/ui/write_with_newline.stderr b/tests/ui/write_with_newline.stderr index 4d95bfeb17f4..0c9b6242f96d 100644 --- a/tests/ui/write_with_newline.stderr +++ b/tests/ui/write_with_newline.stderr @@ -88,5 +88,27 @@ LL | &mut v, LL | r"" | -error: aborting due to 7 previous errors +error: using `write!()` with a format string that ends in a single newline + --> $DIR/write_with_newline.rs:56:5 + | +LL | write!(&mut v, "/r/n"); //~ ERROR + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `writeln!()` instead + | +LL | writeln!(&mut v, "/r"); //~ ERROR + | ^^^^^^^ -- + +error: using `write!()` with a format string that ends in a single newline + --> $DIR/write_with_newline.rs:57:5 + | +LL | write!(&mut v, "foo/rbar/n"); //~ ERROR + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `writeln!()` instead + | +LL | writeln!(&mut v, "foo/rbar"); //~ ERROR + | ^^^^^^^ -- + +error: aborting due to 9 previous errors