rustc_session/config/
externs.rs1use std::path::PathBuf;
4
5use rustc_errors::{Diag, FatalAbort};
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>(
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 (None, name)
40 } else {
41 (Some(opts.to_string()), crate_name.to_string())
42 }
43 }
44 };
45
46 if unstable_opts.namespaced_crates && crate_name.split("::").count() > 2 {
48 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!(
49 "crate name `{crate_name}` passed to `--extern` can have at most two segments."
50 )));
51 }
52
53 if !valid_crate_name(&crate_name, unstable_opts) {
54 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!(
55 "crate name `{crate_name}` passed to `--extern` is not a valid ASCII identifier"
56 ));
57 let adjusted_name = crate_name.replace('-', "_");
58 if is_ascii_ident(&adjusted_name) {
59 error
60 .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}`"));
61 }
62 return Err(error);
63 }
64
65 Ok(ExternOpt { crate_name, path, options })
66}
67
68fn valid_crate_name(name: &str, unstable_opts: &UnstableOptions) -> bool {
69 match name.split_once("::") {
70 Some((a, b)) if unstable_opts.namespaced_crates => is_ascii_ident(a) && is_ascii_ident(b),
71 Some(_) => false,
72 None => is_ascii_ident(name),
73 }
74}
75
76fn is_ascii_ident(string: &str) -> bool {
77 let mut chars = string.chars();
78 if let Some(start) = chars.next()
79 && (start.is_ascii_alphabetic() || start == '_')
80 {
81 chars.all(|char| char.is_ascii_alphanumeric() || char == '_')
82 } else {
83 false
84 }
85}