Skip to content

Commit

Permalink
Add import_rename lint, this adds a field on the Conf struct
Browse files Browse the repository at this point in the history
  • Loading branch information
DevinR528 committed May 31, 2021
1 parent 5cdba7d commit 405712b
Show file tree
Hide file tree
Showing 8 changed files with 156 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2432,6 +2432,7 @@ Released 2018-09-13
[`implicit_hasher`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_hasher
[`implicit_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_return
[`implicit_saturating_sub`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_saturating_sub
[`import_rename`]: https://rust-lang.github.io/rust-clippy/master/index.html#import_rename
[`imprecise_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#imprecise_flops
[`inconsistent_digit_grouping`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_digit_grouping
[`inconsistent_struct_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_struct_constructor
Expand Down
97 changes: 97 additions & 0 deletions clippy_lints/src/import_rename.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
use clippy_utils::{diagnostics::span_lint_and_sugg, source::snippet_opt};

use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
use rustc_hir::{def::Res, Item, ItemKind, UseKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::Symbol;

declare_clippy_lint! {
/// **What it does:** Checks that any import matching `import_renames` is renamed.
///
/// **Why is this bad?** This is purely about consistency, it is not bad.
///
/// **Known problems:** Any import must use the original fully qualified path.
///
/// For example, if you want to rename `serde_json::Value`, your clippy.toml
/// configuration would look like
/// `import-renames = [ { path: "serde_json::value::Value", rename = "JsonValue" }]` and not
/// `import-renames = [ { path: "serde_json::Value", rename = "JsonValue" }]` as you might expect.
///
///
/// **Example:**
///
/// ```rust,ignore
/// use serde_json::Value;
/// ```
/// Use instead:
/// ```rust,ignore
/// use serde_json::Value as JsonValue;
/// ```
pub IMPORT_RENAME,
restriction,
"enforce import renames"
}

pub struct ImportRename {
imports: FxHashSet<(Vec<Symbol>, Symbol)>,
}

impl ImportRename {
pub fn new(imports: &FxHashSet<(String, String)>) -> Self {
Self {
imports: imports
.iter()
.map(|(orig, rename)| {
(
orig.split("::").map(|seg| Symbol::intern(seg)).collect::<Vec<_>>(),
Symbol::intern(rename),
)
})
.collect(),
}
}
}

impl_lint_pass!(ImportRename => [IMPORT_RENAME]);

impl LateLintPass<'_> for ImportRename {
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
if_chain! {
if let ItemKind::Use(path, UseKind::Single) = &item.kind;
if let Res::Def(_, id) = path.res;
if let Some(snip) = snippet_opt(cx, item.span);
let use_path = cx.get_def_path(id);
if let Some((_, name)) = self.imports.iter().find(|(path, _)| path == &use_path);

// TODO: alternatively we could use the def_path_str() instead of the full path which would somewhat fix the
// problem of having to use fully qualified paths... although then nested imports kinda breaks down
//
// let orig_import = cx.tcx.def_path_str(id)
// .map(|seg| Symbol::intern(seg)).collect::<Vec<_>>();
// if let Some((_, name)) = self.imports.iter().find(|(path, _)| {
// path == &orig_import
// });

// Remove semicolon since it is not present for nested imports
if !snip.replace(";", "").ends_with(&format!("{}", name));
then {
span_lint_and_sugg(
cx,
IMPORT_RENAME,
item.span,
"this import should be renamed",
"try",
format!(
"{} as {}{}",
snip.split_whitespace().take(2).collect::<Vec<_>>().join(" "),
name,
if snip.ends_with(';') { ";" } else { "" },
),
Applicability::MachineApplicable,
);
}
}
}
}
5 changes: 5 additions & 0 deletions clippy_lints/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ mod if_then_some_else_none;
mod implicit_hasher;
mod implicit_return;
mod implicit_saturating_sub;
mod import_rename;
mod inconsistent_struct_constructor;
mod indexing_slicing;
mod infinite_iter;
Expand Down Expand Up @@ -646,6 +647,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
implicit_hasher::IMPLICIT_HASHER,
implicit_return::IMPLICIT_RETURN,
implicit_saturating_sub::IMPLICIT_SATURATING_SUB,
import_rename::IMPORT_RENAME,
inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR,
indexing_slicing::INDEXING_SLICING,
indexing_slicing::OUT_OF_BOUNDS_INDEXING,
Expand Down Expand Up @@ -994,6 +996,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(float_literal::LOSSY_FLOAT_LITERAL),
LintId::of(if_then_some_else_none::IF_THEN_SOME_ELSE_NONE),
LintId::of(implicit_return::IMPLICIT_RETURN),
LintId::of(import_rename::IMPORT_RENAME),
LintId::of(indexing_slicing::INDEXING_SLICING),
LintId::of(inherent_impl::MULTIPLE_INHERENT_IMPL),
LintId::of(integer_division::INTEGER_DIVISION),
Expand Down Expand Up @@ -2060,6 +2063,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(move || box if_then_some_else_none::IfThenSomeElseNone::new(msrv));
store.register_early_pass(|| box bool_assert_comparison::BoolAssertComparison);
store.register_late_pass(|| box unused_async::UnusedAsync);
let renames = conf.import_renames.iter().map(|rn| (rn.path.clone(), rn.rename.clone())).collect::<FxHashSet<_>>();
store.register_late_pass(move || box import_rename::ImportRename::new(&renames));

}

Expand Down
9 changes: 9 additions & 0 deletions clippy_lints/src/utils/conf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ use std::error::Error;
use std::path::{Path, PathBuf};
use std::{env, fmt, fs, io};

/// Holds information used by `IMPORT_RENAME` lint.
#[derive(Debug, Default, Deserialize)]
pub struct Rename {
pub path: String,
pub rename: String,
}

/// Conf with parse errors
#[derive(Default)]
pub struct TryConf {
Expand Down Expand Up @@ -196,6 +203,8 @@ define_Conf! {
(upper_case_acronyms_aggressive: bool = false),
/// Lint: _CARGO_COMMON_METADATA. For internal testing only, ignores the current `publish` settings in the Cargo manifest.
(cargo_ignore_publish: bool = false),
/// Lint: IMPORT_RENAMES. The list of imports to always rename, a fully qualified path followed by the rename.
(import_renames: Vec<crate::utils::conf::Rename> = Vec::new()),
}

/// Search for the configuration file.
Expand Down
7 changes: 7 additions & 0 deletions tests/ui-toml/toml_import_rename/clippy.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import-renames = [
{ path = "syn::path::Path", rename = "SynPath" },
{ path = "serde::de::Deserializer", rename = "SomethingElse" },
{ path = "serde::ser::Serializer", rename = "DarkTower" },
{ path = "alloc::collections::btree::map::BTreeMap", rename = "Map" },
{ path = "regex::bytes", rename = "foo" },
]
14 changes: 14 additions & 0 deletions tests/ui-toml/toml_import_rename/conf_import_rename.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#![allow(unused_imports, dead_code)]
#![warn(clippy::import_rename)]

extern crate regex;
extern crate serde;
extern crate syn;

use regex::bytes as xegersetyb;
use serde::{de::Deserializer as WrongRename, ser::Serializer as DarkTower};
use syn::Path as SynPath;

fn main() {
use std::collections::BTreeMap as OopsWrongRename;
}
22 changes: 22 additions & 0 deletions tests/ui-toml/toml_import_rename/conf_import_rename.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
error: this import should be renamed
--> $DIR/conf_import_rename.rs:8:1
|
LL | use regex::bytes as xegersetyb;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `use regex::bytes as foo;`
|
= note: `-D clippy::import-rename` implied by `-D warnings`

error: this import should be renamed
--> $DIR/conf_import_rename.rs:9:13
|
LL | use serde::{de::Deserializer as WrongRename, ser::Serializer as DarkTower};
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `de::Deserializer as as SomethingElse`

error: this import should be renamed
--> $DIR/conf_import_rename.rs:13:5
|
LL | use std::collections::BTreeMap as OopsWrongRename;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `use std::collections::BTreeMap as Map;`

error: aborting due to 3 previous errors

2 changes: 1 addition & 1 deletion tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `third-party` at line 5 column 1
error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `import-renames`, `third-party` at line 5 column 1

error: aborting due to previous error

0 comments on commit 405712b

Please sign in to comment.