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/README.md b/README.md index 41b8b4199ec5..87ef441eadd5 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 331 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are 332 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/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index 2ac5dca8c2ee..f399b4a78c6d 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -4,129 +4,128 @@ macro_rules! 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! { + /// **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" } -/// **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! { + /// **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" } -/// **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! { + /// **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" } -/// **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! { + /// **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" } - -/// **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! { + /// **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" } -/// **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! { + /// **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" } -/// **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! { + /// **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" } -/// **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! { + /// **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" } -/// **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" } -/// **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! { + /// **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" } -/// **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! { + /// **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" } -/// **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! { + /// **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`" } -/// **What it does:** Nothing. This lint has been deprecated. -/// -/// **Deprecation reason:** This lint has been superseded by #[must_use] in rustc. declare_deprecated_lint! { + /// **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" } diff --git a/clippy_lints/src/exit.rs b/clippy_lints/src/exit.rs new file mode 100644 index 000000000000..7220833b9f23 --- /dev/null +++ b/clippy_lints/src/exit.rs @@ -0,0 +1,47 @@ +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}; +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:** None. + /// + /// **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 { + let mut parent = cx.tcx.hir().get_parent_item(e.hir_id); + 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 1bd117dae943..9f40d51291ba 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; @@ -188,6 +190,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; @@ -501,6 +504,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, @@ -941,12 +945,14 @@ 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_pass(|| box exit::Exit); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), 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/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index c526858a7a2d..18ac8f0f4736 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); 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/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/src/lintlist/mod.rs b/src/lintlist/mod.rs index 35cbeae39889..5c92d69a4993 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; 331] = [ +pub const ALL_LINTS: [Lint; 332] = [ Lint { name: "absurd_extreme_comparisons", group: "correctness", @@ -490,6 +490,13 @@ pub const ALL_LINTS: [Lint; 331] = [ 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", diff --git a/tests/ui/exit1.rs b/tests/ui/exit1.rs new file mode 100644 index 000000000000..4eac6eb74672 --- /dev/null +++ b/tests/ui/exit1.rs @@ -0,0 +1,15 @@ +#[warn(clippy::exit)] + +fn not_main() { + if true { + std::process::exit(4); + } +} + +fn main() { + if true { + std::process::exit(2); + }; + 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/exit2.stderr b/tests/ui/exit2.stderr new file mode 100644 index 000000000000..7263e156a9d2 --- /dev/null +++ b/tests/ui/exit2.stderr @@ -0,0 +1,10 @@ +error: usage of `process::exit` + --> $DIR/exit2.rs:4:5 + | +LL | std::process::exit(3); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::exit` implied by `-D warnings` + +error: aborting due to previous error + 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); +} 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