rustc_session/config/
externs.rs1use std::path::PathBuf;
4
5use rustc_errors::Diag;
6
7use super::UnstableOptions;
8use crate::EarlyDiagCtxt;
9
10#[cfg(test)]
11mod tests;
12
13pub(crate) struct ExternOpt {
15 pub(crate) crate_name: String,
16 pub(crate) path: Option<PathBuf>,
17 pub(crate) options: Option<String>,
18}
19
20pub(crate) fn split_extern_opt<'a>(
27 early_dcx: &'a EarlyDiagCtxt,
28 unstable_opts: &UnstableOptions,
29 extern_opt: &str,
30) -> Result<ExternOpt, Diag<'a>> {
31 let (name, path) = match extern_opt.split_once('=') {
32 None => (extern_opt.to_string(), None),
33 Some((name, path)) => (name.to_string(), Some(PathBuf::from(path))),
34 };
35 let (options, crate_name) = match name.split_once(':') {
36 None => (None, name),
37 Some((opts, crate_name)) => {
38 if unstable_opts.namespaced_crates && crate_name.starts_with(':') {
39 (None, name)
42 } else {
43 (Some(opts.to_string()), crate_name.to_string())
44 }
45 }
46 };
47
48 if unstable_opts.namespaced_crates && crate_name.split("::").count() > 2 {
50 return Err(early_dcx.early_struct_fatal(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("crate name `{0}` passed to `--extern` can have at most two segments.",
crate_name))
})format!(
51 "crate name `{crate_name}` passed to `--extern` can have at most two segments."
52 )));
53 }
54
55 if !valid_crate_name(&crate_name, unstable_opts) {
56 let mut error = early_dcx.early_struct_fatal(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("crate name `{0}` passed to `--extern` is not a valid ASCII identifier",
crate_name))
})format!(
57 "crate name `{crate_name}` passed to `--extern` is not a valid ASCII identifier"
58 ));
59 let adjusted_name = crate_name.replace('-', "_");
60 if is_ascii_ident(&adjusted_name) {
61 error
62 .help(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("consider replacing the dashes with underscores: `{0}`",
adjusted_name))
})format!("consider replacing the dashes with underscores: `{adjusted_name}`"));
63 }
64 return Err(error);
65 }
66
67 Ok(ExternOpt { crate_name, path, options })
68}
69
70fn valid_crate_name(name: &str, unstable_opts: &UnstableOptions) -> bool {
71 match name.split_once("::") {
72 Some((a, b)) if unstable_opts.namespaced_crates => is_ascii_ident(a) && is_ascii_ident(b),
73 Some(_) => false,
74 None => is_ascii_ident(name),
75 }
76}
77
78fn is_ascii_ident(string: &str) -> bool {
79 let mut chars = string.chars();
80 if let Some(start) = chars.next()
81 && (start.is_ascii_alphabetic() || start == '_')
82 {
83 chars.all(|char| char.is_ascii_alphanumeric() || char == '_')
84 } else {
85 false
86 }
87}