]> git.lizzy.rs Git - rust.git/commitdiff
Merge commit '7ea7cd165ad6705603852771bf82cc2fd6560db5' into clippyup2
authorflip1995 <hello@philkrones.com>
Thu, 28 May 2020 13:45:24 +0000 (15:45 +0200)
committerflip1995 <hello@philkrones.com>
Thu, 28 May 2020 13:45:24 +0000 (15:45 +0200)
84 files changed:
.github/workflows/clippy.yml
.github/workflows/clippy_bors.yml
CHANGELOG.md
CONTRIBUTING.md
clippy_dev/src/new_lint.rs
clippy_lints/Cargo.toml
clippy_lints/src/attrs.rs
clippy_lints/src/cargo_common_metadata.rs
clippy_lints/src/float_literal.rs
clippy_lints/src/lib.rs
clippy_lints/src/matches.rs
clippy_lints/src/methods/mod.rs
clippy_lints/src/misc_early.rs
clippy_lints/src/missing_const_for_fn.rs
clippy_lints/src/multiple_crate_versions.rs
clippy_lints/src/mut_reference.rs
clippy_lints/src/needless_pass_by_value.rs
clippy_lints/src/new_without_default.rs
clippy_lints/src/non_expressive_names.rs
clippy_lints/src/ptr.rs
clippy_lints/src/ranges.rs
clippy_lints/src/redundant_field_names.rs
clippy_lints/src/trivially_copy_pass_by_ref.rs
clippy_lints/src/useless_conversion.rs
clippy_lints/src/utils/conf.rs
clippy_lints/src/utils/inspector.rs
clippy_lints/src/utils/mod.rs
clippy_lints/src/utils/paths.rs
clippy_lints/src/utils/sugg.rs
doc/adding_lints.md
doc/common_tools_writing_lints.md [new file with mode: 0644]
rustc_tools_util/LICENSE-APACHE [new symlink]
rustc_tools_util/LICENSE-MIT [new symlink]
setup-toolchain.sh
src/driver.rs
src/lintlist/mod.rs
tests/compile-test.rs
tests/ui-cargo/cargo_common_metadata/fail/Cargo.toml [new file with mode: 0644]
tests/ui-cargo/cargo_common_metadata/fail/src/main.rs [new file with mode: 0644]
tests/ui-cargo/cargo_common_metadata/fail/src/main.stderr [new file with mode: 0644]
tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml [new file with mode: 0644]
tests/ui-cargo/cargo_common_metadata/pass/src/main.rs [new file with mode: 0644]
tests/ui-cargo/multiple_crate_versions/5041_allow_dev_build/Cargo.toml [new file with mode: 0644]
tests/ui-cargo/multiple_crate_versions/5041_allow_dev_build/src/main.rs [new file with mode: 0644]
tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml [new file with mode: 0644]
tests/ui-cargo/multiple_crate_versions/fail/src/main.rs [new file with mode: 0644]
tests/ui-cargo/multiple_crate_versions/fail/src/main.stderr [new file with mode: 0644]
tests/ui-cargo/multiple_crate_versions/pass/Cargo.toml [new file with mode: 0644]
tests/ui-cargo/multiple_crate_versions/pass/src/main.rs [new file with mode: 0644]
tests/ui-cargo/update-all-references.sh [new file with mode: 0755]
tests/ui-cargo/update-references.sh [new file with mode: 0755]
tests/ui-cargo/wildcard_dependencies/fail/Cargo.toml [new file with mode: 0644]
tests/ui-cargo/wildcard_dependencies/fail/src/main.rs [new file with mode: 0644]
tests/ui-cargo/wildcard_dependencies/fail/src/main.stderr [new file with mode: 0644]
tests/ui-cargo/wildcard_dependencies/pass/Cargo.toml [new file with mode: 0644]
tests/ui-cargo/wildcard_dependencies/pass/src/main.rs [new file with mode: 0644]
tests/ui/auxiliary/proc_macro_attr.rs [new file with mode: 0644]
tests/ui/cognitive_complexity.rs
tests/ui/cognitive_complexity_attr_used.rs
tests/ui/empty_line_after_outer_attribute.rs
tests/ui/empty_line_after_outer_attribute.stderr
tests/ui/future_not_send.stderr
tests/ui/match_wild_err_arm.stderr
tests/ui/match_wildcard_for_single_variants.fixed [new file with mode: 0644]
tests/ui/match_wildcard_for_single_variants.rs [new file with mode: 0644]
tests/ui/match_wildcard_for_single_variants.stderr [new file with mode: 0644]
tests/ui/new_without_default.rs
tests/ui/new_without_default.stderr
tests/ui/option_option.rs
tests/ui/option_option.stderr
tests/ui/or_fun_call.fixed
tests/ui/or_fun_call.rs
tests/ui/ptr_arg.rs
tests/ui/ptr_offset_with_cast.fixed
tests/ui/ptr_offset_with_cast.rs
tests/ui/ptr_offset_with_cast.stderr
tests/ui/reversed_empty_ranges_fixable.fixed
tests/ui/reversed_empty_ranges_fixable.rs
tests/ui/reversed_empty_ranges_fixable.stderr
tests/ui/reversed_empty_ranges_unfixable.rs
tests/ui/reversed_empty_ranges_unfixable.stderr
tests/ui/useless_conversion.stderr
tests/ui/useless_conversion_try.rs [new file with mode: 0644]
tests/ui/useless_conversion_try.stderr [new file with mode: 0644]

index 8edf0c23860aa0915c860b9d0a3a255bf0f864eb..5fa8009a8b42c8fad795d61114ad70deacdcd5fc 100644 (file)
@@ -49,7 +49,7 @@ jobs:
       run: cargo update
 
     - name: Cache cargo dir
-      uses: actions/cache@v1
+      uses: actions/cache@v2
       with:
         path: ~/.cargo
         key: ${{ runner.os }}-x86_64-unknown-linux-gnu-${{ hashFiles('Cargo.lock') }}
index 6675a1029bbc8ad2cf7b86adbc6fc65c672530c2..3958ba012467b5c9428ccd4427766b8b8871d87e 100644 (file)
@@ -94,7 +94,7 @@ jobs:
       run: cargo update
 
     - name: Cache cargo dir
-      uses: actions/cache@v1
+      uses: actions/cache@v2
       with:
         path: ~/.cargo
         key: ${{ runner.os }}-${{ matrix.host }}-${{ hashFiles('Cargo.lock') }}
@@ -190,7 +190,7 @@ jobs:
       run: cargo update
 
     - name: Cache cargo dir
-      uses: actions/cache@v1
+      uses: actions/cache@v2
       with:
         path: ~/.cargo
         key: ${{ runner.os }}-x86_64-unknown-linux-gnu-${{ hashFiles('Cargo.lock') }}
@@ -269,7 +269,7 @@ jobs:
       run: cargo update
 
     - name: Cache cargo dir
-      uses: actions/cache@v1
+      uses: actions/cache@v2
       with:
         path: ~/.cargo
         key: ${{ runner.os }}-x86_64-unknown-linux-gnu-${{ hashFiles('Cargo.lock') }}
@@ -312,7 +312,7 @@ jobs:
     name: bors test finished
     if: github.event.pusher.name == 'bors' && success()
     runs-on: ubuntu-latest
-    needs: [base, integration]
+    needs: [changelog, base, integration_build, integration]
 
     steps:
       - name: Mark the job as successful
@@ -322,7 +322,7 @@ jobs:
     name: bors test finished
     if: github.event.pusher.name == 'bors' && (failure() || cancelled())
     runs-on: ubuntu-latest
-    needs: [base, integration]
+    needs: [changelog, base, integration_build, integration]
 
     steps:
       - name: Mark the job as a failure
index 583c32ca9e032ed67f6cf333bcbcc8c107936d58..2ac9057199ff33ff2f451a31900efe89b207c3ab 100644 (file)
@@ -1439,6 +1439,7 @@ Released 2018-09-13
 [`match_same_arms`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_same_arms
 [`match_single_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_single_binding
 [`match_wild_err_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_wild_err_arm
+[`match_wildcard_for_single_variants`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_wildcard_for_single_variants
 [`maybe_infinite_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#maybe_infinite_iter
 [`mem_discriminant_non_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_discriminant_non_enum
 [`mem_forget`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_forget
index 50a5ee8bbf3c83bc0e583927d97ace463b3a3794..0f47ac98fd20ab25bcab738968d3558e70ab5f4d 100644 (file)
@@ -155,47 +155,77 @@ That's why the `else_if_without_else` example uses the `register_early_pass` fun
 
 ## Fixing build failures caused by Rust
 
-Clippy will sometimes fail to build from source because building it depends on unstable internal Rust features. Most of
-the times we have to adapt to the changes and only very rarely there's an actual bug in Rust. Fixing build failures
-caused by Rust updates, can be a good way to learn about Rust internals.
+Clippy currently gets built with `rustc` of the `rust-lang/rust` `master`
+branch. Most of the times we have to adapt to the changes and only very rarely
+there's an actual bug in Rust.
+
+If you decide to make Clippy work again with a Rust commit that breaks it, you
+have to sync the `rust-lang/rust-clippy` repository with the `subtree` copy of
+Clippy in the `rust-lang/rust` repository.
+
+For general information about `subtree`s in the Rust repository see [Rust's
+`CONTRIBUTING.md`][subtree].
+
+Here is a TL;DR version of the sync process (all of the following commands have
+to be run inside the `rust` directory):
+
+1. Clone the [`rust-lang/rust`] repository
+2. Sync the changes to the rust-copy of Clippy to your Clippy fork:
+    ```bash
+    # Make sure to change `your-github-name` to your github name in the following command
+    git subtree push -P src/tools/clippy git@github.com:your-github-name/rust-clippy sync-from-rust
+    ```
+    _Note:_ This will directly push to the remote repository. You can also push
+    to your local copy by replacing the remote address with `/path/to/rust-clippy`
+    directory.
+
+    _Note:_ Most of the time you have to create a merge commit in the
+    `rust-clippy` repo (this has to be done in the Clippy repo, not in the
+    rust-copy of Clippy):
+    ```bash
+    git fetch origin && git fetch upstream
+    git checkout sync-from-rust
+    git merge upstream/master
+    ```
+3. Open a PR to `rust-lang/rust-clippy` and wait for it to get merged (to
+   accelerate the process ping the `@rust-lang/clippy` team in your PR and/or
+   ~~annoy~~ ask them in the [Discord] channel.)
+4. Sync the `rust-lang/rust-clippy` master to the rust-copy of Clippy:
+    ```bash
+    git checkout -b sync-from-clippy
+    git subtree pull -P src/tools/clippy https://github.com/rust-lang/rust-clippy master
+    ```
+5. Open a PR to [`rust-lang/rust`]
+
+Also, you may want to define remotes, so you don't have to type out the remote
+addresses on every sync. You can do this with the following commands (these
+commands still have to be run inside the `rust` directory):
 
-In order to find out why Clippy does not work properly with a new Rust commit, you can use the [rust-toolstate commit
-history][toolstate_commit_history]. You will then have to look for the last commit that contains
-`test-pass -> build-fail` or `test-pass -> test-fail` for the `clippy-driver` component.
-[Here][toolstate_commit] is an example.
-
-The commit message contains a link to the PR. The PRs are usually small enough to discover the breaking API change and
-if they are bigger, they likely include some discussion that may help you to fix Clippy.
-
-To check if Clippy is available for a specific target platform, you can check
-the [rustup component history][rustup_component_history].
-
-If you decide to make Clippy work again with a Rust commit that breaks it,
-you probably want to install the latest Rust from master locally and run Clippy
-using that version of Rust.
-
-You can set up the master toolchain by running `./setup-toolchain.sh`. That script will install
-[rustup-toolchain-install-master][rtim] and master toolchain, then run `rustup override set master`.
-
-After fixing the build failure on this repository, we can submit a pull request
-to [`rust-lang/rust`] to fix the toolstate.
+```bash
+# Set clippy-upstream remote for pulls
+$ git remote add clippy-upstream https://github.com/rust-lang/rust-clippy
+# Make sure to not push to the upstream repo
+$ git remote set-url --push clippy-upstream DISABLED
+# Set clippy-origin remote to your fork for pushes
+$ git remote add clippy-origin git@github.com:your-github-name/rust-clippy
+# Set a local remote
+$ git remote add clippy-local /path/to/rust-clippy
+```
 
-To submit a pull request, you should follow these steps:
+You can then sync with the remote names from above, e.g.:
 
 ```bash
-# Assuming you already cloned the rust-lang/rust repo and you're in the correct directory
-git submodule update --remote src/tools/clippy
-cargo update -p clippy
-git add -u
-git commit -m "Update Clippy"
-./x.py test -i --stage 1 src/tools/clippy # This is optional and should succeed anyway
-# Open a PR in rust-lang/rust
+$ git subtree push -P src/tools/clippy clippy-local sync-from-rust
 ```
 
-[rustup_component_history]: https://rust-lang.github.io/rustup-components-history
-[toolstate_commit_history]: https://github.com/rust-lang-nursery/rust-toolstate/commits/master
-[toolstate_commit]: https://github.com/rust-lang-nursery/rust-toolstate/commit/aad74d8294e198a7cf8ac81a91aebb7f3bbcf727
-[rtim]: https://github.com/kennytm/rustup-toolchain-install-master
+_Note:_ The first time running `git subtree push` a cache has to be built. This
+involves going through the complete Clippy history once. For this you have to
+increase the stack limit though, which you can do with `ulimit -s 60000`. For
+this to work, you will need the fix of `git subtree` available
+[here][gitgitgadget-pr].
+
+[gitgitgadget-pr]: https://github.com/gitgitgadget/git/pull/493
+[subtree]: https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md#external-dependencies-subtree
 [`rust-lang/rust`]: https://github.com/rust-lang/rust
 
 ## Issue and PR triage
index 44b2a5383d2113f367c1a6262e351b236ea33e16..c0b2dac2f60ff0105364e0c371b54d6bfb244869 100644 (file)
 use crate::clippy_project_root;
-use std::fs::{File, OpenOptions};
-use std::io;
+use std::fs::{self, OpenOptions};
 use std::io::prelude::*;
-use std::io::ErrorKind;
-use std::path::Path;
+use std::io::{self, ErrorKind};
+use std::path::{Path, PathBuf};
+
+struct LintData<'a> {
+    pass: &'a str,
+    name: &'a str,
+    category: &'a str,
+    project_root: PathBuf,
+}
+
+trait Context {
+    fn context<C: AsRef<str>>(self, text: C) -> Self;
+}
+
+impl<T> Context for io::Result<T> {
+    fn context<C: AsRef<str>>(self, text: C) -> Self {
+        match self {
+            Ok(t) => Ok(t),
+            Err(e) => {
+                let message = format!("{}: {}", text.as_ref(), e);
+                Err(io::Error::new(ErrorKind::Other, message))
+            },
+        }
+    }
+}
 
-/// Creates files required to implement and test a new lint and runs `update_lints`.
+/// Creates the files required to implement and test a new lint and runs `update_lints`.
 ///
 /// # Errors
 ///
-/// This function errors, if the files couldn't be created
-pub fn create(pass: Option<&str>, lint_name: Option<&str>, category: Option<&str>) -> Result<(), io::Error> {
-    let pass = pass.expect("`pass` argument is validated by clap");
-    let lint_name = lint_name.expect("`name` argument is validated by clap");
-    let category = category.expect("`category` argument is validated by clap");
-
-    match open_files(lint_name) {
-        Ok((mut test_file, mut lint_file)) => {
-            let (pass_type, pass_lifetimes, pass_import, context_import) = match pass {
-                "early" => ("EarlyLintPass", "", "use rustc_ast::ast::*;", "EarlyContext"),
-                "late" => ("LateLintPass", "<'_, '_>", "use rustc_hir::*;", "LateContext"),
-                _ => {
-                    unreachable!("`pass_type` should only ever be `early` or `late`!");
-                },
-            };
-
-            let camel_case_name = to_camel_case(lint_name);
-
-            if let Err(e) = test_file.write_all(get_test_file_contents(lint_name).as_bytes()) {
-                return Err(io::Error::new(
-                    ErrorKind::Other,
-                    format!("Could not write to test file: {}", e),
-                ));
-            };
-
-            if let Err(e) = lint_file.write_all(
-                get_lint_file_contents(
-                    pass_type,
-                    pass_lifetimes,
-                    lint_name,
-                    &camel_case_name,
-                    category,
-                    pass_import,
-                    context_import,
-                )
-                .as_bytes(),
-            ) {
-                return Err(io::Error::new(
-                    ErrorKind::Other,
-                    format!("Could not write to lint file: {}", e),
-                ));
-            }
-            Ok(())
+/// This function errors out if the files couldn't be created or written to.
+pub fn create(pass: Option<&str>, lint_name: Option<&str>, category: Option<&str>) -> io::Result<()> {
+    let lint = LintData {
+        pass: pass.expect("`pass` argument is validated by clap"),
+        name: lint_name.expect("`name` argument is validated by clap"),
+        category: category.expect("`category` argument is validated by clap"),
+        project_root: clippy_project_root(),
+    };
+
+    create_lint(&lint).context("Unable to create lint implementation")?;
+    create_test(&lint).context("Unable to create a test for the new lint")
+}
+
+fn create_lint(lint: &LintData) -> io::Result<()> {
+    let (pass_type, pass_lifetimes, pass_import, context_import) = match lint.pass {
+        "early" => ("EarlyLintPass", "", "use rustc_ast::ast::*;", "EarlyContext"),
+        "late" => ("LateLintPass", "<'_, '_>", "use rustc_hir::*;", "LateContext"),
+        _ => {
+            unreachable!("`pass_type` should only ever be `early` or `late`!");
         },
-        Err(e) => Err(io::Error::new(
-            ErrorKind::Other,
-            format!("Unable to create lint: {}", e),
-        )),
-    }
+    };
+
+    let camel_case_name = to_camel_case(lint.name);
+    let lint_contents = get_lint_file_contents(
+        pass_type,
+        pass_lifetimes,
+        lint.name,
+        &camel_case_name,
+        lint.category,
+        pass_import,
+        context_import,
+    );
+
+    let lint_path = format!("clippy_lints/src/{}.rs", lint.name);
+    write_file(lint.project_root.join(&lint_path), lint_contents.as_bytes())
 }
 
-fn open_files(lint_name: &str) -> Result<(File, File), io::Error> {
-    let project_root = clippy_project_root();
+fn create_test(lint: &LintData) -> io::Result<()> {
+    fn create_project_layout<P: Into<PathBuf>>(lint_name: &str, location: P, case: &str, hint: &str) -> io::Result<()> {
+        let mut path = location.into().join(case);
+        fs::create_dir(&path)?;
+        write_file(path.join("Cargo.toml"), get_manifest_contents(lint_name, hint))?;
 
-    let test_file_path = project_root.join("tests").join("ui").join(format!("{}.rs", lint_name));
-    let lint_file_path = project_root
-        .join("clippy_lints")
-        .join("src")
-        .join(format!("{}.rs", lint_name));
+        path.push("src");
+        fs::create_dir(&path)?;
+        let header = format!("// compile-flags: --crate-name={}", lint_name);
+        write_file(path.join("main.rs"), get_test_file_contents(lint_name, Some(&header)))?;
 
-    if Path::new(&test_file_path).exists() {
-        return Err(io::Error::new(
-            ErrorKind::AlreadyExists,
-            format!("test file {:?} already exists", test_file_path),
-        ));
+        Ok(())
     }
-    if Path::new(&lint_file_path).exists() {
-        return Err(io::Error::new(
-            ErrorKind::AlreadyExists,
-            format!("lint file {:?} already exists", lint_file_path),
-        ));
+
+    if lint.category == "cargo" {
+        let relative_test_dir = format!("tests/ui-cargo/{}", lint.name);
+        let test_dir = lint.project_root.join(relative_test_dir);
+        fs::create_dir(&test_dir)?;
+
+        create_project_layout(lint.name, &test_dir, "fail", "Content that triggers the lint goes here")?;
+        create_project_layout(lint.name, &test_dir, "pass", "This file should not trigger the lint")
+    } else {
+        let test_path = format!("tests/ui/{}.rs", lint.name);
+        let test_contents = get_test_file_contents(lint.name, None);
+        write_file(lint.project_root.join(test_path), test_contents)
     }
+}
 
-    let test_file = OpenOptions::new().write(true).create_new(true).open(test_file_path)?;
-    let lint_file = OpenOptions::new().write(true).create_new(true).open(lint_file_path)?;
+fn write_file<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> {
+    fn inner(path: &Path, contents: &[u8]) -> io::Result<()> {
+        OpenOptions::new()
+            .write(true)
+            .create_new(true)
+            .open(path)?
+            .write_all(contents)
+    }
 
-    Ok((test_file, lint_file))
+    inner(path.as_ref(), contents.as_ref()).context(format!("writing to file: {}", path.as_ref().display()))
 }
 
 fn to_camel_case(name: &str) -> String {
@@ -100,8 +120,8 @@ fn to_camel_case(name: &str) -> String {
         .collect()
 }
 
-fn get_test_file_contents(lint_name: &str) -> String {
-    format!(
+fn get_test_file_contents(lint_name: &str, header_commands: Option<&str>) -> String {
+    let mut contents = format!(
         "#![warn(clippy::{})]
 
 fn main() {{
@@ -109,6 +129,26 @@ fn main() {{
 }}
 ",
         lint_name
+    );
+
+    if let Some(header) = header_commands {
+        contents = format!("{}\n{}", header, contents);
+    }
+
+    contents
+}
+
+fn get_manifest_contents(lint_name: &str, hint: &str) -> String {
+    format!(
+        r#"
+# {}
+
+[package]
+name = "{}"
+version = "0.1.0"
+publish = false
+"#,
+        hint, lint_name
     )
 }
 
index 1c0be727834625b54970c77b3209613bcf26c3e5..76baf27fb2dbfad8f5ab38416e93c4eb6faaf09d 100644 (file)
@@ -32,6 +32,8 @@ semver = "0.9.0"
 # NOTE: cargo requires serde feat in its url dep
 # see <https://github.com/rust-lang/rust/pull/63587#issuecomment-522343864>
 url = { version =  "2.1.0", features = ["serde"] }
+quote = "1"
+syn = { version = "1", features = ["full"] }
 
 [features]
 deny-warnings = []
index 64abc9fdc71742ed7a488a47b9f8b3fc9eaad20b..41f125d48398fd4aba769e031d601631215715b3 100644 (file)
     INLINE_ALWAYS,
     DEPRECATED_SEMVER,
     USELESS_ATTRIBUTE,
-    EMPTY_LINE_AFTER_OUTER_ATTR,
     UNKNOWN_CLIPPY_LINTS,
 ]);
 
@@ -480,36 +479,6 @@ fn check_attrs(cx: &LateContext<'_, '_>, span: Span, name: Name, attrs: &[Attrib
     }
 
     for attr in attrs {
-        let attr_item = if let AttrKind::Normal(ref attr) = attr.kind {
-            attr
-        } else {
-            continue;
-        };
-
-        if attr.style == AttrStyle::Outer {
-            if attr_item.args.inner_tokens().is_empty() || !is_present_in_source(cx, attr.span) {
-                return;
-            }
-
-            let begin_of_attr_to_item = Span::new(attr.span.lo(), span.lo(), span.ctxt());
-            let end_of_attr_to_item = Span::new(attr.span.hi(), span.lo(), span.ctxt());
-
-            if let Some(snippet) = snippet_opt(cx, end_of_attr_to_item) {
-                let lines = snippet.split('\n').collect::<Vec<_>>();
-                let lines = without_block_comments(lines);
-
-                if lines.iter().filter(|l| l.trim().is_empty()).count() > 2 {
-                    span_lint(
-                        cx,
-                        EMPTY_LINE_AFTER_OUTER_ATTR,
-                        begin_of_attr_to_item,
-                        "Found an empty line after an outer attribute. \
-                         Perhaps you forgot to add a `!` to make it an inner attribute?",
-                    );
-                }
-            }
-        }
-
         if let Some(values) = attr.meta_item_list() {
             if values.len() != 1 || !attr.check_name(sym!(inline)) {
                 continue;
@@ -551,15 +520,57 @@ fn is_word(nmi: &NestedMetaItem, expected: Symbol) -> bool {
     }
 }
 
-declare_lint_pass!(EarlyAttributes => [DEPRECATED_CFG_ATTR, MISMATCHED_TARGET_OS]);
+declare_lint_pass!(EarlyAttributes => [
+    DEPRECATED_CFG_ATTR,
+    MISMATCHED_TARGET_OS,
+    EMPTY_LINE_AFTER_OUTER_ATTR,
+]);
 
 impl EarlyLintPass for EarlyAttributes {
+    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &rustc_ast::ast::Item) {
+        check_empty_line_after_outer_attr(cx, item);
+    }
+
     fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
         check_deprecated_cfg_attr(cx, attr);
         check_mismatched_target_os(cx, attr);
     }
 }
 
+fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::ast::Item) {
+    for attr in &item.attrs {
+        let attr_item = if let AttrKind::Normal(ref attr) = attr.kind {
+            attr
+        } else {
+            return;
+        };
+
+        if attr.style == AttrStyle::Outer {
+            if attr_item.args.inner_tokens().is_empty() || !is_present_in_source(cx, attr.span) {
+                return;
+            }
+
+            let begin_of_attr_to_item = Span::new(attr.span.lo(), item.span.lo(), item.span.ctxt());
+            let end_of_attr_to_item = Span::new(attr.span.hi(), item.span.lo(), item.span.ctxt());
+
+            if let Some(snippet) = snippet_opt(cx, end_of_attr_to_item) {
+                let lines = snippet.split('\n').collect::<Vec<_>>();
+                let lines = without_block_comments(lines);
+
+                if lines.iter().filter(|l| l.trim().is_empty()).count() > 2 {
+                    span_lint(
+                        cx,
+                        EMPTY_LINE_AFTER_OUTER_ATTR,
+                        begin_of_attr_to_item,
+                        "Found an empty line after an outer attribute. \
+                        Perhaps you forgot to add a `!` to make it an inner attribute?",
+                    );
+                }
+            }
+        }
+    }
+}
+
 fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute) {
     if_chain! {
         // check cfg_attr
index 782da249808d0e71b3af319d9ef86cdb66236338..16b46423c8f013da962bb37aa0ab4d348cc37e3d 100644 (file)
@@ -23,6 +23,7 @@
     /// [package]
     /// name = "clippy"
     /// version = "0.0.212"
+    /// authors = ["Someone <someone@rust-lang.org>"]
     /// description = "A bunch of helpful lints to avoid common pitfalls in Rust"
     /// repository = "https://github.com/rust-lang/rust-clippy"
     /// readme = "README.md"
index 3a52b1d3fc20bdd2580e38e052fb0614f8313e03..4c604cd01075e66f783ce87beb7a7f062a82dd19 100644 (file)
@@ -77,7 +77,7 @@ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>)
                 let type_suffix = match lit_float_ty {
                     LitFloatType::Suffixed(FloatTy::F32) => Some("f32"),
                     LitFloatType::Suffixed(FloatTy::F64) => Some("f64"),
-                    _ => None
+                    LitFloatType::Unsuffixed => None
                 };
                 let (is_whole, mut float_str) = match fty {
                     FloatTy::F32 => {
index e0787ac0887e4fde54a7c8e77a6bfa80b42bb8a2..902f3d56c1e4f6fb9d286acc0d3484120341683b 100644 (file)
@@ -346,13 +346,8 @@ mod reexport {
 /// level (i.e `#![cfg_attr(...)]`) will still be expanded even when using a pre-expansion pass.
 ///
 /// Used in `./src/driver.rs`.
-pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, conf: &Conf) {
+pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore) {
     store.register_pre_expansion_pass(|| box write::Write::default());
-    store.register_pre_expansion_pass(|| box redundant_field_names::RedundantFieldNames);
-    let single_char_binding_names_threshold = conf.single_char_binding_names_threshold;
-    store.register_pre_expansion_pass(move || box non_expressive_names::NonExpressiveNames {
-        single_char_binding_names_threshold,
-    });
     store.register_pre_expansion_pass(|| box attrs::EarlyAttributes);
     store.register_pre_expansion_pass(|| box dbg_macro::DbgMacro);
 }
@@ -638,6 +633,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &matches::MATCH_OVERLAPPING_ARM,
         &matches::MATCH_REF_PATS,
         &matches::MATCH_SINGLE_BINDING,
+        &matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
         &matches::MATCH_WILD_ERR_ARM,
         &matches::REST_PAT_IN_FULLY_BOUND_STRUCTS,
         &matches::SINGLE_MATCH,
@@ -1065,6 +1061,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| box match_on_vec_items::MatchOnVecItems);
     store.register_early_pass(|| box manual_non_exhaustive::ManualNonExhaustive);
     store.register_late_pass(|| box manual_async_fn::ManualAsyncFn);
+    store.register_early_pass(|| box redundant_field_names::RedundantFieldNames);
+    let single_char_binding_names_threshold = conf.single_char_binding_names_threshold;
+    store.register_early_pass(move || box non_expressive_names::NonExpressiveNames {
+        single_char_binding_names_threshold,
+    });
 
     store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
         LintId::of(&arithmetic::FLOAT_ARITHMETIC),
@@ -1139,6 +1140,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&macro_use::MACRO_USE_IMPORTS),
         LintId::of(&match_on_vec_items::MATCH_ON_VEC_ITEMS),
         LintId::of(&matches::MATCH_BOOL),
+        LintId::of(&matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS),
+        LintId::of(&matches::MATCH_WILD_ERR_ARM),
         LintId::of(&matches::SINGLE_MATCH_ELSE),
         LintId::of(&methods::FILTER_MAP),
         LintId::of(&methods::FILTER_MAP_NEXT),
@@ -1283,7 +1286,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&matches::MATCH_OVERLAPPING_ARM),
         LintId::of(&matches::MATCH_REF_PATS),
         LintId::of(&matches::MATCH_SINGLE_BINDING),
-        LintId::of(&matches::MATCH_WILD_ERR_ARM),
         LintId::of(&matches::SINGLE_MATCH),
         LintId::of(&matches::WILDCARD_IN_OR_PATTERNS),
         LintId::of(&mem_discriminant::MEM_DISCRIMINANT_NON_ENUM),
@@ -1474,7 +1476,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH),
         LintId::of(&matches::MATCH_OVERLAPPING_ARM),
         LintId::of(&matches::MATCH_REF_PATS),
-        LintId::of(&matches::MATCH_WILD_ERR_ARM),
         LintId::of(&matches::SINGLE_MATCH),
         LintId::of(&mem_replace::MEM_REPLACE_OPTION_WITH_NONE),
         LintId::of(&mem_replace::MEM_REPLACE_WITH_DEFAULT),
index bbf14374a1f7f0c741fd2336e20900be5e386cf1..94380acfcfd4cb767c84df6bcc46a9fd78e36cef 100644 (file)
     /// **What it does:** Checks for arm which matches all errors with `Err(_)`
     /// and take drastic actions like `panic!`.
     ///
-    /// **Why is this bad?** It is generally a bad practice, just like
+    /// **Why is this bad?** It is generally a bad practice, similar to
     /// catching all exceptions in java with `catch(Exception)`
     ///
     /// **Known problems:** None.
     /// }
     /// ```
     pub MATCH_WILD_ERR_ARM,
-    style,
+    pedantic,
     "a `match` with `Err(_)` arm and take drastic actions"
 }
 
     /// # enum Foo { A(usize), B(usize) }
     /// # let x = Foo::B(1);
     /// match x {
-    ///     A => {},
+    ///     Foo::A(_) => {},
     ///     _ => {},
     /// }
     /// ```
     "a wildcard enum match arm using `_`"
 }
 
+declare_clippy_lint! {
+    /// **What it does:** Checks for wildcard enum matches for a single variant.
+    ///
+    /// **Why is this bad?** New enum variants added by library updates can be missed.
+    ///
+    /// **Known problems:** Suggested replacements may not use correct path to enum
+    /// if it's not present in the current scope.
+    ///
+    /// **Example:**
+    ///
+    /// ```rust
+    /// # enum Foo { A, B, C }
+    /// # let x = Foo::B;
+    /// match x {
+    ///     Foo::A => {},
+    ///     Foo::B => {},
+    ///     _ => {},
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// # enum Foo { A, B, C }
+    /// # let x = Foo::B;
+    /// match x {
+    ///     Foo::A => {},
+    ///     Foo::B => {},
+    ///     Foo::C => {},
+    /// }
+    /// ```
+    pub MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
+    pedantic,
+    "a wildcard enum match for a single variant"
+}
+
 declare_clippy_lint! {
     /// **What it does:** Checks for wildcard pattern used with others patterns in same match arm.
     ///
@@ -356,6 +390,7 @@ pub struct Matches {
     MATCH_WILD_ERR_ARM,
     MATCH_AS_REF,
     WILDCARD_ENUM_MATCH_ARM,
+    MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
     WILDCARD_IN_OR_PATTERNS,
     MATCH_SINGLE_BINDING,
     INFALLIBLE_DESTRUCTURING_MATCH,
@@ -676,7 +711,7 @@ fn check_wild_err_arm(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_>])
                                 arm.pat.span,
                                 &format!("`Err({})` matches all errors", &ident_bind_name),
                                 None,
-                                "match each error separately or use the error output",
+                                "match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable",
                             );
                         }
                     }
@@ -729,9 +764,21 @@ fn check_wild_enum_match(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_
                 if let QPath::Resolved(_, p) = path {
                     missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id()));
                 }
-            } else if let PatKind::TupleStruct(ref path, ..) = arm.pat.kind {
+            } else if let PatKind::TupleStruct(ref path, ref patterns, ..) = arm.pat.kind {
                 if let QPath::Resolved(_, p) = path {
-                    missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id()));
+                    // Some simple checks for exhaustive patterns.
+                    // There is a room for improvements to detect more cases,
+                    // but it can be more expensive to do so.
+                    let is_pattern_exhaustive = |pat: &&Pat<'_>| {
+                        if let PatKind::Wild | PatKind::Binding(.., None) = pat.kind {
+                            true
+                        } else {
+                            false
+                        }
+                    };
+                    if patterns.iter().all(is_pattern_exhaustive) {
+                        missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id()));
+                    }
                 }
             }
         }
@@ -766,6 +813,19 @@ fn check_wild_enum_match(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_
             }
         }
 
+        if suggestion.len() == 1 {
+            // No need to check for non-exhaustive enum as in that case len would be greater than 1
+            span_lint_and_sugg(
+                cx,
+                MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
+                wildcard_span,
+                message,
+                "try this",
+                suggestion[0].clone(),
+                Applicability::MaybeIncorrect,
+            )
+        };
+
         span_lint_and_sugg(
             cx,
             WILDCARD_ENUM_MATCH_ARM,
@@ -773,7 +833,7 @@ fn check_wild_enum_match(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_
             message,
             "try this",
             suggestion.join(" | "),
-            Applicability::MachineApplicable,
+            Applicability::MaybeIncorrect,
         )
     }
 }
index 810a226b50d2a1e454834042404d54efce7a7639..52ca962e7ef978e6ad80474ea52b30d94fbbb270 100644 (file)
@@ -1496,17 +1496,14 @@ fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, impl_item: &'tcx hir::
             if let ty::Opaque(def_id, _) = ret_ty.kind {
                 // one of the associated types must be Self
                 for predicate in cx.tcx.predicates_of(def_id).predicates {
-                    match predicate.0.kind() {
-                        ty::PredicateKind::Projection(poly_projection_predicate) => {
-                            let binder = poly_projection_predicate.ty();
-                            let associated_type = binder.skip_binder();
-
-                            // walk the associated type and check for Self
-                            if contains_self_ty(associated_type) {
-                                return;
-                            }
-                        },
-                        _ => {},
+                    if let ty::PredicateKind::Projection(poly_projection_predicate) = predicate.0.kind() {
+                        let binder = poly_projection_predicate.ty();
+                        let associated_type = binder.skip_binder();
+
+                        // walk the associated type and check for Self
+                        if contains_self_ty(associated_type) {
+                            return;
+                        }
                     }
                 }
             }
@@ -1617,6 +1614,21 @@ fn check_general_case<'a, 'tcx>(
         or_has_args: bool,
         span: Span,
     ) {
+        if let hir::ExprKind::MethodCall(ref path, _, ref args) = &arg.kind {
+            if path.ident.as_str() == "len" {
+                let ty = walk_ptrs_ty(cx.tables.expr_ty(&args[0]));
+
+                match ty.kind {
+                    ty::Slice(_) | ty::Array(_, _) => return,
+                    _ => (),
+                }
+
+                if match_type(cx, ty, &paths::VEC) {
+                    return;
+                }
+            }
+        }
+
         // (path, fn_has_argument, methods, suffix)
         let know_types: &[(&[_], _, &[_], _)] = &[
             (&paths::BTREEMAP_ENTRY, false, &["or_insert"], "with"),
index 62ee051624b488afe3411b463204516acf408169..552222eba2ee2164abbd717f8d92f1db0cfd1cb0 100644 (file)
@@ -379,7 +379,7 @@ fn check_pat(&mut self, cx: &EarlyContext<'_>, pat: &Pat) {
             let left_binding = match left {
                 BindingMode::ByRef(Mutability::Mut) => "ref mut ",
                 BindingMode::ByRef(Mutability::Not) => "ref ",
-                _ => "",
+                BindingMode::ByValue(..) => "",
             };
 
             if let PatKind::Wild = right.kind {
index 4301157e16440ccbea1f95732b57d81335514ca5..9cfc8d1913497882f4da26a404b768d7ceaf0b28 100644 (file)
@@ -113,7 +113,7 @@ fn check_fn(
                     return;
                 }
             },
-            _ => return,
+            FnKind::Closure(..) => return,
         }
 
         let mir = cx.tcx.optimized_mir(def_id);
index ed85d0315bd25d7d232de53e96c9ef97bf53ca0a..b6770804e180118736898314e6f94615d67a93ec 100644 (file)
@@ -1,11 +1,14 @@
 //! lint on multiple versions of a crate being used
 
 use crate::utils::{run_lints, span_lint};
+use rustc_hir::def_id::LOCAL_CRATE;
 use rustc_hir::{Crate, CRATE_HIR_ID};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::source_map::DUMMY_SP;
 
+use cargo_metadata::{DependencyKind, MetadataCommand, Node, Package, PackageId};
+use if_chain::if_chain;
 use itertools::Itertools;
 
 declare_clippy_lint! {
@@ -39,30 +42,61 @@ fn check_crate(&mut self, cx: &LateContext<'_, '_>, _: &Crate<'_>) {
             return;
         }
 
-        let metadata = if let Ok(metadata) = cargo_metadata::MetadataCommand::new().exec() {
+        let metadata = if let Ok(metadata) = MetadataCommand::new().exec() {
             metadata
         } else {
             span_lint(cx, MULTIPLE_CRATE_VERSIONS, DUMMY_SP, "could not read cargo metadata");
-
             return;
         };
 
+        let local_name = cx.tcx.crate_name(LOCAL_CRATE).as_str();
         let mut packages = metadata.packages;
         packages.sort_by(|a, b| a.name.cmp(&b.name));
 
-        for (name, group) in &packages.into_iter().group_by(|p| p.name.clone()) {
-            let group: Vec<cargo_metadata::Package> = group.collect();
+        if_chain! {
+            if let Some(resolve) = &metadata.resolve;
+            if let Some(local_id) = packages
+                .iter()
+                .find_map(|p| if p.name == *local_name { Some(&p.id) } else { None });
+            then {
+                for (name, group) in &packages.iter().group_by(|p| p.name.clone()) {
+                    let group: Vec<&Package> = group.collect();
+
+                    if group.len() <= 1 {
+                        continue;
+                    }
 
-            if group.len() > 1 {
-                let versions = group.into_iter().map(|p| p.version).join(", ");
+                    if group.iter().all(|p| is_normal_dep(&resolve.nodes, local_id, &p.id)) {
+                        let mut versions: Vec<_> = group.into_iter().map(|p| &p.version).collect();
+                        versions.sort();
+                        let versions = versions.iter().join(", ");
 
-                span_lint(
-                    cx,
-                    MULTIPLE_CRATE_VERSIONS,
-                    DUMMY_SP,
-                    &format!("multiple versions for dependency `{}`: {}", name, versions),
-                );
+                        span_lint(
+                            cx,
+                            MULTIPLE_CRATE_VERSIONS,
+                            DUMMY_SP,
+                            &format!("multiple versions for dependency `{}`: {}", name, versions),
+                        );
+                    }
+                }
             }
         }
     }
 }
+
+fn is_normal_dep(nodes: &[Node], local_id: &PackageId, dep_id: &PackageId) -> bool {
+    fn depends_on(node: &Node, dep_id: &PackageId) -> bool {
+        node.deps.iter().any(|dep| {
+            dep.pkg == *dep_id
+                && dep
+                    .dep_kinds
+                    .iter()
+                    .any(|info| matches!(info.kind, DependencyKind::Normal))
+        })
+    }
+
+    nodes
+        .iter()
+        .filter(|node| depends_on(node, dep_id))
+        .any(|node| node.id == *local_id || is_normal_dep(nodes, local_id, &node.id))
+}
index e5680482e5bfba5251f8151c5c869f83b2163670..67a1ac78a677749565e4fb5afd8968dfbef9809d 100644 (file)
@@ -6,7 +6,7 @@
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 
 declare_clippy_lint! {
-    /// **What it does:** Detects giving a mutable reference to a function that only
+    /// **What it does:** Detects passing a mutable reference to a function that only
     /// requires an immutable reference.
     ///
     /// **Why is this bad?** The immutable reference rules out all other references
index 1621c7f947c3314d96ffa3f0187985810acb633f..218b0d27f748620a0a320bd7e3ee6bcc0cde1a88 100644 (file)
@@ -86,7 +86,7 @@ fn check_fn(
                 }
             },
             FnKind::Method(..) => (),
-            _ => return,
+            FnKind::Closure(..) => return,
         }
 
         // Exclude non-inherent impls
index a599667b8d8a8a9f7214c0774f24b3658abe817d..e556e5d59c188d02031ace0d36f258953e7a3ab4 100644 (file)
@@ -1,27 +1,20 @@
 use crate::utils::paths;
 use crate::utils::sugg::DiagnosticBuilderExt;
-use crate::utils::{get_trait_def_id, implements_trait, return_ty, same_tys, span_lint_hir_and_then};
+use crate::utils::{get_trait_def_id, return_ty, same_tys, span_lint_hir_and_then};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
-use rustc_hir::def_id::DefId;
 use rustc_hir::HirIdSet;
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty::{self, Ty};
+use rustc_middle::ty::Ty;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::source_map::Span;
 
 declare_clippy_lint! {
     /// **What it does:** Checks for types with a `fn new() -> Self` method and no
     /// implementation of
     /// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html).
     ///
-    /// It detects both the case when a manual
-    /// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html)
-    /// implementation is required and also when it can be created with
-    /// `#[derive(Default)]`
-    ///
     /// **Why is this bad?** The user might expect to be able to use
     /// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html) as the
     /// type can be constructed without arguments.
     /// }
     /// ```
     ///
-    /// Instead, use:
+    /// To fix the lint, and a `Default` implementation that delegates to `new`:
     ///
     /// ```ignore
     /// struct Foo(Bar);
     ///
     /// impl Default for Foo {
     ///     fn default() -> Self {
-    ///         Foo(Bar::new())
+    ///         Foo::new()
     ///     }
     /// }
     /// ```
-    ///
-    /// Or, if
-    /// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html)
-    /// can be derived by `#[derive(Default)]`:
-    ///
-    /// ```rust
-    /// struct Foo;
-    ///
-    /// impl Foo {
-    ///     fn new() -> Self {
-    ///         Foo
-    ///     }
-    /// }
-    /// ```
-    ///
-    /// Instead, use:
-    ///
-    /// ```rust
-    /// #[derive(Default)]
-    /// struct Foo;
-    ///
-    /// impl Foo {
-    ///     fn new() -> Self {
-    ///         Foo
-    ///     }
-    /// }
-    /// ```
-    ///
-    /// You can also have `new()` call `Default::default()`.
     pub NEW_WITHOUT_DEFAULT,
     style,
     "`fn new() -> Self` method without `Default` implementation"
@@ -126,8 +90,8 @@ fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::Item<'_>)
                             return;
                         }
                         if sig.decl.inputs.is_empty() && name == sym!(new) && cx.access_levels.is_reachable(id) {
-                            let self_did = cx.tcx.hir().local_def_id(cx.tcx.hir().get_parent_item(id));
-                            let self_ty = cx.tcx.type_of(self_did);
+                            let self_def_id = cx.tcx.hir().local_def_id(cx.tcx.hir().get_parent_item(id));
+                            let self_ty = cx.tcx.type_of(self_def_id);
                             if_chain! {
                                 if same_tys(cx, self_ty, return_ty(cx, id));
                                 if let Some(default_trait_id) = get_trait_def_id(cx, &paths::DEFAULT_TRAIT);
@@ -148,56 +112,35 @@ fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::Item<'_>)
                                     // generics
                                     if_chain! {
                                         if let Some(ref impling_types) = self.impling_types;
-                                        if let Some(self_def) = cx.tcx.type_of(self_did).ty_adt_def();
-                                        if let Some(self_def_id) = self_def.did.as_local();
+                                        if let Some(self_def) = cx.tcx.type_of(self_def_id).ty_adt_def();
+                                        if let Some(self_local_did) = self_def.did.as_local();
                                         then {
-                                            let self_id = cx.tcx.hir().local_def_id_to_hir_id(self_def_id);
+                                            let self_id = cx.tcx.hir().local_def_id_to_hir_id(self_local_did);
                                             if impling_types.contains(&self_id) {
                                                 return;
                                             }
                                         }
                                     }
 
-                                    if let Some(sp) = can_derive_default(self_ty, cx, default_trait_id) {
-                                        span_lint_hir_and_then(
-                                            cx,
-                                            NEW_WITHOUT_DEFAULT,
-                                            id,
-                                            impl_item.span,
-                                            &format!(
-                                                "you should consider deriving a `Default` implementation for `{}`",
-                                                self_ty
-                                            ),
-                                            |diag| {
-                                                diag.suggest_item_with_attr(
-                                                    cx,
-                                                    sp,
-                                                    "try this",
-                                                    "#[derive(Default)]",
-                                                    Applicability::MaybeIncorrect,
-                                                );
-                                            });
-                                    } else {
-                                        span_lint_hir_and_then(
-                                            cx,
-                                            NEW_WITHOUT_DEFAULT,
-                                            id,
-                                            impl_item.span,
-                                            &format!(
-                                                "you should consider adding a `Default` implementation for `{}`",
-                                                self_ty
-                                            ),
-                                            |diag| {
-                                                diag.suggest_prepend_item(
-                                                    cx,
-                                                    item.span,
-                                                    "try this",
-                                                    &create_new_without_default_suggest_msg(self_ty),
-                                                    Applicability::MaybeIncorrect,
-                                                );
-                                            },
-                                        );
-                                    }
+                                    span_lint_hir_and_then(
+                                        cx,
+                                        NEW_WITHOUT_DEFAULT,
+                                        id,
+                                        impl_item.span,
+                                        &format!(
+                                            "you should consider adding a `Default` implementation for `{}`",
+                                            self_ty
+                                        ),
+                                        |diag| {
+                                            diag.suggest_prepend_item(
+                                                cx,
+                                                item.span,
+                                                "try this",
+                                                &create_new_without_default_suggest_msg(self_ty),
+                                                Applicability::MaybeIncorrect,
+                                            );
+                                        },
+                                    );
                                 }
                             }
                         }
@@ -217,18 +160,3 @@ fn default() -> Self {{
     }}
 }}", ty)
 }
-
-fn can_derive_default<'t, 'c>(ty: Ty<'t>, cx: &LateContext<'c, 't>, default_trait_id: DefId) -> Option<Span> {
-    match ty.kind {
-        ty::Adt(adt_def, substs) if adt_def.is_struct() => {
-            for field in adt_def.all_fields() {
-                let f_ty = field.ty(cx.tcx, substs);
-                if !implements_trait(cx, f_ty, default_trait_id, &[]) {
-                    return None;
-                }
-            }
-            Some(cx.tcx.def_span(adt_def.did))
-        },
-        _ => None,
-    }
-}
index 2b51b73207585fe978725d17d1ceb31cfc7ed535..5f14fe97afefad36255744823e9fe5799ea680d5 100644 (file)
@@ -5,6 +5,7 @@
 use rustc_ast::attr;
 use rustc_ast::visit::{walk_block, walk_expr, walk_pat, Visitor};
 use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_middle::lint::in_external_macro;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::source_map::Span;
 use rustc_span::symbol::{Ident, SymbolStr};
@@ -131,7 +132,11 @@ fn check_single_char_names(&self) {
 impl<'a, 'tcx, 'b> Visitor<'tcx> for SimilarNamesNameVisitor<'a, 'tcx, 'b> {
     fn visit_pat(&mut self, pat: &'tcx Pat) {
         match pat.kind {
-            PatKind::Ident(_, ident, _) => self.check_ident(ident),
+            PatKind::Ident(_, ident, _) => {
+                if !pat.span.from_expansion() {
+                    self.check_ident(ident);
+                }
+            },
             PatKind::Struct(_, ref fields, _) => {
                 for field in fields {
                     if !field.is_shorthand {
@@ -354,12 +359,20 @@ fn visit_mac(&mut self, _mac: &MacCall) {
 
 impl EarlyLintPass for NonExpressiveNames {
     fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
+        if in_external_macro(cx.sess, item.span) {
+            return;
+        }
+
         if let ItemKind::Fn(_, ref sig, _, Some(ref blk)) = item.kind {
             do_check(self, cx, &item.attrs, &sig.decl, blk);
         }
     }
 
     fn check_impl_item(&mut self, cx: &EarlyContext<'_>, item: &AssocItem) {
+        if in_external_macro(cx.sess, item.span) {
+            return;
+        }
+
         if let AssocItemKind::Fn(_, ref sig, _, Some(ref blk)) = item.kind {
             do_check(self, cx, &item.attrs, &sig.decl, blk);
         }
index 2cdf96714195abbbec79deccf117807c16714113..4eac571f96620597bc37adea99c16cf7b8a74a4a 100644 (file)
@@ -2,7 +2,7 @@
 
 use crate::utils::ptr::get_spans;
 use crate::utils::{
-    is_type_diagnostic_item, match_qpath, match_type, paths, snippet_opt, span_lint, span_lint_and_sugg,
+    is_allowed, is_type_diagnostic_item, match_qpath, match_type, paths, snippet_opt, span_lint, span_lint_and_sugg,
     span_lint_and_then, walk_ptrs_hir_ty,
 };
 use if_chain::if_chain;
@@ -150,8 +150,16 @@ fn check_fn(cx: &LateContext<'_, '_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_
     let fn_def_id = cx.tcx.hir().local_def_id(fn_id);
     let sig = cx.tcx.fn_sig(fn_def_id);
     let fn_ty = sig.skip_binder();
+    let body = opt_body_id.map(|id| cx.tcx.hir().body(id));
 
     for (idx, (arg, ty)) in decl.inputs.iter().zip(fn_ty.inputs()).enumerate() {
+        // Honor the allow attribute on parameters. See issue 5644.
+        if let Some(body) = &body {
+            if is_allowed(cx, PTR_ARG, body.params[idx].hir_id) {
+                continue;
+            }
+        }
+
         if let ty::Ref(_, ty, Mutability::Not) = ty.kind {
             if is_type_diagnostic_item(cx, ty, sym!(vec_type)) {
                 let mut ty_snippet = None;
index 83c6faac04149f344ea4916f50e05d437f7411e4..1eb26d97ed4d275faf644f0c029e2a841859285e 100644 (file)
@@ -241,14 +241,14 @@ fn check_inclusive_range_minus_one(cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
 }
 
 fn check_reversed_empty_range(cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
-    fn inside_indexing_expr(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool {
-        matches!(
-            get_parent_expr(cx, expr),
-            Some(Expr {
+    fn inside_indexing_expr<'a>(cx: &'a LateContext<'_, '_>, expr: &Expr<'_>) -> Option<&'a Expr<'a>> {
+        match get_parent_expr(cx, expr) {
+            parent_expr @ Some(Expr {
                 kind: ExprKind::Index(..),
                 ..
-            })
-        )
+            }) => parent_expr,
+            _ => None,
+        }
     }
 
     fn is_empty_range(limits: RangeLimits, ordering: Ordering) -> bool {
@@ -267,18 +267,32 @@ fn is_empty_range(limits: RangeLimits, ordering: Ordering) -> bool {
         if let Some(ordering) = Constant::partial_cmp(cx.tcx, ty, &start_idx, &end_idx);
         if is_empty_range(limits, ordering);
         then {
-            if inside_indexing_expr(cx, expr) {
+            if let Some(parent_expr) = inside_indexing_expr(cx, expr) {
                 let (reason, outcome) = if ordering == Ordering::Equal {
                     ("empty", "always yield an empty slice")
                 } else {
                     ("reversed", "panic at run-time")
                 };
 
-                span_lint(
+                span_lint_and_then(
                     cx,
                     REVERSED_EMPTY_RANGES,
                     expr.span,
                     &format!("this range is {} and using it to index a slice will {}", reason, outcome),
+                    |diag| {
+                        if_chain! {
+                            if ordering == Ordering::Equal;
+                            if let ty::Slice(slice_ty) = cx.tables.expr_ty(parent_expr).kind;
+                            then {
+                                diag.span_suggestion(
+                                    parent_expr.span,
+                                    "if you want an empty slice, use",
+                                    format!("[] as &[{}]", slice_ty),
+                                    Applicability::MaybeIncorrect
+                                );
+                            }
+                        }
+                    }
                 );
             } else {
                 span_lint_and_then(
index b12c3c344ef4cbfc1fd61078157edf224d3e1bbb..2a81170e49e75026ce6c328adbb7eaa144ac30e6 100644 (file)
@@ -2,6 +2,7 @@
 use rustc_ast::ast::{Expr, ExprKind};
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_middle::lint::in_external_macro;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 
 declare_clippy_lint! {
@@ -36,6 +37,9 @@
 
 impl EarlyLintPass for RedundantFieldNames {
     fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
+        if in_external_macro(cx.sess, expr.span) {
+            return;
+        }
         if let ExprKind::Struct(_, ref fields, _) = expr.kind {
             for field in fields {
                 if field.is_shorthand {
index 2c101220c5d6897f943b0799d615993283d2194e..8e0cb94317affc2ce1bf3c0e81d3407fda751bf9 100644 (file)
@@ -161,7 +161,7 @@ fn check_fn(
                 }
             },
             FnKind::Method(..) => (),
-            _ => return,
+            FnKind::Closure(..) => return,
         }
 
         // Exclude non-inherent impls
index 95921518986bcb464581a61ca20b7680e9094ede..7fa97b246991219c204d6087949c3ecb9cc85c27 100644 (file)
@@ -1,14 +1,17 @@
 use crate::utils::{
-    match_def_path, match_trait_method, paths, same_tys, snippet, snippet_with_macro_callsite, span_lint_and_sugg,
+    is_type_diagnostic_item, match_def_path, match_trait_method, paths, same_tys, snippet, snippet_with_macro_callsite,
+    span_lint_and_help, span_lint_and_sugg,
 };
+use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, HirId, MatchSource};
 use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 
 declare_clippy_lint! {
-    /// **What it does:** Checks for `Into`/`From`/`IntoIter` calls that useless converts
-    /// to the same type as caller.
+    /// **What it does:** Checks for `Into`, `TryInto`, `From`, `TryFrom`,`IntoIter` calls
+    /// that useless converts to the same type as caller.
     ///
     /// **Why is this bad?** Redundant code.
     ///
@@ -26,7 +29,7 @@
     /// ```
     pub USELESS_CONVERSION,
     complexity,
-    "calls to `Into`/`From`/`IntoIter` that performs useless conversions to the same type"
+    "calls to `Into`, `TryInto`, `From`, `TryFrom`, `IntoIter` that performs useless conversions to the same type"
 }
 
 #[derive(Default)]
@@ -36,6 +39,7 @@ pub struct UselessConversion {
 
 impl_lint_pass!(UselessConversion => [USELESS_CONVERSION]);
 
+#[allow(clippy::too_many_lines)]
 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion {
     fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) {
         if e.span.from_expansion() {
@@ -63,12 +67,11 @@ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) {
                     let b = cx.tables.expr_ty(&args[0]);
                     if same_tys(cx, a, b) {
                         let sugg = snippet_with_macro_callsite(cx, args[0].span, "<expr>").to_string();
-
                         span_lint_and_sugg(
                             cx,
                             USELESS_CONVERSION,
                             e.span,
-                            "useless conversion",
+                            "useless conversion to the same type",
                             "consider removing `.into()`",
                             sugg,
                             Applicability::MachineApplicable, // snippet
@@ -84,22 +87,70 @@ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) {
                             cx,
                             USELESS_CONVERSION,
                             e.span,
-                            "useless conversion",
+                            "useless conversion to the same type",
                             "consider removing `.into_iter()`",
                             sugg,
                             Applicability::MachineApplicable, // snippet
                         );
                     }
                 }
+                if match_trait_method(cx, e, &paths::TRY_INTO_TRAIT) && &*name.ident.as_str() == "try_into" {
+                    if_chain! {
+                        let a = cx.tables.expr_ty(e);
+                        let b = cx.tables.expr_ty(&args[0]);
+                        if is_type_diagnostic_item(cx, a, sym!(result_type));
+                        if let ty::Adt(_, substs) = a.kind;
+                        if let Some(a_type) = substs.types().next();
+                        if same_tys(cx, a_type, b);
+
+                        then {
+                            span_lint_and_help(
+                                cx,
+                                USELESS_CONVERSION,
+                                e.span,
+                                "useless conversion to the same type",
+                                None,
+                                "consider removing `.try_into()`",
+                            );
+                        }
+                    }
+                }
             },
 
             ExprKind::Call(ref path, ref args) => {
-                if let ExprKind::Path(ref qpath) = path.kind {
-                    if let Some(def_id) = cx.tables.qpath_res(qpath, path.hir_id).opt_def_id() {
-                        if match_def_path(cx, def_id, &paths::FROM_FROM) {
-                            let a = cx.tables.expr_ty(e);
-                            let b = cx.tables.expr_ty(&args[0]);
-                            if same_tys(cx, a, b) {
+                if_chain! {
+                    if args.len() == 1;
+                    if let ExprKind::Path(ref qpath) = path.kind;
+                    if let Some(def_id) = cx.tables.qpath_res(qpath, path.hir_id).opt_def_id();
+                    let a = cx.tables.expr_ty(e);
+                    let b = cx.tables.expr_ty(&args[0]);
+
+                    then {
+                        if_chain! {
+                            if match_def_path(cx, def_id, &paths::TRY_FROM);
+                            if is_type_diagnostic_item(cx, a, sym!(result_type));
+                            if let ty::Adt(_, substs) = a.kind;
+                            if let Some(a_type) = substs.types().next();
+                            if same_tys(cx, a_type, b);
+
+                            then {
+                                let hint = format!("consider removing `{}()`", snippet(cx, path.span, "TryFrom::try_from"));
+                                span_lint_and_help(
+                                    cx,
+                                    USELESS_CONVERSION,
+                                    e.span,
+                                    "useless conversion to the same type",
+                                    None,
+                                    &hint,
+                                );
+                            }
+                        }
+
+                        if_chain! {
+                            if match_def_path(cx, def_id, &paths::FROM_FROM);
+                            if same_tys(cx, a, b);
+
+                            then {
                                 let sugg = snippet(cx, args[0].span.source_callsite(), "<expr>").into_owned();
                                 let sugg_msg =
                                     format!("consider removing `{}()`", snippet(cx, path.span, "From::from"));
@@ -107,7 +158,7 @@ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) {
                                     cx,
                                     USELESS_CONVERSION,
                                     e.span,
-                                    "useless conversion",
+                                    "useless conversion to the same type",
                                     &sugg_msg,
                                     sugg,
                                     Applicability::MachineApplicable, // snippet
index 57b9eafd14dbd4a8760998f0a1662a486765b2b7..9e8e0ff30ec6b761a2d29566716163f6ca4ac014 100644 (file)
@@ -120,10 +120,12 @@ fn $config() -> $Ty {
         "GPLv2", "GPLv3",
         "GitHub", "GitLab",
         "IPv4", "IPv6",
-        "JavaScript",
+        "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript",
         "NaN", "NaNs",
         "OAuth",
-        "OpenGL", "OpenSSH", "OpenSSL", "OpenStreetMap",
+        "OCaml",
+        "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap",
+        "TensorFlow",
         "TrueType",
         "iOS", "macOS",
         "TeX", "LaTeX", "BibTeX", "BibLaTeX",
index 748c11fac64ff523a01ad0c454204a4e2134b159..9b672b9ec225b989085dfbdd65917b0c99139e4e 100644 (file)
@@ -289,21 +289,21 @@ fn print_expr(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, indent: usize) {
             println!("{}operands:", ind);
             for op in asm.operands {
                 match op {
-                    hir::InlineAsmOperand::In { expr, .. } => print_expr(cx, expr, indent + 1),
+                    hir::InlineAsmOperand::In { expr, .. }
+                    | hir::InlineAsmOperand::InOut { expr, .. }
+                    | hir::InlineAsmOperand::Const { expr }
+                    | hir::InlineAsmOperand::Sym { expr } => print_expr(cx, expr, indent + 1),
                     hir::InlineAsmOperand::Out { expr, .. } => {
                         if let Some(expr) = expr {
                             print_expr(cx, expr, indent + 1);
                         }
                     },
-                    hir::InlineAsmOperand::InOut { expr, .. } => print_expr(cx, expr, indent + 1),
                     hir::InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => {
                         print_expr(cx, in_expr, indent + 1);
                         if let Some(out_expr) = out_expr {
                             print_expr(cx, out_expr, indent + 1);
                         }
                     },
-                    hir::InlineAsmOperand::Const { expr } => print_expr(cx, expr, indent + 1),
-                    hir::InlineAsmOperand::Sym { expr } => print_expr(cx, expr, indent + 1),
                 }
             }
         },
index f22473275c466ea3bb020f3dac3c123150e9ac6b..6c1488664bf0086d9faf2b8a5e049aef450a57c2 100644 (file)
@@ -358,7 +358,7 @@ pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'_, 'tcx>, hir_id: HirId) -> O
 pub fn has_drop<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool {
     match ty.ty_adt_def() {
         Some(def) => def.has_dtor(cx.tcx),
-        _ => false,
+        None => false,
     }
 }
 
index b3ad2ad9d9987e73205b42299003e146a7c80bd6..779da7e6bf23ce01c423991f8a0b490022a77876 100644 (file)
 pub const TO_STRING: [&str; 3] = ["alloc", "string", "ToString"];
 pub const TO_STRING_METHOD: [&str; 4] = ["alloc", "string", "ToString", "to_string"];
 pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"];
+pub const TRY_FROM: [&str; 4] = ["core", "convert", "TryFrom", "try_from"];
 pub const TRY_FROM_ERROR: [&str; 4] = ["std", "ops", "Try", "from_error"];
 pub const TRY_INTO_RESULT: [&str; 4] = ["std", "ops", "Try", "into_result"];
+pub const TRY_INTO_TRAIT: [&str; 3] = ["core", "convert", "TryInto"];
 pub const VEC: [&str; 3] = ["alloc", "vec", "Vec"];
 pub const VEC_AS_MUT_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_mut_slice"];
 pub const VEC_AS_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_slice"];
index 4ebe2e2852fb4445492161c8bfd693db4dc4fed1..73758b7eeb7eb28e27651a9ec36b9cc333e622d4 100644 (file)
@@ -530,7 +530,7 @@ fn suggest_item_with_attr<D: Display + ?Sized>(
 
     /// Suggest to add an item before another.
     ///
-    /// The item should not be indented (expect for inner indentation).
+    /// The item should not be indented (except for inner indentation).
     ///
     /// # Example
     ///
index 9ad1315c17521c146f2d396ae38f5a9f7b1d8017..8092be277cca01af4bfbf8905cee24e1bfc567c5 100644 (file)
@@ -42,8 +42,10 @@ case), and we don't need type information so it will have an early pass type
 `cargo dev new_lint --name=foo_functions --pass=early --category=pedantic`
 (category will default to nursery if not provided). This command will create
 two files: `tests/ui/foo_functions.rs` and `clippy_lints/src/foo_functions.rs`,
-as well as run `cargo dev update_lints` to register the new lint. Next, we'll
-open up these files and add our lint!
+as well as run `cargo dev update_lints` to register the new lint. For cargo lints,
+two project hierarchies (fail/pass) will be created by default under `tests/ui-cargo`.
+
+Next, we'll open up these files and add our lint!
 
 ## Testing
 
@@ -105,6 +107,24 @@ our lint, we need to commit the generated `.stderr` files, too. In general, you
 should only commit files changed by `tests/ui/update-all-references.sh` for the
 specific lint you are creating/editing.
 
+### Cargo lints
+
+For cargo lints, the process of testing differs in that we are interested in
+the `Cargo.toml` manifest file. We also need a minimal crate associated
+with that manifest.
+
+If our new lint is named e.g. `foo_categories`, after running `cargo dev new_lint` 
+we will find by default two new crates, each with its manifest file:
+
+* `tests/ui-cargo/foo_categories/fail/Cargo.toml`: this file should cause the new lint to raise an error.
+* `tests/ui-cargo/foo_categories/pass/Cargo.toml`: this file should not trigger the lint.
+
+If you need more cases, you can copy one of those crates (under `foo_categories`) and rename it.
+
+The process of generating the `.stderr` file is the same, and prepending the `TESTNAME`
+variable to `cargo uitest` works too, but the script to update the references
+is in another path: `tests/ui-cargo/update-all-references.sh`.
+
 ## Rustfix tests
 
 If the lint you are working on is making use of structured suggestions, the
@@ -445,6 +465,7 @@ Here are some pointers to things you are likely going to need for every lint:
 * [`from_expansion`][from_expansion] and [`in_external_macro`][in_external_macro]
 * [`Span`][span]
 * [`Applicability`][applicability]
+* [Common tools for writing lints](common_tools_writing_lints.md) helps with common operations
 * [The rustc-dev-guide][rustc-dev-guide] explains a lot of internal compiler concepts
 * [The nightly rustc docs][nightly_docs] which has been linked to throughout
   this guide
diff --git a/doc/common_tools_writing_lints.md b/doc/common_tools_writing_lints.md
new file mode 100644 (file)
index 0000000..ed33b37
--- /dev/null
@@ -0,0 +1,152 @@
+# Common tools for writing lints
+
+You may need following tooltips to catch up with common operations.
+
+- [Common tools for writing lints](#common-tools-for-writing-lints)
+  - [Retrieving the type of an expression](#retrieving-the-type-of-an-expression)
+  - [Checking if a type implements a specific trait](#checking-if-a-type-implements-a-specific-trait)
+  - [Dealing with macros](#dealing-with-macros)
+
+Useful Rustc dev guide links:
+- [Stages of compilation](https://rustc-dev-guide.rust-lang.org/compiler-src.html#the-main-stages-of-compilation)
+- [Type checking](https://rustc-dev-guide.rust-lang.org/type-checking.html)
+- [Ty module](https://rustc-dev-guide.rust-lang.org/ty.html)
+
+# Retrieving the type of an expression
+
+Sometimes you may want to retrieve the type `Ty` of an expression `Expr`, for example to answer following questions:
+
+- which type does this expression correspond to (using its [`TyKind`][TyKind])?
+- is it a sized type? 
+- is it a primitive type?
+- does it implement a trait?
+
+This operation is performed using the [`expr_ty()`][expr_ty] method from the [`TypeckTables`][TypeckTables] struct, 
+that gives you access to the underlying structure [`TyS`][TyS].
+
+Example of use:
+```rust
+impl LateLintPass<'_, '_> for MyStructLint {
+    fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
+        // Get type of `expr`
+        let ty = cx.tables.expr_ty(expr);
+        // Match its kind to enter its type
+        match ty.kind {
+            ty::Adt(adt_def, _) if adt_def.is_struct() => println!("Our `expr` is a struct!"),
+            _ => ()
+        }
+    }
+}
+```
+
+Similarly in [`TypeckTables`][TypeckTables] methods, you have the [`pat_ty()`][pat_ty] method 
+to retrieve a type from a pattern.
+
+Two noticeable items here:
+- `cx` is the lint context [`LateContext`][LateContext]. 
+  The two most useful data structures in this context are `tcx` and `tables`, 
+  allowing us to jump to type definitions and other compilation stages such as HIR.
+- `tables` is [`TypeckTables`][TypeckTables] and is created by type checking step, 
+  it includes useful information such as types of expressions, ways to resolve methods and so on.
+
+# Checking if a type implements a specific trait
+
+There are two ways to do this, depending if the target trait is part of lang items.
+
+```rust
+use crate::utils::{implements_trait, match_trait_method, paths};
+
+impl LateLintPass<'_, '_> for MyStructLint {
+    fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
+        // 1. Using expression and Clippy's convenient method
+        // we use `match_trait_method` function from Clippy's toolbox
+        if match_trait_method(cx, expr, &paths::INTO) {
+            // `expr` implements `Into` trait
+        }
+
+        // 2. Using type context `TyCtxt`
+        let ty = cx.tables.expr_ty(expr);
+        if cx.tcx.lang_items()
+            // we are looking for the `DefId` of `Drop` trait in lang items
+            .drop_trait()
+            // then we use it with our type `ty` by calling `implements_trait` from Clippy's utils
+            .map_or(false, |id| implements_trait(cx, ty, id, &[])) {
+                // `expr` implements `Drop` trait
+            }
+    }
+}
+```
+
+> Prefer using lang items, if the target trait is available there.
+
+A list of defined paths for Clippy can be found in [paths.rs][paths]
+
+We access lang items through the type context `tcx`. `tcx` is of type [`TyCtxt`][TyCtxt] and is defined in the `rustc_middle` crate.
+
+# Dealing with macros
+
+There are several helpers in Clippy's utils to deal with macros:
+
+- `in_macro()`: detect if the given span is expanded by a macro
+
+You may want to use this for example to not start linting in any macro.
+
+```rust
+macro_rules! foo {
+    ($param:expr) => {
+        match $param {
+            "bar" => println!("whatever"),
+            _ => ()
+        }
+    };
+}
+
+foo!("bar");
+
+// if we lint the `match` of `foo` call and test its span
+assert_eq!(in_macro(match_span), true);
+```
+
+- `in_external_macro()`: detect if the given span is from an external macro, defined in a foreign crate
+
+You may want to use it for example to not start linting in macros from other crates
+
+```rust
+#[macro_use]
+extern crate a_crate_with_macros;
+
+// `foo` is defined in `a_crate_with_macros`
+foo!("bar");
+
+// if we lint the `match` of `foo` call and test its span
+assert_eq!(in_external_macro(cx.sess(), match_span), true);
+```
+
+- `differing_macro_contexts()`: returns true if the two given spans are not from the same context
+
+```rust
+macro_rules! m {
+    ($a:expr, $b:expr) => {
+        if $a.is_some() {
+            $b;
+        }
+    }
+}
+
+let x: Option<u32> = Some(42);
+m!(x, x.unwrap());
+
+// These spans are not from the same context
+// x.is_some() is from inside the macro
+// x.unwrap() is from outside the macro
+assert_eq!(differing_macro_contexts(x_is_some_span, x_unwrap_span), true);
+```
+
+[TyS]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyS.html
+[TyKind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.TyKind.html
+[TypeckTables]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckTables.html
+[expr_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckTables.html#method.expr_ty
+[LateContext]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LateContext.html
+[TyCtxt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html
+[pat_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TypeckTables.html#method.pat_ty
+[paths]: ../clippy_lints/src/utils/paths.rs
diff --git a/rustc_tools_util/LICENSE-APACHE b/rustc_tools_util/LICENSE-APACHE
new file mode 120000 (symlink)
index 0000000..965b606
--- /dev/null
@@ -0,0 +1 @@
+../LICENSE-APACHE
\ No newline at end of file
diff --git a/rustc_tools_util/LICENSE-MIT b/rustc_tools_util/LICENSE-MIT
new file mode 120000 (symlink)
index 0000000..76219eb
--- /dev/null
@@ -0,0 +1 @@
+../LICENSE-MIT
\ No newline at end of file
index 6038ed697f91ea7f2ccbec3672c6ac63bb2a695f..191ea4315a6b5a8a29e56f6440f88115c466e8b9 100755 (executable)
@@ -32,5 +32,5 @@ else
     TOOLCHAIN=()
 fi
 
-rustup-toolchain-install-master -f -n master "${TOOLCHAIN[@]}" -c rustc-dev -- "$RUST_COMMIT"
+rustup-toolchain-install-master -f -n master "${TOOLCHAIN[@]}" -c rustc-dev -c llvm-tools -- "$RUST_COMMIT"
 rustup override set master
index d3a7e24937f95e865ff81cd7ae31ec6d994a8026..70c47b426825e7f9b4a7cded8f9251ca5673adfc 100644 (file)
@@ -79,7 +79,7 @@ fn config(&mut self, config: &mut interface::Config) {
 
             let conf = clippy_lints::read_conf(&[], &sess);
             clippy_lints::register_plugins(&mut lint_store, &sess, &conf);
-            clippy_lints::register_pre_expansion_lints(&mut lint_store, &conf);
+            clippy_lints::register_pre_expansion_lints(&mut lint_store);
             clippy_lints::register_renamed(&mut lint_store);
         }));
 
index 9457a64f9c6b96e48048d6118c9f731f0a4056a8..f63301c7db0a6c9019ad83bd89c283c370437744 100644 (file)
     },
     Lint {
         name: "match_wild_err_arm",
-        group: "style",
+        group: "pedantic",
         desc: "a `match` with `Err(_)` arm and take drastic actions",
         deprecation: None,
         module: "matches",
     },
+    Lint {
+        name: "match_wildcard_for_single_variants",
+        group: "pedantic",
+        desc: "a wildcard enum match for a single variant",
+        deprecation: None,
+        module: "matches",
+    },
     Lint {
         name: "maybe_infinite_iter",
         group: "pedantic",
     Lint {
         name: "useless_conversion",
         group: "complexity",
-        desc: "calls to `Into`/`From`/`IntoIter` that performs useless conversions to the same type",
+        desc: "calls to `Into`, `TryInto`, `From`, `TryFrom`, `IntoIter` that performs useless conversions to the same type",
         deprecation: None,
         module: "useless_conversion",
     },
index de2cf6d7873f8dac5276c3768531eaeb343a0661..2758b9a7e760439433abd5011244713d2b9c75c8 100644 (file)
@@ -38,13 +38,13 @@ fn clippy_driver_path() -> PathBuf {
 //        as what we manually pass to `cargo` invocation
 fn third_party_crates() -> String {
     use std::collections::HashMap;
-    static CRATES: &[&str] = &["serde", "serde_derive", "regex", "clippy_lints"];
+    static CRATES: &[&str] = &["serde", "serde_derive", "regex", "clippy_lints", "syn", "quote"];
     let dep_dir = cargo::TARGET_LIB.join("deps");
     let mut crates: HashMap<&str, PathBuf> = HashMap::with_capacity(CRATES.len());
     for entry in fs::read_dir(dep_dir).unwrap() {
         let path = match entry {
             Ok(entry) => entry.path(),
-            _ => continue,
+            Err(_) => continue,
         };
         if let Some(name) = path.file_name().and_then(OsStr::to_str) {
             for dep in CRATES {
@@ -101,54 +101,133 @@ fn run_mode(cfg: &mut compiletest::Config) {
     compiletest::run_tests(&cfg);
 }
 
-#[allow(clippy::identity_conversion)]
-fn run_ui_toml_tests(config: &compiletest::Config, mut tests: Vec<tester::TestDescAndFn>) -> Result<bool, io::Error> {
-    let mut result = true;
-    let opts = compiletest::test_opts(config);
-    for dir in fs::read_dir(&config.src_base)? {
-        let dir = dir?;
-        if !dir.file_type()?.is_dir() {
-            continue;
-        }
-        let dir_path = dir.path();
-        set_var("CARGO_MANIFEST_DIR", &dir_path);
-        for file in fs::read_dir(&dir_path)? {
-            let file = file?;
-            let file_path = file.path();
-            if file.file_type()?.is_dir() {
+fn run_ui_toml(config: &mut compiletest::Config) {
+    fn run_tests(config: &compiletest::Config, mut tests: Vec<tester::TestDescAndFn>) -> Result<bool, io::Error> {
+        let mut result = true;
+        let opts = compiletest::test_opts(config);
+        for dir in fs::read_dir(&config.src_base)? {
+            let dir = dir?;
+            if !dir.file_type()?.is_dir() {
                 continue;
             }
-            if file_path.extension() != Some(OsStr::new("rs")) {
-                continue;
+            let dir_path = dir.path();
+            set_var("CARGO_MANIFEST_DIR", &dir_path);
+            for file in fs::read_dir(&dir_path)? {
+                let file = file?;
+                let file_path = file.path();
+                if file.file_type()?.is_dir() {
+                    continue;
+                }
+                if file_path.extension() != Some(OsStr::new("rs")) {
+                    continue;
+                }
+                let paths = compiletest::common::TestPaths {
+                    file: file_path,
+                    base: config.src_base.clone(),
+                    relative_dir: dir_path.file_name().unwrap().into(),
+                };
+                let test_name = compiletest::make_test_name(&config, &paths);
+                let index = tests
+                    .iter()
+                    .position(|test| test.desc.name == test_name)
+                    .expect("The test should be in there");
+                result &= tester::run_tests_console(&opts, vec![tests.swap_remove(index)])?;
             }
-            let paths = compiletest::common::TestPaths {
-                file: file_path,
-                base: config.src_base.clone(),
-                relative_dir: dir_path.file_name().unwrap().into(),
-            };
-            let test_name = compiletest::make_test_name(&config, &paths);
-            let index = tests
-                .iter()
-                .position(|test| test.desc.name == test_name)
-                .expect("The test should be in there");
-            result &= tester::run_tests_console(&opts, vec![tests.swap_remove(index)])?;
         }
+        Ok(result)
     }
-    Ok(result)
-}
 
-fn run_ui_toml(config: &mut compiletest::Config) {
     config.mode = TestMode::Ui;
     config.src_base = Path::new("tests").join("ui-toml").canonicalize().unwrap();
 
     let tests = compiletest::make_tests(&config);
 
-    let res = run_ui_toml_tests(&config, tests);
+    let res = run_tests(&config, tests);
+    match res {
+        Ok(true) => {},
+        Ok(false) => panic!("Some tests failed"),
+        Err(e) => {
+            panic!("I/O failure during tests: {:?}", e);
+        },
+    }
+}
+
+fn run_ui_cargo(config: &mut compiletest::Config) {
+    fn run_tests(
+        config: &compiletest::Config,
+        filter: &Option<String>,
+        mut tests: Vec<tester::TestDescAndFn>,
+    ) -> Result<bool, io::Error> {
+        let mut result = true;
+        let opts = compiletest::test_opts(config);
+
+        for dir in fs::read_dir(&config.src_base)? {
+            let dir = dir?;
+            if !dir.file_type()?.is_dir() {
+                continue;
+            }
+
+            // Use the filter if provided
+            let dir_path = dir.path();
+            match &filter {
+                Some(name) if !dir_path.ends_with(name) => continue,
+                _ => {},
+            }
+
+            for case in fs::read_dir(&dir_path)? {
+                let case = case?;
+                if !case.file_type()?.is_dir() {
+                    continue;
+                }
+
+                let src_path = case.path().join("src");
+                env::set_current_dir(&src_path)?;
+
+                for file in fs::read_dir(&src_path)? {
+                    let file = file?;
+                    if file.file_type()?.is_dir() {
+                        continue;
+                    }
+
+                    // Search for the main file to avoid running a test for each file in the project
+                    let file_path = file.path();
+                    match file_path.file_name().and_then(OsStr::to_str) {
+                        Some("main.rs") => {},
+                        _ => continue,
+                    }
+
+                    let paths = compiletest::common::TestPaths {
+                        file: file_path,
+                        base: config.src_base.clone(),
+                        relative_dir: src_path.strip_prefix(&config.src_base).unwrap().into(),
+                    };
+                    let test_name = compiletest::make_test_name(&config, &paths);
+                    let index = tests
+                        .iter()
+                        .position(|test| test.desc.name == test_name)
+                        .expect("The test should be in there");
+                    result &= tester::run_tests_console(&opts, vec![tests.swap_remove(index)])?;
+                }
+            }
+        }
+        Ok(result)
+    }
+
+    config.mode = TestMode::Ui;
+    config.src_base = Path::new("tests").join("ui-cargo").canonicalize().unwrap();
+
+    let tests = compiletest::make_tests(&config);
+
+    let current_dir = env::current_dir().unwrap();
+    let filter = env::var("TESTNAME").ok();
+    let res = run_tests(&config, &filter, tests);
+    env::set_current_dir(current_dir).unwrap();
+
     match res {
         Ok(true) => {},
         Ok(false) => panic!("Some tests failed"),
         Err(e) => {
-            println!("I/O failure during tests: {:?}", e);
+            panic!("I/O failure during tests: {:?}", e);
         },
     }
 }
@@ -165,4 +244,5 @@ fn compile_test() {
     let mut config = default_config();
     run_mode(&mut config);
     run_ui_toml(&mut config);
+    run_ui_cargo(&mut config);
 }
diff --git a/tests/ui-cargo/cargo_common_metadata/fail/Cargo.toml b/tests/ui-cargo/cargo_common_metadata/fail/Cargo.toml
new file mode 100644 (file)
index 0000000..c64adcf
--- /dev/null
@@ -0,0 +1,4 @@
+[package]
+name = "cargo_common_metadata"
+version = "0.1.0"
+publish = false
diff --git a/tests/ui-cargo/cargo_common_metadata/fail/src/main.rs b/tests/ui-cargo/cargo_common_metadata/fail/src/main.rs
new file mode 100644 (file)
index 0000000..27841e1
--- /dev/null
@@ -0,0 +1,4 @@
+// compile-flags: --crate-name=cargo_common_metadata
+#![warn(clippy::cargo_common_metadata)]
+
+fn main() {}
diff --git a/tests/ui-cargo/cargo_common_metadata/fail/src/main.stderr b/tests/ui-cargo/cargo_common_metadata/fail/src/main.stderr
new file mode 100644 (file)
index 0000000..c8ae6c8
--- /dev/null
@@ -0,0 +1,18 @@
+error: package `cargo_common_metadata` is missing `package.authors` metadata
+   |
+   = note: `-D clippy::cargo-common-metadata` implied by `-D warnings`
+
+error: package `cargo_common_metadata` is missing `package.description` metadata
+
+error: package `cargo_common_metadata` is missing `either package.license or package.license_file` metadata
+
+error: package `cargo_common_metadata` is missing `package.repository` metadata
+
+error: package `cargo_common_metadata` is missing `package.readme` metadata
+
+error: package `cargo_common_metadata` is missing `package.keywords` metadata
+
+error: package `cargo_common_metadata` is missing `package.categories` metadata
+
+error: aborting due to 7 previous errors
+
diff --git a/tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml b/tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml
new file mode 100644 (file)
index 0000000..c8233f3
--- /dev/null
@@ -0,0 +1,11 @@
+[package]
+name = "cargo_common_metadata"
+version = "0.1.0"
+publish = false
+authors = ["Random person from the Internet <someone@someplace.org>"]
+description = "A test package for the cargo_common_metadata lint"
+repository = "https://github.com/someone/cargo_common_metadata"
+readme = "README.md"
+license = "MIT OR Apache-2.0"
+keywords = ["metadata", "lint", "clippy"]
+categories = ["development-tools::testing"]
diff --git a/tests/ui-cargo/cargo_common_metadata/pass/src/main.rs b/tests/ui-cargo/cargo_common_metadata/pass/src/main.rs
new file mode 100644 (file)
index 0000000..27841e1
--- /dev/null
@@ -0,0 +1,4 @@
+// compile-flags: --crate-name=cargo_common_metadata
+#![warn(clippy::cargo_common_metadata)]
+
+fn main() {}
diff --git a/tests/ui-cargo/multiple_crate_versions/5041_allow_dev_build/Cargo.toml b/tests/ui-cargo/multiple_crate_versions/5041_allow_dev_build/Cargo.toml
new file mode 100644 (file)
index 0000000..72731fb
--- /dev/null
@@ -0,0 +1,17 @@
+# Should not lint for dev or build dependencies. See issue 5041.
+
+[package]
+name = "multiple_crate_versions"
+version = "0.1.0"
+publish = false
+
+# One of the versions of winapi is only a dev dependency: allowed
+[dependencies]
+ctrlc = "=3.1.0"
+[dev-dependencies]
+ansi_term = "=0.11.0"
+
+# Both versions of winapi are a build dependency: allowed
+[build-dependencies]
+ctrlc = "=3.1.0"
+ansi_term = "=0.11.0"
diff --git a/tests/ui-cargo/multiple_crate_versions/5041_allow_dev_build/src/main.rs b/tests/ui-cargo/multiple_crate_versions/5041_allow_dev_build/src/main.rs
new file mode 100644 (file)
index 0000000..1b2d3ec
--- /dev/null
@@ -0,0 +1,4 @@
+// compile-flags: --crate-name=multiple_crate_versions
+#![warn(clippy::multiple_crate_versions)]
+
+fn main() {}
diff --git a/tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml b/tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml
new file mode 100644 (file)
index 0000000..3a94b72
--- /dev/null
@@ -0,0 +1,8 @@
+[package]
+name = "multiple_crate_versions"
+version = "0.1.0"
+publish = false
+
+[dependencies]
+ctrlc = "=3.1.0"
+ansi_term = "=0.11.0"
diff --git a/tests/ui-cargo/multiple_crate_versions/fail/src/main.rs b/tests/ui-cargo/multiple_crate_versions/fail/src/main.rs
new file mode 100644 (file)
index 0000000..1b2d3ec
--- /dev/null
@@ -0,0 +1,4 @@
+// compile-flags: --crate-name=multiple_crate_versions
+#![warn(clippy::multiple_crate_versions)]
+
+fn main() {}
diff --git a/tests/ui-cargo/multiple_crate_versions/fail/src/main.stderr b/tests/ui-cargo/multiple_crate_versions/fail/src/main.stderr
new file mode 100644 (file)
index 0000000..4f66859
--- /dev/null
@@ -0,0 +1,6 @@
+error: multiple versions for dependency `winapi`: 0.2.8, 0.3.8
+   |
+   = note: `-D clippy::multiple-crate-versions` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/tests/ui-cargo/multiple_crate_versions/pass/Cargo.toml b/tests/ui-cargo/multiple_crate_versions/pass/Cargo.toml
new file mode 100644 (file)
index 0000000..a9b0642
--- /dev/null
@@ -0,0 +1,8 @@
+[package]
+name = "cargo_common_metadata"
+version = "0.1.0"
+publish = false
+
+[dependencies]
+regex = "1.3.7"
+serde = "1.0.110"
diff --git a/tests/ui-cargo/multiple_crate_versions/pass/src/main.rs b/tests/ui-cargo/multiple_crate_versions/pass/src/main.rs
new file mode 100644 (file)
index 0000000..1b2d3ec
--- /dev/null
@@ -0,0 +1,4 @@
+// compile-flags: --crate-name=multiple_crate_versions
+#![warn(clippy::multiple_crate_versions)]
+
+fn main() {}
diff --git a/tests/ui-cargo/update-all-references.sh b/tests/ui-cargo/update-all-references.sh
new file mode 100755 (executable)
index 0000000..7028b25
--- /dev/null
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# A script to update the references for all tests. The idea is that
+# you do a run, which will generate files in the build directory
+# containing the (normalized) actual output of the compiler. You then
+# run this script, which will copy those files over. If you find
+# yourself manually editing a foo.stderr file, you're doing it wrong.
+#
+# See all `update-references.sh`, if you just want to update a single test.
+
+if [[ "$1" == "--help" || "$1" == "-h" ]]; then
+    echo "usage: $0"
+fi
+
+BUILD_DIR=$PWD/target/debug/test_build_base
+MY_DIR=$(dirname "$0")
+cd "$MY_DIR" || exit
+find . -name '*.rs' -exec ./update-references.sh "$BUILD_DIR" {} +
diff --git a/tests/ui-cargo/update-references.sh b/tests/ui-cargo/update-references.sh
new file mode 100755 (executable)
index 0000000..50d4267
--- /dev/null
@@ -0,0 +1,38 @@
+#!/bin/bash
+
+# A script to update the references for particular tests. The idea is
+# that you do a run, which will generate files in the build directory
+# containing the (normalized) actual output of the compiler. This
+# script will then copy that output and replace the "expected output"
+# files. You can then commit the changes.
+#
+# If you find yourself manually editing a foo.stderr file, you're
+# doing it wrong.
+
+if [[ "$1" == "--help" || "$1" == "-h" || "$1" == "" || "$2" == "" ]]; then
+    echo "usage: $0 <build-directory> <relative-path-to-rs-files>"
+    echo ""
+    echo "For example:"
+    echo "   $0 ../../../build/x86_64-apple-darwin/test/ui *.rs */*.rs"
+fi
+
+MYDIR=$(dirname "$0")
+
+BUILD_DIR="$1"
+shift
+
+while [[ "$1" != "" ]]; do
+    STDERR_NAME="${1/%.rs/.stderr}"
+    STDOUT_NAME="${1/%.rs/.stdout}"
+    shift
+    if [[ -f "$BUILD_DIR"/"$STDOUT_NAME" ]] && \
+           ! (cmp -s -- "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME"); then
+        echo updating "$MYDIR"/"$STDOUT_NAME"
+        cp "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME"
+    fi
+    if [[ -f "$BUILD_DIR"/"$STDERR_NAME" ]] && \
+           ! (cmp -s -- "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME"); then
+        echo updating "$MYDIR"/"$STDERR_NAME"
+        cp "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME"
+    fi
+done
diff --git a/tests/ui-cargo/wildcard_dependencies/fail/Cargo.toml b/tests/ui-cargo/wildcard_dependencies/fail/Cargo.toml
new file mode 100644 (file)
index 0000000..fd2a341
--- /dev/null
@@ -0,0 +1,7 @@
+[package]
+name = "wildcard_dependencies"
+version = "0.1.0"
+publish = false
+
+[dependencies]
+regex = "*"
diff --git a/tests/ui-cargo/wildcard_dependencies/fail/src/main.rs b/tests/ui-cargo/wildcard_dependencies/fail/src/main.rs
new file mode 100644 (file)
index 0000000..581babf
--- /dev/null
@@ -0,0 +1,4 @@
+// compile-flags: --crate-name=wildcard_dependencies
+#![warn(clippy::wildcard_dependencies)]
+
+fn main() {}
diff --git a/tests/ui-cargo/wildcard_dependencies/fail/src/main.stderr b/tests/ui-cargo/wildcard_dependencies/fail/src/main.stderr
new file mode 100644 (file)
index 0000000..9e65d2f
--- /dev/null
@@ -0,0 +1,6 @@
+error: wildcard dependency for `regex`
+   |
+   = note: `-D clippy::wildcard-dependencies` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/tests/ui-cargo/wildcard_dependencies/pass/Cargo.toml b/tests/ui-cargo/wildcard_dependencies/pass/Cargo.toml
new file mode 100644 (file)
index 0000000..38cb139
--- /dev/null
@@ -0,0 +1,7 @@
+[package]
+name = "wildcard_dependencies"
+version = "0.1.0"
+publish = false
+
+[dependencies]
+regex = "1"
diff --git a/tests/ui-cargo/wildcard_dependencies/pass/src/main.rs b/tests/ui-cargo/wildcard_dependencies/pass/src/main.rs
new file mode 100644 (file)
index 0000000..581babf
--- /dev/null
@@ -0,0 +1,4 @@
+// compile-flags: --crate-name=wildcard_dependencies
+#![warn(clippy::wildcard_dependencies)]
+
+fn main() {}
diff --git a/tests/ui/auxiliary/proc_macro_attr.rs b/tests/ui/auxiliary/proc_macro_attr.rs
new file mode 100644 (file)
index 0000000..e6626d5
--- /dev/null
@@ -0,0 +1,37 @@
+// no-prefer-dynamic
+
+#![crate_type = "proc-macro"]
+#![feature(repr128, proc_macro_hygiene, proc_macro_quote)]
+#![allow(clippy::useless_conversion)]
+
+extern crate proc_macro;
+extern crate quote;
+extern crate syn;
+
+use proc_macro::TokenStream;
+use quote::{quote, quote_spanned};
+use syn::parse_macro_input;
+use syn::{parse_quote, ItemTrait, TraitItem};
+
+#[proc_macro_attribute]
+pub fn fake_async_trait(_args: TokenStream, input: TokenStream) -> TokenStream {
+    let mut item = parse_macro_input!(input as ItemTrait);
+    for inner in &mut item.items {
+        if let TraitItem::Method(method) = inner {
+            let sig = &method.sig;
+            let block = &mut method.default;
+            if let Some(block) = block {
+                let brace = block.brace_token;
+
+                let my_block = quote_spanned!( brace.span => {
+                    // Should not trigger `empty_line_after_outer_attr`
+                    #[crate_type = "lib"]
+                    #sig #block
+                    Vec::new()
+                });
+                *block = parse_quote!(#my_block);
+            }
+        }
+    }
+    TokenStream::from(quote!(#item))
+}
index 1d3fe405521cdbc936c2f82836611571a85acb7f..912e6788afddcacce8f7f3b2887ebe54ca85377a 100644 (file)
@@ -1,6 +1,6 @@
 #![allow(clippy::all)]
 #![warn(clippy::cognitive_complexity)]
-#![allow(unused)]
+#![allow(unused, unused_crate_dependencies)]
 
 #[rustfmt::skip]
 fn main() {
index 403eff566ed6d34d2a2ac92e58d9d94735782f62..771a26fc9a86d5f5257628eaf0a7024fdb13f1c9 100644 (file)
@@ -1,5 +1,5 @@
-#![warn(clippy::cognitive_complexity)]
-#![warn(unused)]
+#![warn(unused, clippy::cognitive_complexity)]
+#![allow(unused_crate_dependencies)]
 
 fn main() {
     kaboom();
index 5343dff9da1db26078505730abf8edcea2383d2c..3e92bca986ab55b4990a8d32e4f3f64a89f65289 100644 (file)
@@ -1,8 +1,12 @@
+// aux-build:proc_macro_attr.rs
 #![warn(clippy::empty_line_after_outer_attr)]
 #![allow(clippy::assertions_on_constants)]
 #![feature(custom_inner_attributes)]
 #![rustfmt::skip]
 
+#[macro_use]
+extern crate proc_macro_attr;
+
 // This should produce a warning
 #[crate_type = "lib"]
 
@@ -93,4 +97,17 @@ pub enum FooFighter {
 /* test */
 pub struct T;
 
-fn main() { }
+// This should not produce a warning
+// See https://github.com/rust-lang/rust-clippy/issues/5567
+#[fake_async_trait]
+pub trait Bazz {
+    fn foo() -> Vec<u8> {
+        let _i = "";
+
+
+
+        vec![]
+    }
+}
+
+fn main() {}
index d8c9786541f0b75f19a7836b73861322924b4a73..bf753a732f000f78c956833f7ea3f7f25bed7286 100644 (file)
@@ -1,5 +1,5 @@
 error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
-  --> $DIR/empty_line_after_outer_attribute.rs:7:1
+  --> $DIR/empty_line_after_outer_attribute.rs:11:1
    |
 LL | / #[crate_type = "lib"]
 LL | |
@@ -10,7 +10,7 @@ LL | | fn with_one_newline_and_comment() { assert!(true) }
    = note: `-D clippy::empty-line-after-outer-attr` implied by `-D warnings`
 
 error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
-  --> $DIR/empty_line_after_outer_attribute.rs:19:1
+  --> $DIR/empty_line_after_outer_attribute.rs:23:1
    |
 LL | / #[crate_type = "lib"]
 LL | |
@@ -18,7 +18,7 @@ LL | | fn with_one_newline() { assert!(true) }
    | |_
 
 error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
-  --> $DIR/empty_line_after_outer_attribute.rs:24:1
+  --> $DIR/empty_line_after_outer_attribute.rs:28:1
    |
 LL | / #[crate_type = "lib"]
 LL | |
@@ -27,7 +27,7 @@ LL | | fn with_two_newlines() { assert!(true) }
    | |_
 
 error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
-  --> $DIR/empty_line_after_outer_attribute.rs:31:1
+  --> $DIR/empty_line_after_outer_attribute.rs:35:1
    |
 LL | / #[crate_type = "lib"]
 LL | |
@@ -35,7 +35,7 @@ LL | | enum Baz {
    | |_
 
 error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
-  --> $DIR/empty_line_after_outer_attribute.rs:39:1
+  --> $DIR/empty_line_after_outer_attribute.rs:43:1
    |
 LL | / #[crate_type = "lib"]
 LL | |
@@ -43,7 +43,7 @@ LL | | struct Foo {
    | |_
 
 error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
-  --> $DIR/empty_line_after_outer_attribute.rs:47:1
+  --> $DIR/empty_line_after_outer_attribute.rs:51:1
    |
 LL | / #[crate_type = "lib"]
 LL | |
index d1863701bfe7c14dd0cd0a161d3b224530b4343e..b59dbb3e76c6455ac99d678f4bba6cca14bd7fd6 100644 (file)
@@ -47,17 +47,32 @@ error: future cannot be sent between threads safely
   --> $DIR/future_not_send.rs:20:63
    |
 LL | async fn private_future2(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
-   |                                                               ^^^^
+   |                                                               ^^^^ future returned by `private_future2` is not `Send`
    |
+note: captured value is not `Send`
+  --> $DIR/future_not_send.rs:20:26
+   |
+LL | async fn private_future2(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
+   |                          ^^ has type `std::rc::Rc<[u8]>` which is not `Send`
    = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send`
+note: captured value is not `Send`
+  --> $DIR/future_not_send.rs:20:40
+   |
+LL | async fn private_future2(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
+   |                                        ^^^^ has type `&std::cell::Cell<usize>` which is not `Send`
    = note: `std::cell::Cell<usize>` doesn't implement `std::marker::Sync`
 
 error: future cannot be sent between threads safely
   --> $DIR/future_not_send.rs:24:43
    |
 LL | pub async fn public_future2(rc: Rc<[u8]>) {}
-   |                                           ^
+   |                                           ^ future returned by `public_future2` is not `Send`
    |
+note: captured value is not `Send`
+  --> $DIR/future_not_send.rs:24:29
+   |
+LL | pub async fn public_future2(rc: Rc<[u8]>) {}
+   |                             ^^ has type `std::rc::Rc<[u8]>` which is not `Send`
    = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send`
 
 error: future cannot be sent between threads safely
@@ -117,8 +132,13 @@ error: future cannot be sent between threads safely
   --> $DIR/future_not_send.rs:66:34
    |
 LL | async fn unclear_future<T>(t: T) {}
-   |                                  ^
+   |                                  ^ future returned by `unclear_future` is not `Send`
    |
+note: captured value is not `Send`
+  --> $DIR/future_not_send.rs:66:28
+   |
+LL | async fn unclear_future<T>(t: T) {}
+   |                            ^ has type `T` which is not `Send`
    = note: `T` doesn't implement `std::marker::Send`
 
 error: aborting due to 8 previous errors
index 20d4c933418b7a928cd2b292e799a4f6161b547e..6a2a02987dea7497b3773f4443c12b3d98fc76d9 100644 (file)
@@ -5,7 +5,7 @@ LL |         Err(_) => panic!("err"),
    |         ^^^^^^
    |
    = note: `-D clippy::match-wild-err-arm` implied by `-D warnings`
-   = note: match each error separately or use the error output
+   = note: match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable
 
 error: `Err(_)` matches all errors
   --> $DIR/match_wild_err_arm.rs:17:9
@@ -13,7 +13,7 @@ error: `Err(_)` matches all errors
 LL |         Err(_) => panic!(),
    |         ^^^^^^
    |
-   = note: match each error separately or use the error output
+   = note: match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable
 
 error: `Err(_)` matches all errors
   --> $DIR/match_wild_err_arm.rs:23:9
@@ -21,7 +21,7 @@ error: `Err(_)` matches all errors
 LL |         Err(_) => {
    |         ^^^^^^
    |
-   = note: match each error separately or use the error output
+   = note: match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable
 
 error: `Err(_e)` matches all errors
   --> $DIR/match_wild_err_arm.rs:31:9
@@ -29,7 +29,7 @@ error: `Err(_e)` matches all errors
 LL |         Err(_e) => panic!(),
    |         ^^^^^^^
    |
-   = note: match each error separately or use the error output
+   = note: match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable
 
 error: aborting due to 4 previous errors
 
diff --git a/tests/ui/match_wildcard_for_single_variants.fixed b/tests/ui/match_wildcard_for_single_variants.fixed
new file mode 100644 (file)
index 0000000..5192009
--- /dev/null
@@ -0,0 +1,59 @@
+// run-rustfix
+
+#![warn(clippy::match_wildcard_for_single_variants)]
+#![allow(dead_code)]
+
+enum Foo {
+    A,
+    B,
+    C,
+}
+
+enum Color {
+    Red,
+    Green,
+    Blue,
+    Rgb(u8, u8, u8),
+}
+
+fn main() {
+    let f = Foo::A;
+    match f {
+        Foo::A => {},
+        Foo::B => {},
+        Foo::C => {},
+    }
+
+    let color = Color::Red;
+
+    // check exhaustive bindings
+    match color {
+        Color::Red => {},
+        Color::Green => {},
+        Color::Rgb(_r, _g, _b) => {},
+        Color::Blue => {},
+    }
+
+    // check exhaustive wild
+    match color {
+        Color::Red => {},
+        Color::Green => {},
+        Color::Rgb(..) => {},
+        Color::Blue => {},
+    }
+    match color {
+        Color::Red => {},
+        Color::Green => {},
+        Color::Rgb(_, _, _) => {},
+        Color::Blue => {},
+    }
+
+    // shouldn't lint as there is one missing variant
+    // and one that isn't exhaustively covered
+    match color {
+        Color::Red => {},
+        Color::Green => {},
+        Color::Rgb(255, _, _) => {},
+        _ => {},
+    }
+}
diff --git a/tests/ui/match_wildcard_for_single_variants.rs b/tests/ui/match_wildcard_for_single_variants.rs
new file mode 100644 (file)
index 0000000..1df917e
--- /dev/null
@@ -0,0 +1,59 @@
+// run-rustfix
+
+#![warn(clippy::match_wildcard_for_single_variants)]
+#![allow(dead_code)]
+
+enum Foo {
+    A,
+    B,
+    C,
+}
+
+enum Color {
+    Red,
+    Green,
+    Blue,
+    Rgb(u8, u8, u8),
+}
+
+fn main() {
+    let f = Foo::A;
+    match f {
+        Foo::A => {},
+        Foo::B => {},
+        _ => {},
+    }
+
+    let color = Color::Red;
+
+    // check exhaustive bindings
+    match color {
+        Color::Red => {},
+        Color::Green => {},
+        Color::Rgb(_r, _g, _b) => {},
+        _ => {},
+    }
+
+    // check exhaustive wild
+    match color {
+        Color::Red => {},
+        Color::Green => {},
+        Color::Rgb(..) => {},
+        _ => {},
+    }
+    match color {
+        Color::Red => {},
+        Color::Green => {},
+        Color::Rgb(_, _, _) => {},
+        _ => {},
+    }
+
+    // shouldn't lint as there is one missing variant
+    // and one that isn't exhaustively covered
+    match color {
+        Color::Red => {},
+        Color::Green => {},
+        Color::Rgb(255, _, _) => {},
+        _ => {},
+    }
+}
diff --git a/tests/ui/match_wildcard_for_single_variants.stderr b/tests/ui/match_wildcard_for_single_variants.stderr
new file mode 100644 (file)
index 0000000..82790aa
--- /dev/null
@@ -0,0 +1,28 @@
+error: wildcard match will miss any future added variants
+  --> $DIR/match_wildcard_for_single_variants.rs:24:9
+   |
+LL |         _ => {},
+   |         ^ help: try this: `Foo::C`
+   |
+   = note: `-D clippy::match-wildcard-for-single-variants` implied by `-D warnings`
+
+error: wildcard match will miss any future added variants
+  --> $DIR/match_wildcard_for_single_variants.rs:34:9
+   |
+LL |         _ => {},
+   |         ^ help: try this: `Color::Blue`
+
+error: wildcard match will miss any future added variants
+  --> $DIR/match_wildcard_for_single_variants.rs:42:9
+   |
+LL |         _ => {},
+   |         ^ help: try this: `Color::Blue`
+
+error: wildcard match will miss any future added variants
+  --> $DIR/match_wildcard_for_single_variants.rs:48:9
+   |
+LL |         _ => {},
+   |         ^ help: try this: `Color::Blue`
+
+error: aborting due to 4 previous errors
+
index 781ea7bb152836eda343116409d8c84406266540..3b6041823d8786672ec6bb3d7bdae19acaeaa428 100644 (file)
@@ -148,4 +148,15 @@ pub fn new() -> Self {
     }
 }
 
+pub struct NewNotEqualToDerive {
+    foo: i32,
+}
+
+impl NewNotEqualToDerive {
+    // This `new` implementation is not equal to a derived `Default`, so do not suggest deriving.
+    pub fn new() -> Self {
+        NewNotEqualToDerive { foo: 1 }
+    }
+}
+
 fn main() {}
index 5e485d40663f3e9ffb3540ec912a470ed2cffa3f..e529e441eb735c6b60cb59d5090fa5a22763bfdb 100644 (file)
@@ -1,4 +1,4 @@
-error: you should consider deriving a `Default` implementation for `Foo`
+error: you should consider adding a `Default` implementation for `Foo`
   --> $DIR/new_without_default.rs:8:5
    |
 LL | /     pub fn new() -> Foo {
@@ -9,10 +9,14 @@ LL | |     }
    = note: `-D clippy::new-without-default` implied by `-D warnings`
 help: try this
    |
-LL | #[derive(Default)]
+LL | impl Default for Foo {
+LL |     fn default() -> Self {
+LL |         Self::new()
+LL |     }
+LL | }
    |
 
-error: you should consider deriving a `Default` implementation for `Bar`
+error: you should consider adding a `Default` implementation for `Bar`
   --> $DIR/new_without_default.rs:16:5
    |
 LL | /     pub fn new() -> Self {
@@ -22,7 +26,11 @@ LL | |     }
    |
 help: try this
    |
-LL | #[derive(Default)]
+LL | impl Default for Bar {
+LL |     fn default() -> Self {
+LL |         Self::new()
+LL |     }
+LL | }
    |
 
 error: you should consider adding a `Default` implementation for `LtKo<'c>`
@@ -42,5 +50,22 @@ LL |     }
 LL | }
    |
 
-error: aborting due to 3 previous errors
+error: you should consider adding a `Default` implementation for `NewNotEqualToDerive`
+  --> $DIR/new_without_default.rs:157:5
+   |
+LL | /     pub fn new() -> Self {
+LL | |         NewNotEqualToDerive { foo: 1 }
+LL | |     }
+   | |_____^
+   |
+help: try this
+   |
+LL | impl Default for NewNotEqualToDerive {
+LL |     fn default() -> Self {
+LL |         Self::new()
+LL |     }
+LL | }
+   |
+
+error: aborting due to 4 previous errors
 
index 904c50e14039a4067cce2899b7b1f5a7e7232fb0..a2617a13ecace67dd0543b49da8e62996b5bdec7 100644 (file)
@@ -60,3 +60,28 @@ fn main() {
     // The lint allows this
     let expr = Some(Some(true));
 }
+
+extern crate serde;
+mod issue_4298 {
+    use serde::{Deserialize, Deserializer, Serialize};
+    use std::borrow::Cow;
+
+    #[derive(Serialize, Deserialize)]
+    struct Foo<'a> {
+        #[serde(deserialize_with = "func")]
+        #[serde(skip_serializing_if = "Option::is_none")]
+        #[serde(default)]
+        #[serde(borrow)]
+        // FIXME: should not lint here
+        #[allow(clippy::option_option)]
+        foo: Option<Option<Cow<'a, str>>>,
+    }
+
+    #[allow(clippy::option_option)]
+    fn func<'a, D>(_: D) -> Result<Option<Option<Cow<'a, str>>>, D::Error>
+    where
+        D: Deserializer<'a>,
+    {
+        Ok(Some(Some(Cow::Borrowed("hi"))))
+    }
+}
index 79db186d7ea77128aef51c329c99711a4f0e8752..0cd4c96eb4f9a17988b9cc86bbd0c71c5ba42353 100644 (file)
@@ -58,5 +58,11 @@ error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enu
 LL |     Struct { x: Option<Option<u8>> },
    |                 ^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 9 previous errors
+error: consider using `Option<T>` instead of `Option<Option<T>>` or a custom enum if you need to distinguish all 3 cases
+  --> $DIR/option_option.rs:77:14
+   |
+LL |         foo: Option<Option<Cow<'a, str>>>,
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 10 previous errors
 
index 8ea03fe42616c3339b09befee410f49e71fcd30a..7bb08797ef3960545251e6c9a79ea6d0d2572433 100644 (file)
@@ -95,6 +95,15 @@ fn test_or_with_ctors() {
     let b = "b".to_string();
     let _ = Some(Bar("a".to_string(), Duration::from_secs(1)))
         .or(Some(Bar(b, Duration::from_secs(2))));
+
+    let vec = vec!["foo"];
+    let _ = opt.ok_or(vec.len());
+
+    let array = ["foo"];
+    let _ = opt.ok_or(array.len());
+
+    let slice = &["foo"][..];
+    let _ = opt.ok_or(slice.len());
 }
 
 // Issue 4514 - early return
index 7599b945a9137ed63a21cc06ad70aeed0f5f6532..522f31b72d01f4d94a590dd179a86c287d1c6db4 100644 (file)
@@ -95,6 +95,15 @@ fn test_or_with_ctors() {
     let b = "b".to_string();
     let _ = Some(Bar("a".to_string(), Duration::from_secs(1)))
         .or(Some(Bar(b, Duration::from_secs(2))));
+
+    let vec = vec!["foo"];
+    let _ = opt.ok_or(vec.len());
+
+    let array = ["foo"];
+    let _ = opt.ok_or(array.len());
+
+    let slice = &["foo"][..];
+    let _ = opt.ok_or(slice.len());
 }
 
 // Issue 4514 - early return
index 30f39e9b06398519302fc7a870da680f0fe5806b..541225e635102b73626c4c7e960c7796f2161f8f 100644 (file)
@@ -71,7 +71,6 @@ fn false_positive_capacity_too(x: &String) -> String {
 #[allow(dead_code)]
 fn test_cow_with_ref(c: &Cow<[i32]>) {}
 
-#[allow(dead_code)]
 fn test_cow(c: Cow<[i32]>) {
     let _c = c;
 }
@@ -84,3 +83,34 @@ trait Foo2 {
 impl Foo2 for String {
     fn do_string(&self) {}
 }
+
+// Check that the allow attribute on parameters is honored
+mod issue_5644 {
+    use std::borrow::Cow;
+
+    fn allowed(
+        #[allow(clippy::ptr_arg)] _v: &Vec<u32>,
+        #[allow(clippy::ptr_arg)] _s: &String,
+        #[allow(clippy::ptr_arg)] _c: &Cow<[i32]>,
+    ) {
+    }
+
+    struct S {}
+    impl S {
+        fn allowed(
+            #[allow(clippy::ptr_arg)] _v: &Vec<u32>,
+            #[allow(clippy::ptr_arg)] _s: &String,
+            #[allow(clippy::ptr_arg)] _c: &Cow<[i32]>,
+        ) {
+        }
+    }
+
+    trait T {
+        fn allowed(
+            #[allow(clippy::ptr_arg)] _v: &Vec<u32>,
+            #[allow(clippy::ptr_arg)] _s: &String,
+            #[allow(clippy::ptr_arg)] _c: &Cow<[i32]>,
+        ) {
+        }
+    }
+}
index ebdd6c4003d191386460f72d7f5561c1f7139fd2..718e391e8bf697b59fbf1e02a4c38bad7108a716 100644 (file)
@@ -9,12 +9,12 @@ fn main() {
     let offset_isize = 1_isize;
 
     unsafe {
-        ptr.add(offset_usize);
-        ptr.offset(offset_isize as isize);
-        ptr.offset(offset_u8 as isize);
+        let _ = ptr.add(offset_usize);
+        let _ = ptr.offset(offset_isize as isize);
+        let _ = ptr.offset(offset_u8 as isize);
 
-        ptr.wrapping_add(offset_usize);
-        ptr.wrapping_offset(offset_isize as isize);
-        ptr.wrapping_offset(offset_u8 as isize);
+        let _ = ptr.wrapping_add(offset_usize);
+        let _ = ptr.wrapping_offset(offset_isize as isize);
+        let _ = ptr.wrapping_offset(offset_u8 as isize);
     }
 }
index 3416c4b727a53750dc01cb856803d2a4b3338c6b..f613742c741ef5660e339f0f560ce97576b8153a 100644 (file)
@@ -9,12 +9,12 @@ fn main() {
     let offset_isize = 1_isize;
 
     unsafe {
-        ptr.offset(offset_usize as isize);
-        ptr.offset(offset_isize as isize);
-        ptr.offset(offset_u8 as isize);
+        let _ = ptr.offset(offset_usize as isize);
+        let _ = ptr.offset(offset_isize as isize);
+        let _ = ptr.offset(offset_u8 as isize);
 
-        ptr.wrapping_offset(offset_usize as isize);
-        ptr.wrapping_offset(offset_isize as isize);
-        ptr.wrapping_offset(offset_u8 as isize);
+        let _ = ptr.wrapping_offset(offset_usize as isize);
+        let _ = ptr.wrapping_offset(offset_isize as isize);
+        let _ = ptr.wrapping_offset(offset_u8 as isize);
     }
 }
index b5c7a03e2775e8ff55d60d168eeddcb7da2cb97a..fd45224ca067fa3450bc07cd12f5ebef51052495 100644 (file)
@@ -1,16 +1,16 @@
 error: use of `offset` with a `usize` casted to an `isize`
-  --> $DIR/ptr_offset_with_cast.rs:12:9
+  --> $DIR/ptr_offset_with_cast.rs:12:17
    |
-LL |         ptr.offset(offset_usize as isize);
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr.add(offset_usize)`
+LL |         let _ = ptr.offset(offset_usize as isize);
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr.add(offset_usize)`
    |
    = note: `-D clippy::ptr-offset-with-cast` implied by `-D warnings`
 
 error: use of `wrapping_offset` with a `usize` casted to an `isize`
-  --> $DIR/ptr_offset_with_cast.rs:16:9
+  --> $DIR/ptr_offset_with_cast.rs:16:17
    |
-LL |         ptr.wrapping_offset(offset_usize as isize);
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr.wrapping_add(offset_usize)`
+LL |         let _ = ptr.wrapping_offset(offset_usize as isize);
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr.wrapping_add(offset_usize)`
 
 error: aborting due to 2 previous errors
 
index ee2cbc3cf540eb1309ced9d7a3caee6720047611..332c0427ef65a224ffe323e5429fd08bb61bbc40 100644 (file)
@@ -4,18 +4,23 @@
 const ANSWER: i32 = 42;
 
 fn main() {
+    let arr = [1, 2, 3, 4, 5];
+
+    // These should be linted:
+
     (21..=42).rev().for_each(|x| println!("{}", x));
     let _ = (21..ANSWER).rev().filter(|x| x % 2 == 0).take(10).collect::<Vec<_>>();
 
     for _ in (-42..=-21).rev() {}
     for _ in (21u32..42u32).rev() {}
 
+    let _ = &[] as &[i32];
+
     // These should be ignored as they are not empty ranges:
 
     (21..=42).for_each(|x| println!("{}", x));
     (21..42).for_each(|x| println!("{}", x));
 
-    let arr = [1, 2, 3, 4, 5];
     let _ = &arr[1..=3];
     let _ = &arr[1..3];
 
index 6ed5ca6daa0e8fd537e8b7e0635234dbb6f491cb..901ec8bcc09f4e31eff5766e18959eccf4414edf 100644 (file)
@@ -4,18 +4,23 @@
 const ANSWER: i32 = 42;
 
 fn main() {
+    let arr = [1, 2, 3, 4, 5];
+
+    // These should be linted:
+
     (42..=21).for_each(|x| println!("{}", x));
     let _ = (ANSWER..21).filter(|x| x % 2 == 0).take(10).collect::<Vec<_>>();
 
     for _ in -21..=-42 {}
     for _ in 42u32..21u32 {}
 
+    let _ = &arr[3..3];
+
     // These should be ignored as they are not empty ranges:
 
     (21..=42).for_each(|x| println!("{}", x));
     (21..42).for_each(|x| println!("{}", x));
 
-    let arr = [1, 2, 3, 4, 5];
     let _ = &arr[1..=3];
     let _ = &arr[1..3];
 
index 97933b8ff851530496f750deb848bb0a87668e1d..9a646fd99398d5c97577ddef7cab0c70063f7372 100644 (file)
@@ -1,5 +1,5 @@
 error: this range is empty so it will yield no values
-  --> $DIR/reversed_empty_ranges_fixable.rs:7:5
+  --> $DIR/reversed_empty_ranges_fixable.rs:11:5
    |
 LL |     (42..=21).for_each(|x| println!("{}", x));
    |     ^^^^^^^^^
@@ -11,7 +11,7 @@ LL |     (21..=42).rev().for_each(|x| println!("{}", x));
    |     ^^^^^^^^^^^^^^^
 
 error: this range is empty so it will yield no values
-  --> $DIR/reversed_empty_ranges_fixable.rs:8:13
+  --> $DIR/reversed_empty_ranges_fixable.rs:12:13
    |
 LL |     let _ = (ANSWER..21).filter(|x| x % 2 == 0).take(10).collect::<Vec<_>>();
    |             ^^^^^^^^^^^^
@@ -22,7 +22,7 @@ LL |     let _ = (21..ANSWER).rev().filter(|x| x % 2 == 0).take(10).collect::<Ve
    |             ^^^^^^^^^^^^^^^^^^
 
 error: this range is empty so it will yield no values
-  --> $DIR/reversed_empty_ranges_fixable.rs:10:14
+  --> $DIR/reversed_empty_ranges_fixable.rs:14:14
    |
 LL |     for _ in -21..=-42 {}
    |              ^^^^^^^^^
@@ -33,7 +33,7 @@ LL |     for _ in (-42..=-21).rev() {}
    |              ^^^^^^^^^^^^^^^^^
 
 error: this range is empty so it will yield no values
-  --> $DIR/reversed_empty_ranges_fixable.rs:11:14
+  --> $DIR/reversed_empty_ranges_fixable.rs:15:14
    |
 LL |     for _ in 42u32..21u32 {}
    |              ^^^^^^^^^^^^
@@ -43,5 +43,11 @@ help: consider using the following if you are attempting to iterate over this ra
 LL |     for _ in (21u32..42u32).rev() {}
    |              ^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 4 previous errors
+error: this range is empty and using it to index a slice will always yield an empty slice
+  --> $DIR/reversed_empty_ranges_fixable.rs:17:18
+   |
+LL |     let _ = &arr[3..3];
+   |              ----^^^^- help: if you want an empty slice, use: `[] as &[i32]`
+
+error: aborting due to 5 previous errors
 
index c9ca4c476683101ccd049f1ede61d4526025f1b2..561a35625f02ec6dcb799ced453ec9123467451f 100644 (file)
@@ -9,7 +9,6 @@ fn main() {
     let arr = [1, 2, 3, 4, 5];
     let _ = &arr[3usize..=1usize];
     let _ = &arr[SOME_NUM..1];
-    let _ = &arr[3..3];
 
     for _ in ANSWER..ANSWER {}
 }
index 12e5483ecdfff05e6d5869cbe51ea8b0038b72d4..240188cbb46cb17b151748b279c6edf0433a3b29 100644 (file)
@@ -18,17 +18,11 @@ error: this range is reversed and using it to index a slice will panic at run-ti
 LL |     let _ = &arr[SOME_NUM..1];
    |                  ^^^^^^^^^^^
 
-error: this range is empty and using it to index a slice will always yield an empty slice
-  --> $DIR/reversed_empty_ranges_unfixable.rs:12:18
-   |
-LL |     let _ = &arr[3..3];
-   |                  ^^^^
-
 error: this range is empty so it will yield no values
-  --> $DIR/reversed_empty_ranges_unfixable.rs:14:14
+  --> $DIR/reversed_empty_ranges_unfixable.rs:13:14
    |
 LL |     for _ in ANSWER..ANSWER {}
    |              ^^^^^^^^^^^^^^
 
-error: aborting due to 5 previous errors
+error: aborting due to 4 previous errors
 
index 7df3507edfd9ebb25f2af54cb6f6885836e9ad5e..84ec53702788c12592f02387ec2deaef7f5c36c0 100644 (file)
@@ -1,4 +1,4 @@
-error: useless conversion
+error: useless conversion to the same type
   --> $DIR/useless_conversion.rs:6:13
    |
 LL |     let _ = T::from(val);
@@ -10,55 +10,55 @@ note: the lint level is defined here
 LL | #![deny(clippy::useless_conversion)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: useless conversion
+error: useless conversion to the same type
   --> $DIR/useless_conversion.rs:7:5
    |
 LL |     val.into()
    |     ^^^^^^^^^^ help: consider removing `.into()`: `val`
 
-error: useless conversion
+error: useless conversion to the same type
   --> $DIR/useless_conversion.rs:19:22
    |
 LL |         let _: i32 = 0i32.into();
    |                      ^^^^^^^^^^^ help: consider removing `.into()`: `0i32`
 
-error: useless conversion
+error: useless conversion to the same type
   --> $DIR/useless_conversion.rs:51:21
    |
 LL |     let _: String = "foo".to_string().into();
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `"foo".to_string()`
 
-error: useless conversion
+error: useless conversion to the same type
   --> $DIR/useless_conversion.rs:52:21
    |
 LL |     let _: String = From::from("foo".to_string());
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `From::from()`: `"foo".to_string()`
 
-error: useless conversion
+error: useless conversion to the same type
   --> $DIR/useless_conversion.rs:53:13
    |
 LL |     let _ = String::from("foo".to_string());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `"foo".to_string()`
 
-error: useless conversion
+error: useless conversion to the same type
   --> $DIR/useless_conversion.rs:54:13
    |
 LL |     let _ = String::from(format!("A: {:04}", 123));
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `format!("A: {:04}", 123)`
 
-error: useless conversion
+error: useless conversion to the same type
   --> $DIR/useless_conversion.rs:55:13
    |
 LL |     let _ = "".lines().into_iter();
    |             ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `"".lines()`
 
-error: useless conversion
+error: useless conversion to the same type
   --> $DIR/useless_conversion.rs:56:13
    |
 LL |     let _ = vec![1, 2, 3].into_iter().into_iter();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![1, 2, 3].into_iter()`
 
-error: useless conversion
+error: useless conversion to the same type
   --> $DIR/useless_conversion.rs:57:21
    |
 LL |     let _: String = format!("Hello {}", "world").into();
diff --git a/tests/ui/useless_conversion_try.rs b/tests/ui/useless_conversion_try.rs
new file mode 100644 (file)
index 0000000..3787ea9
--- /dev/null
@@ -0,0 +1,42 @@
+#![deny(clippy::useless_conversion)]
+
+use std::convert::{TryFrom, TryInto};
+
+fn test_generic<T: Copy>(val: T) -> T {
+    let _ = T::try_from(val).unwrap();
+    val.try_into().unwrap()
+}
+
+fn test_generic2<T: Copy + Into<i32> + Into<U>, U: From<T>>(val: T) {
+    // ok
+    let _: i32 = val.try_into().unwrap();
+    let _: U = val.try_into().unwrap();
+    let _ = U::try_from(val).unwrap();
+}
+
+fn main() {
+    test_generic(10i32);
+    test_generic2::<i32, i32>(10i32);
+
+    let _: String = "foo".try_into().unwrap();
+    let _: String = TryFrom::try_from("foo").unwrap();
+    let _ = String::try_from("foo").unwrap();
+    #[allow(clippy::useless_conversion)]
+    {
+        let _ = String::try_from("foo").unwrap();
+        let _: String = "foo".try_into().unwrap();
+    }
+    let _: String = "foo".to_string().try_into().unwrap();
+    let _: String = TryFrom::try_from("foo".to_string()).unwrap();
+    let _ = String::try_from("foo".to_string()).unwrap();
+    let _ = String::try_from(format!("A: {:04}", 123)).unwrap();
+    let _: String = format!("Hello {}", "world").try_into().unwrap();
+    let _: String = "".to_owned().try_into().unwrap();
+    let _: String = match String::from("_").try_into() {
+        Ok(a) => a,
+        Err(_) => "".into(),
+    };
+    // FIXME this is a false negative
+    #[allow(clippy::cmp_owned)]
+    if String::from("a") == TryInto::<String>::try_into(String::from("a")).unwrap() {}
+}
diff --git a/tests/ui/useless_conversion_try.stderr b/tests/ui/useless_conversion_try.stderr
new file mode 100644 (file)
index 0000000..b765727
--- /dev/null
@@ -0,0 +1,79 @@
+error: useless conversion to the same type
+  --> $DIR/useless_conversion_try.rs:6:13
+   |
+LL |     let _ = T::try_from(val).unwrap();
+   |             ^^^^^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/useless_conversion_try.rs:1:9
+   |
+LL | #![deny(clippy::useless_conversion)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = help: consider removing `T::try_from()`
+
+error: useless conversion to the same type
+  --> $DIR/useless_conversion_try.rs:7:5
+   |
+LL |     val.try_into().unwrap()
+   |     ^^^^^^^^^^^^^^
+   |
+   = help: consider removing `.try_into()`
+
+error: useless conversion to the same type
+  --> $DIR/useless_conversion_try.rs:29:21
+   |
+LL |     let _: String = "foo".to_string().try_into().unwrap();
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider removing `.try_into()`
+
+error: useless conversion to the same type
+  --> $DIR/useless_conversion_try.rs:30:21
+   |
+LL |     let _: String = TryFrom::try_from("foo".to_string()).unwrap();
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider removing `TryFrom::try_from()`
+
+error: useless conversion to the same type
+  --> $DIR/useless_conversion_try.rs:31:13
+   |
+LL |     let _ = String::try_from("foo".to_string()).unwrap();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider removing `String::try_from()`
+
+error: useless conversion to the same type
+  --> $DIR/useless_conversion_try.rs:32:13
+   |
+LL |     let _ = String::try_from(format!("A: {:04}", 123)).unwrap();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider removing `String::try_from()`
+
+error: useless conversion to the same type
+  --> $DIR/useless_conversion_try.rs:33:21
+   |
+LL |     let _: String = format!("Hello {}", "world").try_into().unwrap();
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider removing `.try_into()`
+
+error: useless conversion to the same type
+  --> $DIR/useless_conversion_try.rs:34:21
+   |
+LL |     let _: String = "".to_owned().try_into().unwrap();
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider removing `.try_into()`
+
+error: useless conversion to the same type
+  --> $DIR/useless_conversion_try.rs:35:27
+   |
+LL |     let _: String = match String::from("_").try_into() {
+   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider removing `.try_into()`
+
+error: aborting due to 9 previous errors
+