diff --git a/CHANGELOG.md b/CHANGELOG.md index e691ec9412f0..1713fda031f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -899,6 +899,7 @@ All notable changes to this project will be documented in this file. [`useless_let_if_seq`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_let_if_seq [`useless_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_transmute [`useless_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_vec +[`vec_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_box [`verbose_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_bit_mask [`while_immutable_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_immutable_condition [`while_let_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_let_loop diff --git a/README.md b/README.md index f42771709fb7..5c5d32e4a89d 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. -[There are 290 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are 291 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/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs index 80f0267a981a..a4d834da13ff 100644 --- a/clippy_lints/src/collapsible_if.rs +++ b/clippy_lints/src/collapsible_if.rs @@ -116,7 +116,7 @@ fn check_if(cx: &EarlyContext<'_>, expr: &ast::Expr) { fn block_starts_with_comment(cx: &EarlyContext<'_>, expr: &ast::Block) -> bool { // We trim all opening braces and whitespaces and then check if the next string is a comment. let trimmed_block_text = snippet_block(cx, expr.span, "..") - .trim_left_matches(|c: char| c.is_whitespace() || c == '{') + .trim_start_matches(|c: char| c.is_whitespace() || c == '{') .to_owned(); trimmed_block_text.starts_with("//") || trimmed_block_text.starts_with("/*") } diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index d6c96fa62a90..f8e31a4b2e76 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -116,7 +116,7 @@ pub fn strip_doc_comment_decoration(comment: &str, span: Span) -> (String, Vec<( for line in doc.lines() { let offset = line.as_ptr() as usize - comment.as_ptr() as usize; debug_assert_eq!(offset as u32 as usize, offset); - contains_initial_stars |= line.trim_left().starts_with('*'); + contains_initial_stars |= line.trim_start().starts_with('*'); // +1 for the newline sizes.push((line.len() + 1, span.with_lo(span.lo() + BytePos(offset as u32)))); } @@ -281,6 +281,10 @@ fn check_word(cx: &EarlyContext<'_>, word: &str, span: Span) { s != "_" && !s.contains("\\_") && s.contains('_') } + fn has_hyphen(s: &str) -> bool { + s != "-" && s.contains('-') + } + if let Ok(url) = Url::parse(word) { // try to get around the fact that `foo::bar` parses as a valid URL if !url.cannot_be_a_base() { @@ -295,6 +299,11 @@ fn check_word(cx: &EarlyContext<'_>, word: &str, span: Span) { } } + // We assume that mixed-case words are not meant to be put inside bacticks. (Issue #2343) + if has_underscore(word) && has_hyphen(word) { + return; + } + if has_underscore(word) || word.contains("::") || is_camel_case(word) { span_lint( cx, diff --git a/clippy_lints/src/explicit_write.rs b/clippy_lints/src/explicit_write.rs index 94bd0ab209cb..a0db3ae8df35 100644 --- a/clippy_lints/src/explicit_write.rs +++ b/clippy_lints/src/explicit_write.rs @@ -10,8 +10,9 @@ use crate::rustc::hir::*; use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass}; use crate::rustc::{declare_tool_lint, lint_array}; -use crate::utils::opt_def_id; -use crate::utils::{is_expn_of, match_def_path, resolve_node, span_lint}; +use crate::rustc_errors::Applicability; +use crate::syntax::ast::LitKind; +use crate::utils::{is_expn_of, match_def_path, opt_def_id, resolve_node, span_lint, span_lint_and_sugg}; use if_chain::if_chain; /// **What it does:** Checks for usage of `write!()` / `writeln()!` which can be @@ -81,32 +82,85 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass { } else { "" }; - if let Some(macro_name) = calling_macro { - span_lint( - cx, - EXPLICIT_WRITE, - expr.span, - &format!( - "use of `{}!({}(), ...).unwrap()`. Consider using `{}{}!` instead", - macro_name, - dest_name, - prefix, - macro_name.replace("write", "print") - ) - ); + + // We need to remove the last trailing newline from the string because the + // underlying `fmt::write` function doesn't know whether `println!` or `print!` was + // used. + if let Some(mut write_output) = write_output_string(write_args) { + if write_output.ends_with('\n') { + write_output.pop(); + } + + if let Some(macro_name) = calling_macro { + span_lint_and_sugg( + cx, + EXPLICIT_WRITE, + expr.span, + &format!( + "use of `{}!({}(), ...).unwrap()`", + macro_name, + dest_name + ), + "try this", + format!("{}{}!(\"{}\")", prefix, macro_name.replace("write", "print"), write_output.escape_default()), + Applicability::MachineApplicable + ); + } else { + span_lint_and_sugg( + cx, + EXPLICIT_WRITE, + expr.span, + &format!("use of `{}().write_fmt(...).unwrap()`", dest_name), + "try this", + format!("{}print!(\"{}\")", prefix, write_output.escape_default()), + Applicability::MachineApplicable + ); + } } else { - span_lint( - cx, - EXPLICIT_WRITE, - expr.span, - &format!( - "use of `{}().write_fmt(...).unwrap()`. Consider using `{}print!` instead", - dest_name, - prefix, - ) - ); + // We don't have a proper suggestion + if let Some(macro_name) = calling_macro { + span_lint( + cx, + EXPLICIT_WRITE, + expr.span, + &format!( + "use of `{}!({}(), ...).unwrap()`. Consider using `{}{}!` instead", + macro_name, + dest_name, + prefix, + macro_name.replace("write", "print") + ) + ); + } else { + span_lint( + cx, + EXPLICIT_WRITE, + expr.span, + &format!("use of `{}().write_fmt(...).unwrap()`. Consider using `{}print!` instead", dest_name, prefix), + ); + } } + } } } } + +// Extract the output string from the given `write_args`. +fn write_output_string(write_args: &HirVec) -> Option { + if_chain! { + // Obtain the string that should be printed + if write_args.len() > 1; + if let ExprKind::Call(_, ref output_args) = write_args[1].node; + if output_args.len() > 0; + if let ExprKind::AddrOf(_, ref output_string_expr) = output_args[0].node; + if let ExprKind::Array(ref string_exprs) = output_string_expr.node; + if string_exprs.len() > 0; + if let ExprKind::Lit(ref lit) = string_exprs[0].node; + if let LitKind::Str(ref write_output, _) = lit.node; + then { + return Some(write_output.to_string()) + } + } + None +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index ee41c632077c..f4b5edee84c5 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -14,6 +14,7 @@ #![feature(slice_patterns)] #![feature(stmt_expr_attributes)] #![feature(range_contains)] +#![feature(str_escape)] #![allow(clippy::missing_docs_in_private_items)] #![recursion_limit = "256"] #![warn(rust_2018_idioms, trivial_casts, trivial_numeric_casts)] @@ -765,6 +766,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) { types::UNIT_ARG, types::UNIT_CMP, types::UNNECESSARY_CAST, + types::VEC_BOX, unicode::ZERO_WIDTH_SPACE, unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME, unused_io_amount::UNUSED_IO_AMOUNT, @@ -930,6 +932,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) { types::TYPE_COMPLEXITY, types::UNIT_ARG, types::UNNECESSARY_CAST, + types::VEC_BOX, unused_label::UNUSED_LABEL, zero_div_zero::ZERO_DIVIDED_BY_ZERO, ]); diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 384e027db276..06d2f2cb5fc6 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2350,8 +2350,8 @@ const PATTERN_METHODS: [(&str, usize); 17] = [ ("rmatches", 1), ("match_indices", 1), ("rmatch_indices", 1), - ("trim_left_matches", 1), - ("trim_right_matches", 1), + ("trim_start_matches", 1), + ("trim_end_matches", 1), ]; #[derive(Clone, Copy, PartialEq, Debug)] diff --git a/clippy_lints/src/misc_early.rs b/clippy_lints/src/misc_early.rs index 53da89dfcb0b..9319ada13f46 100644 --- a/clippy_lints/src/misc_early.rs +++ b/clippy_lints/src/misc_early.rs @@ -446,13 +446,13 @@ impl MiscEarly { db.span_suggestion_with_applicability( lit.span, "if you mean to use a decimal constant, remove the `0` to remove confusion", - src.trim_left_matches(|c| c == '_' || c == '0').to_string(), + src.trim_start_matches(|c| c == '_' || c == '0').to_string(), Applicability::MaybeIncorrect, ); db.span_suggestion_with_applicability( lit.span, "if you mean to use an octal constant, use `0o`", - format!("0o{}", src.trim_left_matches(|c| c == '_' || c == '0')), + format!("0o{}", src.trim_start_matches(|c| c == '_' || c == '0')), Applicability::MaybeIncorrect, ); }); diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index 7b838fdee951..86c345b025ca 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -17,7 +17,7 @@ use crate::rustc_errors::Applicability; use crate::syntax::source_map::Span; use crate::utils::paths; use crate::utils::sugg::DiagnosticBuilderExt; -use crate::utils::{get_trait_def_id, implements_trait, return_ty, same_tys, span_lint_and_then}; +use crate::utils::{get_trait_def_id, implements_trait, return_ty, same_tys, span_lint_node_and_then}; use if_chain::if_chain; /// **What it does:** Checks for types with a `fn new() -> Self` method and no @@ -165,9 +165,10 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NewWithoutDefault { } if let Some(sp) = can_derive_default(self_ty, cx, default_trait_id) { - span_lint_and_then( + span_lint_node_and_then( cx, NEW_WITHOUT_DEFAULT_DERIVE, + id, impl_item.span, &format!( "you should consider deriving a `Default` implementation for `{}`", @@ -183,9 +184,10 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NewWithoutDefault { ); }); } else { - span_lint_and_then( + span_lint_node_and_then( cx, NEW_WITHOUT_DEFAULT, + id, impl_item.span, &format!( "you should consider adding a `Default` implementation for `{}`", diff --git a/clippy_lints/src/partialeq_ne_impl.rs b/clippy_lints/src/partialeq_ne_impl.rs index 70c93c5978b6..02935cf773d2 100644 --- a/clippy_lints/src/partialeq_ne_impl.rs +++ b/clippy_lints/src/partialeq_ne_impl.rs @@ -10,7 +10,7 @@ use crate::rustc::hir::*; use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass}; use crate::rustc::{declare_tool_lint, lint_array}; -use crate::utils::{is_automatically_derived, span_lint}; +use crate::utils::{is_automatically_derived, span_lint_node}; use if_chain::if_chain; /// **What it does:** Checks for manual re-implementations of `PartialEq::ne`. @@ -56,10 +56,13 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass { then { for impl_item in impl_items { if impl_item.ident.name == "ne" { - span_lint(cx, - PARTIALEQ_NE_IMPL, - impl_item.span, - "re-implementing `PartialEq::ne` is unnecessary") + span_lint_node( + cx, + PARTIALEQ_NE_IMPL, + impl_item.id.node_id, + impl_item.span, + "re-implementing `PartialEq::ne` is unnecessary", + ); } } } diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index 3a62a3a1526f..057b4850e4f4 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -17,7 +17,7 @@ use if_chain::if_chain; use crate::rustc_errors::Applicability; use crate::utils::paths::*; -use crate::utils::{match_def_path, match_type, span_lint_and_then}; +use crate::utils::{match_def_path, match_type, span_lint_and_then, SpanlessEq}; /// **What it does:** Checks for expressions that could be replaced by the question mark operator /// @@ -64,14 +64,40 @@ impl Pass { /// If it matches, it will suggest to use the question mark operator instead fn check_is_none_and_early_return_none(cx: &LateContext<'_, '_>, expr: &Expr) { if_chain! { - if let ExprKind::If(ref if_expr, ref body, _) = expr.node; - if let ExprKind::MethodCall(ref segment, _, ref args) = if_expr.node; + if let ExprKind::If(if_expr, body, else_) = &expr.node; + if let ExprKind::MethodCall(segment, _, args) = &if_expr.node; if segment.ident.name == "is_none"; if Self::expression_returns_none(cx, body); if let Some(subject) = args.get(0); if Self::is_option(cx, subject); then { + if let Some(else_) = else_ { + if_chain! { + if let ExprKind::Block(block, None) = &else_.node; + if block.stmts.len() == 0; + if let Some(block_expr) = &block.expr; + if SpanlessEq::new(cx).ignore_fn().eq_expr(subject, block_expr); + then { + span_lint_and_then( + cx, + QUESTION_MARK, + expr.span, + "this block may be rewritten with the `?` operator", + |db| { + db.span_suggestion_with_applicability( + expr.span, + "replace_it_with", + format!("Some({}?)", Sugg::hir(cx, subject, "..")), + Applicability::MaybeIncorrect, // snippet + ); + } + ) + } + } + return; + } + span_lint_and_then( cx, QUESTION_MARK, @@ -84,7 +110,7 @@ impl Pass { expr.span, "replace_it_with", format!("{}?;", receiver_str), - Applicability::MachineApplicable, // snippet + Applicability::MaybeIncorrect, // snippet ); } ) @@ -133,9 +159,13 @@ impl Pass { } } - // Check if the block has an implicit return expression - if let Some(ref ret_expr) = block.expr { - return Some(ret_expr.clone()); + // Check for `return` without a semicolon. + if_chain! { + if block.stmts.len() == 0; + if let Some(ExprKind::Ret(Some(ret_expr))) = block.expr.as_ref().map(|e| &e.node); + then { + return Some(ret_expr.clone()); + } } None diff --git a/clippy_lints/src/redundant_field_names.rs b/clippy_lints/src/redundant_field_names.rs index 308f0066b69f..d8d80f2d128f 100644 --- a/clippy_lints/src/redundant_field_names.rs +++ b/clippy_lints/src/redundant_field_names.rs @@ -57,7 +57,10 @@ impl EarlyLintPass for RedundantFieldNames { continue; } if let ExprKind::Path(None, path) = &field.expr.node { - if path.segments.len() == 1 && path.segments[0].ident == field.ident { + if path.segments.len() == 1 + && path.segments[0].ident == field.ident + && path.segments[0].args.is_none() + { span_lint_and_sugg( cx, REDUNDANT_FIELD_NAMES, diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 820f2fdf32db..dfa4cfdcf94f 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -68,6 +68,34 @@ declare_clippy_lint! { "usage of `Box>`, vector elements are already on the heap" } +/// **What it does:** Checks for use of `Vec>` where T: Sized anywhere in the code. +/// +/// **Why is this bad?** `Vec` already keeps its contents in a separate area on +/// the heap. So if you `Box` its contents, you just add another level of indirection. +/// +/// **Known problems:** Vec> makes sense if T is a large type (see #3530, +/// 1st comment). +/// +/// **Example:** +/// ```rust +/// struct X { +/// values: Vec>, +/// } +/// ``` +/// +/// Better: +/// +/// ```rust +/// struct X { +/// values: Vec, +/// } +/// ``` +declare_clippy_lint! { + pub VEC_BOX, + complexity, + "usage of `Vec>` where T: Sized, vector elements are already on the heap" +} + /// **What it does:** Checks for use of `Option>` in function signatures and type /// definitions /// @@ -148,7 +176,7 @@ declare_clippy_lint! { impl LintPass for TypePass { fn get_lints(&self) -> LintArray { - lint_array!(BOX_VEC, OPTION_OPTION, LINKEDLIST, BORROWED_BOX) + lint_array!(BOX_VEC, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX) } } @@ -238,6 +266,43 @@ fn check_ty(cx: &LateContext<'_, '_>, ast_ty: &hir::Ty, is_local: bool) { ); return; // don't recurse into the type } + } else if match_def_path(cx.tcx, def_id, &paths::VEC) { + if_chain! { + // Get the _ part of Vec<_> + if let Some(ref last) = last_path_segment(qpath).args; + if let Some(ty) = last.args.iter().find_map(|arg| match arg { + GenericArg::Type(ty) => Some(ty), + GenericArg::Lifetime(_) => None, + }); + // ty is now _ at this point + if let TyKind::Path(ref ty_qpath) = ty.node; + let def = cx.tables.qpath_def(ty_qpath, ty.hir_id); + if let Some(def_id) = opt_def_id(def); + if Some(def_id) == cx.tcx.lang_items().owned_box(); + // At this point, we know ty is Box, now get T + if let Some(ref last) = last_path_segment(ty_qpath).args; + if let Some(ty) = last.args.iter().find_map(|arg| match arg { + GenericArg::Type(ty) => Some(ty), + GenericArg::Lifetime(_) => None, + }); + if let TyKind::Path(ref ty_qpath) = ty.node; + let def = cx.tables.qpath_def(ty_qpath, ty.hir_id); + if let Some(def_id) = opt_def_id(def); + let boxed_type = cx.tcx.type_of(def_id); + if boxed_type.is_sized(cx.tcx.at(ty.span), cx.param_env); + then { + span_lint_and_sugg( + cx, + VEC_BOX, + ast_ty.span, + "`Vec` is already on the heap, the boxing is unnecessary.", + "try", + format!("Vec<{}>", boxed_type), + Applicability::MaybeIncorrect, + ); + return; // don't recurse into the type + } + } } else if match_def_path(cx.tcx, def_id, &paths::OPTION) { if match_type_parameter(cx, qpath, &paths::OPTION) { span_lint( diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 440ab7433cc8..416ba4ca18d5 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -206,10 +206,7 @@ impl EarlyLintPass for Pass { } else if mac.node.path == "print" { span_lint(cx, PRINT_STDOUT, mac.span, "use of `print!`"); if let Some(fmtstr) = check_tts(cx, &mac.node.tts, false).0 { - if fmtstr.ends_with("\\n") && - // don't warn about strings with several `\n`s (#3126) - fmtstr.matches("\\n").count() == 1 - { + if check_newlines(&fmtstr) { span_lint( cx, PRINT_WITH_NEWLINE, @@ -221,10 +218,7 @@ impl EarlyLintPass for Pass { } } else if mac.node.path == "write" { if let Some(fmtstr) = check_tts(cx, &mac.node.tts, true).0 { - if fmtstr.ends_with("\\n") && - // don't warn about strings with several `\n`s (#3126) - fmtstr.matches("\\n").count() == 1 - { + if check_newlines(&fmtstr) { span_lint( cx, WRITE_WITH_NEWLINE, @@ -375,3 +369,29 @@ fn check_tts<'a>(cx: &EarlyContext<'a>, tts: &ThinTokenStream, is_write: bool) - } } } + +// Checks if `s` constains a single newline that terminates it +fn check_newlines(s: &str) -> bool { + if s.len() < 2 { + return false; + } + + 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; + } + escaping = false; + } else if byte == b'\\' { + escaping = true; + } + } + + false +} diff --git a/tests/ui/doc.rs b/tests/ui/doc.rs index c09cacd6be01..d4ba83a86f37 100644 --- a/tests/ui/doc.rs +++ b/tests/ui/doc.rs @@ -181,3 +181,7 @@ fn issue_2395() {} /// An iterator over mycrate::Collection's values. /// It should not lint a `'static` lifetime in ticks. fn issue_2210() {} + +/// This should not cause the lint to trigger: +/// #REQ-data-family.lint_partof_exists +fn issue_2343() {} diff --git a/tests/ui/explicit_write.rs b/tests/ui/explicit_write.rs index 10a4bca9f492..01a63b3a95f2 100644 --- a/tests/ui/explicit_write.rs +++ b/tests/ui/explicit_write.rs @@ -27,6 +27,10 @@ fn main() { writeln!(std::io::stderr(), "test").unwrap(); std::io::stdout().write_fmt(format_args!("test")).unwrap(); std::io::stderr().write_fmt(format_args!("test")).unwrap(); + + // including newlines + writeln!(std::io::stdout(), "test\ntest").unwrap(); + writeln!(std::io::stderr(), "test\ntest").unwrap(); } // these should not warn, different destination { diff --git a/tests/ui/explicit_write.stderr b/tests/ui/explicit_write.stderr index 171bf312a9bd..1a11dbc169bd 100644 --- a/tests/ui/explicit_write.stderr +++ b/tests/ui/explicit_write.stderr @@ -1,40 +1,52 @@ -error: use of `write!(stdout(), ...).unwrap()`. Consider using `print!` instead +error: use of `write!(stdout(), ...).unwrap()` --> $DIR/explicit_write.rs:24:9 | 24 | write!(std::io::stdout(), "test").unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `print!("test")` | = note: `-D clippy::explicit-write` implied by `-D warnings` -error: use of `write!(stderr(), ...).unwrap()`. Consider using `eprint!` instead +error: use of `write!(stderr(), ...).unwrap()` --> $DIR/explicit_write.rs:25:9 | 25 | write!(std::io::stderr(), "test").unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprint!("test")` -error: use of `writeln!(stdout(), ...).unwrap()`. Consider using `println!` instead +error: use of `writeln!(stdout(), ...).unwrap()` --> $DIR/explicit_write.rs:26:9 | 26 | writeln!(std::io::stdout(), "test").unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `println!("test")` -error: use of `writeln!(stderr(), ...).unwrap()`. Consider using `eprintln!` instead +error: use of `writeln!(stderr(), ...).unwrap()` --> $DIR/explicit_write.rs:27:9 | 27 | writeln!(std::io::stderr(), "test").unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("test")` -error: use of `stdout().write_fmt(...).unwrap()`. Consider using `print!` instead +error: use of `stdout().write_fmt(...).unwrap()` --> $DIR/explicit_write.rs:28:9 | 28 | std::io::stdout().write_fmt(format_args!("test")).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `print!("test")` -error: use of `stderr().write_fmt(...).unwrap()`. Consider using `eprint!` instead +error: use of `stderr().write_fmt(...).unwrap()` --> $DIR/explicit_write.rs:29:9 | 29 | std::io::stderr().write_fmt(format_args!("test")).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprint!("test")` -error: aborting due to 6 previous errors +error: use of `writeln!(stdout(), ...).unwrap()` + --> $DIR/explicit_write.rs:32:9 + | +32 | writeln!(std::io::stdout(), "test/ntest").unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `println!("test/ntest")` + +error: use of `writeln!(stderr(), ...).unwrap()` + --> $DIR/explicit_write.rs:33:9 + | +33 | writeln!(std::io::stderr(), "test/ntest").unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("test/ntest")` + +error: aborting due to 8 previous errors diff --git a/tests/ui/new_without_default.rs b/tests/ui/new_without_default.rs index a1818e037a70..2e715a6f8ba8 100644 --- a/tests/ui/new_without_default.rs +++ b/tests/ui/new_without_default.rs @@ -139,4 +139,18 @@ impl<'a, T: 'a> OptionRefWrapper<'a, T> { } } +pub struct Allow(Foo); + +impl Allow { + #[allow(clippy::new_without_default)] + pub fn new() -> Self { unimplemented!() } +} + +pub struct AllowDerive; + +impl AllowDerive { + #[allow(clippy::new_without_default_derive)] + pub fn new() -> Self { unimplemented!() } +} + fn main() {} diff --git a/tests/ui/partialeq_ne_impl.rs b/tests/ui/partialeq_ne_impl.rs index 3f9f91c81b1d..fabeee24b305 100644 --- a/tests/ui/partialeq_ne_impl.rs +++ b/tests/ui/partialeq_ne_impl.rs @@ -20,4 +20,12 @@ impl PartialEq for Foo { } } +struct Bar; + +impl PartialEq for Bar { + fn eq(&self, _: &Bar) -> bool { true } + #[allow(clippy::partialeq_ne_impl)] + fn ne(&self, _: &Bar) -> bool { false } +} + fn main() {} diff --git a/tests/ui/question_mark.rs b/tests/ui/question_mark.rs index 7f1d06fbd29e..b1edec32eeeb 100644 --- a/tests/ui/question_mark.rs +++ b/tests/ui/question_mark.rs @@ -42,11 +42,22 @@ pub struct SomeStruct { } impl SomeStruct { + #[rustfmt::skip] pub fn func(&self) -> Option { if (self.opt).is_none() { return None; } + if self.opt.is_none() { + return None + } + + let _ = if self.opt.is_none() { + return None; + } else { + self.opt + }; + self.opt } } diff --git a/tests/ui/question_mark.stderr b/tests/ui/question_mark.stderr index d3daaaa92705..c9d5538f36f4 100644 --- a/tests/ui/question_mark.stderr +++ b/tests/ui/question_mark.stderr @@ -9,12 +9,31 @@ error: this block may be rewritten with the `?` operator = note: `-D clippy::question-mark` implied by `-D warnings` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:46:9 + --> $DIR/question_mark.rs:47:9 | -46 | / if (self.opt).is_none() { -47 | | return None; -48 | | } +47 | / if (self.opt).is_none() { +48 | | return None; +49 | | } | |_________^ help: replace_it_with: `(self.opt)?;` -error: aborting due to 2 previous errors +error: this block may be rewritten with the `?` operator + --> $DIR/question_mark.rs:51:9 + | +51 | / if self.opt.is_none() { +52 | | return None +53 | | } + | |_________^ help: replace_it_with: `self.opt?;` + +error: this block may be rewritten with the `?` operator + --> $DIR/question_mark.rs:55:17 + | +55 | let _ = if self.opt.is_none() { + | _________________^ +56 | | return None; +57 | | } else { +58 | | self.opt +59 | | }; + | |_________^ help: replace_it_with: `Some(self.opt?)` + +error: aborting due to 4 previous errors diff --git a/tests/ui/redundant_field_names.rs b/tests/ui/redundant_field_names.rs index 68adba92f8a2..3d727ee6e6a1 100644 --- a/tests/ui/redundant_field_names.rs +++ b/tests/ui/redundant_field_names.rs @@ -68,3 +68,14 @@ fn main() { let _ = RangeInclusive::new(start, end); let _ = RangeToInclusive { end: end }; } + +fn issue_3476() { + fn foo() { + } + + struct S { + foo: fn(), + } + + S { foo: foo:: }; +} diff --git a/tests/ui/single_char_pattern.rs b/tests/ui/single_char_pattern.rs index 5277841fe325..eeee953ab844 100644 --- a/tests/ui/single_char_pattern.rs +++ b/tests/ui/single_char_pattern.rs @@ -42,8 +42,8 @@ fn main() { x.rmatches("x"); x.match_indices("x"); x.rmatch_indices("x"); - x.trim_left_matches("x"); - x.trim_right_matches("x"); + x.trim_start_matches("x"); + x.trim_end_matches("x"); // Make sure we escape characters correctly. x.split("\n"); diff --git a/tests/ui/single_char_pattern.stderr b/tests/ui/single_char_pattern.stderr index 273bf7796401..353796b39282 100644 --- a/tests/ui/single_char_pattern.stderr +++ b/tests/ui/single_char_pattern.stderr @@ -91,16 +91,16 @@ error: single-character string constant used as pattern | ^^^ help: try using a char instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:45:25 + --> $DIR/single_char_pattern.rs:45:26 | -45 | x.trim_left_matches("x"); - | ^^^ help: try using a char instead: `'x'` +45 | x.trim_start_matches("x"); + | ^^^ help: try using a char instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:46:26 + --> $DIR/single_char_pattern.rs:46:24 | -46 | x.trim_right_matches("x"); - | ^^^ help: try using a char instead: `'x'` +46 | x.trim_end_matches("x"); + | ^^^ help: try using a char instead: `'x'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:48:13 diff --git a/tests/ui/unused_io_amount.rs b/tests/ui/unused_io_amount.rs index a125d0397af8..0ec8ce57ad7f 100644 --- a/tests/ui/unused_io_amount.rs +++ b/tests/ui/unused_io_amount.rs @@ -12,7 +12,7 @@ use std::io; -// FIXME: compiletest doesn't understand errors from macro invocation span + fn try_macro(s: &mut T) -> io::Result<()> { try!(s.write(b"test")); let mut buf = [0u8; 4]; diff --git a/tests/ui/vec_box_sized.rs b/tests/ui/vec_box_sized.rs new file mode 100644 index 000000000000..d740f95edfe2 --- /dev/null +++ b/tests/ui/vec_box_sized.rs @@ -0,0 +1,17 @@ +struct SizedStruct { + _a: i32, +} + +struct UnsizedStruct { + _a: [i32], +} + +struct StructWithVecBox { + sized_type: Vec>, +} + +struct StructWithVecBoxButItsUnsized { + unsized_type: Vec>, +} + +fn main() {} diff --git a/tests/ui/vec_box_sized.stderr b/tests/ui/vec_box_sized.stderr new file mode 100644 index 000000000000..7f4bdfb5aed9 --- /dev/null +++ b/tests/ui/vec_box_sized.stderr @@ -0,0 +1,10 @@ +error: `Vec` is already on the heap, the boxing is unnecessary. + --> $DIR/vec_box_sized.rs:10:14 + | +10 | sized_type: Vec>, + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `Vec` + | + = note: `-D clippy::vec-box` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui/write_with_newline.rs b/tests/ui/write_with_newline.rs index dbfa02d20a1f..5d8543e578de 100644 --- a/tests/ui/write_with_newline.rs +++ b/tests/ui/write_with_newline.rs @@ -35,4 +35,9 @@ fn main() { write!(&mut v, "Hello {} {}\n\n", "world", "#2"); writeln!(&mut v, "\ndon't\nwarn\nfor\nmultiple\nnewlines\n"); // #3126 writeln!(&mut v, "\nbla\n\n"); // #3126 + + // Escaping + write!(&mut v, "\\n"); // #3514 + write!(&mut v, "\\\n"); + write!(&mut v, "\\\\n"); } diff --git a/tests/ui/write_with_newline.stderr b/tests/ui/write_with_newline.stderr index 8d2ad1b4d97a..ead6b5d08a01 100644 --- a/tests/ui/write_with_newline.stderr +++ b/tests/ui/write_with_newline.stderr @@ -24,5 +24,11 @@ error: using `write!()` with a format string that ends in a single newline, cons 22 | write!(&mut v, "{}/n", 1265); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 4 previous errors +error: using `write!()` with a format string that ends in a single newline, consider using `writeln!()` instead + --> $DIR/write_with_newline.rs:41:5 + | +41 | write!(&mut v, "//n"); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors