rustc_driver_impl/
args.rs
1use std::{env, error, fmt, fs, io};
2
3use rustc_session::EarlyDiagCtxt;
4
5#[derive(Default)]
7struct Expander {
8 shell_argfiles: bool,
9 next_is_unstable_option: bool,
10 expanded: Vec<String>,
11}
12
13impl Expander {
14 fn arg(&mut self, arg: &str) -> Result<(), Error> {
17 if let Some(argfile) = arg.strip_prefix('@') {
18 match argfile.split_once(':') {
19 Some(("shell", path)) if self.shell_argfiles => {
20 shlex::split(&Self::read_file(path)?)
21 .ok_or_else(|| Error::ShellParseError(path.to_string()))?
22 .into_iter()
23 .for_each(|arg| self.push(arg));
24 }
25 _ => {
26 let contents = Self::read_file(argfile)?;
27 contents.lines().for_each(|arg| self.push(arg.to_string()));
28 }
29 }
30 } else {
31 self.push(arg.to_string());
32 }
33
34 Ok(())
35 }
36
37 fn push(&mut self, arg: String) {
39 if self.next_is_unstable_option {
56 self.inspect_unstable_option(&arg);
57 self.next_is_unstable_option = false;
58 } else if let Some(unstable_option) = arg.strip_prefix("-Z") {
59 if unstable_option.is_empty() {
60 self.next_is_unstable_option = true;
61 } else {
62 self.inspect_unstable_option(unstable_option);
63 }
64 }
65
66 self.expanded.push(arg);
67 }
68
69 fn finish(self) -> Vec<String> {
71 self.expanded
72 }
73
74 fn inspect_unstable_option(&mut self, option: &str) {
76 match option {
77 "shell-argfiles" => self.shell_argfiles = true,
78 _ => (),
79 }
80 }
81
82 fn read_file(path: &str) -> Result<String, Error> {
84 fs::read_to_string(path).map_err(|e| {
85 if e.kind() == io::ErrorKind::InvalidData {
86 Error::Utf8Error(path.to_string())
87 } else {
88 Error::IOError(path.to_string(), e)
89 }
90 })
91 }
92}
93
94#[allow(rustc::untranslatable_diagnostic)] pub fn arg_expand_all(early_dcx: &EarlyDiagCtxt, at_args: &[String]) -> Vec<String> {
102 let mut expander = Expander::default();
103 let mut result = Ok(());
104 for arg in at_args {
105 if let Err(err) = expander.arg(arg) {
106 result = Err(early_dcx.early_err(format!("failed to load argument file: {err}")));
107 }
108 }
109 if let Err(guar) = result {
110 guar.raise_fatal();
111 }
112 expander.finish()
113}
114
115pub fn raw_args(early_dcx: &EarlyDiagCtxt) -> Vec<String> {
121 let mut args = Vec::new();
122 let mut guar = Ok(());
123 for (i, arg) in env::args_os().enumerate() {
124 match arg.into_string() {
125 Ok(arg) => args.push(arg),
126 Err(arg) => {
127 guar =
128 Err(early_dcx.early_err(format!("argument {i} is not valid Unicode: {arg:?}")))
129 }
130 }
131 }
132 if let Err(guar) = guar {
133 guar.raise_fatal();
134 }
135 args
136}
137
138#[derive(Debug)]
139enum Error {
140 Utf8Error(String),
141 IOError(String, io::Error),
142 ShellParseError(String),
143}
144
145impl fmt::Display for Error {
146 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
147 match self {
148 Error::Utf8Error(path) => write!(fmt, "UTF-8 error in {path}"),
149 Error::IOError(path, err) => write!(fmt, "IO error: {path}: {err}"),
150 Error::ShellParseError(path) => write!(fmt, "invalid shell-style arguments in {path}"),
151 }
152 }
153}
154
155impl error::Error for Error {}