Skip to content

Commit 9156164

Browse files
committed
Fix error message for env! when env var is not valid Unicode
1 parent 35dfc67 commit 9156164

File tree

8 files changed

+96
-23
lines changed

8 files changed

+96
-23
lines changed

compiler/rustc_builtin_macros/messages.ftl

+2
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ builtin_macros_env_not_defined = environment variable `{$var}` not defined at co
114114
.cargo = Cargo sets build script variables at run time. Use `std::env::var({$var_expr})` instead
115115
.custom = use `std::env::var({$var_expr})` to read the variable at run time
116116
117+
builtin_macros_env_not_unicode = environment variable `{$var}` is not a valid Unicode string
118+
117119
builtin_macros_env_takes_args = `env!()` takes 1 or 2 arguments
118120
119121
builtin_macros_expected_one_cfg_pattern = expected 1 cfg-pattern

compiler/rustc_builtin_macros/src/env.rs

+30-21
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,19 @@ use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpa
1111
use rustc_span::symbol::{kw, sym, Ident, Symbol};
1212
use rustc_span::Span;
1313
use std::env;
14+
use std::env::VarError;
1415
use thin_vec::thin_vec;
1516

1617
use crate::errors;
1718

18-
fn lookup_env<'cx>(cx: &'cx ExtCtxt<'_>, var: Symbol) -> Option<Symbol> {
19+
fn lookup_env<'cx>(cx: &'cx ExtCtxt<'_>, var: Symbol) -> Result<Symbol, VarError> {
1920
let var = var.as_str();
2021
if let Some(value) = cx.sess.opts.logical_env.get(var) {
21-
return Some(Symbol::intern(value));
22+
return Ok(Symbol::intern(value));
2223
}
2324
// If the environment variable was not defined with the `--env-set` option, we try to retrieve it
2425
// from rustc's environment.
25-
env::var(var).ok().as_deref().map(Symbol::intern)
26+
Ok(Symbol::intern(&env::var(var)?))
2627
}
2728

2829
pub fn expand_option_env<'cx>(
@@ -39,7 +40,7 @@ pub fn expand_option_env<'cx>(
3940
};
4041

4142
let sp = cx.with_def_site_ctxt(sp);
42-
let value = lookup_env(cx, var);
43+
let value = lookup_env(cx, var).ok();
4344
cx.sess.psess.env_depinfo.borrow_mut().insert((var, value));
4445
let e = match value {
4546
None => {
@@ -108,35 +109,43 @@ pub fn expand_env<'cx>(
108109

109110
let span = cx.with_def_site_ctxt(sp);
110111
let value = lookup_env(cx, var);
111-
cx.sess.psess.env_depinfo.borrow_mut().insert((var, value));
112+
cx.sess.psess.env_depinfo.borrow_mut().insert((var, value.as_ref().ok().copied()));
112113
let e = match value {
113-
None => {
114+
Err(err) => {
114115
let ExprKind::Lit(token::Lit {
115116
kind: LitKind::Str | LitKind::StrRaw(..), symbol, ..
116117
}) = &var_expr.kind
117118
else {
118119
unreachable!("`expr_to_string` ensures this is a string lit")
119120
};
120121

121-
let guar = if let Some(msg_from_user) = custom_msg {
122-
cx.dcx().emit_err(errors::EnvNotDefinedWithUserMessage { span, msg_from_user })
123-
} else if is_cargo_env_var(var.as_str()) {
124-
cx.dcx().emit_err(errors::EnvNotDefined::CargoEnvVar {
125-
span,
126-
var: *symbol,
127-
var_expr: var_expr.ast_deref(),
128-
})
129-
} else {
130-
cx.dcx().emit_err(errors::EnvNotDefined::CustomEnvVar {
131-
span,
132-
var: *symbol,
133-
var_expr: var_expr.ast_deref(),
134-
})
122+
let guar = match err {
123+
VarError::NotPresent => {
124+
if let Some(msg_from_user) = custom_msg {
125+
cx.dcx()
126+
.emit_err(errors::EnvNotDefinedWithUserMessage { span, msg_from_user })
127+
} else if is_cargo_env_var(var.as_str()) {
128+
cx.dcx().emit_err(errors::EnvNotDefined::CargoEnvVar {
129+
span,
130+
var: *symbol,
131+
var_expr: var_expr.ast_deref(),
132+
})
133+
} else {
134+
cx.dcx().emit_err(errors::EnvNotDefined::CustomEnvVar {
135+
span,
136+
var: *symbol,
137+
var_expr: var_expr.ast_deref(),
138+
})
139+
}
140+
}
141+
VarError::NotUnicode(_) => {
142+
cx.dcx().emit_err(errors::EnvNotUnicode { span, var: *symbol })
143+
}
135144
};
136145

137146
return ExpandResult::Ready(DummyResult::any(sp, guar));
138147
}
139-
Some(value) => cx.expr_str(span, value),
148+
Ok(value) => cx.expr_str(span, value),
140149
};
141150
ExpandResult::Ready(MacEager::expr(e))
142151
}

compiler/rustc_builtin_macros/src/errors.rs

+8
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,14 @@ pub(crate) enum EnvNotDefined<'a> {
458458
},
459459
}
460460

461+
#[derive(Diagnostic)]
462+
#[diag(builtin_macros_env_not_unicode)]
463+
pub(crate) struct EnvNotUnicode {
464+
#[primary_span]
465+
pub(crate) span: Span,
466+
pub(crate) var: Symbol,
467+
}
468+
461469
#[derive(Diagnostic)]
462470
#[diag(builtin_macros_format_requires_string)]
463471
pub(crate) struct FormatRequiresString {

library/core/src/macros/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1053,7 +1053,8 @@ pub(crate) mod builtin {
10531053
///
10541054
/// If the environment variable is not defined, then a compilation error
10551055
/// will be emitted. To not emit a compile error, use the [`option_env!`]
1056-
/// macro instead.
1056+
/// macro instead. A compilation error will also be emitted if the
1057+
/// environment variable is not a vaild Unicode string.
10571058
///
10581059
/// # Examples
10591060
///

src/tools/run-make-support/src/lib.rs

+27-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use std::env;
2+
use std::ffi::OsStr;
23
use std::path::{Path, PathBuf};
34
use std::process::{Command, Output};
45

@@ -16,7 +17,11 @@ fn setup_common_build_cmd() -> Command {
1617
}
1718

1819
fn handle_failed_output(cmd: &str, output: Output, caller_line_number: u32) -> ! {
19-
eprintln!("command failed at line {caller_line_number}");
20+
if output.status.success() {
21+
eprintln!("command incorrectly succeeded at line {caller_line_number}");
22+
} else {
23+
eprintln!("command failed at line {caller_line_number}");
24+
}
2025
eprintln!("{cmd}");
2126
eprintln!("output status: `{}`", output.status);
2227
eprintln!("=== STDOUT ===\n{}\n\n", String::from_utf8(output.stdout).unwrap());
@@ -53,6 +58,15 @@ impl RustcInvocationBuilder {
5358
self
5459
}
5560

61+
pub fn env(
62+
&mut self,
63+
name: impl AsRef<OsStr>,
64+
value: impl AsRef<OsStr>,
65+
) -> &mut RustcInvocationBuilder {
66+
self.cmd.env(name, value);
67+
self
68+
}
69+
5670
#[track_caller]
5771
pub fn run(&mut self) -> Output {
5872
let caller_location = std::panic::Location::caller();
@@ -64,6 +78,18 @@ impl RustcInvocationBuilder {
6478
}
6579
output
6680
}
81+
82+
#[track_caller]
83+
pub fn run_fail(&mut self) -> Output {
84+
let caller_location = std::panic::Location::caller();
85+
let caller_line_number = caller_location.line();
86+
87+
let output = self.cmd.output().unwrap();
88+
if output.status.success() {
89+
handle_failed_output(&format!("{:#?}", self.cmd), output, caller_line_number);
90+
}
91+
output
92+
}
6793
}
6894

6995
#[derive(Debug)]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
fn main() {
2+
let _ = env!("NON_UNICODE_VAR");
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
error: environment variable `NON_UNICODE_VAR` is not a valid Unicode string
2+
--> non_unicode_env.rs:2:13
3+
|
4+
2 | let _ = env!("NON_UNICODE_VAR");
5+
| ^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: this error originates in the macro `env` (in Nightly builds, run with -Z macro-backtrace for more info)
8+
9+
error: aborting due to 1 previous error
10+
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
extern crate run_make_support;
2+
3+
use run_make_support::rustc;
4+
5+
fn main() {
6+
#[cfg(unix)]
7+
let non_unicode: &std::ffi::OsStr = std::os::unix::ffi::OsStrExt::from_bytes(&[0xFF]);
8+
#[cfg(windows)]
9+
let non_unicode: std::ffi::OsString = std::os::windows::ffi::OsStringExt::from_wide(&[0xD800]);
10+
let output = rustc().arg("non_unicode_env.rs").env("NON_UNICODE_VAR", non_unicode).run_fail();
11+
let actual = std::str::from_utf8(&output.stderr).unwrap();
12+
let expected = std::fs::read_to_string("non_unicode_env.stderr").unwrap();
13+
assert_eq!(actual, expected);
14+
}

0 commit comments

Comments
 (0)