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

path: Fix joining Windows path when the receiver is "C:" #11579

Merged
merged 1 commit into from
Jan 16, 2014

Conversation

lilyball
Copy link
Contributor

WindowsPath::new("C:").join("a") produces r"C:\a". This is incorrect.
It should produce "C:a".

WindowsPath::new("C:").join("a") produces r"C:\a". This is incorrect.
It should produce "C:a".
@liigo
Copy link
Contributor

liigo commented Jan 16, 2014

No. C:a is an invalid path in Windows (7). C:\a is the right.

@lilyball
Copy link
Contributor Author

@liigo Unless Windows 7 changed paths dramatically, C:a is a path referencing the folder a, relative to the current working directory in the C: volume. See http://msdn.microsoft.com/en-us/library/aa365247.aspx for details.

@liigo
Copy link
Contributor

liigo commented Jan 16, 2014

@kballard As a user of Windows for over 10 years, I always use C:\Windows\notepad.exe (not C:Windows\notepad.exe).

@lilyball
Copy link
Contributor Author

@liigo Well yes, C:Windows\notepad.exe wouldn't work unless your current directory was C:\. But try this for me: open up cmd, cd into a random folder on your C: drive that has a subdir in it, then try cd C:subdir. It should work.

bors added a commit that referenced this pull request Jan 16, 2014
WindowsPath::new("C:").join("a") produces r"C:�". This is incorrect.
It should produce "C:a".
@chris-morgan
Copy link
Member

@liigo C: is a valid path with the specific meaning @kballard is pointing out and has been since MS-DOS days—it is significantly different semantically to C:\ as it depends upon the process's working directory on the C: drive. This is a well-known and well-documented feature which is at times very useful and at times a nuisance (I dare say it's directly caused a fair bit of data loss by accidental use) but it's certainly real.

@bors bors closed this Jan 16, 2014
@bors bors merged commit c57920b into rust-lang:master Jan 16, 2014
@ytinasni
Copy link

@chris-morgan "...process's working directory on the C: drive..."

A windows process has only one current directory, not one per drive.

The concept of a current directory per-drive (and the use of C: to refer to the current directory on drive C) only exists within cmd.exe, not in the Windows API.

There is no useful action a rust program can perform with a drive-relative path. This patch should be reverted.

The correct behaviour of WindowsPath::new("C:") is to error or to be equivalent to WindowsPath::new("C:")

(The other option is for rust to resurrect the concept of a current directory per drive. This will interact poorly with non-rust libraries that use relative paths or alter the current directory, and will be not be understood by windows programmers. Don't do this.)

See also: http://blogs.msdn.com/b/oldnewthing/archive/2010/10/11/10073890.aspx

@lilyball
Copy link
Contributor Author

@ytinasni If this is true, than we need to do more than just revert this patch. There's other behavior in std::path::windows that treats C:foo as being current-dir-on-C-relative.

What does the Windows API do if you try and use a path that looks like C:foo/bar? Is this even legal? I need to know how this is supposed to be parsed.

@lilyball lilyball deleted the windows-path-join branch January 19, 2014 07:34
@ytinasni
Copy link

https://gist.github.com/ytinasni/8507921

Summary:

  • A drive-relative path is relative to the current directory if the current directory is on that drive.
  • A drive-relative path is treated as an absolute path otherwise.
  • Changing the current directory to a different drive and back will leave you in the root of a drive.

As a user, I would expect that "C:" refers to C:, and "C:a" is an error. I request that Rust implement this.

I maintain my opinion that there is no useful action a rust program can perform with a drive-relative path.

For comparison, in c# the following throws an exception.
Directory.SetCurrentDirectory( @"C:\a" );
Console.WriteLine( File.ReadAllText( "C:a\b.txt" ) ); // throws ArgumentException("Illegal characters in path.")

@lilyball
Copy link
Contributor Author

@ytinasni We don't currently have a notion of an illegal path. There are paths that perhaps don't parse the way you expect, but the only way to actually throw an error is if your path contains non-utf8 (on Windows) or NUL. I don't think it's appropriate to make e.g. "C:a\b" throw an error.

Given that, we need to figure out some way to parse "C:a" that makes sense. We could just interpret it as "C:\a", although that strikes me as less than ideal.

@liigo
Copy link
Contributor

liigo commented Jan 20, 2014

It's bad to be merged. I'll give it a -1. @erickt

drive-relative path (a relative path to the current directory on the drive, such as C:Windows), is belongs to MS-DOS generation, it's out of date, nobody should use it any more, even Windows itself doesn't accept the path C:Windows, see the snapshot (Windows 7, explorer.exe):

invalid-path

On Windows (after Windows 95), the current directory of a drive (C: or D:) is always changeable, you even don't know where is the current directory. That's bad to support.

@alexcrichton @brson

@chris-morgan
Copy link
Member

The summary of the situation appears to be that drive-relative paths are now in fact down to the level of "broken Windows feature", functioning as it did in the days of long ago in some places, not functioning at all in others and implemented brokenly in others. (Thanks for the clarifications there, @ytinasni.)

My own experience (mostly only up to XP) has been that most things work with drive-relative paths, but not quite all (mostly those things that would be doing their own path management rather than using native APIs directly). On a nearby Windows 7 machine with lots of tools on it from the mid '90s, I just tried the GNU ls that was present on it (hmm, from 1997/fileutils 3.16; perhaps this isn't a valuable anecdote after all)—it works just fine with drive-relative paths, just like dir would (with the curious exception that it couldn't cope with E:, claiming "no such file or directory", while E:. or such things would work fine—and of the same set of tools, rm -r E: had worked fine!). 7za 4.42, from 2006: given a command like 7za a E:home.gif.zip E:home.gif it started writing E:\blunt\home.gif.zip as it should but then complained that it couldn't find E:home.gif—so it gets partial marks only. Those are my two readily available pieces of evidence.

@liigo We do not care whether people "should" or "should not" use such things: that is not our domain unless we do rigorous validation of absolutely everything in it at great expense—a model that was tried earlier, weighed in the balances and found wanting for various reasons. What we care about is what behaviour we use when we do encounter such paths, which we inevitably will. Please be careful about taking solely from your own experiences and declaring from that alone that a feature is bad and should not be used—especially when designing libraries, one must necessarily be liberal in what one accepts. Also, as noted by @kballard, reverting this patch alone is not an option; we must do something, one way or another, consistently throughout std::path::windows.

@kballard I am of the opinion that the behaviour that we now have is entirely acceptable and most likely to be what is desired by most users.

@erickt
Copy link
Contributor

erickt commented Jan 21, 2014

If this is only partially supported by applications written by Microsoft itself, I'd be in favor of undoing this patch. To handle this error, we could add a std::path::windows::drive_relative_path condition that gets raised for "C:foo" paths. Then it's at least clear to the user that we're explicitly disallowing this path and not just silently passing it along like Python ntpath library appears to do with:

>>> ntpath.abspath("C:foo")
'\\Users\\erickt\\C:foo'

@ytinasni
Copy link

@erickt The python behaviour is correct. The filename 'c:foo' can refer to an Alternate Data Stream (on ntfs) or an actual file (e.g. a UNC path that refers to a file on a linux server.)

flip1995 pushed a commit to flip1995/rust that referenced this pull request Oct 6, 2023
[`manual_let_else`]: only omit block if span is from same ctxt

Fixes rust-lang#11579.

The lint already had logic for omitting a block in `else` if a block is already present, however this didn't handle the case where the block is from a different expansion/syntax context. E.g.
```rs
macro_rules! panic_in_block {
  () => { { panic!() } }
}

let _ = match Some(1) {
  Some(v) => v,
  _ => panic_in_block!()
};
```
It would see this in its expanded form as `_ => { panic!() }` and think it doesn't have to include a block in its suggestion because it is already there, however that's not true if it's from a different expansion like in this case.

changelog: [`manual_let_else`]: only omit block in suggestion if the block is from the same expansion
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.

6 participants