Merge commit '1fcc74cc9e03bc91eaa80ecf92976b0b14b3aeb6' into clippyup

This commit is contained in:
flip1995 2021-01-02 16:29:43 +01:00
parent 48dec842f2
commit ba4bf4f9c5
39 changed files with 646 additions and 248 deletions

View File

@ -0,0 +1,35 @@
---
name: Bug Report (False Negative)
about: Create a bug report about missing warnings from a lint
labels: L-bug, L-false-negative
---
<!--
Thank you for filing a bug report! 🐛 Please provide a short summary of the bug,
along with any information you feel relevant to replicating the bug.
-->
Lint name:
I tried this code:
```rust
<code>
```
I expected to see this happen: *explanation*
Instead, this happened: *explanation*
### Meta
- `cargo clippy -V`: e.g. clippy 0.0.212 (f455e46 2020-06-20)
- `rustc -Vv`:
```
rustc 1.46.0-nightly (f455e46ea 2020-06-20)
binary: rustc
commit-hash: f455e46eae1a227d735091091144601b467e1565
commit-date: 2020-06-20
host: x86_64-unknown-linux-gnu
release: 1.46.0-nightly
LLVM version: 10.0
```

View File

@ -0,0 +1,35 @@
---
name: Bug Report (False Positive)
about: Create a bug report about a wrongly emitted lint warning
labels: L-bug, L-false-positive
---
<!--
Thank you for filing a bug report! 🐛 Please provide a short summary of the bug,
along with any information you feel relevant to replicating the bug.
-->
Lint name:
I tried this code:
```rust
<code>
```
I expected to see this happen: *explanation*
Instead, this happened: *explanation*
### Meta
- `cargo clippy -V`: e.g. clippy 0.0.212 (f455e46 2020-06-20)
- `rustc -Vv`:
```
rustc 1.46.0-nightly (f455e46ea 2020-06-20)
binary: rustc
commit-hash: f455e46eae1a227d735091091144601b467e1565
commit-date: 2020-06-20
host: x86_64-unknown-linux-gnu
release: 1.46.0-nightly
LLVM version: 10.0
```

View File

@ -50,6 +50,9 @@ jobs:
- name: Build
run: cargo build --features deny-warnings,internal-lints
- name: Test "--fix -Zunstable-options"
run: cargo run --features deny-warnings,internal-lints --bin cargo-clippy -- clippy --fix -Zunstable-options
- name: Test
run: cargo test --features deny-warnings,internal-lints

View File

@ -1841,6 +1841,7 @@ Released 2018-09-13
[`forget_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_copy
[`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref
[`from_iter_instead_of_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_iter_instead_of_collect
[`from_over_into`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into
[`future_not_send`]: https://rust-lang.github.io/rust-clippy/master/index.html#future_not_send
[`get_last_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_last_with_len
[`get_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_unwrap

View File

@ -1,6 +1,6 @@
[package]
name = "clippy"
version = "0.0.212"
version = "0.1.51"
authors = [
"Manish Goregaokar <manishsmail@gmail.com>",
"Andre Bogus <bogusandre@gmail.com>",
@ -29,7 +29,7 @@ path = "src/driver.rs"
[dependencies]
# begin automatic update
clippy_lints = { version = "0.0.212", path = "clippy_lints" }
clippy_lints = { version = "0.1.50", path = "clippy_lints" }
# end automatic update
semver = "0.11"
rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util" }

View File

@ -10,16 +10,16 @@ A collection of lints to catch common mistakes and improve your [Rust](https://g
Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html).
You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category.
Category | Description | Default level
-- | -- | --
`clippy::all` | all lints that are on by default (correctness, style, complexity, perf) | **warn/deny**
`clippy::correctness` | code that is outright wrong or very useless | **deny**
`clippy::style` | code that should be written in a more idiomatic way | **warn**
`clippy::complexity` | code that does something simple but in a complex way | **warn**
`clippy::perf` | code that can be written to run faster | **warn**
`clippy::pedantic` | lints which are rather strict or might have false positives | allow
`clippy::nursery` | new lints that are still under development | allow
`clippy::cargo` | lints for the cargo manifest | allow
| Category | Description | Default level |
| --------------------- | ----------------------------------------------------------------------- | ------------- |
| `clippy::all` | all lints that are on by default (correctness, style, complexity, perf) | **warn/deny** |
| `clippy::correctness` | code that is outright wrong or very useless | **deny** |
| `clippy::style` | code that should be written in a more idiomatic way | **warn** |
| `clippy::complexity` | code that does something simple but in a complex way | **warn** |
| `clippy::perf` | code that can be written to run faster | **warn** |
| `clippy::pedantic` | lints which are rather strict or might have false positives | allow |
| `clippy::nursery` | new lints that are still under development | allow |
| `clippy::cargo` | lints for the cargo manifest | allow |
More to come, please [file an issue](https://github.com/rust-lang/rust-clippy/issues) if you have ideas!
@ -98,17 +98,6 @@ If you want to run Clippy **only** on the given crate, use the `--no-deps` optio
cargo clippy -p example -- --no-deps
```
### Running Clippy from the command line without installing it
To have cargo compile your crate with Clippy without Clippy installation
in your code, you can use:
```terminal
cargo run --bin cargo-clippy --manifest-path=path_to_clippys_Cargo.toml
```
*Note:* Be sure that Clippy was compiled with the same version of rustc that cargo invokes here!
### Travis CI
You can add Clippy to Travis CI in the same way you use it locally:
@ -130,18 +119,6 @@ script:
# etc.
```
If you are on nightly, It might happen that Clippy is not available for a certain nightly release.
In this case you can try to conditionally install Clippy from the Git repo.
```yaml
language: rust
rust:
- nightly
before_script:
- rustup component add clippy --toolchain=nightly || cargo install --git https://github.com/rust-lang/rust-clippy/ --force clippy
# etc.
```
Note that adding `-D warnings` will cause your build to fail if **any** warnings are found in your code.
That includes warnings found by rustc (e.g. `dead_code`, etc.). If you want to avoid this and only cause
an error for Clippy warnings, use `#![deny(clippy::all)]` in your code or `-D clippy::all` on the command

View File

@ -3,7 +3,7 @@
use std::fs;
use std::fs::File;
use std::io::prelude::*;
use std::path::PathBuf;
use std::path::{Path, PathBuf};
// This module takes an absolute path to a rustc repo and alters the dependencies to point towards
// the respective rustc subcrates instead of using extern crate xyz.
@ -44,7 +44,7 @@ pub fn run(rustc_path: Option<&str>) {
}
fn inject_deps_into_manifest(
rustc_source_dir: &PathBuf,
rustc_source_dir: &Path,
manifest_path: &str,
cargo_toml: &str,
lib_rs: &str,

View File

@ -1,7 +1,7 @@
[package]
name = "clippy_lints"
# begin automatic update
version = "0.0.212"
version = "0.1.51"
# end automatic update
authors = [
"Manish Goregaokar <manishsmail@gmail.com>",

View File

@ -6,7 +6,7 @@ use rustc_errors::Applicability;
use rustc_hir::def::Res;
use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, Adt, Ty};
use rustc_middle::ty;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::symbol::{Ident, Symbol};
use rustc_span::Span;
@ -103,18 +103,41 @@ impl LateLintPass<'_> for Default {
}
fn check_block<'tcx>(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) {
// find all binding statements like `let mut _ = T::default()` where `T::default()` is the
// `default` method of the `Default` trait, and store statement index in current block being
// checked and the name of the bound variable
let binding_statements_using_default = enumerate_bindings_using_default(cx, block);
// start from the `let mut _ = _::default();` and look at all the following
// statements, see if they re-assign the fields of the binding
for (stmt_idx, binding_name, binding_type, span) in binding_statements_using_default {
// the last statement of a block cannot trigger the lint
if stmt_idx == block.stmts.len() - 1 {
break;
}
let stmts_head = match block.stmts {
// Skip the last statement since there cannot possibly be any following statements that re-assign fields.
[head @ .., _] if !head.is_empty() => head,
_ => return,
};
for (stmt_idx, stmt) in stmts_head.iter().enumerate() {
// find all binding statements like `let mut _ = T::default()` where `T::default()` is the
// `default` method of the `Default` trait, and store statement index in current block being
// checked and the name of the bound variable
let (local, variant, binding_name, binding_type, span) = if_chain! {
// only take `let ...` statements
if let StmtKind::Local(local) = stmt.kind;
if let Some(expr) = local.init;
// only take bindings to identifiers
if let PatKind::Binding(_, binding_id, ident, _) = local.pat.kind;
// only when assigning `... = Default::default()`
if is_expr_default(expr, cx);
let binding_type = cx.typeck_results().node_type(binding_id);
if let Some(adt) = binding_type.ty_adt_def();
if adt.is_struct();
let variant = adt.non_enum_variant();
if adt.did.is_local() || !variant.is_field_list_non_exhaustive();
let module_did = cx.tcx.parent_module(stmt.hir_id).to_def_id();
if variant
.fields
.iter()
.all(|field| field.vis.is_accessible_from(module_did, cx.tcx));
then {
(local, variant, ident.name, binding_type, expr.span)
} else {
continue;
}
};
// find all "later statement"'s where the fields of the binding set as
// Default::default() get reassigned, unless the reassignment refers to the original binding
@ -122,15 +145,8 @@ impl LateLintPass<'_> for Default {
let mut assigned_fields = Vec::new();
let mut cancel_lint = false;
for consecutive_statement in &block.stmts[stmt_idx + 1..] {
// interrupt if the statement is a let binding (`Local`) that shadows the original
// binding
if stmt_shadows_binding(consecutive_statement, binding_name) {
break;
}
// find out if and which field was set by this `consecutive_statement`
else if let Some((field_ident, assign_rhs)) =
field_reassigned_by_stmt(consecutive_statement, binding_name)
{
if let Some((field_ident, assign_rhs)) = field_reassigned_by_stmt(consecutive_statement, binding_name) {
// interrupt and cancel lint if assign_rhs references the original binding
if contains_name(binding_name, assign_rhs) {
cancel_lint = true;
@ -152,7 +168,7 @@ impl LateLintPass<'_> for Default {
first_assign = Some(consecutive_statement);
}
}
// interrupt also if no field was assigned, since we only want to look at consecutive statements
// interrupt if no field was assigned, since we only want to look at consecutive statements
else {
break;
}
@ -161,55 +177,45 @@ impl LateLintPass<'_> for Default {
// if there are incorrectly assigned fields, do a span_lint_and_note to suggest
// construction using `Ty { fields, ..Default::default() }`
if !assigned_fields.is_empty() && !cancel_lint {
// take the original assignment as span
let stmt = &block.stmts[stmt_idx];
// if all fields of the struct are not assigned, add `.. Default::default()` to the suggestion.
let ext_with_default = !variant
.fields
.iter()
.all(|field| assigned_fields.iter().any(|(a, _)| a == &field.ident.name));
if let StmtKind::Local(preceding_local) = &stmt.kind {
// filter out fields like `= Default::default()`, because the FRU already covers them
let assigned_fields = assigned_fields
.into_iter()
.filter(|(_, rhs)| !is_expr_default(rhs, cx))
.collect::<Vec<(Symbol, &Expr<'_>)>>();
let field_list = assigned_fields
.into_iter()
.map(|(field, rhs)| {
// extract and store the assigned value for help message
let value_snippet = snippet(cx, rhs.span, "..");
format!("{}: {}", field, value_snippet)
})
.collect::<Vec<String>>()
.join(", ");
// if all fields of the struct are not assigned, add `.. Default::default()` to the suggestion.
let ext_with_default = !fields_of_type(binding_type)
.iter()
.all(|field| assigned_fields.iter().any(|(a, _)| a == &field.name));
let field_list = assigned_fields
.into_iter()
.map(|(field, rhs)| {
// extract and store the assigned value for help message
let value_snippet = snippet(cx, rhs.span, "..");
format!("{}: {}", field, value_snippet)
})
.collect::<Vec<String>>()
.join(", ");
let sugg = if ext_with_default {
if field_list.is_empty() {
format!("{}::default()", binding_type)
} else {
format!("{} {{ {}, ..Default::default() }}", binding_type, field_list)
}
let sugg = if ext_with_default {
if field_list.is_empty() {
format!("{}::default()", binding_type)
} else {
format!("{} {{ {} }}", binding_type, field_list)
};
format!("{} {{ {}, ..Default::default() }}", binding_type, field_list)
}
} else {
format!("{} {{ {} }}", binding_type, field_list)
};
// span lint once per statement that binds default
span_lint_and_note(
cx,
FIELD_REASSIGN_WITH_DEFAULT,
first_assign.unwrap().span,
"field assignment outside of initializer for an instance created with Default::default()",
Some(preceding_local.span),
&format!(
"consider initializing the variable with `{}` and removing relevant reassignments",
sugg
),
);
self.reassigned_linted.insert(span);
}
// span lint once per statement that binds default
span_lint_and_note(
cx,
FIELD_REASSIGN_WITH_DEFAULT,
first_assign.unwrap().span,
"field assignment outside of initializer for an instance created with Default::default()",
Some(local.span),
&format!(
"consider initializing the variable with `{}` and removing relevant reassignments",
sugg
),
);
self.reassigned_linted.insert(span);
}
}
}
@ -230,47 +236,6 @@ fn is_expr_default<'tcx>(expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> bool
}
}
/// Returns the block indices, identifiers and types of bindings set as `Default::default()`, except
/// for when the pattern type is a tuple.
fn enumerate_bindings_using_default<'tcx>(
cx: &LateContext<'tcx>,
block: &Block<'tcx>,
) -> Vec<(usize, Symbol, Ty<'tcx>, Span)> {
block
.stmts
.iter()
.enumerate()
.filter_map(|(idx, stmt)| {
if_chain! {
// only take `let ...` statements
if let StmtKind::Local(ref local) = stmt.kind;
// only take bindings to identifiers
if let PatKind::Binding(_, _, ident, _) = local.pat.kind;
// that are not tuples
let ty = cx.typeck_results().pat_ty(local.pat);
if !matches!(ty.kind(), ty::Tuple(_));
// only when assigning `... = Default::default()`
if let Some(ref expr) = local.init;
if is_expr_default(expr, cx);
then {
Some((idx, ident.name, ty, expr.span))
} else {
None
}
}
})
.collect()
}
fn stmt_shadows_binding(this: &Stmt<'_>, shadowed: Symbol) -> bool {
if let StmtKind::Local(local) = &this.kind {
if let PatKind::Binding(_, _, ident, _) = local.pat.kind {
return ident.name == shadowed;
}
}
false
}
/// Returns the reassigned field and the assigning expression (right-hand side of assign).
fn field_reassigned_by_stmt<'tcx>(this: &Stmt<'tcx>, binding_name: Symbol) -> Option<(Ident, &'tcx Expr<'tcx>)> {
if_chain! {
@ -290,14 +255,3 @@ fn field_reassigned_by_stmt<'tcx>(this: &Stmt<'tcx>, binding_name: Symbol) -> Op
}
}
}
/// Returns the vec of fields for a struct and an empty vec for non-struct ADTs.
fn fields_of_type(ty: Ty<'_>) -> Vec<Ident> {
if let Adt(adt, _) = ty.kind() {
if adt.is_struct() {
let variant = &adt.non_enum_variant();
return variant.fields.iter().map(|f| f.ident).collect();
}
}
vec![]
}

View File

@ -0,0 +1,83 @@
use crate::utils::paths::INTO;
use crate::utils::{match_def_path, meets_msrv, span_lint_and_help};
use if_chain::if_chain;
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
const FROM_OVER_INTO_MSRV: RustcVersion = RustcVersion::new(1, 41, 0);
declare_clippy_lint! {
/// **What it does:** Searches for implementations of the `Into<..>` trait and suggests to implement `From<..>` instead.
///
/// **Why is this bad?** According the std docs implementing `From<..>` is preferred since it gives you `Into<..>` for free where the reverse isn't true.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// struct StringWrapper(String);
///
/// impl Into<StringWrapper> for String {
/// fn into(self) -> StringWrapper {
/// StringWrapper(self)
/// }
/// }
/// ```
/// Use instead:
/// ```rust
/// struct StringWrapper(String);
///
/// impl From<String> for StringWrapper {
/// fn from(s: String) -> StringWrapper {
/// StringWrapper(s)
/// }
/// }
/// ```
pub FROM_OVER_INTO,
style,
"Warns on implementations of `Into<..>` to use `From<..>`"
}
pub struct FromOverInto {
msrv: Option<RustcVersion>,
}
impl FromOverInto {
#[must_use]
pub fn new(msrv: Option<RustcVersion>) -> Self {
FromOverInto { msrv }
}
}
impl_lint_pass!(FromOverInto => [FROM_OVER_INTO]);
impl LateLintPass<'_> for FromOverInto {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
if !meets_msrv(self.msrv.as_ref(), &FROM_OVER_INTO_MSRV) {
return;
}
let impl_def_id = cx.tcx.hir().local_def_id(item.hir_id);
if_chain! {
if let hir::ItemKind::Impl{ .. } = &item.kind;
if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(impl_def_id);
if match_def_path(cx, impl_trait_ref.def_id, &INTO);
then {
span_lint_and_help(
cx,
FROM_OVER_INTO,
item.span,
"an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true",
None,
"consider to implement `From` instead",
);
}
}
}
extract_msrv_attr!(LateContext);
}

View File

@ -4,6 +4,7 @@ use crate::utils::{snippet_opt, span_lint_and_then};
use rustc_errors::Applicability;
use rustc_hir::{Item, ItemKind, VariantData};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_target::abi::LayoutOf;
@ -58,6 +59,9 @@ impl_lint_pass!(LargeEnumVariant => [LARGE_ENUM_VARIANT]);
impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant {
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
if in_external_macro(cx.tcx.sess, item.span) {
return;
}
let did = cx.tcx.hir().local_def_id(item.hir_id);
if let ItemKind::Enum(ref def, _) = item.kind {
let ty = cx.tcx.type_of(did);

View File

@ -207,6 +207,7 @@ mod float_literal;
mod floating_point_arithmetic;
mod format;
mod formatting;
mod from_over_into;
mod functions;
mod future_not_send;
mod get_last_with_len;
@ -614,6 +615,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING,
&formatting::SUSPICIOUS_ELSE_FORMATTING,
&formatting::SUSPICIOUS_UNARY_OP_FORMATTING,
&from_over_into::FROM_OVER_INTO,
&functions::DOUBLE_MUST_USE,
&functions::MUST_USE_CANDIDATE,
&functions::MUST_USE_UNIT,
@ -1014,6 +1016,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(move || box checked_conversions::CheckedConversions::new(msrv));
store.register_late_pass(move || box mem_replace::MemReplace::new(msrv));
store.register_late_pass(move || box ranges::Ranges::new(msrv));
store.register_late_pass(move || box from_over_into::FromOverInto::new(msrv));
store.register_late_pass(move || box use_self::UseSelf::new(msrv));
store.register_late_pass(move || box missing_const_for_fn::MissingConstForFn::new(msrv));
@ -1417,6 +1420,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING),
LintId::of(&formatting::SUSPICIOUS_ELSE_FORMATTING),
LintId::of(&formatting::SUSPICIOUS_UNARY_OP_FORMATTING),
LintId::of(&from_over_into::FROM_OVER_INTO),
LintId::of(&functions::DOUBLE_MUST_USE),
LintId::of(&functions::MUST_USE_UNIT),
LintId::of(&functions::NOT_UNSAFE_PTR_ARG_DEREF),
@ -1663,6 +1667,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING),
LintId::of(&formatting::SUSPICIOUS_ELSE_FORMATTING),
LintId::of(&formatting::SUSPICIOUS_UNARY_OP_FORMATTING),
LintId::of(&from_over_into::FROM_OVER_INTO),
LintId::of(&functions::DOUBLE_MUST_USE),
LintId::of(&functions::MUST_USE_UNIT),
LintId::of(&functions::RESULT_UNIT_ERR),

View File

@ -105,7 +105,7 @@ impl MacroUseImports {
impl<'tcx> LateLintPass<'tcx> for MacroUseImports {
fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
if_chain! {
if cx.sess().opts.edition == Edition::Edition2018;
if cx.sess().opts.edition >= Edition::Edition2018;
if let hir::ItemKind::Use(path, _kind) = &item.kind;
if let Some(mac_attr) = item
.attrs

View File

@ -69,7 +69,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn {
|diag| {
if_chain! {
if let Some(header_snip) = snippet_opt(cx, header_span);
if let Some(ret_pos) = position_before_rarrow(header_snip.clone());
if let Some(ret_pos) = position_before_rarrow(&header_snip);
if let Some((ret_sugg, ret_snip)) = suggested_ret(cx, output);
then {
let help = format!("make the function `async` and {}", ret_sugg);

View File

@ -7,7 +7,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
/// **What it does:** Checks for instances of `map_err(|_| Some::Enum)`
///
/// **Why is this bad?** This map_err throws away the original error rather than allowing the enum to contain and report the cause of the error
/// **Why is this bad?** This `map_err` throws away the original error rather than allowing the enum to contain and report the cause of the error
///
/// **Known problems:** None.
///
@ -135,7 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for MapErrIgnore {
body_span,
"`map_err(|_|...` wildcard pattern discards the original error",
None,
"Consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`)",
"consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`)",
);
}
}

View File

@ -182,20 +182,6 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id:
if let ty::Ref(_, ty, Mutability::Not) = ty.kind() {
if is_type_diagnostic_item(cx, ty, sym::vec_type) {
let mut ty_snippet = None;
if_chain! {
if let TyKind::Path(QPath::Resolved(_, ref path)) = walk_ptrs_hir_ty(arg).kind;
if let Some(&PathSegment{args: Some(ref parameters), ..}) = path.segments.last();
then {
let types: Vec<_> = parameters.args.iter().filter_map(|arg| match arg {
GenericArg::Type(ty) => Some(ty),
_ => None,
}).collect();
if types.len() == 1 {
ty_snippet = snippet_opt(cx, types[0].span);
}
}
};
if let Some(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_owned()")]) {
span_lint_and_then(
cx,
@ -204,7 +190,7 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id:
"writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used \
with non-Vec-based slices.",
|diag| {
if let Some(ref snippet) = ty_snippet {
if let Some(ref snippet) = get_only_generic_arg_snippet(cx, arg) {
diag.span_suggestion(
arg.span,
"change this to",
@ -247,6 +233,33 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id:
},
);
}
} else if match_type(cx, ty, &paths::PATH_BUF) {
if let Some(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_path_buf()"), ("as_path", "")]) {
span_lint_and_then(
cx,
PTR_ARG,
arg.span,
"writing `&PathBuf` instead of `&Path` involves a new object where a slice will do.",
|diag| {
diag.span_suggestion(
arg.span,
"change this to",
"&Path".into(),
Applicability::Unspecified,
);
for (clonespan, suggestion) in spans {
diag.span_suggestion_short(
clonespan,
&snippet_opt(cx, clonespan).map_or("change the call to".into(), |x| {
Cow::Owned(format!("change `{}` to", x))
}),
suggestion.into(),
Applicability::Unspecified,
);
}
},
);
}
} else if match_type(cx, ty, &paths::COW) {
if_chain! {
if let TyKind::Rptr(_, MutTy { ref ty, ..} ) = arg.kind;
@ -309,6 +322,23 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id:
}
}
fn get_only_generic_arg_snippet(cx: &LateContext<'_>, arg: &Ty<'_>) -> Option<String> {
if_chain! {
if let TyKind::Path(QPath::Resolved(_, ref path)) = walk_ptrs_hir_ty(arg).kind;
if let Some(&PathSegment{args: Some(ref parameters), ..}) = path.segments.last();
let types: Vec<_> = parameters.args.iter().filter_map(|arg| match arg {
GenericArg::Type(ty) => Some(ty),
_ => None,
}).collect();
if types.len() == 1;
then {
snippet_opt(cx, types[0].span)
} else {
None
}
}
}
fn get_rptr_lm<'tcx>(ty: &'tcx Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability, Span)> {
if let TyKind::Rptr(ref lt, ref m) = ty.kind {
Some((lt, m.mutbl, ty.span))

View File

@ -40,7 +40,7 @@ impl EarlyLintPass for SingleComponentPathImports {
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
if_chain! {
if !in_macro(item.span);
if cx.sess.opts.edition == Edition::Edition2018;
if cx.sess.opts.edition >= Edition::Edition2018;
if !item.vis.kind.is_pub();
if let ItemKind::Use(use_tree) = &item.kind;
if let segments = &use_tree.prefix.segments;

View File

@ -120,7 +120,7 @@ fn is_unit_expr(expr: &ast::Expr) -> bool {
fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) {
let (ret_span, appl) = if let Ok(fn_source) = cx.sess().source_map().span_to_snippet(span.with_hi(ty.span.hi())) {
position_before_rarrow(fn_source).map_or((ty.span, Applicability::MaybeIncorrect), |rpos| {
position_before_rarrow(&fn_source).map_or((ty.span, Applicability::MaybeIncorrect), |rpos| {
(
#[allow(clippy::cast_possible_truncation)]
ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)),

View File

@ -501,8 +501,18 @@ pub fn eq_generic_param(l: &GenericParam, r: &GenericParam) -> bool {
&& match (&l.kind, &r.kind) {
(Lifetime, Lifetime) => true,
(Type { default: l }, Type { default: r }) => both(l, r, |l, r| eq_ty(l, r)),
(Const { ty: lt, kw_span: _ , default: ld}, Const { ty: rt, kw_span: _, default: rd }) =>
eq_ty(lt, rt) && both(ld, rd, |ld, rd| eq_anon_const(ld, rd)),
(
Const {
ty: lt,
kw_span: _,
default: ld,
},
Const {
ty: rt,
kw_span: _,
default: rd,
},
) => eq_ty(lt, rt) && both(ld, rd, |ld, rd| eq_anon_const(ld, rd)),
_ => false,
}
&& over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))

View File

@ -788,8 +788,7 @@ pub fn indent_of<T: LintContext>(cx: &T, span: Span) -> Option<usize> {
/// fn into3(self) -> () {}
/// ^
/// ```
#[allow(clippy::needless_pass_by_value)]
pub fn position_before_rarrow(s: String) -> Option<usize> {
pub fn position_before_rarrow(s: &str) -> Option<usize> {
s.rfind("->").map(|rpos| {
let mut rpos = rpos;
let chars: Vec<char> = s.chars().collect();

View File

@ -72,7 +72,6 @@ impl<'a, 'tcx> Visitor<'tcx> for PtrCloneVisitor<'a, 'tcx> {
}
}
}
return;
}
walk_expr(self, expr);
}

View File

@ -1,3 +1,3 @@
[toolchain]
channel = "nightly-2020-12-20"
channel = "nightly-2021-01-02"
components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"]

View File

@ -84,3 +84,13 @@ macro_rules! as_conv {
0u32 as u64
};
}
#[macro_export]
macro_rules! large_enum_variant {
() => {
enum LargeEnumInMacro {
A(i32),
B([i32; 8000]),
}
};
}

View File

@ -107,4 +107,16 @@ fn main() {
x.i = side_effect.next();
x.j = 2;
x.i = side_effect.next();
// don't lint - some private fields
let mut x = m::F::default();
x.a = 1;
}
mod m {
#[derive(Default)]
pub struct F {
pub a: u64,
b: u64,
}
}

View File

@ -53,7 +53,7 @@ error: field assignment outside of initializer for an instance created with Defa
LL | a.i = Default::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: consider initializing the variable with `A::default()` and removing relevant reassignments
note: consider initializing the variable with `A { i: Default::default(), ..Default::default() }` and removing relevant reassignments
--> $DIR/field_reassign_with_default.rs:90:5
|
LL | let mut a: A = Default::default();
@ -65,7 +65,7 @@ error: field assignment outside of initializer for an instance created with Defa
LL | a.i = Default::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: consider initializing the variable with `A { j: 45, ..Default::default() }` and removing relevant reassignments
note: consider initializing the variable with `A { i: Default::default(), j: 45 }` and removing relevant reassignments
--> $DIR/field_reassign_with_default.rs:94:5
|
LL | let mut a: A = Default::default();

View File

@ -0,0 +1,21 @@
#![warn(clippy::from_over_into)]
// this should throw an error
struct StringWrapper(String);
impl Into<StringWrapper> for String {
fn into(self) -> StringWrapper {
StringWrapper(self)
}
}
// this is fine
struct A(String);
impl From<String> for A {
fn from(s: String) -> A {
A(s)
}
}
fn main() {}

View File

@ -0,0 +1,15 @@
error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true
--> $DIR/from_over_into.rs:6:1
|
LL | / impl Into<StringWrapper> for String {
LL | | fn into(self) -> StringWrapper {
LL | | StringWrapper(self)
LL | | }
LL | | }
| |_^
|
= note: `-D clippy::from-over-into` implied by `-D warnings`
= help: consider to implement `From` instead
error: aborting due to previous error

View File

@ -1,7 +1,12 @@
// aux-build:macro_rules.rs
#![allow(dead_code)]
#![allow(unused_variables)]
#![warn(clippy::large_enum_variant)]
#[macro_use]
extern crate macro_rules;
enum LargeEnum {
A(i32),
B([i32; 8000]),
@ -51,4 +56,6 @@ enum LargeEnumOk {
LargeB([i32; 8001]),
}
fn main() {}
fn main() {
large_enum_variant!();
}

View File

@ -1,12 +1,12 @@
error: large size difference between variants
--> $DIR/large_enum_variant.rs:7:5
--> $DIR/large_enum_variant.rs:12:5
|
LL | B([i32; 8000]),
| ^^^^^^^^^^^^^^ this variant is 32000 bytes
|
= note: `-D clippy::large-enum-variant` implied by `-D warnings`
note: and the second-largest variant is 4 bytes:
--> $DIR/large_enum_variant.rs:6:5
--> $DIR/large_enum_variant.rs:11:5
|
LL | A(i32),
| ^^^^^^
@ -16,13 +16,13 @@ LL | B(Box<[i32; 8000]>),
| ^^^^^^^^^^^^^^^^
error: large size difference between variants
--> $DIR/large_enum_variant.rs:31:5
--> $DIR/large_enum_variant.rs:36:5
|
LL | ContainingLargeEnum(LargeEnum),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this variant is 32004 bytes
|
note: and the second-largest variant is 8 bytes:
--> $DIR/large_enum_variant.rs:30:5
--> $DIR/large_enum_variant.rs:35:5
|
LL | VariantOk(i32, u32),
| ^^^^^^^^^^^^^^^^^^^
@ -32,30 +32,30 @@ LL | ContainingLargeEnum(Box<LargeEnum>),
| ^^^^^^^^^^^^^^
error: large size difference between variants
--> $DIR/large_enum_variant.rs:41:5
--> $DIR/large_enum_variant.rs:46:5
|
LL | StructLikeLarge { x: [i32; 8000], y: i32 },
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this variant is 32004 bytes
|
note: and the second-largest variant is 8 bytes:
--> $DIR/large_enum_variant.rs:40:5
--> $DIR/large_enum_variant.rs:45:5
|
LL | VariantOk(i32, u32),
| ^^^^^^^^^^^^^^^^^^^
help: consider boxing the large fields to reduce the total size of the enum
--> $DIR/large_enum_variant.rs:41:5
--> $DIR/large_enum_variant.rs:46:5
|
LL | StructLikeLarge { x: [i32; 8000], y: i32 },
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: large size difference between variants
--> $DIR/large_enum_variant.rs:46:5
--> $DIR/large_enum_variant.rs:51:5
|
LL | StructLikeLarge2 { x: [i32; 8000] },
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this variant is 32000 bytes
|
note: and the second-largest variant is 8 bytes:
--> $DIR/large_enum_variant.rs:45:5
--> $DIR/large_enum_variant.rs:50:5
|
LL | VariantOk(i32, u32),
| ^^^^^^^^^^^^^^^^^^^

View File

@ -5,7 +5,7 @@ LL | println!("{:?}", x.map_err(|_| Errors::Ignored));
| ^^^
|
= note: `-D clippy::map-err-ignore` implied by `-D warnings`
= help: Consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`)
= help: consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`)
error: aborting due to previous error

View File

@ -57,6 +57,14 @@ pub fn checked_conversion() {
let _ = value <= (u32::MAX as i64) && value >= 0;
}
pub struct FromOverInto(String);
impl Into<FromOverInto> for String {
fn into(self) -> FromOverInto {
FromOverInto(self)
}
}
pub fn filter_map_next() {
let a = ["1", "lol", "3", "NaN", "5"];

View File

@ -1,12 +1,12 @@
error: stripping a prefix manually
--> $DIR/min_rust_version_attr.rs:142:24
--> $DIR/min_rust_version_attr.rs:150:24
|
LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
| ^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::manual-strip` implied by `-D warnings`
note: the prefix was tested here
--> $DIR/min_rust_version_attr.rs:141:9
--> $DIR/min_rust_version_attr.rs:149:9
|
LL | if s.starts_with("hello, ") {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -17,13 +17,13 @@ LL | assert_eq!(<stripped>.to_uppercase(), "WORLD!");
|
error: stripping a prefix manually
--> $DIR/min_rust_version_attr.rs:154:24
--> $DIR/min_rust_version_attr.rs:162:24
|
LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
| ^^^^^^^^^^^^^^^^^^^^
|
note: the prefix was tested here
--> $DIR/min_rust_version_attr.rs:153:9
--> $DIR/min_rust_version_attr.rs:161:9
|
LL | if s.starts_with("hello, ") {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -2,6 +2,7 @@
#![warn(clippy::ptr_arg)]
use std::borrow::Cow;
use std::path::PathBuf;
fn do_vec(x: &Vec<i64>) {
//Nothing here
@ -21,6 +22,15 @@ fn do_str_mut(x: &mut String) {
//Nothing here either
}
fn do_path(x: &PathBuf) {
//Nothing here either
}
fn do_path_mut(x: &mut PathBuf) {
// no error here
//Nothing here either
}
fn main() {}
trait Foo {
@ -55,6 +65,14 @@ fn str_cloned(x: &String) -> String {
x.clone()
}
fn path_cloned(x: &PathBuf) -> PathBuf {
let a = x.clone();
let b = x.clone();
let c = b.clone();
let d = a.clone().clone().clone();
x.clone()
}
fn false_positive_capacity(x: &Vec<u8>, y: &String) {
let a = x.capacity();
let b = y.clone();
@ -87,10 +105,12 @@ impl Foo2 for String {
// Check that the allow attribute on parameters is honored
mod issue_5644 {
use std::borrow::Cow;
use std::path::PathBuf;
fn allowed(
#[allow(clippy::ptr_arg)] _v: &Vec<u32>,
#[allow(clippy::ptr_arg)] _s: &String,
#[allow(clippy::ptr_arg)] _p: &PathBuf,
#[allow(clippy::ptr_arg)] _c: &Cow<[i32]>,
) {
}
@ -100,6 +120,7 @@ mod issue_5644 {
fn allowed(
#[allow(clippy::ptr_arg)] _v: &Vec<u32>,
#[allow(clippy::ptr_arg)] _s: &String,
#[allow(clippy::ptr_arg)] _p: &PathBuf,
#[allow(clippy::ptr_arg)] _c: &Cow<[i32]>,
) {
}
@ -109,8 +130,28 @@ mod issue_5644 {
fn allowed(
#[allow(clippy::ptr_arg)] _v: &Vec<u32>,
#[allow(clippy::ptr_arg)] _s: &String,
#[allow(clippy::ptr_arg)] _p: &PathBuf,
#[allow(clippy::ptr_arg)] _c: &Cow<[i32]>,
) {
}
}
}
mod issue6509 {
use std::path::PathBuf;
fn foo_vec(vec: &Vec<u8>) {
let _ = vec.clone().pop();
let _ = vec.clone().clone();
}
fn foo_path(path: &PathBuf) {
let _ = path.clone().pop();
let _ = path.clone().clone();
}
fn foo_str(str: &PathBuf) {
let _ = str.clone().pop();
let _ = str.clone().clone();
}
}

View File

@ -1,5 +1,5 @@
error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices.
--> $DIR/ptr_arg.rs:6:14
--> $DIR/ptr_arg.rs:7:14
|
LL | fn do_vec(x: &Vec<i64>) {
| ^^^^^^^^^ help: change this to: `&[i64]`
@ -7,19 +7,25 @@ LL | fn do_vec(x: &Vec<i64>) {
= note: `-D clippy::ptr-arg` implied by `-D warnings`
error: writing `&String` instead of `&str` involves a new object where a slice will do.
--> $DIR/ptr_arg.rs:15:14
--> $DIR/ptr_arg.rs:16:14
|
LL | fn do_str(x: &String) {
| ^^^^^^^ help: change this to: `&str`
error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do.
--> $DIR/ptr_arg.rs:25:15
|
LL | fn do_path(x: &PathBuf) {
| ^^^^^^^^ help: change this to: `&Path`
error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices.
--> $DIR/ptr_arg.rs:28:18
--> $DIR/ptr_arg.rs:38:18
|
LL | fn do_vec(x: &Vec<i64>);
| ^^^^^^^^^ help: change this to: `&[i64]`
error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices.
--> $DIR/ptr_arg.rs:41:14
--> $DIR/ptr_arg.rs:51:14
|
LL | fn cloned(x: &Vec<u8>) -> Vec<u8> {
| ^^^^^^^^
@ -38,7 +44,7 @@ LL | x.to_owned()
|
error: writing `&String` instead of `&str` involves a new object where a slice will do.
--> $DIR/ptr_arg.rs:50:18
--> $DIR/ptr_arg.rs:60:18
|
LL | fn str_cloned(x: &String) -> String {
| ^^^^^^^
@ -60,8 +66,31 @@ help: change `x.clone()` to
LL | x.to_string()
|
error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do.
--> $DIR/ptr_arg.rs:68:19
|
LL | fn path_cloned(x: &PathBuf) -> PathBuf {
| ^^^^^^^^
|
help: change this to
|
LL | fn path_cloned(x: &Path) -> PathBuf {
| ^^^^^
help: change `x.clone()` to
|
LL | let a = x.to_path_buf();
| ^^^^^^^^^^^^^^^
help: change `x.clone()` to
|
LL | let b = x.to_path_buf();
| ^^^^^^^^^^^^^^^
help: change `x.clone()` to
|
LL | x.to_path_buf()
|
error: writing `&String` instead of `&str` involves a new object where a slice will do.
--> $DIR/ptr_arg.rs:58:44
--> $DIR/ptr_arg.rs:76:44
|
LL | fn false_positive_capacity(x: &Vec<u8>, y: &String) {
| ^^^^^^^
@ -80,10 +109,67 @@ LL | let c = y;
| ^
error: using a reference to `Cow` is not recommended.
--> $DIR/ptr_arg.rs:72:25
--> $DIR/ptr_arg.rs:90:25
|
LL | fn test_cow_with_ref(c: &Cow<[i32]>) {}
| ^^^^^^^^^^^ help: change this to: `&[i32]`
error: aborting due to 7 previous errors
error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices.
--> $DIR/ptr_arg.rs:143:21
|
LL | fn foo_vec(vec: &Vec<u8>) {
| ^^^^^^^^
|
help: change this to
|
LL | fn foo_vec(vec: &[u8]) {
| ^^^^^
help: change `vec.clone()` to
|
LL | let _ = vec.to_owned().pop();
| ^^^^^^^^^^^^^^
help: change `vec.clone()` to
|
LL | let _ = vec.to_owned().clone();
| ^^^^^^^^^^^^^^
error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do.
--> $DIR/ptr_arg.rs:148:23
|
LL | fn foo_path(path: &PathBuf) {
| ^^^^^^^^
|
help: change this to
|
LL | fn foo_path(path: &Path) {
| ^^^^^
help: change `path.clone()` to
|
LL | let _ = path.to_path_buf().pop();
| ^^^^^^^^^^^^^^^^^^
help: change `path.clone()` to
|
LL | let _ = path.to_path_buf().clone();
| ^^^^^^^^^^^^^^^^^^
error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do.
--> $DIR/ptr_arg.rs:153:21
|
LL | fn foo_str(str: &PathBuf) {
| ^^^^^^^^
|
help: change this to
|
LL | fn foo_str(str: &Path) {
| ^^^^^
help: change `str.clone()` to
|
LL | let _ = str.to_path_buf().pop();
| ^^^^^^^^^^^^^^^^^
help: change `str.clone()` to
|
LL | let _ = str.to_path_buf().clone();
| ^^^^^^^^^^^^^^^^^
error: aborting due to 12 previous errors

View File

@ -11,6 +11,7 @@
#![deny(clippy::unused_unit)]
#![allow(dead_code)]
#![allow(clippy::from_over_into)]
struct Unitter;
impl Unitter {

View File

@ -11,6 +11,7 @@
#![deny(clippy::unused_unit)]
#![allow(dead_code)]
#![allow(clippy::from_over_into)]
struct Unitter;
impl Unitter {

View File

@ -1,5 +1,5 @@
error: unneeded unit return type
--> $DIR/unused_unit.rs:18:28
--> $DIR/unused_unit.rs:19:28
|
LL | pub fn get_unit<F: Fn() -> (), G>(&self, f: F, _g: G) -> ()
| ^^^^^^ help: remove the `-> ()`
@ -11,109 +11,109 @@ LL | #![deny(clippy::unused_unit)]
| ^^^^^^^^^^^^^^^^^^^
error: unneeded unit return type
--> $DIR/unused_unit.rs:19:18
--> $DIR/unused_unit.rs:20:18
|
LL | where G: Fn() -> () {
| ^^^^^^ help: remove the `-> ()`
error: unneeded unit return type
--> $DIR/unused_unit.rs:18:58
--> $DIR/unused_unit.rs:19:58
|
LL | pub fn get_unit<F: Fn() -> (), G>(&self, f: F, _g: G) -> ()
| ^^^^^^ help: remove the `-> ()`
error: unneeded unit return type
--> $DIR/unused_unit.rs:20:26
--> $DIR/unused_unit.rs:21:26
|
LL | let _y: &dyn Fn() -> () = &f;
| ^^^^^^ help: remove the `-> ()`
error: unneeded unit return type
--> $DIR/unused_unit.rs:27:18
--> $DIR/unused_unit.rs:28:18
|
LL | fn into(self) -> () {
| ^^^^^^ help: remove the `-> ()`
error: unneeded unit expression
--> $DIR/unused_unit.rs:28:9
--> $DIR/unused_unit.rs:29:9
|
LL | ()
| ^^ help: remove the final `()`
error: unneeded unit return type
--> $DIR/unused_unit.rs:33:29
--> $DIR/unused_unit.rs:34:29
|
LL | fn redundant<F: FnOnce() -> (), G, H>(&self, _f: F, _g: G, _h: H)
| ^^^^^^ help: remove the `-> ()`
error: unneeded unit return type
--> $DIR/unused_unit.rs:35:19
--> $DIR/unused_unit.rs:36:19
|
LL | G: FnMut() -> (),
| ^^^^^^ help: remove the `-> ()`
error: unneeded unit return type
--> $DIR/unused_unit.rs:36:16
--> $DIR/unused_unit.rs:37:16
|
LL | H: Fn() -> ();
| ^^^^^^ help: remove the `-> ()`
error: unneeded unit return type
--> $DIR/unused_unit.rs:40:29
--> $DIR/unused_unit.rs:41:29
|
LL | fn redundant<F: FnOnce() -> (), G, H>(&self, _f: F, _g: G, _h: H)
| ^^^^^^ help: remove the `-> ()`
error: unneeded unit return type
--> $DIR/unused_unit.rs:42:19
--> $DIR/unused_unit.rs:43:19
|
LL | G: FnMut() -> (),
| ^^^^^^ help: remove the `-> ()`
error: unneeded unit return type
--> $DIR/unused_unit.rs:43:16
--> $DIR/unused_unit.rs:44:16
|
LL | H: Fn() -> () {}
| ^^^^^^ help: remove the `-> ()`
error: unneeded unit return type
--> $DIR/unused_unit.rs:46:17
--> $DIR/unused_unit.rs:47:17
|
LL | fn return_unit() -> () { () }
| ^^^^^^ help: remove the `-> ()`
error: unneeded unit expression
--> $DIR/unused_unit.rs:46:26
--> $DIR/unused_unit.rs:47:26
|
LL | fn return_unit() -> () { () }
| ^^ help: remove the final `()`
error: unneeded `()`
--> $DIR/unused_unit.rs:56:14
--> $DIR/unused_unit.rs:57:14
|
LL | break();
| ^^ help: remove the `()`
error: unneeded `()`
--> $DIR/unused_unit.rs:58:11
--> $DIR/unused_unit.rs:59:11
|
LL | return();
| ^^ help: remove the `()`
error: unneeded unit return type
--> $DIR/unused_unit.rs:75:10
--> $DIR/unused_unit.rs:76:10
|
LL | fn test()->(){}
| ^^^^ help: remove the `-> ()`
error: unneeded unit return type
--> $DIR/unused_unit.rs:78:11
--> $DIR/unused_unit.rs:79:11
|
LL | fn test2() ->(){}
| ^^^^^ help: remove the `-> ()`
error: unneeded unit return type
--> $DIR/unused_unit.rs:81:11
--> $DIR/unused_unit.rs:82:11
|
LL | fn test3()-> (){}
| ^^^^^ help: remove the `-> ()`

View File

@ -1,3 +1,6 @@
#![allow(clippy::single_match_else)]
use rustc_tools_util::VersionInfo;
#[test]
fn check_that_clippy_lints_has_the_same_version_as_clippy() {
let clippy_meta = cargo_metadata::MetadataCommand::new()
@ -17,3 +20,53 @@ fn check_that_clippy_lints_has_the_same_version_as_clippy() {
}
}
}
#[test]
fn check_that_clippy_has_the_same_major_version_as_rustc() {
let clippy_version = rustc_tools_util::get_version_info!();
let clippy_major = clippy_version.major;
let clippy_minor = clippy_version.minor;
let clippy_patch = clippy_version.patch;
// get the rustc version
// this way the rust-toolchain file version is honored
let rustc_version = String::from_utf8(
std::process::Command::new("rustc")
.arg("--version")
.output()
.expect("failed to run `rustc --version`")
.stdout,
)
.unwrap();
// extract "1 XX 0" from "rustc 1.XX.0-nightly (<commit> <date>)"
let vsplit: Vec<&str> = rustc_version
.split(' ')
.nth(1)
.unwrap()
.split('-')
.next()
.unwrap()
.split('.')
.collect();
match vsplit.as_slice() {
[rustc_major, rustc_minor, _rustc_patch] => {
// clippy 0.1.XX should correspond to rustc 1.XX.0
assert_eq!(clippy_major, 0); // this will probably stay the same for a long time
assert_eq!(
clippy_minor.to_string(),
*rustc_major,
"clippy minor version does not equal rustc major version"
);
assert_eq!(
clippy_patch.to_string(),
*rustc_minor,
"clippy patch version does not equal rustc minor version"
);
// do not check rustc_patch because when a stable-patch-release is made (like 1.50.2),
// we don't want our tests failing suddenly
},
_ => {
panic!("Failed to parse rustc version: {:?}", vsplit);
},
};
}

View File

@ -77,7 +77,7 @@
<div class="col-md-12 form-horizontal">
<div class="input-group">
<label class="input-group-addon" id="filter-label" for="filter-input">Filter:</label>
<input type="text" class="form-control" placeholder="Keywords or search string" id="filter-input" ng-model="search" />
<input type="text" class="form-control" placeholder="Keywords or search string" id="filter-input" ng-model="search" ng-model-options="{debounce: 50}"/>
<span class="input-group-btn">
<button class="btn btn-default" type="button" ng-click="search = ''">
Clear
@ -119,6 +119,7 @@
{{title}}
</h4>
<div class="list-group-item-text" ng-bind-html="text | markdown"></div>
<a ng-if="title == 'Known problems'" href="https://github.com/rust-lang/rust-clippy/issues?q=is%3Aissue+is%3Aopen+{{lint.id}}">Search on GitHub</a>
</li>
</ul>
</article>
@ -180,6 +181,22 @@
}
}
function searchLint(lint, term) {
for (const field in lint.docs) {
// Continue if it's not a property
if (!lint.docs.hasOwnProperty(field)) {
continue;
}
// Return if not found
if (lint.docs[field].toLowerCase().indexOf(term) !== -1) {
return true;
}
}
return false;
}
angular.module("clippy", [])
.filter('markdown', function ($sce) {
return function (text) {
@ -216,40 +233,31 @@
};
$scope.bySearch = function (lint, index, array) {
let search_str = $scope.search;
let searchStr = $scope.search;
// It can be `null` I haven't missed this value
if (search_str == null || search_str.length == 0) {
if (searchStr == null || searchStr.length < 3) {
return true;
}
search_str = search_str.toLowerCase();
searchStr = searchStr.toLowerCase();
// Search by id
let id_search = search_str.trim().replace(/(\-| )/g, "_");
if (lint.id.includes(id_search)) {
if (lint.id.indexOf(searchStr.replace("-", "_")) !== -1) {
return true;
}
// Search the description
// The use of `for`-loops instead of `foreach` enables us to return early
let search_lint = (lint, therm) => {
for (const field in lint.docs) {
// Continue if it's not a property
if (!lint.docs.hasOwnProperty(field)) {
continue;
}
let terms = searchStr.split(" ");
for (index = 0; index < terms.length; index++) {
if (lint.id.indexOf(terms[index]) !== -1) {
continue;
}
// Return if not found
if (lint.docs[field].toLowerCase().includes(therm)) {
return true;
}
if (searchLint(lint, terms[index])) {
continue;
}
return false;
};
let therms = search_str.split(" ");
for (index = 0; index < therms.length; index++) {
if (!search_lint(lint, therms[index])) {
return false;
}
}
return true;