Skip to content

Commit a1fd944

Browse files
committed
Auto merge of #29456 - alexcrichton:path-hash, r=aturon
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
2 parents 2249b07 + d2dd700 commit a1fd944

File tree

1 file changed

+37
-3
lines changed

1 file changed

+37
-3
lines changed

src/libstd/path.rs

+37-3
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ use borrow::{Borrow, IntoCow, ToOwned, Cow};
103103
use cmp;
104104
use fmt;
105105
use fs;
106+
use hash::{Hash, Hasher};
106107
use io;
107108
use iter;
108109
use mem;
@@ -446,7 +447,7 @@ enum State {
446447
///
447448
/// Does not occur on Unix.
448449
#[stable(feature = "rust1", since = "1.0.0")]
449-
#[derive(Copy, Clone, Eq, Hash, Debug)]
450+
#[derive(Copy, Clone, Eq, Debug)]
450451
pub struct PrefixComponent<'a> {
451452
/// The prefix as an unparsed `OsStr` slice.
452453
raw: &'a OsStr,
@@ -490,6 +491,13 @@ impl<'a> cmp::Ord for PrefixComponent<'a> {
490491
}
491492
}
492493

494+
#[stable(feature = "rust1", since = "1.0.0")]
495+
impl<'a> Hash for PrefixComponent<'a> {
496+
fn hash<H: Hasher>(&self, h: &mut H) {
497+
self.parsed.hash(h);
498+
}
499+
}
500+
493501
/// A single component of a path.
494502
///
495503
/// See the module documentation for an in-depth explanation of components and
@@ -932,7 +940,7 @@ impl<'a> cmp::Ord for Components<'a> {
932940
/// path.push("system32");
933941
/// path.set_extension("dll");
934942
/// ```
935-
#[derive(Clone, Hash)]
943+
#[derive(Clone)]
936944
#[stable(feature = "rust1", since = "1.0.0")]
937945
pub struct PathBuf {
938946
inner: OsString
@@ -1171,6 +1179,13 @@ impl cmp::PartialEq for PathBuf {
11711179
}
11721180
}
11731181

1182+
#[stable(feature = "rust1", since = "1.0.0")]
1183+
impl Hash for PathBuf {
1184+
fn hash<H: Hasher>(&self, h: &mut H) {
1185+
self.as_path().hash(h)
1186+
}
1187+
}
1188+
11741189
#[stable(feature = "rust1", since = "1.0.0")]
11751190
impl cmp::Eq for PathBuf {}
11761191

@@ -1224,7 +1239,6 @@ impl Into<OsString> for PathBuf {
12241239
/// let parent_dir = path.parent();
12251240
/// ```
12261241
///
1227-
#[derive(Hash)]
12281242
#[stable(feature = "rust1", since = "1.0.0")]
12291243
pub struct Path {
12301244
inner: OsStr
@@ -1809,6 +1823,15 @@ impl cmp::PartialEq for Path {
18091823
}
18101824
}
18111825

1826+
#[stable(feature = "rust1", since = "1.0.0")]
1827+
impl Hash for Path {
1828+
fn hash<H: Hasher>(&self, h: &mut H) {
1829+
for component in self.components() {
1830+
component.hash(h);
1831+
}
1832+
}
1833+
}
1834+
18121835
#[stable(feature = "rust1", since = "1.0.0")]
18131836
impl cmp::Eq for Path {}
18141837

@@ -3035,6 +3058,14 @@ mod tests {
30353058

30363059
#[test]
30373060
pub fn test_compare() {
3061+
use hash::{Hash, Hasher, SipHasher};
3062+
3063+
fn hash<T: Hash>(t: T) -> u64 {
3064+
let mut s = SipHasher::new_with_keys(0, 0);
3065+
t.hash(&mut s);
3066+
s.finish()
3067+
}
3068+
30383069
macro_rules! tc(
30393070
($path1:expr, $path2:expr, eq: $eq:expr,
30403071
starts_with: $starts_with:expr, ends_with: $ends_with:expr,
@@ -3045,6 +3076,9 @@ mod tests {
30453076
let eq = path1 == path2;
30463077
assert!(eq == $eq, "{:?} == {:?}, expected {:?}, got {:?}",
30473078
$path1, $path2, $eq, eq);
3079+
assert!($eq == (hash(path1) == hash(path2)),
3080+
"{:?} == {:?}, expected {:?}, got {} and {}",
3081+
$path1, $path2, $eq, hash(path1), hash(path2));
30483082

30493083
let starts_with = path1.starts_with(path2);
30503084
assert!(starts_with == $starts_with,

0 commit comments

Comments
 (0)