| //! This module contains code to help parse and manipulate `--extern` arguments. | |
| use std::path::PathBuf; | |
| use rustc_errors::{Diag, FatalAbort}; | |
| use super::UnstableOptions; | |
| use crate::EarlyDiagCtxt; | |
| #[cfg(test)] | |
| mod tests; | |
| /// Represents the pieces of an `--extern` argument. | |
| pub(crate) struct ExternOpt { | |
| pub(crate) crate_name: String, | |
| pub(crate) path: Option<PathBuf>, | |
| pub(crate) options: Option<String>, | |
| } | |
| /// Breaks out the major components of an `--extern` argument. | |
| /// | |
| /// The options field will be a string containing comma-separated options that will need further | |
| /// parsing and processing. | |
| pub(crate) fn split_extern_opt<'a>( | |
| early_dcx: &'a EarlyDiagCtxt, | |
| unstable_opts: &UnstableOptions, | |
| extern_opt: &str, | |
| ) -> Result<ExternOpt, Diag<'a, FatalAbort>> { | |
| let (name, path) = match extern_opt.split_once('=') { | |
| None => (extern_opt.to_string(), None), | |
| Some((name, path)) => (name.to_string(), Some(PathBuf::from(path))), | |
| }; | |
| let (options, crate_name) = match name.split_once(':') { | |
| None => (None, name), | |
| Some((opts, crate_name)) => { | |
| if unstable_opts.namespaced_crates && crate_name.starts_with(':') { | |
| // If the name starts with `:`, we know this was actually something like `foo::bar` and | |
| // not a set of options. We can just use the original name as the crate name. | |
| (None, name) | |
| } else { | |
| (Some(opts.to_string()), crate_name.to_string()) | |
| } | |
| } | |
| }; | |
| // Reject paths with more than two segments. | |
| if unstable_opts.namespaced_crates && crate_name.split("::").count() > 2 { | |
| return Err(early_dcx.early_struct_fatal(format!( | |
| "crate name `{crate_name}` passed to `--extern` can have at most two segments." | |
| ))); | |
| } | |
| if !valid_crate_name(&crate_name, unstable_opts) { | |
| let mut error = early_dcx.early_struct_fatal(format!( | |
| "crate name `{crate_name}` passed to `--extern` is not a valid ASCII identifier" | |
| )); | |
| let adjusted_name = crate_name.replace('-', "_"); | |
| if is_ascii_ident(&adjusted_name) { | |
| error | |
| .help(format!("consider replacing the dashes with underscores: `{adjusted_name}`")); | |
| } | |
| return Err(error); | |
| } | |
| Ok(ExternOpt { crate_name, path, options }) | |
| } | |
| fn valid_crate_name(name: &str, unstable_opts: &UnstableOptions) -> bool { | |
| match name.split_once("::") { | |
| Some((a, b)) if unstable_opts.namespaced_crates => is_ascii_ident(a) && is_ascii_ident(b), | |
| Some(_) => false, | |
| None => is_ascii_ident(name), | |
| } | |
| } | |
| fn is_ascii_ident(string: &str) -> bool { | |
| let mut chars = string.chars(); | |
| if let Some(start) = chars.next() | |
| && (start.is_ascii_alphabetic() || start == '_') | |
| { | |
| chars.all(|char| char.is_ascii_alphanumeric() || char == '_') | |
| } else { | |
| false | |
| } | |
| } |