From b8f24e02a0766f8553f08d416b61c1e9449745d4 Mon Sep 17 00:00:00 2001
From: Michael Aaron Murphy <mmstickman@gmail.com>
Date: Thu, 1 Nov 2018 05:25:06 +0000
Subject: [PATCH] Hashbrown Maps

---
 Cargo.lock                                    | 30 ++++++++++++++-----
 Cargo.toml                                    |  2 +-
 examples/maps.ion                             |  9 +++---
 examples/maps.out                             |  9 +++---
 members/builtins/src/test.rs                  |  2 +-
 members/ranges/src/parse.rs                   |  6 +---
 src/lib/builtins/exists.rs                    |  2 +-
 src/lib/lib.rs                                |  2 +-
 src/lib/parser/assignments/actions.rs         |  2 +-
 src/lib/parser/pipelines/collector.rs         |  4 +--
 src/lib/parser/shell_expand/mod.rs            |  8 ++---
 .../shell_expand/words/methods/arrays.rs      |  2 +-
 .../shell_expand/words/methods/strings.rs     |  6 ++--
 src/lib/parser/statement/functions.rs         |  2 +-
 src/lib/shell/assignments.rs                  |  2 +-
 src/lib/shell/binary/readln.rs                |  2 +-
 src/lib/shell/directory_stack.rs              |  2 +-
 src/lib/shell/flow.rs                         | 12 ++++----
 src/lib/shell/flow_control.rs                 | 20 ++++++-------
 src/lib/shell/pipe_exec/mod.rs                |  6 ++--
 src/lib/shell/variables/mod.rs                | 12 ++++----
 src/lib/types.rs                              |  7 ++---
 22 files changed, 81 insertions(+), 68 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 27b3710a..2defe508 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -55,6 +55,11 @@ name = "bytecount"
 version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
+[[package]]
+name = "byteorder"
+version = "1.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
 [[package]]
 name = "calculate"
 version = "0.5.1"
@@ -131,11 +136,6 @@ dependencies = [
  "synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
-[[package]]
-name = "fnv"
-version = "1.0.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
 [[package]]
 name = "fuchsia-zircon"
 version = "0.3.3"
@@ -155,6 +155,15 @@ name = "glob"
 version = "0.2.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
+[[package]]
+name = "hashbrown"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "ion-shell"
 version = "1.0.0-alpha"
@@ -164,8 +173,8 @@ dependencies = [
  "calculate 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "hashbrown 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "ion_braces 0.1.0",
  "ion_builtins 0.1.0",
  "ion_lexers 0.1.0",
@@ -363,6 +372,11 @@ name = "rustc-serialize"
 version = "0.3.24"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
+[[package]]
+name = "scopeguard"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
 [[package]]
 name = "serde"
 version = "1.0.80"
@@ -535,6 +549,7 @@ source = "git+https://github.com/whitequark/rust-xdg#090afef2509d746e48d6bfa9b2e
 "checksum backtrace-sys 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)" = "c66d56ac8dabd07f6aacdaf633f4b8262f5b3601a810a0dcddffd5c22c69daa0"
 "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
 "checksum bytecount 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f861d9ce359f56dbcb6e0c2a1cb84e52ad732cadb57b806adeb3c7668caccbd8"
+"checksum byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "94f88df23a25417badc922ab0f5716cc1330e87f71ddd9203b3a3ccd9cedf75d"
 "checksum calculate 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dfe3fe310e5858ba47beb9443acec7fb39b90ea5677d35636306fe7b495a547c"
 "checksum cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "f159dfd43363c4d08055a07703eb7a3406b0dac4d0584d96965a3262db3c9d16"
 "checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4"
@@ -543,10 +558,10 @@ source = "git+https://github.com/whitequark/rust-xdg#090afef2509d746e48d6bfa9b2e
 "checksum decimal 2.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e6458723bc760383275fbc02f4c769b2e5f3de782abaf5e7e0b9b7f0368a63ed"
 "checksum failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6dd377bcc1b1b7ce911967e3ec24fa19c3224394ec05b54aa7b083d498341ac7"
 "checksum failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "64c2d913fe8ed3b6c6518eedf4538255b989945c14c2a7d5cbff62a5e2120596"
-"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3"
 "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
 "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
 "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
+"checksum hashbrown 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "74c957b0092b8afcac86fd64b83c983b5d2251fd7ad3bac6584e3f76b93825c8"
 "checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b"
 "checksum lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca488b89a5657b0a2ecd45b95609b3e848cf1755da332a0da46e2b2b1cb371a7"
 "checksum libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)" = "76e3a3ef172f1a0b9a9ff0dd1491ae5e6c948b94479a3021819ba7d860c8645d"
@@ -566,6 +581,7 @@ source = "git+https://github.com/whitequark/rust-xdg#090afef2509d746e48d6bfa9b2e
 "checksum regex-syntax 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "747ba3b235651f6e2f67dfa8bcdcd073ddb7c243cb21c442fc12395dfcac212d"
 "checksum rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "bcfe5b13211b4d78e5c2cadfebd7769197d95c639c35a50057eb4c05de811395"
 "checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
+"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27"
 "checksum serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)" = "15c141fc7027dd265a47c090bf864cf62b42c4d228bbcf4e51a0c9e2b0d3f7ef"
 "checksum small 0.1.0 (git+https://gitlab.redox-os.org/redox-os/small)" = "<none>"
 "checksum smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "153ffa32fd170e9944f7e0838edf824a754ec4c1fc64746fcc9fe1f8fa602e5d"
diff --git a/Cargo.toml b/Cargo.toml
index b23c07c0..1be1c4e6 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -37,7 +37,6 @@ bitflags = "1.0"
 calculate = "0.5"
 failure = "0.1"
 failure_derive = "0.1"
-fnv = "1.0"
 glob = "0.2"
 itoa = "0.4"
 lazy_static = "1.0"
@@ -53,6 +52,7 @@ ion_builtins = { path = "members/builtins" }
 ion_lexers = { path = "members/lexers" }
 ion_sys = { path = "members/sys" }
 ion_ranges = { path = "members/ranges" }
+hashbrown = "0.1.2"
 
 [lib]
 path = "src/lib/lib.rs"
diff --git a/examples/maps.ion b/examples/maps.ion
index 64c90c9c..ac17a08e 100644
--- a/examples/maps.ion
+++ b/examples/maps.ion
@@ -1,16 +1,17 @@
 let map:hmap[] = [key1=one key3=three]
-echo @map
 let map[key2] = two
-echo @map
 echo @map[key1]
 echo @map[key2]
 echo @map[key3]
 
 let map:hmap[int] = [uno=1 dos=2 tres=3]
-echo @map
+echo @map[uno]
+echo @map[tres]
+echo @map[dos]
 
 let map:hmap[int[]] = [key1=[1 2 3 4 5] key2=[6 7 8]]
-echo @map
+echo @map[key1]
+echo @map[key2]
 
 let map:bmap[] = [key1=one key3=three]
 echo @map
diff --git a/examples/maps.out b/examples/maps.out
index ba0dd15a..42c1b13a 100644
--- a/examples/maps.out
+++ b/examples/maps.out
@@ -1,10 +1,11 @@
-one three
-one three two
 one
 two
 three
-3 2 1
-1 2 3 4 5 6 7 8
+1
+3
+2
+1 2 3 4 5
+6 7 8
 one three
 one two three
 one
diff --git a/members/builtins/src/test.rs b/members/builtins/src/test.rs
index c4e0e6d7..e4cf98a4 100644
--- a/members/builtins/src/test.rs
+++ b/members/builtins/src/test.rs
@@ -114,7 +114,7 @@ AUTHOR
     Written by Michael Murphy.
 "#;
 
-const QUICK_GUIDE: &'static str = r#"Usage: test [EXPRESSION]
+const QUICK_GUIDE: &str = r#"Usage: test [EXPRESSION]
 Try 'test --help' for more information."#;
 
 pub fn test(args: &[small::String]) -> Result<bool, small::String> {
diff --git a/members/ranges/src/parse.rs b/members/ranges/src/parse.rs
index 9f246042..63e1d08e 100644
--- a/members/ranges/src/parse.rs
+++ b/members/ranges/src/parse.rs
@@ -8,11 +8,7 @@ fn stepped_range_numeric<'a>(
     step: isize,
     nb_digits: usize,
 ) -> Option<Box<Iterator<Item = small::String> + 'a>> {
-    if step == 0 {
-        None
-    } else if start < end && step < 0 {
-        None
-    } else if start > end && step > 0 {
+    if step == 0 || (start < end && step < 0) || (start > end && step > 0) {
         None
     } else {
         let (x, y, ordering) = if start < end {
diff --git a/src/lib/builtins/exists.rs b/src/lib/builtins/exists.rs
index 847a46f6..566328fb 100644
--- a/src/lib/builtins/exists.rs
+++ b/src/lib/builtins/exists.rs
@@ -193,7 +193,7 @@ fn test_evaluate_arguments() {
     // TODO: see test_binary_is_in_path()
     // no argument means we treat it as a string
     assert_eq!(evaluate_arguments(&["-b".into()], &shell), Ok(true));
-    let oldpath = shell.get::<types::Str>("PATH").unwrap_or("/usr/bin".into());
+    let oldpath = shell.get::<types::Str>("PATH").unwrap_or_else(|| "/usr/bin".into());
     shell.set("PATH", "testing/");
 
     assert_eq!(
diff --git a/src/lib/lib.rs b/src/lib/lib.rs
index 32c79798..09755f78 100644
--- a/src/lib/lib.rs
+++ b/src/lib/lib.rs
@@ -7,8 +7,8 @@ extern crate calc;
 extern crate failure;
 #[macro_use]
 extern crate failure_derive;
-extern crate fnv;
 extern crate glob;
+extern crate hashbrown;
 extern crate itoa;
 #[macro_use]
 extern crate lazy_static;
diff --git a/src/lib/parser/assignments/actions.rs b/src/lib/parser/assignments/actions.rs
index 0ccd52c1..aec3c5c5 100644
--- a/src/lib/parser/assignments/actions.rs
+++ b/src/lib/parser/assignments/actions.rs
@@ -81,7 +81,7 @@ impl<'a> Iterator for AssignmentActions<'a> {
             (Some(key), Some(value)) => match key {
                 Ok(key) => {
                     if self.prevkeys.contains(&key.name) {
-                        return Some(Err(AssignmentError::RepeatedKey(key.name)));
+                        Some(Err(AssignmentError::RepeatedKey(key.name)))
                     } else {
                         self.prevkeys.push(key.name);
                         self.prevval = value;
diff --git a/src/lib/parser/pipelines/collector.rs b/src/lib/parser/pipelines/collector.rs
index 183679e0..a0be2223 100644
--- a/src/lib/parser/pipelines/collector.rs
+++ b/src/lib/parser/pipelines/collector.rs
@@ -46,7 +46,7 @@ impl<'a> Collector<'a> {
                 args.push(v.into());
                 Ok(())
             }
-            Err(why) => return Err(why),
+            Err(why) => Err(why),
             _ => Ok(()),
         }
     }
@@ -380,7 +380,7 @@ impl<'a> Collector<'a> {
             // We return an inclusive range to keep the quote type intact
             if let b'\'' = b {
                 bytes.next();
-                return Ok(&self.data[start..i + 1]);
+                return Ok(&self.data[start..=i]);
             }
             bytes.next();
         }
diff --git a/src/lib/parser/shell_expand/mod.rs b/src/lib/parser/shell_expand/mod.rs
index 924f3d1d..a12df51b 100644
--- a/src/lib/parser/shell_expand/mod.rs
+++ b/src/lib/parser/shell_expand/mod.rs
@@ -47,7 +47,7 @@ fn expand_process<E: Expander>(
             return;
         } else if quoted {
             let output: &str = if let Some(pos) = output.rfind(|x| x != '\n') {
-                &output[..pos + 1]
+                &output[..=pos]
             } else {
                 &output
             };
@@ -373,7 +373,7 @@ fn expand_braces<E: Expander>(
         }
     }
     if expanders.is_empty() {
-        expanded_words.push(output.into());
+        expanded_words.push(output);
     } else {
         if !output.is_empty() {
             tokens.push(BraceToken::Normal(output));
@@ -384,7 +384,7 @@ fn expand_braces<E: Expander>(
             .collect();
         let vector_of_arrays: Vec<&[&str]> = tmp.iter().map(AsRef::as_ref).collect();
         for word in braces::expand(&tokens, &*vector_of_arrays) {
-            expanded_words.push(word.into());
+            expanded_words.push(word);
         }
     }
 
@@ -694,7 +694,7 @@ pub(crate) fn expand_tokens<E: Expander>(
 /// if `x=5` and `y=7`
 fn expand_arithmetic<E: Expander>(output: &mut small::String, input: &str, expander: &E) {
     // small::String cannot be created with a capacity of 0 without causing a panic
-    let len = if input.as_bytes().len() > 0 { input.as_bytes().len() } else { 1 };
+    let len = if input.as_bytes().is_empty() { input.as_bytes().len() } else { 1 };
     let mut intermediate = small::String::with_capacity(len);
     let mut varbuf = small::String::new();
     let flush = |var: &mut small::String, out: &mut small::String| {
diff --git a/src/lib/parser/shell_expand/words/methods/arrays.rs b/src/lib/parser/shell_expand/words/methods/arrays.rs
index d6ed6a0f..0e7269da 100644
--- a/src/lib/parser/shell_expand/words/methods/arrays.rs
+++ b/src/lib/parser/shell_expand/words/methods/arrays.rs
@@ -29,7 +29,7 @@ impl<'a> ArrayMethod<'a> {
 
     fn lines<E: Expander>(&self, expand_func: &E) -> Result<Array, &'static str> {
         let variable = self.resolve_var(expand_func);
-        Ok(variable.lines().into_iter().map(types::Str::from).collect())
+        Ok(variable.lines().map(types::Str::from).collect())
     }
 
     fn chars<E: Expander>(&self, expand_func: &E) -> Result<Array, &'static str> {
diff --git a/src/lib/parser/shell_expand/words/methods/strings.rs b/src/lib/parser/shell_expand/words/methods/strings.rs
index 447e80e0..e1ad69ad 100644
--- a/src/lib/parser/shell_expand/words/methods/strings.rs
+++ b/src/lib/parser/shell_expand/words/methods/strings.rs
@@ -14,7 +14,7 @@ use unicode_segmentation::UnicodeSegmentation;
 pub(crate) fn unescape(input: &str) -> Result<small::String, &'static str> {
     let mut check = false;
     // small::String cannot be created with a capacity of 0 without causing a panic
-    let len = if input.len() > 0 { input.len() } else { 1 };
+    let len = if ! input.is_empty() { input.len() } else { 1 };
     let mut out = small::String::with_capacity(len);
     let add_char = |out: &mut small::String, check: &mut bool, c| {
         out.push(c);
@@ -324,8 +324,8 @@ impl<'a> StringMethod<'a> {
                                 if elem != "" && elem != "," {
                                     let elem_str = elem.to_string();
                                     // If the separation commas are properly removed from the pattern, then the cleaning on the next 7 lines is unnecessary
-                                    let elem_str_clean = if elem_str.ends_with(",") {
-                                        let comma_pos = elem_str.rfind(",").unwrap();
+                                    let elem_str_clean = if elem_str.ends_with(',') {
+                                        let comma_pos = elem_str.rfind(',').unwrap();
                                         let (clean, _) = elem_str.split_at(comma_pos);
                                         clean.to_owned()
                                     } else {
diff --git a/src/lib/parser/statement/functions.rs b/src/lib/parser/statement/functions.rs
index 67630f37..e07f5f12 100644
--- a/src/lib/parser/statement/functions.rs
+++ b/src/lib/parser/statement/functions.rs
@@ -38,7 +38,7 @@ pub(crate) fn collect_arguments(args: KeyIterator) -> Result<Vec<KeyBuf>, Functi
             Ok(key) => {
                 let key: KeyBuf = key.into();
                 if keybuf.iter().any(|k| k.name == key.name) {
-                    return Err(FunctionParseError::RepeatedArgument(key.name.into()));
+                    return Err(FunctionParseError::RepeatedArgument(key.name));
                 } else {
                     keybuf.push(key);
                 }
diff --git a/src/lib/shell/assignments.rs b/src/lib/shell/assignments.rs
index 4cbac343..066be075 100644
--- a/src/lib/shell/assignments.rs
+++ b/src/lib/shell/assignments.rs
@@ -3,13 +3,13 @@ use super::{
     status::*,
     Shell,
 };
+use hashbrown::HashMap;
 use itoa;
 use lexers::assignments::{Operator, Primitive};
 use parser::assignments::*;
 use shell::{history::ShellHistory, variables::VariableType};
 use small;
 use std::{
-    collections::HashMap,
     env,
     ffi::OsStr,
     fmt::{self, Display},
diff --git a/src/lib/shell/binary/readln.rs b/src/lib/shell/binary/readln.rs
index b44d34e8..b7a78da1 100644
--- a/src/lib/shell/binary/readln.rs
+++ b/src/lib/shell/binary/readln.rs
@@ -2,7 +2,7 @@ use super::super::{completer::*, Binary, DirectoryStack, Shell, Variables};
 use liner::{BasicCompleter, CursorPosition, Event, EventKind};
 use std::{
     env,
-    io::{self, ErrorKind, Write},
+    io::ErrorKind,
     mem,
     path::PathBuf,
 };
diff --git a/src/lib/shell/directory_stack.rs b/src/lib/shell/directory_stack.rs
index c7f5b2a8..0f559229 100644
--- a/src/lib/shell/directory_stack.rs
+++ b/src/lib/shell/directory_stack.rs
@@ -21,7 +21,7 @@ fn set_current_dir_ion(dir: &Path) -> Result<(), Cow<'static, str>> {
             .unwrap_or_else(|| "?".into()),
     );
 
-    env::set_var("PWD", dir.to_str().unwrap_or_else(|| "?".into()));
+    env::set_var("PWD", dir.to_str().unwrap_or("?"));
     Ok(())
 }
 
diff --git a/src/lib/shell/flow.rs b/src/lib/shell/flow.rs
index e4f9c484..1f6ce50c 100644
--- a/src/lib/shell/flow.rs
+++ b/src/lib/shell/flow.rs
@@ -369,7 +369,7 @@ impl FlowLogic for Shell {
         // in a case statement). For example, checking to see if the value
         // "foo" matches the pattern "bar" would be invoked like so :
         // ```ignore
-        // matches("foo", "bar") 
+        // matches("foo", "bar")
         // ```
         fn matches(lhs: &types::Array, rhs: &types::Array) -> bool {
             for v in lhs {
@@ -394,13 +394,13 @@ impl FlowLogic for Shell {
                             previous_bind = self
                                 .variables
                                 .get::<types::Array>(bind)
-                                .map(|x| VariableType::Array(x));
+                                .map(VariableType::Array);
                             self.variables.set(&bind, value.clone());
                         } else {
                             previous_bind = self
                                 .variables
                                 .get::<types::Str>(bind)
-                                .map(|x| VariableType::Str(x));
+                                .map(VariableType::Str);
                             self.set(&bind, value.join(" "));
                         }
                     }
@@ -440,13 +440,13 @@ impl FlowLogic for Shell {
                             previous_bind = self
                                 .variables
                                 .get::<types::Array>(bind)
-                                .map(|x| VariableType::Array(x));
+                                .map(VariableType::Array);
                             self.variables.set(&bind, value.clone());
                         } else {
                             previous_bind = self
                                 .variables
                                 .get::<types::Str>(bind)
-                                .map(|x| VariableType::Str(x));
+                                .map(VariableType::Str);
                             self.set(&bind, value.join(" "));
                         }
                     }
@@ -559,7 +559,7 @@ fn expand_pipeline(
             statements.remove(0);
 
             // Handle pipeline being broken half by i.e.: '&&' or '||'
-            if statements.len() >= 1 {
+            if ! statements.is_empty() {
                 let err = match statements.last_mut().unwrap() {
                     Statement::And(ref mut boxed_stm)
                     | Statement::Or(ref mut boxed_stm)
diff --git a/src/lib/shell/flow_control.rs b/src/lib/shell/flow_control.rs
index b8bfc9dc..3871a44a 100644
--- a/src/lib/shell/flow_control.rs
+++ b/src/lib/shell/flow_control.rs
@@ -143,7 +143,7 @@ impl FlowControl {
     pub(crate) fn reset(&mut self) { self.block.clear() }
 
     /// Check if there isn't an unfinished block.
-    pub(crate) fn unclosed_block(&self) -> bool { self.block.len() > 0 }
+    pub(crate) fn unclosed_block(&self) -> bool { ! self.block.is_empty() }
 }
 
 impl Default for FlowControl {
@@ -193,7 +193,7 @@ pub(crate) fn insert_statement(
                             // Merge last Case back and pop off Match too
                             insert_into_block(&mut flow_control.block, block)?;
                             let match_stm = flow_control.block.pop().unwrap();
-                            if flow_control.block.len() > 0 {
+                            if ! flow_control.block.is_empty() {
                                 insert_into_block(&mut flow_control.block, match_stm)?;
                             } else {
                                 return Ok(Some(match_stm));
@@ -204,7 +204,7 @@ pub(crate) fn insert_statement(
                 }
             }
         }
-        Statement::And(_) | Statement::Or(_) if flow_control.block.len() > 0 => {
+        Statement::And(_) | Statement::Or(_) if ! flow_control.block.is_empty() => {
             let mut pushed = true;
             if let Some(top) = flow_control.block.last_mut() {
                 match top {
@@ -215,7 +215,7 @@ pub(crate) fn insert_statement(
                         ref mut else_if,
                         ..
                     } => match *mode {
-                        0 if success.len() == 0 => {
+                        0 if success.is_empty() => {
                             // Insert into If expression if there's no previous statement.
                             expression.push(statement.clone());
                         }
@@ -223,7 +223,7 @@ pub(crate) fn insert_statement(
                             // Try to insert into last ElseIf expression if there's no previous
                             // statement.
                             if let Some(mut eif) = else_if.last_mut() {
-                                if eif.success.len() == 0 {
+                                if eif.success.is_empty() {
                                     eif.expression.push(statement.clone());
                                 } else {
                                     pushed = false;
@@ -238,7 +238,7 @@ pub(crate) fn insert_statement(
                     Statement::While {
                         ref mut expression,
                         ref statements,
-                    } => if statements.len() == 0 {
+                    } => if statements.is_empty() {
                         expression.push(statement.clone());
                     } else {
                         pushed = false;
@@ -252,7 +252,7 @@ pub(crate) fn insert_statement(
                 insert_into_block(&mut flow_control.block, statement)?;
             }
         }
-        _ => if flow_control.block.len() > 0 {
+        _ => if ! flow_control.block.is_empty() {
             insert_into_block(&mut flow_control.block, statement)?;
         } else {
             // Filter out toplevel statements that should produce an error
@@ -401,7 +401,7 @@ impl Function {
         Ok(())
     }
 
-    pub(crate) fn get_description<'a>(&'a self) -> Option<&'a small::String> {
+    pub(crate) fn get_description(&self) -> Option<&small::String> {
         self.description.as_ref()
     }
 
@@ -492,7 +492,7 @@ mod tests {
         assert_eq!(res, Ok(None));
 
         let res = insert_statement(&mut flow_control, Statement::Default);
-        if let Err(_) = res {
+        if res.is_err() {
             flow_control.reset();
             assert_eq!(flow_control.block.len(), 0);
         } else {
@@ -524,7 +524,7 @@ mod tests {
         ];
         for err in errs {
             let res = insert_statement(&mut flow_control, err);
-            if let Ok(_) = res {
+            if res.is_ok() {
                 assert!(false);
             }
         }
diff --git a/src/lib/shell/pipe_exec/mod.rs b/src/lib/shell/pipe_exec/mod.rs
index 61925dbb..2ea49aec 100644
--- a/src/lib/shell/pipe_exec/mod.rs
+++ b/src/lib/shell/pipe_exec/mod.rs
@@ -236,7 +236,7 @@ fn do_redirection(piped_commands: SmallVec<[RefinedItem; 16]>)
             (1, _) => job.stdin(inputs[0].get_infile()?),
             _ => {
                 let mut sources = Vec::new();
-                for ref mut input in inputs {
+                for input in &mut inputs {
                     sources.push(if let Some(f) = input.get_infile() {
                         f
                     } else {
@@ -662,11 +662,11 @@ impl PipelineExecution for Shell {
                     .get::<Function>(job.args[0].as_str())
                     .is_some()
                 {
-                    RefinedJob::function(job.args[0].clone().into(), job.args.drain().collect())
+                    RefinedJob::function(job.args[0].clone(), job.args.drain().collect())
                 } else if let Some(builtin) = job.builtin {
                     RefinedJob::builtin(builtin, job.args.drain().collect())
                 } else {
-                    RefinedJob::external(job.args[0].clone().into(), job.args.drain().collect())
+                    RefinedJob::external(job.args[0].clone(), job.args.drain().collect())
                 }
             };
             results.push((refined, job.kind, outputs, inputs));
diff --git a/src/lib/shell/variables/mod.rs b/src/lib/shell/variables/mod.rs
index a1170a54..89b53f05 100644
--- a/src/lib/shell/variables/mod.rs
+++ b/src/lib/shell/variables/mod.rs
@@ -4,7 +4,7 @@ use super::{
     flow_control::Function,
     status::{FAILURE, SUCCESS},
 };
-use fnv::FnvHashMap;
+use hashbrown::HashMap;
 use liner::Context;
 use std::{
     any::TypeId,
@@ -155,14 +155,14 @@ impl fmt::Display for VariableType {
 
 #[derive(Clone, Debug)]
 pub struct Scope {
-    vars: FnvHashMap<types::Str, VariableType>,
+    vars: HashMap<types::Str, VariableType>,
     /// This scope is on a namespace boundary.
     /// Any previous scopes need to be accessed through `super::`.
     namespace: bool,
 }
 
 impl Deref for Scope {
-    type Target = FnvHashMap<types::Str, VariableType>;
+    type Target = HashMap<types::Str, VariableType>;
 
     fn deref(&self) -> &Self::Target { &self.vars }
 }
@@ -180,8 +180,8 @@ pub struct Variables {
 
 impl Default for Variables {
     fn default() -> Self {
-        let mut map: FnvHashMap<types::Str, VariableType> =
-            FnvHashMap::with_capacity_and_hasher(64, Default::default());
+        let mut map: HashMap<types::Str, VariableType> =
+            HashMap::with_capacity(64);
         map.insert(
             "DIRECTORY_STACK_SIZE".into(),
             VariableType::Str("1000".into()),
@@ -266,7 +266,7 @@ impl Variables {
         self.current += 1;
         if self.current >= self.scopes.len() {
             self.scopes.push(Scope {
-                vars: FnvHashMap::with_capacity_and_hasher(64, Default::default()),
+                vars: HashMap::with_capacity(64),
                 namespace,
             });
         } else {
diff --git a/src/lib/types.rs b/src/lib/types.rs
index 9f00d6ea..09eb7229 100644
--- a/src/lib/types.rs
+++ b/src/lib/types.rs
@@ -1,4 +1,4 @@
-use fnv::FnvHashMap;
+use hashbrown::HashMap as HashbrownMap;
 use shell::variables::VariableType;
 use small;
 use smallvec::SmallVec;
@@ -8,7 +8,7 @@ use std::{
 };
 
 pub type Array = SmallVec<[Str; 4]>;
-pub type HashMap = FnvHashMap<Str, VariableType>;
+pub type HashMap = HashbrownMap<Str, VariableType>;
 pub type BTreeMap = StdBTreeMap<Str, VariableType>;
 pub type Str = small::String;
 
@@ -38,7 +38,7 @@ impl Into<Str> for Alias {
 /// `array!` acts like the standard library's `vec!` macro, and can be thought
 /// of as a shorthand for:
 /// ```ignore,rust
-/// Array::from_vec(vec![...]) 
+/// Array::from_vec(vec![...])
 /// ```
 /// Additionally it will call `Into::into` on each of its members so that one
 /// can pass in any type with some `To<SmallString>` implementation; they will
@@ -59,7 +59,6 @@ macro_rules! array [
     ( $($x:expr), *) => ({
         let mut _arr = Array::new();
         $(_arr.push($x.into());)*
-        #[allow(let_and_return)]
         _arr
     })
 ];
-- 
GitLab