Skip to content

Commit a40d15c

Browse files
authored
Unrolled build for rust-lang#139647
Rollup merge of rust-lang#139647 - eholk:package-namespace, r=fmease Add unstable parsing of `--extern foo::bar=libbar.rlib` command line options This is a tiny step towards implementing the rustc side of support for implementing packages as optional namespaces (rust-lang#122349). We add support for parsing command line options like `--extern foo::bar=libbar.rlib` when the `-Z namespaced-crates` option is present. We don't do anything further with them. The next step is to plumb this down to the name resolver. This PR also generally refactors the extern argument parsing code and adds some unit tests to make it clear what forms should be accepted with and without the flag. cc ```@epage``` ```@ehuss```
2 parents c6aad02 + f35c85f commit a40d15c

File tree

4 files changed

+178
-34
lines changed

4 files changed

+178
-34
lines changed

Diff for: compiler/rustc_session/src/config.rs

+5-34
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use std::str::{self, FromStr};
1414
use std::sync::LazyLock;
1515
use std::{cmp, fmt, fs, iter};
1616

17+
use externs::{ExternOpt, split_extern_opt};
1718
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
1819
use rustc_data_structures::stable_hasher::{StableOrd, ToStableHashKey};
1920
use rustc_errors::emitter::HumanReadableErrorType;
@@ -39,6 +40,7 @@ use crate::utils::CanonicalizedPath;
3940
use crate::{EarlyDiagCtxt, HashStableContext, Session, filesearch, lint};
4041

4142
mod cfg;
43+
mod externs;
4244
mod native_libs;
4345
pub mod sigpipe;
4446

@@ -2205,44 +2207,13 @@ pub fn parse_externs(
22052207
matches: &getopts::Matches,
22062208
unstable_opts: &UnstableOptions,
22072209
) -> Externs {
2208-
fn is_ascii_ident(string: &str) -> bool {
2209-
let mut chars = string.chars();
2210-
if let Some(start) = chars.next()
2211-
&& (start.is_ascii_alphabetic() || start == '_')
2212-
{
2213-
chars.all(|char| char.is_ascii_alphanumeric() || char == '_')
2214-
} else {
2215-
false
2216-
}
2217-
}
2218-
22192210
let is_unstable_enabled = unstable_opts.unstable_options;
22202211
let mut externs: BTreeMap<String, ExternEntry> = BTreeMap::new();
22212212
for arg in matches.opt_strs("extern") {
2222-
let (name, path) = match arg.split_once('=') {
2223-
None => (arg, None),
2224-
Some((name, path)) => (name.to_string(), Some(Path::new(path))),
2225-
};
2226-
let (options, name) = match name.split_once(':') {
2227-
None => (None, name),
2228-
Some((opts, name)) => (Some(opts), name.to_string()),
2229-
};
2230-
2231-
if !is_ascii_ident(&name) {
2232-
let mut error = early_dcx.early_struct_fatal(format!(
2233-
"crate name `{name}` passed to `--extern` is not a valid ASCII identifier"
2234-
));
2235-
let adjusted_name = name.replace('-', "_");
2236-
if is_ascii_ident(&adjusted_name) {
2237-
#[allow(rustc::diagnostic_outside_of_impl)] // FIXME
2238-
error.help(format!(
2239-
"consider replacing the dashes with underscores: `{adjusted_name}`"
2240-
));
2241-
}
2242-
error.emit();
2243-
}
2213+
let ExternOpt { crate_name: name, path, options } =
2214+
split_extern_opt(early_dcx, unstable_opts, &arg).unwrap_or_else(|e| e.emit());
22442215

2245-
let path = path.map(|p| CanonicalizedPath::new(p));
2216+
let path = path.map(|p| CanonicalizedPath::new(p.as_path()));
22462217

22472218
let entry = externs.entry(name.to_owned());
22482219

Diff for: compiler/rustc_session/src/config/externs.rs

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
//! This module contains code to help parse and manipulate `--extern` arguments.
2+
3+
use std::path::PathBuf;
4+
5+
use rustc_errors::{Diag, FatalAbort};
6+
7+
use super::UnstableOptions;
8+
use crate::EarlyDiagCtxt;
9+
10+
#[cfg(test)]
11+
mod tests;
12+
13+
/// Represents the pieces of an `--extern` argument.
14+
pub(crate) struct ExternOpt {
15+
pub(crate) crate_name: String,
16+
pub(crate) path: Option<PathBuf>,
17+
pub(crate) options: Option<String>,
18+
}
19+
20+
/// Breaks out the major components of an `--extern` argument.
21+
///
22+
/// The options field will be a string containing comma-separated options that will need further
23+
/// parsing and processing.
24+
pub(crate) fn split_extern_opt<'a>(
25+
early_dcx: &'a EarlyDiagCtxt,
26+
unstable_opts: &UnstableOptions,
27+
extern_opt: &str,
28+
) -> Result<ExternOpt, Diag<'a, FatalAbort>> {
29+
let (name, path) = match extern_opt.split_once('=') {
30+
None => (extern_opt.to_string(), None),
31+
Some((name, path)) => (name.to_string(), Some(PathBuf::from(path))),
32+
};
33+
let (options, crate_name) = match name.split_once(':') {
34+
None => (None, name),
35+
Some((opts, crate_name)) => {
36+
if unstable_opts.namespaced_crates && crate_name.starts_with(':') {
37+
// If the name starts with `:`, we know this was actually something like `foo::bar` and
38+
// not a set of options. We can just use the original name as the crate name.
39+
(None, name)
40+
} else {
41+
(Some(opts.to_string()), crate_name.to_string())
42+
}
43+
}
44+
};
45+
46+
if !valid_crate_name(&crate_name, unstable_opts) {
47+
let mut error = early_dcx.early_struct_fatal(format!(
48+
"crate name `{crate_name}` passed to `--extern` is not a valid ASCII identifier"
49+
));
50+
let adjusted_name = crate_name.replace('-', "_");
51+
if is_ascii_ident(&adjusted_name) {
52+
#[allow(rustc::diagnostic_outside_of_impl)] // FIXME
53+
error
54+
.help(format!("consider replacing the dashes with underscores: `{adjusted_name}`"));
55+
}
56+
return Err(error);
57+
}
58+
59+
Ok(ExternOpt { crate_name, path, options })
60+
}
61+
62+
fn valid_crate_name(name: &str, unstable_opts: &UnstableOptions) -> bool {
63+
match name.split_once("::") {
64+
Some((a, b)) if unstable_opts.namespaced_crates => is_ascii_ident(a) && is_ascii_ident(b),
65+
Some(_) => false,
66+
None => is_ascii_ident(name),
67+
}
68+
}
69+
70+
fn is_ascii_ident(string: &str) -> bool {
71+
let mut chars = string.chars();
72+
if let Some(start) = chars.next()
73+
&& (start.is_ascii_alphabetic() || start == '_')
74+
{
75+
chars.all(|char| char.is_ascii_alphanumeric() || char == '_')
76+
} else {
77+
false
78+
}
79+
}

Diff for: compiler/rustc_session/src/config/externs/tests.rs

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
use std::path::PathBuf;
2+
3+
use super::split_extern_opt;
4+
use crate::EarlyDiagCtxt;
5+
use crate::config::UnstableOptions;
6+
7+
/// Verifies split_extern_opt handles the supported cases.
8+
#[test]
9+
fn test_split_extern_opt() {
10+
let early_dcx = EarlyDiagCtxt::new(<_>::default());
11+
let unstable_opts = &UnstableOptions::default();
12+
13+
let extern_opt =
14+
split_extern_opt(&early_dcx, unstable_opts, "priv,noprelude:foo=libbar.rlib").unwrap();
15+
assert_eq!(extern_opt.crate_name, "foo");
16+
assert_eq!(extern_opt.path, Some(PathBuf::from("libbar.rlib")));
17+
assert_eq!(extern_opt.options, Some("priv,noprelude".to_string()));
18+
19+
let extern_opt = split_extern_opt(&early_dcx, unstable_opts, "priv,noprelude:foo").unwrap();
20+
assert_eq!(extern_opt.crate_name, "foo");
21+
assert_eq!(extern_opt.path, None);
22+
assert_eq!(extern_opt.options, Some("priv,noprelude".to_string()));
23+
24+
let extern_opt = split_extern_opt(&early_dcx, unstable_opts, "foo=libbar.rlib").unwrap();
25+
assert_eq!(extern_opt.crate_name, "foo");
26+
assert_eq!(extern_opt.path, Some(PathBuf::from("libbar.rlib")));
27+
assert_eq!(extern_opt.options, None);
28+
29+
let extern_opt = split_extern_opt(&early_dcx, unstable_opts, "foo").unwrap();
30+
assert_eq!(extern_opt.crate_name, "foo");
31+
assert_eq!(extern_opt.path, None);
32+
assert_eq!(extern_opt.options, None);
33+
}
34+
35+
/// Tests some invalid cases for split_extern_opt.
36+
#[test]
37+
fn test_split_extern_opt_invalid() {
38+
let early_dcx = EarlyDiagCtxt::new(<_>::default());
39+
let unstable_opts = &UnstableOptions::default();
40+
41+
// too many `:`s
42+
let result = split_extern_opt(&early_dcx, unstable_opts, "priv:noprelude:foo=libbar.rlib");
43+
assert!(result.is_err());
44+
let _ = result.map_err(|e| e.cancel());
45+
46+
// can't nest externs without the unstable flag
47+
let result = split_extern_opt(&early_dcx, unstable_opts, "noprelude:foo::bar=libbar.rlib");
48+
assert!(result.is_err());
49+
let _ = result.map_err(|e| e.cancel());
50+
}
51+
52+
/// Tests some cases for split_extern_opt with nested crates like `foo::bar`.
53+
#[test]
54+
fn test_split_extern_opt_nested() {
55+
let early_dcx = EarlyDiagCtxt::new(<_>::default());
56+
let unstable_opts = &UnstableOptions { namespaced_crates: true, ..Default::default() };
57+
58+
let extern_opt =
59+
split_extern_opt(&early_dcx, unstable_opts, "priv,noprelude:foo::bar=libbar.rlib").unwrap();
60+
assert_eq!(extern_opt.crate_name, "foo::bar");
61+
assert_eq!(extern_opt.path, Some(PathBuf::from("libbar.rlib")));
62+
assert_eq!(extern_opt.options, Some("priv,noprelude".to_string()));
63+
64+
let extern_opt =
65+
split_extern_opt(&early_dcx, unstable_opts, "priv,noprelude:foo::bar").unwrap();
66+
assert_eq!(extern_opt.crate_name, "foo::bar");
67+
assert_eq!(extern_opt.path, None);
68+
assert_eq!(extern_opt.options, Some("priv,noprelude".to_string()));
69+
70+
let extern_opt = split_extern_opt(&early_dcx, unstable_opts, "foo::bar=libbar.rlib").unwrap();
71+
assert_eq!(extern_opt.crate_name, "foo::bar");
72+
assert_eq!(extern_opt.path, Some(PathBuf::from("libbar.rlib")));
73+
assert_eq!(extern_opt.options, None);
74+
75+
let extern_opt = split_extern_opt(&early_dcx, unstable_opts, "foo::bar").unwrap();
76+
assert_eq!(extern_opt.crate_name, "foo::bar");
77+
assert_eq!(extern_opt.path, None);
78+
assert_eq!(extern_opt.options, None);
79+
}
80+
81+
/// Tests some invalid cases for split_extern_opt with nested crates like `foo::bar`.
82+
#[test]
83+
fn test_split_extern_opt_nested_invalid() {
84+
let early_dcx = EarlyDiagCtxt::new(<_>::default());
85+
let unstable_opts = &UnstableOptions { namespaced_crates: true, ..Default::default() };
86+
87+
// crates can only be nested one deep.
88+
let result =
89+
split_extern_opt(&early_dcx, unstable_opts, "priv,noprelude:foo::bar::baz=libbar.rlib");
90+
assert!(result.is_err());
91+
let _ = result.map_err(|e| e.cancel());
92+
}

Diff for: compiler/rustc_session/src/options.rs

+2
Original file line numberDiff line numberDiff line change
@@ -2334,6 +2334,8 @@ options! {
23342334
"the size at which the `large_assignments` lint starts to be emitted"),
23352335
mutable_noalias: bool = (true, parse_bool, [TRACKED],
23362336
"emit noalias metadata for mutable references (default: yes)"),
2337+
namespaced_crates: bool = (false, parse_bool, [TRACKED],
2338+
"allow crates to be namespaced by other crates (default: no)"),
23372339
next_solver: NextSolverConfig = (NextSolverConfig::default(), parse_next_solver_config, [TRACKED],
23382340
"enable and configure the next generation trait solver used by rustc"),
23392341
nll_facts: bool = (false, parse_bool, [UNTRACKED],

0 commit comments

Comments
 (0)