about summary refs log tree commit diff
path: root/src/libstd
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2015-10-29 10:56:54 -0700
committerAlex Crichton <alex@alexcrichton.com>2015-11-02 12:58:54 -0800
commitd2dd700891f25e71884276d47288f66e4745c5f9 (patch)
treeed2e0e2e5fe724a1ae24e59b15af944015250daf /src/libstd
parent01fd4d622746f03334a6543c1476e5e65712b1cc (diff)
downloadrust-d2dd700891f25e71884276d47288f66e4745c5f9.tar.gz
rust-d2dd700891f25e71884276d47288f66e4745c5f9.zip
std: Base Hash for Path on its iterator
Almost all operations on Path are based on the components iterator in one form
or another to handle equivalent paths. The `Hash` implementations, however,
mistakenly just went straight to the underlying `OsStr`, causing these
equivalent paths to not get merged together.

This commit updates the `Hash` implementation to also be based on the iterator
which should ensure that if two paths are equal they hash to the same thing.

cc #29008, but doesn't close it
Diffstat (limited to 'src/libstd')
-rw-r--r--src/libstd/path.rs40
1 files changed, 37 insertions, 3 deletions
diff --git a/src/libstd/path.rs b/src/libstd/path.rs
index fe12b671235..f1422bb501a 100644
--- a/src/libstd/path.rs
+++ b/src/libstd/path.rs
@@ -103,6 +103,7 @@ use borrow::{Borrow, IntoCow, ToOwned, Cow};
 use cmp;
 use fmt;
 use fs;
+use hash::{Hash, Hasher};
 use io;
 use iter;
 use mem;
@@ -446,7 +447,7 @@ enum State {
 ///
 /// Does not occur on Unix.
 #[stable(feature = "rust1", since = "1.0.0")]
-#[derive(Copy, Clone, Eq, Hash, Debug)]
+#[derive(Copy, Clone, Eq, Debug)]
 pub struct PrefixComponent<'a> {
     /// The prefix as an unparsed `OsStr` slice.
     raw: &'a OsStr,
@@ -490,6 +491,13 @@ impl<'a> cmp::Ord for PrefixComponent<'a> {
     }
 }
 
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<'a> Hash for PrefixComponent<'a> {
+    fn hash<H: Hasher>(&self, h: &mut H) {
+        self.parsed.hash(h);
+    }
+}
+
 /// A single component of a path.
 ///
 /// See the module documentation for an in-depth explanation of components and
@@ -932,7 +940,7 @@ impl<'a> cmp::Ord for Components<'a> {
 /// path.push("system32");
 /// path.set_extension("dll");
 /// ```
-#[derive(Clone, Hash)]
+#[derive(Clone)]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct PathBuf {
     inner: OsString
@@ -1172,6 +1180,13 @@ impl cmp::PartialEq for PathBuf {
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
+impl Hash for PathBuf {
+    fn hash<H: Hasher>(&self, h: &mut H) {
+        self.as_path().hash(h)
+    }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
 impl cmp::Eq for PathBuf {}
 
 #[stable(feature = "rust1", since = "1.0.0")]
@@ -1224,7 +1239,6 @@ impl Into<OsString> for PathBuf {
 /// let parent_dir = path.parent();
 /// ```
 ///
-#[derive(Hash)]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct Path {
     inner: OsStr
@@ -1810,6 +1824,15 @@ impl cmp::PartialEq for Path {
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
+impl Hash for Path {
+    fn hash<H: Hasher>(&self, h: &mut H) {
+        for component in self.components() {
+            component.hash(h);
+        }
+    }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
 impl cmp::Eq for Path {}
 
 #[stable(feature = "rust1", since = "1.0.0")]
@@ -3035,6 +3058,14 @@ mod tests {
 
     #[test]
     pub fn test_compare() {
+        use hash::{Hash, Hasher, SipHasher};
+
+        fn hash<T: Hash>(t: T) -> u64 {
+            let mut s = SipHasher::new_with_keys(0, 0);
+            t.hash(&mut s);
+            s.finish()
+        }
+
         macro_rules! tc(
             ($path1:expr, $path2:expr, eq: $eq:expr,
              starts_with: $starts_with:expr, ends_with: $ends_with:expr,
@@ -3045,6 +3076,9 @@ mod tests {
                  let eq = path1 == path2;
                  assert!(eq == $eq, "{:?} == {:?}, expected {:?}, got {:?}",
                          $path1, $path2, $eq, eq);
+                 assert!($eq == (hash(path1) == hash(path2)),
+                         "{:?} == {:?}, expected {:?}, got {} and {}",
+                         $path1, $path2, $eq, hash(path1), hash(path2));
 
                  let starts_with = path1.starts_with(path2);
                  assert!(starts_with == $starts_with,