Skip to content

Commit

Permalink
Auto merge of #55007 - davidtwco:issue-39175, r=petrochenkov
Browse files Browse the repository at this point in the history
Help suggests non-existent package std::os::ext::process::CommandExt

Fixes #39175.
  • Loading branch information
bors committed Oct 20, 2018
2 parents 42dde96 + 4334aaa commit 89be71a
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 10 deletions.
74 changes: 64 additions & 10 deletions src/librustc/ty/item_path.rs
Expand Up @@ -10,7 +10,7 @@

use hir::map::DefPathData;
use hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX, LOCAL_CRATE};
use ty::{self, Ty, TyCtxt};
use ty::{self, DefIdTree, Ty, TyCtxt};
use middle::cstore::{ExternCrate, ExternCrateSource};
use syntax::ast;
use syntax::symbol::{keywords, LocalInternedString, Symbol};
Expand Down Expand Up @@ -219,19 +219,73 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
cur_def_key = self.def_key(parent);
}

let visible_parent = visible_parent_map.get(&cur_def).cloned();
let actual_parent = self.parent(cur_def);
debug!(
"try_push_visible_item_path: visible_parent={:?} actual_parent={:?}",
visible_parent, actual_parent,
);

let data = cur_def_key.disambiguated_data.data;
let symbol = data.get_opt_name().map(|n| n.as_str()).unwrap_or_else(|| {
if let DefPathData::CrateRoot = data { // reexported `extern crate` (#43189)
self.original_crate_name(cur_def.krate).as_str()
} else {
Symbol::intern("<unnamed>").as_str()
}
});
let symbol = match data {
// In order to output a path that could actually be imported (valid and visible),
// we need to handle re-exports correctly.
//
// For example, take `std::os::unix::process::CommandExt`, this trait is actually
// defined at `std::sys::unix::ext::process::CommandExt` (at time of writing).
//
// `std::os::unix` rexports the contents of `std::sys::unix::ext`. `std::sys` is
// private so the "true" path to `CommandExt` isn't accessible.
//
// In this case, the `visible_parent_map` will look something like this:
//
// (child) -> (parent)
// `std::sys::unix::ext::process::CommandExt` -> `std::sys::unix::ext::process`
// `std::sys::unix::ext::process` -> `std::sys::unix::ext`
// `std::sys::unix::ext` -> `std::os`
//
// This is correct, as the visible parent of `std::sys::unix::ext` is in fact
// `std::os`.
//
// When printing the path to `CommandExt` and looking at the `cur_def_key` that
// corresponds to `std::sys::unix::ext`, we would normally print `ext` and then go
// to the parent - resulting in a mangled path like
// `std::os::ext::process::CommandExt`.
//
// Instead, we must detect that there was a re-export and instead print `unix`
// (which is the name `std::sys::unix::ext` was re-exported as in `std::os`). To
// do this, we compare the parent of `std::sys::unix::ext` (`std::sys::unix`) with
// the visible parent (`std::os`). If these do not match, then we iterate over
// the children of the visible parent (as was done when computing
// `visible_parent_map`), looking for the specific child we currently have and then
// have access to the re-exported name.
DefPathData::Module(module_name) if visible_parent != actual_parent => {
let mut name: Option<ast::Ident> = None;
if let Some(visible_parent) = visible_parent {
for child in self.item_children(visible_parent).iter() {
if child.def.def_id() == cur_def {
name = Some(child.ident);
}
}
}
name.map(|n| n.as_str()).unwrap_or(module_name.as_str())
},
_ => {
data.get_opt_name().map(|n| n.as_str()).unwrap_or_else(|| {
// Re-exported `extern crate` (#43189).
if let DefPathData::CrateRoot = data {
self.original_crate_name(cur_def.krate).as_str()
} else {
Symbol::intern("<unnamed>").as_str()
}
})
},
};
debug!("try_push_visible_item_path: symbol={:?}", symbol);
cur_path.push(symbol);

match visible_parent_map.get(&cur_def) {
Some(&def) => cur_def = def,
match visible_parent {
Some(def) => cur_def = def,
None => return false,
};
}
Expand Down
25 changes: 25 additions & 0 deletions src/test/ui/issues/issue-39175.rs
@@ -0,0 +1,25 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// This test ignores some platforms as the particular extension trait used
// to demonstrate the issue is only available on unix. This is fine as
// the fix to suggested paths is not platform-dependent and will apply on
// these platforms also.

// ignore-windows
// ignore-cloudabi
// ignore-emscripten

use std::process::Command;
// use std::os::unix::process::CommandExt;

fn main() {
Command::new("echo").arg("hello").exec();
}
15 changes: 15 additions & 0 deletions src/test/ui/issues/issue-39175.stderr
@@ -0,0 +1,15 @@
error[E0599]: no method named `exec` found for type `&mut std::process::Command` in the current scope
--> $DIR/issue-39175.rs:24:39
|
LL | Command::new("echo").arg("hello").exec();
| ^^^^
|
= help: items from traits can only be used if the trait is in scope
help: the following trait is implemented but not in scope, perhaps add a `use` for it:
|
LL | use std::os::unix::process::CommandExt;
|

error: aborting due to previous error

For more information about this error, try `rustc --explain E0599`.

0 comments on commit 89be71a

Please sign in to comment.