Skip to main content

rustc_session/config/
externs.rs

1//! This module contains code to help parse and manipulate `--extern` arguments.
2
3use std::path::PathBuf;
4
5use rustc_errors::Diag;
6
7use super::UnstableOptions;
8use crate::EarlyDiagCtxt;
9
10#[cfg(test)]
11mod tests;
12
13/// Represents the pieces of an `--extern` argument.
14pub(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///
25/// On error, the returned `Diag` is fatal.
26pub(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                // If the name starts with `:`, we know this was actually something like `foo::bar` and
40                // not a set of options. We can just use the original name as the crate name.
41                (None, name)
42            } else {
43                (Some(opts.to_string()), crate_name.to_string())
44            }
45        }
46    };
47
48    // Reject paths with more than two segments.
49    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}