diff --git a/src/librustc_interface/tests.rs b/src/librustc_interface/tests.rs index 4c630b56cb4..d4b5e833dfb 100644 --- a/src/librustc_interface/tests.rs +++ b/src/librustc_interface/tests.rs @@ -7,7 +7,7 @@ use rustc::middle::cstore; use rustc::session::config::{build_configuration, build_session_options, to_crate_config}; use rustc::session::config::{LtoCli, LinkerPluginLto, SwitchWithOptPath, ExternEntry}; use rustc::session::config::{Externs, OutputType, OutputTypes, SymbolManglingVersion}; -use rustc::session::config::{rustc_optgroups, Options, ErrorOutputType, Passes}; +use rustc::session::config::{rustc_optgroups, Options, ErrorOutputType, Passes, ExternLocation}; use rustc::session::{build_session, Session}; use rustc::session::search_paths::SearchPath; use std::collections::{BTreeMap, BTreeSet}; @@ -38,14 +38,15 @@ fn mk_session(matches: getopts::Matches) -> (Session, CfgSpecs) { fn new_public_extern_entry(locations: I) -> ExternEntry where S: Into, - I: IntoIterator>, + I: IntoIterator, { - let locations: BTreeSet<_> = locations.into_iter().map(|o| o.map(|s| s.into())) + let locations: BTreeSet<_> = locations.into_iter().map(|s| s.into()) .collect(); ExternEntry { - locations, - is_private_dep: false + location: ExternLocation::ExactPaths(locations), + is_private_dep: false, + add_prelude: true, } } @@ -160,33 +161,33 @@ fn test_externs_tracking_hash_different_construction_order() { v1.externs = Externs::new(mk_map(vec![ ( String::from("a"), - new_public_extern_entry(vec![Some("b"), Some("c")]) + new_public_extern_entry(vec!["b", "c"]) ), ( String::from("d"), - new_public_extern_entry(vec![Some("e"), Some("f")]) + new_public_extern_entry(vec!["e", "f"]) ), ])); v2.externs = Externs::new(mk_map(vec![ ( String::from("d"), - new_public_extern_entry(vec![Some("e"), Some("f")]) + new_public_extern_entry(vec!["e", "f"]) ), ( String::from("a"), - new_public_extern_entry(vec![Some("b"), Some("c")]) + new_public_extern_entry(vec!["b", "c"]) ), ])); v3.externs = Externs::new(mk_map(vec![ ( String::from("a"), - new_public_extern_entry(vec![Some("b"), Some("c")]) + new_public_extern_entry(vec!["b", "c"]) ), ( String::from("d"), - new_public_extern_entry(vec![Some("f"), Some("e")]) + new_public_extern_entry(vec!["f", "e"]) ), ])); diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs index dbf2dcf1c0a..71871373e35 100644 --- a/src/librustc_metadata/creader.rs +++ b/src/librustc_metadata/creader.rs @@ -218,13 +218,14 @@ impl<'a> CrateLoader<'a> { let source = self.cstore.get_crate_data(cnum).source(); if let Some(entry) = self.sess.opts.externs.get(&name.as_str()) { // Only use `--extern crate_name=path` here, not `--extern crate_name`. - let found = entry.locations.iter().filter_map(|l| l.as_ref()).any(|l| { - let l = fs::canonicalize(l).ok(); - source.dylib.as_ref().map(|p| &p.0) == l.as_ref() || - source.rlib.as_ref().map(|p| &p.0) == l.as_ref() - }); - if found { - ret = Some(cnum); + if let Some(mut files) = entry.files() { + if files.any(|l| { + let l = fs::canonicalize(l).ok(); + source.dylib.as_ref().map(|p| &p.0) == l.as_ref() || + source.rlib.as_ref().map(|p| &p.0) == l.as_ref() + }) { + ret = Some(cnum); + } } return } diff --git a/src/librustc_metadata/locator.rs b/src/librustc_metadata/locator.rs index c6fb80eca05..8a1eeea0251 100644 --- a/src/librustc_metadata/locator.rs +++ b/src/librustc_metadata/locator.rs @@ -328,8 +328,9 @@ impl<'a> CrateLocator<'a> { crate_name, exact_paths: if hash.is_none() { sess.opts.externs.get(&crate_name.as_str()).into_iter() - .flat_map(|entry| entry.locations.iter()) - .filter_map(|location| location.clone().map(PathBuf::from)).collect() + .filter_map(|entry| entry.files()) + .flatten() + .map(|location| PathBuf::from(location)).collect() } else { // SVH being specified means this is a transitive dependency, // so `--extern` options do not apply. diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index be36e02f5b5..2aa79d7b0da 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -1126,8 +1126,10 @@ impl<'a> Resolver<'a> { definitions.create_root_def(crate_name, session.local_crate_disambiguator()); let mut extern_prelude: FxHashMap> = - session.opts.externs.iter().map(|kv| (Ident::from_str(kv.0), Default::default())) - .collect(); + session.opts.externs.iter() + .filter(|(_, entry)| entry.add_prelude) + .map(|(name, _)| (Ident::from_str(name), Default::default())) + .collect(); if !attr::contains_name(&krate.attrs, sym::no_core) { extern_prelude.insert(Ident::with_dummy_span(sym::core), Default::default()); diff --git a/src/librustc_session/config.rs b/src/librustc_session/config.rs index 58113bb8cd6..7f3bab8f232 100644 --- a/src/librustc_session/config.rs +++ b/src/librustc_session/config.rs @@ -1,3 +1,5 @@ +// ignore-tidy-filelength + //! Contains infrastructure for configuring the compiler, including parsing //! command-line options. @@ -31,7 +33,7 @@ use std::fmt; use std::str::{self, FromStr}; use std::hash::Hasher; use std::collections::hash_map::DefaultHasher; -use std::iter::FromIterator; +use std::iter::{self, FromIterator}; use std::path::{Path, PathBuf}; pub struct Config { @@ -322,10 +324,35 @@ impl OutputTypes { #[derive(Clone)] pub struct Externs(BTreeMap); -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug)] pub struct ExternEntry { - pub locations: BTreeSet>, - pub is_private_dep: bool + pub location: ExternLocation, + /// Indicates this is a "private" dependency for the + /// `exported_private_dependencies` lint. + /// + /// This can be set with the `priv` option like + /// `--extern priv:name=foo.rlib`. + pub is_private_dep: bool, + /// Add the extern entry to the extern prelude. + /// + /// This can be disabled with the `noprelude` option like + /// `--extern noprelude:name`. + pub add_prelude: bool, +} + +#[derive(Clone, Debug)] +pub enum ExternLocation { + /// Indicates to look for the library in the search paths. + /// + /// Added via `--extern name`. + FoundInLibrarySearchDirectories, + /// The locations where this extern entry must be found. + /// + /// The `CrateLoader` is responsible for loading these and figuring out + /// which one to use. + /// + /// Added via `--extern prelude_name=some_file.rlib` + ExactPaths(BTreeSet), } impl Externs { @@ -342,6 +369,18 @@ impl Externs { } } +impl ExternEntry { + fn new(location: ExternLocation) -> ExternEntry { + ExternEntry { location, is_private_dep: false, add_prelude: false } + } + + pub fn files(&self) -> Option> { + match &self.location { + ExternLocation::ExactPaths(set) => Some(set.iter()), + _ => None, + } + } +} macro_rules! hash_option { ($opt_name:ident, $opt_expr:expr, $sub_hashes:expr, [UNTRACKED]) => ({}); @@ -1869,12 +1908,6 @@ pub fn rustc_optgroups() -> Vec { "Specify where an external rust library is located", "NAME[=PATH]", ), - opt::multi_s( - "", - "extern-private", - "Specify where an extern rust library is located, marking it as a private dependency", - "NAME=PATH", - ), opt::opt_s("", "sysroot", "Override the system root", "PATH"), opt::multi("Z", "", "Set internal debugging options", "FLAG"), opt::opt_s( @@ -2435,43 +2468,105 @@ fn parse_borrowck_mode(dopts: &DebuggingOptions, error_format: ErrorOutputType) } } -fn parse_externs( +pub fn parse_externs( matches: &getopts::Matches, debugging_opts: &DebuggingOptions, error_format: ErrorOutputType, ) -> Externs { - if matches.opt_present("extern-private") && !debugging_opts.unstable_options { - early_error( - ErrorOutputType::default(), - "'--extern-private' is unstable and only \ - available for nightly builds of rustc." - ) - } - - // We start out with a `Vec<(Option, bool)>>`, - // and later convert it into a `BTreeSet<(Option, bool)>` - // This allows to modify entries in-place to set their correct - // 'public' value. + let is_unstable_enabled = debugging_opts.unstable_options; let mut externs: BTreeMap = BTreeMap::new(); - for (arg, private) in matches.opt_strs("extern").into_iter().map(|v| (v, false)) - .chain(matches.opt_strs("extern-private").into_iter().map(|v| (v, true))) { - + for arg in matches.opt_strs("extern") { let mut parts = arg.splitn(2, '='); - let name = parts.next().unwrap_or_else(|| - early_error(error_format, "--extern value must not be empty")); - let location = parts.next().map(|s| s.to_string()); + let name = parts + .next() + .unwrap_or_else(|| early_error(error_format, "--extern value must not be empty")); + let path = parts.next().map(|s| s.to_string()); - let entry = externs - .entry(name.to_owned()) - .or_default(); + let mut name_parts = name.splitn(2, ':'); + let first_part = name_parts.next(); + let second_part = name_parts.next(); + let (options, name) = match (first_part, second_part) { + (Some(opts), Some(name)) => (Some(opts), name), + (Some(name), None) => (None, name), + (None, None) => early_error(error_format, "--extern name must not be empty"), + _ => unreachable!(), + }; + let entry = externs.entry(name.to_owned()); - entry.locations.insert(location.clone()); + use std::collections::btree_map::Entry; - // Crates start out being not private, - // and go to being private if we see an '--extern-private' - // flag - entry.is_private_dep |= private; + let entry = if let Some(path) = path { + // --extern prelude_name=some_file.rlib + match entry { + Entry::Vacant(vacant) => { + let files = BTreeSet::from_iter(iter::once(path)); + vacant.insert(ExternEntry::new(ExternLocation::ExactPaths(files))) + } + Entry::Occupied(occupied) => { + let ext_ent = occupied.into_mut(); + match ext_ent { + ExternEntry { location: ExternLocation::ExactPaths(files), .. } => { + files.insert(path); + } + ExternEntry { + location: location @ ExternLocation::FoundInLibrarySearchDirectories, + .. + } => { + // Exact paths take precedence over search directories. + let files = BTreeSet::from_iter(iter::once(path)); + *location = ExternLocation::ExactPaths(files); + } + } + ext_ent + } + } + } else { + // --extern prelude_name + match entry { + Entry::Vacant(vacant) => { + vacant.insert(ExternEntry::new(ExternLocation::FoundInLibrarySearchDirectories)) + } + Entry::Occupied(occupied) => { + // Ignore if already specified. + occupied.into_mut() + } + } + }; + + let mut is_private_dep = false; + let mut add_prelude = true; + if let Some(opts) = options { + if !is_unstable_enabled { + early_error( + error_format, + "the `-Z unstable-options` flag must also be passed to \ + enable `--extern options", + ); + } + for opt in opts.split(',') { + match opt { + "priv" => is_private_dep = true, + "noprelude" => { + if let ExternLocation::ExactPaths(_) = &entry.location { + add_prelude = false; + } else { + early_error( + error_format, + "the `noprelude` --extern option requires a file path", + ); + } + } + _ => early_error(error_format, &format!("unknown --extern option `{}`", opt)), + } + } + } + + // Crates start out being not private, and go to being private `priv` + // is specified. + entry.is_private_dep |= is_private_dep; + // If any flag is missing `noprelude`, then add to the prelude. + entry.add_prelude |= add_prelude; } Externs(externs) } diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index cdb1a1f6997..0db3d28bf0e 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -7,10 +7,10 @@ use errors; use getopts; use rustc::lint::Level; use rustc::session; -use rustc::session::config::{CrateType, parse_crate_types_from_list}; +use rustc::session::config::{CrateType, parse_crate_types_from_list, parse_externs}; use rustc::session::config::{CodegenOptions, DebuggingOptions, ErrorOutputType, Externs}; use rustc::session::config::{nightly_options, build_codegen_options, build_debugging_options, - get_cmd_lint_options, host_triple, ExternEntry}; + get_cmd_lint_options, host_triple}; use rustc::session::search_paths::SearchPath; use rustc_driver; use rustc_target::spec::TargetTriple; @@ -320,13 +320,7 @@ impl Options { let libs = matches.opt_strs("L").iter() .map(|s| SearchPath::from_cli_opt(s, error_format)) .collect(); - let externs = match parse_externs(&matches) { - Ok(ex) => ex, - Err(err) => { - diag.struct_err(&err).emit(); - return Err(1); - } - }; + let externs = parse_externs(&matches, &debugging_options, error_format); let extern_html_root_urls = match parse_extern_html_roots(&matches) { Ok(ex) => ex, Err(err) => { @@ -617,24 +611,3 @@ fn parse_extern_html_roots( Ok(externs) } - -/// Extracts `--extern CRATE=PATH` arguments from `matches` and -/// returns a map mapping crate names to their paths or else an -/// error message. -/// Also handles `--extern-private` which for the purposes of rustdoc -/// we can treat as `--extern` -// FIXME(eddyb) This shouldn't be duplicated with `rustc::session`. -fn parse_externs(matches: &getopts::Matches) -> Result { - let mut externs: BTreeMap<_, ExternEntry> = BTreeMap::new(); - for arg in matches.opt_strs("extern").iter().chain(matches.opt_strs("extern-private").iter()) { - let mut parts = arg.splitn(2, '='); - let name = parts.next().ok_or("--extern value must not be empty".to_string())?; - let location = parts.next().map(|s| s.to_string()); - let name = name.to_string(); - // For Rustdoc purposes, we can treat all externs as public - externs.entry(name) - .or_default() - .locations.insert(location.clone()); - } - Ok(Externs::new(externs)) -} diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index b77b1c720cf..a524801bea6 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -248,7 +248,9 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt .. } = options; - let extern_names: Vec = externs.iter().map(|(s,_)| s).cloned().collect(); + let extern_names: Vec = externs.iter() + .filter(|(_, entry)| entry.add_prelude) + .map(|(name, _)| name).cloned().collect(); // Add the doc cfg into the doc build. cfgs.push("doc".to_string()); diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index be3644ecf96..a4be3dee938 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -145,10 +145,6 @@ fn opts() -> Vec { stable("extern", |o| { o.optmulti("", "extern", "pass an --extern to rustc", "NAME[=PATH]") }), - unstable("extern-private", |o| { - o.optmulti("", "extern-private", - "pass an --extern to rustc (compatibility only)", "NAME=PATH") - }), unstable("extern-html-root-url", |o| { o.optmulti("", "extern-html-root-url", "base URL to use for dependencies", "NAME=URL") diff --git a/src/test/run-make-fulldeps/extern-flag-noprelude/Makefile b/src/test/run-make-fulldeps/extern-flag-noprelude/Makefile new file mode 100644 index 00000000000..18f9d8bab60 --- /dev/null +++ b/src/test/run-make-fulldeps/extern-flag-noprelude/Makefile @@ -0,0 +1,11 @@ +-include ../tools.mk + +# Test --extern noprelude + +all: + $(RUSTC) dep.rs --crate-name=dep --crate-type=rlib + $(RUSTC) foo.rs --edition=2018 -Zunstable-options \ + --extern noprelude:dep=$(TMPDIR)/libdep.rlib 2>&1 | \ + $(CGREP) -e 'failed to resolve.*`dep`' + $(RUSTC) foo.rs --edition=2018 -Zunstable-options \ + --extern dep=$(TMPDIR)/libdep.rlib diff --git a/src/test/run-make-fulldeps/extern-flag-noprelude/dep.rs b/src/test/run-make-fulldeps/extern-flag-noprelude/dep.rs new file mode 100644 index 00000000000..dd2f373f849 --- /dev/null +++ b/src/test/run-make-fulldeps/extern-flag-noprelude/dep.rs @@ -0,0 +1,3 @@ +pub fn somefun() {} + +pub struct S; diff --git a/src/test/run-make-fulldeps/extern-flag-noprelude/foo.rs b/src/test/run-make-fulldeps/extern-flag-noprelude/foo.rs new file mode 100644 index 00000000000..9bb1b78409f --- /dev/null +++ b/src/test/run-make-fulldeps/extern-flag-noprelude/foo.rs @@ -0,0 +1,3 @@ +fn main() { + dep::somefun(); +} diff --git a/src/test/ui/extern-flag/multiple-opts.rs b/src/test/ui/extern-flag/multiple-opts.rs new file mode 100644 index 00000000000..3dc2f1d73f8 --- /dev/null +++ b/src/test/ui/extern-flag/multiple-opts.rs @@ -0,0 +1,20 @@ +// aux-crate:priv,noprelude:somedep=somedep.rs +// compile-flags: -Zunstable-options +// edition:2018 + +// Test for multiple options to --extern. Can't test for errors from both +// options at the same time, so this only checks that noprelude is honored. + +#![warn(exported_private_dependencies)] + +// Module to avoid adding to prelude. +pub mod m { + extern crate somedep; + pub struct PublicType { + pub field: somedep::S, + } +} + +fn main() { + somedep::somefun(); //~ ERROR failed to resolve +} diff --git a/src/test/ui/extern-flag/multiple-opts.stderr b/src/test/ui/extern-flag/multiple-opts.stderr new file mode 100644 index 00000000000..3bf73d11cfd --- /dev/null +++ b/src/test/ui/extern-flag/multiple-opts.stderr @@ -0,0 +1,9 @@ +error[E0433]: failed to resolve: use of undeclared type or module `somedep` + --> $DIR/multiple-opts.rs:19:5 + | +LL | somedep::somefun(); + | ^^^^^^^ use of undeclared type or module `somedep` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0433`. diff --git a/src/test/ui/extern-flag/noprelude-and-prelude.rs b/src/test/ui/extern-flag/noprelude-and-prelude.rs new file mode 100644 index 00000000000..e6a150b9e8b --- /dev/null +++ b/src/test/ui/extern-flag/noprelude-and-prelude.rs @@ -0,0 +1,10 @@ +// check-pass +// aux-crate:noprelude:somedep=somedep.rs +// compile-flags: -Zunstable-options --extern somedep +// edition:2018 + +// Having a flag with `noprelude` and one without, will add to the prelude. + +fn main() { + somedep::somefun(); +} diff --git a/src/test/ui/extern-flag/public-and-private.rs b/src/test/ui/extern-flag/public-and-private.rs new file mode 100644 index 00000000000..a3a81cbf372 --- /dev/null +++ b/src/test/ui/extern-flag/public-and-private.rs @@ -0,0 +1,13 @@ +// aux-crate:priv:somedep=somedep.rs +// compile-flags: -Zunstable-options --extern somedep +// edition:2018 + +#![deny(exported_private_dependencies)] + +// Having a flag with `priv` and one without, will remain private (it is sticky). + +pub struct PublicType { + pub field: somedep::S, //~ ERROR from private dependency +} + +fn main() {} diff --git a/src/test/ui/extern-flag/public-and-private.stderr b/src/test/ui/extern-flag/public-and-private.stderr new file mode 100644 index 00000000000..72f1bb2d26f --- /dev/null +++ b/src/test/ui/extern-flag/public-and-private.stderr @@ -0,0 +1,14 @@ +error: type `somedep::S` from private dependency 'somedep' in public interface + --> $DIR/public-and-private.rs:10:5 + | +LL | pub field: somedep::S, + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: lint level defined here + --> $DIR/public-and-private.rs:5:9 + | +LL | #![deny(exported_private_dependencies)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index dc4811e5d24..ca30e782c50 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -311,7 +311,7 @@ pub struct TestProps { // directory as the test, but for backwards compatibility reasons // we also check the auxiliary directory) pub aux_builds: Vec, - // A list of crates to pass '--extern-private name:PATH' flags for + // A list of crates to pass '--extern priv:name=PATH' flags for // This should be a subset of 'aux_build' // FIXME: Replace this with a better solution: https://github.com/rust-lang/rust/pull/54020 pub extern_private: Vec, diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 15ae67fb12c..ca68fe3e39b 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -1782,8 +1782,8 @@ impl<'test> TestCx<'test> { let mut add_extern_priv = |priv_dep: &str, dylib: bool| { let lib_name = get_lib_name(priv_dep, dylib); rustc - .arg("--extern-private") - .arg(format!("{}={}", priv_dep, aux_dir.join(lib_name).to_str().unwrap())); + .arg("--extern") + .arg(format!("priv:{}={}", priv_dep, aux_dir.join(lib_name).to_str().unwrap())); }; for rel_ab in &self.props.aux_builds { @@ -1829,9 +1829,9 @@ impl<'test> TestCx<'test> { let trimmed = rel_ab.trim_end_matches(".rs").to_string(); - // Normally, every 'extern-private' has a correspodning 'aux-build' + // Normally, every 'extern-private' has a corresponding 'aux-build' // entry. If so, we remove it from our list of private crates, - // and add an '--extern-private' flag to rustc + // and add an '--extern priv:NAME=PATH' flag to rustc if extern_priv.remove_item(&trimmed).is_some() { add_extern_priv(&trimmed, dylib); } @@ -1859,7 +1859,7 @@ impl<'test> TestCx<'test> { } } - // Add any '--extern-private' entries without a matching + // Add any '--extern' private entries without a matching // 'aux-build' for private_lib in extern_priv { add_extern_priv(&private_lib, true);