about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJason Newcomb <jsnewcomb@pm.me>2022-07-24 20:17:55 -0400
committerJason Newcomb <jsnewcomb@pm.me>2022-07-24 23:15:39 -0400
commit6bc024df188526a984827d9ecca978414df0afff (patch)
tree8d2f9a22a759cdf83312228f6d7239656cc50b54
parent20e420473064e899c1079c65dddf942b8ca23b7e (diff)
downloadrust-6bc024df188526a984827d9ecca978414df0afff.tar.gz
rust-6bc024df188526a984827d9ecca978414df0afff.zip
Improve `[std|alloc]_instead_of_[alloc|core]` lints
* Don't call `TyCtxt::crate_name` unless necessary
* Don't lint on `use std::env`
* Only lint once on `use std::vec`
-rw-r--r--clippy_lints/src/lib.rs2
-rw-r--r--clippy_lints/src/std_instead_of_core.rs94
-rw-r--r--tests/ui/std_instead_of_core.rs6
-rw-r--r--tests/ui/std_instead_of_core.stderr28
4 files changed, 79 insertions, 51 deletions
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index 8444532f6bb..fd4da166dfe 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -916,7 +916,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold;
     store.register_late_pass(move || Box::new(operators::Operators::new(verbose_bit_mask_threshold)));
     store.register_late_pass(|| Box::new(invalid_utf8_in_unchecked::InvalidUtf8InUnchecked));
-    store.register_late_pass(|| Box::new(std_instead_of_core::StdReexports));
+    store.register_late_pass(|| Box::new(std_instead_of_core::StdReexports::default()));
     // add lints here, do not remove this comment, it's used in `new_lint`
 }
 
diff --git a/clippy_lints/src/std_instead_of_core.rs b/clippy_lints/src/std_instead_of_core.rs
index 560a3164563..ffd63cc687a 100644
--- a/clippy_lints/src/std_instead_of_core.rs
+++ b/clippy_lints/src/std_instead_of_core.rs
@@ -1,8 +1,8 @@
 use clippy_utils::diagnostics::span_lint_and_help;
 use rustc_hir::{def::Res, HirId, Path, PathSegment};
-use rustc_lint::{LateContext, LateLintPass, Lint};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::{sym, symbol::kw, Symbol};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::{sym, symbol::kw, Span};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -81,39 +81,55 @@ declare_clippy_lint! {
     "type is imported from alloc when available in core"
 }
 
-declare_lint_pass!(StdReexports => [STD_INSTEAD_OF_CORE, STD_INSTEAD_OF_ALLOC, ALLOC_INSTEAD_OF_CORE]);
+#[derive(Default)]
+pub struct StdReexports {
+    // Paths which can be either a module or a macro (e.g. `std::env`) will cause this check to happen
+    // twice. First for the mod, second for the macro. This is used to avoid the lint reporting for the macro
+    // when the path could be also be used to access the module.
+    prev_span: Span,
+}
+impl_lint_pass!(StdReexports => [STD_INSTEAD_OF_CORE, STD_INSTEAD_OF_ALLOC, ALLOC_INSTEAD_OF_CORE]);
 
 impl<'tcx> LateLintPass<'tcx> for StdReexports {
     fn check_path(&mut self, cx: &LateContext<'tcx>, path: &Path<'tcx>, _: HirId) {
-        // std_instead_of_core
-        check_path(cx, path, sym::std, sym::core, STD_INSTEAD_OF_CORE);
-        // std_instead_of_alloc
-        check_path(cx, path, sym::std, sym::alloc, STD_INSTEAD_OF_ALLOC);
-        // alloc_instead_of_core
-        check_path(cx, path, sym::alloc, sym::core, ALLOC_INSTEAD_OF_CORE);
-    }
-}
-
-fn check_path(cx: &LateContext<'_>, path: &Path<'_>, krate: Symbol, suggested_crate: Symbol, lint: &'static Lint) {
-    if_chain! {
-        // check if path resolves to the suggested crate.
-        if let Res::Def(_, def_id) = path.res;
-        if suggested_crate == cx.tcx.crate_name(def_id.krate);
-
-        // check if the first segment of the path is the crate we want to identify
-        if let Some(path_root_segment) = get_first_segment(path);
-
-        // check if the path matches the crate we want to suggest the other path for.
-        if krate == path_root_segment.ident.name;
-        then {
-            span_lint_and_help(
-                cx,
-                lint,
-                path.span,
-                &format!("used import from `{}` instead of `{}`", krate, suggested_crate),
-                None,
-                &format!("consider importing the item from `{}`", suggested_crate),
-            );
+        if let Res::Def(_, def_id) = path.res
+            && let Some(first_segment) = get_first_segment(path)
+        {
+            let (lint, msg, help) = match first_segment.ident.name {
+                sym::std => match cx.tcx.crate_name(def_id.krate) {
+                    sym::core => (
+                        STD_INSTEAD_OF_CORE,
+                        "used import from `std` instead of `core`",
+                        "consider importing the item from `core`",
+                    ),
+                    sym::alloc => (
+                        STD_INSTEAD_OF_ALLOC,
+                        "used import from `std` instead of `alloc`",
+                        "consider importing the item from `alloc`",
+                    ),
+                    _ => {
+                        self.prev_span = path.span;
+                        return;
+                    },
+                },
+                sym::alloc => {
+                    if cx.tcx.crate_name(def_id.krate) == sym::core {
+                        (
+                            ALLOC_INSTEAD_OF_CORE,
+                            "used import from `alloc` instead of `core`",
+                            "consider importing the item from `core`",
+                        )
+                    } else {
+                        self.prev_span = path.span;
+                        return;
+                    }
+                },
+                _ => return,
+            };
+            if path.span != self.prev_span {
+                span_lint_and_help(cx, lint, path.span, msg, None, help);
+                self.prev_span = path.span;
+            }
         }
     }
 }
@@ -123,12 +139,10 @@ fn check_path(cx: &LateContext<'_>, path: &Path<'_>, krate: Symbol, suggested_cr
 /// If this is a global path (such as `::std::fmt::Debug`), then the segment after [`kw::PathRoot`]
 /// is returned.
 fn get_first_segment<'tcx>(path: &Path<'tcx>) -> Option<&'tcx PathSegment<'tcx>> {
-    let segment = path.segments.first()?;
-
-    // A global path will have PathRoot as the first segment. In this case, return the segment after.
-    if segment.ident.name == kw::PathRoot {
-        path.segments.get(1)
-    } else {
-        Some(segment)
+    match path.segments {
+        // A global path will have PathRoot as the first segment. In this case, return the segment after.
+        [x, y, ..] if x.ident.name == kw::PathRoot => Some(y),
+        [x, ..] => Some(x),
+        _ => None,
     }
 }
diff --git a/tests/ui/std_instead_of_core.rs b/tests/ui/std_instead_of_core.rs
index 74f05ec1f65..6b27475de4c 100644
--- a/tests/ui/std_instead_of_core.rs
+++ b/tests/ui/std_instead_of_core.rs
@@ -9,6 +9,8 @@ fn std_instead_of_core() {
     use std::hash::Hasher;
     // Absolute path
     use ::std::hash::Hash;
+    // Don't lint on `env` macro
+    use std::env;
 
     // Multiple imports
     use std::fmt::{Debug, Result};
@@ -20,10 +22,14 @@ fn std_instead_of_core() {
     // Types
     let cell = std::cell::Cell::new(8u32);
     let cell_absolute = ::std::cell::Cell::new(8u32);
+
+    let _ = std::env!("PATH");
 }
 
 #[warn(clippy::std_instead_of_alloc)]
 fn std_instead_of_alloc() {
+    // Only lint once.
+    use std::vec;
     use std::vec::Vec;
 }
 
diff --git a/tests/ui/std_instead_of_core.stderr b/tests/ui/std_instead_of_core.stderr
index 9f1644835c1..bc49dabf586 100644
--- a/tests/ui/std_instead_of_core.stderr
+++ b/tests/ui/std_instead_of_core.stderr
@@ -16,7 +16,7 @@ LL |     use ::std::hash::Hash;
    = help: consider importing the item from `core`
 
 error: used import from `std` instead of `core`
-  --> $DIR/std_instead_of_core.rs:14:20
+  --> $DIR/std_instead_of_core.rs:16:20
    |
 LL |     use std::fmt::{Debug, Result};
    |                    ^^^^^
@@ -24,7 +24,7 @@ LL |     use std::fmt::{Debug, Result};
    = help: consider importing the item from `core`
 
 error: used import from `std` instead of `core`
-  --> $DIR/std_instead_of_core.rs:14:27
+  --> $DIR/std_instead_of_core.rs:16:27
    |
 LL |     use std::fmt::{Debug, Result};
    |                           ^^^^^^
@@ -32,7 +32,7 @@ LL |     use std::fmt::{Debug, Result};
    = help: consider importing the item from `core`
 
 error: used import from `std` instead of `core`
-  --> $DIR/std_instead_of_core.rs:17:15
+  --> $DIR/std_instead_of_core.rs:19:15
    |
 LL |     let ptr = std::ptr::null::<u32>();
    |               ^^^^^^^^^^^^^^^^^^^^^
@@ -40,7 +40,7 @@ LL |     let ptr = std::ptr::null::<u32>();
    = help: consider importing the item from `core`
 
 error: used import from `std` instead of `core`
-  --> $DIR/std_instead_of_core.rs:18:19
+  --> $DIR/std_instead_of_core.rs:20:19
    |
 LL |     let ptr_mut = ::std::ptr::null_mut::<usize>();
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -48,7 +48,7 @@ LL |     let ptr_mut = ::std::ptr::null_mut::<usize>();
    = help: consider importing the item from `core`
 
 error: used import from `std` instead of `core`
-  --> $DIR/std_instead_of_core.rs:21:16
+  --> $DIR/std_instead_of_core.rs:23:16
    |
 LL |     let cell = std::cell::Cell::new(8u32);
    |                ^^^^^^^^^^^^^^^
@@ -56,7 +56,7 @@ LL |     let cell = std::cell::Cell::new(8u32);
    = help: consider importing the item from `core`
 
 error: used import from `std` instead of `core`
-  --> $DIR/std_instead_of_core.rs:22:25
+  --> $DIR/std_instead_of_core.rs:24:25
    |
 LL |     let cell_absolute = ::std::cell::Cell::new(8u32);
    |                         ^^^^^^^^^^^^^^^^^
@@ -64,16 +64,24 @@ LL |     let cell_absolute = ::std::cell::Cell::new(8u32);
    = help: consider importing the item from `core`
 
 error: used import from `std` instead of `alloc`
-  --> $DIR/std_instead_of_core.rs:27:9
+  --> $DIR/std_instead_of_core.rs:32:9
+   |
+LL |     use std::vec;
+   |         ^^^^^^^^
+   |
+   = note: `-D clippy::std-instead-of-alloc` implied by `-D warnings`
+   = help: consider importing the item from `alloc`
+
+error: used import from `std` instead of `alloc`
+  --> $DIR/std_instead_of_core.rs:33:9
    |
 LL |     use std::vec::Vec;
    |         ^^^^^^^^^^^^^
    |
-   = note: `-D clippy::std-instead-of-alloc` implied by `-D warnings`
    = help: consider importing the item from `alloc`
 
 error: used import from `alloc` instead of `core`
-  --> $DIR/std_instead_of_core.rs:32:9
+  --> $DIR/std_instead_of_core.rs:38:9
    |
 LL |     use alloc::slice::from_ref;
    |         ^^^^^^^^^^^^^^^^^^^^^^
@@ -81,5 +89,5 @@ LL |     use alloc::slice::from_ref;
    = note: `-D clippy::alloc-instead-of-core` implied by `-D warnings`
    = help: consider importing the item from `core`
 
-error: aborting due to 10 previous errors
+error: aborting due to 11 previous errors