Closed
Description
I tried this code:
use std::ffi::{OsStr, OsString};
use std::os::unix::ffi::{OsStrExt, OsStringExt};
use std::path::{Path, PathBuf};
use std::{env, fs};
fn main() {
println!("# {}", normalize_path("etc/././a/.//..//").display());
}
const PATH_DOT_: u8 = '.' as u8;
const PATH_SEPARATOR: u8 = '/' as u8;
const PATH_DOT: &[u8] = &[PATH_DOT_];
const PATH_DOT_DOT: &[u8] = &[PATH_DOT_, PATH_DOT_];
fn normalize_path(path: impl AsRef<Path>) -> PathBuf {
let (existent_path, existent_len) = try_canonicalize(&path);
let buffer = if let Some(existent_path) = existent_path {
existent_path.into_os_string().into_vec()
} else if path.as_ref().is_absolute() {
vec![PATH_SEPARATOR]
} else {
let cur_dir = env::current_dir().unwrap_or_else(|_e| PathBuf::from("./"));
cur_dir.into_os_string().into_vec()
};
let bytes = &path.as_ref().as_os_str().as_bytes()[existent_len..];
let buffer = bytes
.split(is_path_separator)
.fold(buffer, append_to_existing_path);
PathBuf::from(OsString::from_vec(buffer))
}
fn is_path_separator(e: &u8) -> bool {
*e == PATH_SEPARATOR
}
fn try_canonicalize(path: impl AsRef<Path>) -> (Option<PathBuf>, usize) {
let mut bytes = path.as_ref().as_os_str().as_bytes();
if bytes.is_empty() {
return (None, 0);
}
loop {
let sub_path = Path::new(OsStr::from_bytes(bytes));
if let Ok(r) = fs::canonicalize(sub_path) {
return (Some(r), bytes.len());
}
let mut iter = bytes.rsplitn(2, is_path_separator);
if let Some(_last) = iter.next() {
if let Some(prefix) = iter.next() {
bytes = prefix;
} else {
return (None, 0);
}
} else {
return (None, 0);
}
}
}
enum ParentPathAction {
CurrentDirectory,
TrimRight(usize),
AppendElement,
}
fn append_to_existing_path(mut path: Vec<u8>, element: &[u8]) -> Vec<u8> {
match element {
&[] | PATH_DOT => path,
PATH_DOT_DOT => {
let action = {
let mut iter = path.rsplitn(2, is_path_separator);
if let Some(parent) = iter.next() {
if let Some(prefix) = iter.next() {
if prefix.is_empty() {
match parent {
PATH_DOT | PATH_DOT_DOT => {
// "/../.." => "/"
ParentPathAction::TrimRight(parent.len())
}
_ => {
// "/parent/.." => "/"
ParentPathAction::TrimRight(parent.len())
}
}
} else {
match parent {
PATH_DOT | PATH_DOT_DOT => {
// "prefix/../.." => "prefix/../.."
ParentPathAction::AppendElement
}
_ => {
// "prefix/parent/.." => "prefix"
ParentPathAction::TrimRight(parent.len() + 1)
}
}
}
} else {
// "parent/.." => "<current_dir>"
ParentPathAction::CurrentDirectory
}
} else {
// ".." => ".."
ParentPathAction::AppendElement
}
};
match action {
ParentPathAction::CurrentDirectory => {
let cur_dir = env::current_dir().unwrap_or_else(|_e| PathBuf::from("."));
cur_dir.into_os_string().into_vec()
}
ParentPathAction::TrimRight(trim) => {
let new_len = path.len() - trim;
path.truncate(new_len);
path
}
ParentPathAction::AppendElement => {
let path = PathBuf::from(OsString::from_vec(path));
let element = Path::new(OsStr::from_bytes(element));
path.join(element).into_os_string().into_vec()
}
}
}
_ => {
let path = PathBuf::from(OsString::from_vec(path));
let element = Path::new(OsStr::from_bytes(element));
path.join(element).into_os_string().into_vec()
}
}
}
I expected to see this happen: compilation error or compilation success.
Instead, this happened: compiler crashed into an impossible case.
Meta
$ rustc --version --verbose
rustc 1.26.2 (594fb253c 2018-06-01)
binary: rustc
commit-hash: 594fb253c2b02b320c728391a425d028e6dc7a09
commit-date: 2018-06-01
host: x86_64-unknown-linux-gnu
release: 1.26.2
LLVM version: 6.0
Backtrace:
librustc_mir/hair/pattern/_match.rs:959: impossible case reached
Error messages:
$ cargo build --verbose
Compiling paths v0.1.0 (file:///media/data/openjdk/126101-infrastructure/license-analyzer/paths)
Running `rustc --crate-name paths src/main.rs --crate-type bin --emit=dep-info,link -C debuginfo=2 -C metadata=6e46ca38d9a06af0 -C extra-filename=-6e46ca38d9a06af0 --out-dir /media/data/openjdk/126101-infrastructure/license-analyzer/paths/target/debug/deps -C incremental=/media/data/openjdk/126101-infrastructure/license-analyzer/paths/target/debug/incremental -L dependency=/media/data/openjdk/126101-infrastructure/license-analyzer/paths/target/debug/deps`
error: internal compiler error: librustc_mir/hair/pattern/_match.rs:959: impossible case reached
thread 'rustc' panicked at 'Box<Any>', librustc_errors/lib.rs:543:9
note: Run with `RUST_BACKTRACE=1` for a backtrace.
error: aborting due to previous error
note: the compiler unexpectedly panicked. this is a bug.
note: we would appreciate a bug report: https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md#bug-reports
note: rustc 1.26.2 (594fb253c 2018-06-01) running on x86_64-unknown-linux-gnu
note: compiler flags: -C debuginfo=2 -C incremental --crate-type bin
note: some of the compiler flags provided by cargo are hidden
error: Could not compile `paths`.
Caused by:
process didn't exit successfully: `rustc --crate-name paths src/main.rs --crate-type bin --emit=dep-info,link -C debuginfo=2 -C metadata=6e46ca38d9a06af0 -C extra-filename=-6e46ca38d9a06af0 --out-dir <somewhere>/paths/target/debug/deps -C incremental=<somewhere>/paths/target/debug/incremental -L dependency=<somewhere>/paths/target/debug/deps` (exit code: 101)