Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an internal lint for FxHashMap/FxHashSet #3026

Merged
merged 3 commits into from Aug 14, 2018
Merged

Add an internal lint for FxHashMap/FxHashSet #3026

merged 3 commits into from Aug 14, 2018

Conversation

dwijnand
Copy link
Member

Fixes #2853

My first lint, so feedback and guidance welcome! Thank you.

}

impl EarlyLintPass for PreferFxHash {
fn check_ident(&mut self, cx: &EarlyContext, ident: Ident) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to be the first EarlyLintPass Herr, so you want to use syntax::ast::*;.


impl EarlyLintPass for PreferFxHash {
fn check_ident(&mut self, cx: &EarlyContext, ident: Ident) {
if ident.to_string().contains("HashMap") {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if is an expression in Rust, so you can just use it to decide on the message without all the other code duplicated.


impl EarlyLintPass for PreferFxHash {
fn check_ident(&mut self, cx: &EarlyContext<'_>, ident: Ident) {
if let Some(prefer) = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused variable prefer?


impl EarlyLintPass for PreferFxHash {
fn check_ident(&mut self, cx: &EarlyContext<'_>, ident: Ident) {
if let Some(msg) = {
Copy link
Contributor

@llogiq llogiq Aug 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can get rid of the if let and the Some(_) if you use an early return in the else case.

Like: let msg = if .. { .. } else if .. { .. } else { return; }

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh neat. Will do.

@ghost
Copy link

ghost commented Aug 11, 2018

The name should describe the flaw the lint is catching so that it makes sense when you allow/warn/deny it. #[allow(prefer_fx_hash)] does the opposite of how it reads. This lint should be called something like plain_hash_sets or something like that. Naming is hard. See #2845.

impl EarlyLintPass for PreferFxHash {
fn check_ident(&mut self, cx: &EarlyContext<'_>, ident: Ident) {
if let Some(msg) = {
if ident.to_string().contains("HashMap") {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't this lint FxHashMap as well? Shouldn't it be == instead of contains? 🤔

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah you're right. I don't know Ident, so I'm looking for help on this one..

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Went the easy way..

@dwijnand
Copy link
Member Author

Thanks @mikerite for the feedback. Pushed my attempts to resolving them.


impl LintPass for DefaultHashTypes {
fn get_lints(&self) -> LintArray {
lint_array!(PREFER_FX_HASH)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still the old lint name here

fn check_ident(&mut self, cx: &EarlyContext<'_>, ident: Ident) {
let msg = {
let ident: String = ident.to_string();
if ident.contains("HashMap") && !ident.contains("FxHashMap") {
Copy link
Member

@flip1995 flip1995 Aug 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another solution to consider for this would be to have a FxHashMap with the keys being the disallowed idents (strings) and the value the wanted replacement.

Then you can just

let msg = if let Some(replace) = map.get(ident_string) {
    format!("Prefer {} over {}", replace, ident_string);
} else {
    return
}

This would also be easy to extend, if needed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the to_string of an Ident that .contains("HashMap")? Is it "HashMap"? Or is it something more fully-qualified?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should just be "HashMap" .

@dwijnand dwijnand changed the title Add an internal PreferFxHash lint Add an internal lint for FxHashMap/FxHashSet Aug 11, 2018
@dwijnand
Copy link
Member Author

Even nicer, @flip1995. Thank you!


impl EarlyLintPass for DefaultHashTypes {
fn check_ident(&mut self, cx: &EarlyContext<'_>, ident: Ident) {
let mut map = HashMap::default();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should do this in a new() function on creating the Lint. Doing this here will create a new HashMap everytime you check an ident. This happens pretty often and is bad for the performance.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh and you're using HashMap instead of FxHashMap. 😄

@flip1995
Copy link
Member

LGTM! Could you add a few test cases with #[warn(default_hash_types]?

@dwijnand
Copy link
Member Author

Sure, I'll have to look into how to do that.

@flip1995
Copy link
Member

Thanks! Waiting for the new nightly and a CI rerun. Could you squash some of the commits?

@flip1995
Copy link
Member

Just tested it locally:
You have to update the *.stderr file after afe684c:

stderr diff

 error: Prefer FxHashMap over HashMap, it has better performance and we don't need any collision prevention in clippy
- --> $DIR/fxhash.rs:7:24
+ --> $DIR/fxhash.rs:6:24
   |
-7 | use std::collections::{HashMap, HashSet};
+6 | use std::collections::{HashMap, HashSet};
   |                        ^^^^^^^
   |
   = note: `-D default-hash-types` implied by `-D warnings`

 error: Prefer FxHashSet over HashSet, it has better performance and we don't need any collision prevention in clippy
- --> $DIR/fxhash.rs:7:33
+ --> $DIR/fxhash.rs:6:33
   |
-7 | use std::collections::{HashMap, HashSet};
+6 | use std::collections::{HashMap, HashSet};
   |                                 ^^^^^^^

 error: Prefer FxHashMap over HashMap, it has better performance and we don't need any collision prevention in clippy
-  --> $DIR/fxhash.rs:12:15
+  --> $DIR/fxhash.rs:10:15
    |
-12 |     let _map: HashMap<String, String> = HashMap::default();
+10 |     let _map: HashMap<String, String> = HashMap::default();
    |               ^^^^^^^

 error: Prefer FxHashMap over HashMap, it has better performance and we don't need any collision prevention in clippy
-  --> $DIR/fxhash.rs:12:41
+  --> $DIR/fxhash.rs:10:41
    |
-12 |     let _map: HashMap<String, String> = HashMap::default();
+10 |     let _map: HashMap<String, String> = HashMap::default();
    |                                         ^^^^^^^

 error: Prefer FxHashSet over HashSet, it has better performance and we don't need any collision prevention in clippy
-  --> $DIR/fxhash.rs:13:15
+  --> $DIR/fxhash.rs:11:15
    |
-13 |     let _set: HashSet<String> = HashSet::default();
+11 |     let _set: HashSet<String> = HashSet::default();
    |               ^^^^^^^

 error: Prefer FxHashSet over HashSet, it has better performance and we don't need any collision prevention in clippy
-  --> $DIR/fxhash.rs:13:33
+  --> $DIR/fxhash.rs:11:33
    |
-13 |     let _set: HashSet<String> = HashSet::default();
+11 |     let _set: HashSet<String> = HashSet::default();
    |                                 ^^^^^^^

 error: aborting due to 6 previous errors

let ident_string = ident.to_string();
if let Some(replace) = self.map.get(&ident_string) {
let msg = format!("Prefer {} over {}, it has better performance and we don't need any collision prevention in clippy", replace, ident_string);
cx.span_lint(DEFAULT_HASH_TYPES, ident.span, &msg);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think using the utils::span_lint_and_sugg function here would be even nicer.
https://github.com/rust-lang-nursery/rust-clippy/blob/e6d92f95c6d6964628d75c25fb3d1a58a9f25e5d/clippy_lints/src/utils/mod.rs#L580-L587
help: "use"
sugg: replace

You should always use the span_lint functions from the utils module for Clippy lints, because they add the link to the doc to the warning messages.

Sorry I missed that in the previous review.

@flip1995
Copy link
Member

stderr will need an update

@dwijnand
Copy link
Member Author

Thanks for all the review comments @flip1995: it's been silly mistake after silly mistake from me. 🙂

Copy link
Member

@flip1995 flip1995 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Always happy to help!

@flip1995 flip1995 closed this Aug 14, 2018
@flip1995 flip1995 reopened this Aug 14, 2018
@flip1995 flip1995 merged commit 84aa499 into rust-lang:master Aug 14, 2018
@dwijnand dwijnand deleted the fxhash branch August 14, 2018 11:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants