Rollup merge of #108573 - kornelski:runtimeenvs, r=WaffleLapkin

Explain compile-time vs run-time difference in env!() error message

This PR is clarifying error message of `env!()` based on this user question: https://users.rust-lang.org/t/environment-variable-out-dir-is-undefined/90067

It makes it clear that `env!()` is for env variables defined at compile-time. There's special-case help text for common Cargo build script variables.

I've also rearranged the code to avoid allocating error message on the happy path when the env var is defined.
This commit is contained in:
Matthias Krüger 2023-03-02 23:05:29 +01:00 committed by GitHub
commit 13640e337a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 43 additions and 20 deletions

View File

@ -53,7 +53,7 @@ pub fn expand_env<'cx>(
tts: TokenStream,
) -> Box<dyn base::MacResult + 'cx> {
let mut exprs = match get_exprs_from_tts(cx, tts) {
Some(exprs) if exprs.is_empty() => {
Some(exprs) if exprs.is_empty() || exprs.len() > 2 => {
cx.span_err(sp, "env! takes 1 or 2 arguments");
return DummyResult::any(sp);
}
@ -64,28 +64,48 @@ pub fn expand_env<'cx>(
let Some((var, _style)) = expr_to_string(cx, exprs.next().unwrap(), "expected string literal") else {
return DummyResult::any(sp);
};
let msg = match exprs.next() {
None => Symbol::intern(&format!("environment variable `{}` not defined", var)),
let custom_msg = match exprs.next() {
None => None,
Some(second) => match expr_to_string(cx, second, "expected string literal") {
None => return DummyResult::any(sp),
Some((s, _style)) => s,
Some((s, _style)) => Some(s),
},
};
if exprs.next().is_some() {
cx.span_err(sp, "env! takes 1 or 2 arguments");
return DummyResult::any(sp);
}
let sp = cx.with_def_site_ctxt(sp);
let value = env::var(var.as_str()).ok().as_deref().map(Symbol::intern);
cx.sess.parse_sess.env_depinfo.borrow_mut().insert((var, value));
let e = match value {
None => {
cx.span_err(sp, msg.as_str());
let (msg, help) = match custom_msg {
None => (
format!("environment variable `{var}` not defined at compile time"),
Some(help_for_missing_env_var(var.as_str())),
),
Some(s) => (s.to_string(), None),
};
let mut diag = cx.struct_span_err(sp, &msg);
if let Some(help) = help {
diag.help(help);
}
diag.emit();
return DummyResult::any(sp);
}
Some(value) => cx.expr_str(sp, value),
};
MacEager::expr(e)
}
fn help_for_missing_env_var(var: &str) -> String {
if var.starts_with("CARGO_")
|| var.starts_with("DEP_")
|| matches!(var, "OUT_DIR" | "OPT_LEVEL" | "PROFILE" | "HOST" | "TARGET")
{
format!(
"Cargo sets build script variables at run time. Use `std::env::var(\"{var}\")` instead"
)
} else {
format!("Use `std::env::var(\"{var}\")` to read the variable at run time")
}
}

View File

@ -1,4 +1,4 @@
fn main() {
env!("__HOPEFULLY_NOT_DEFINED__");
//~^ ERROR: environment variable `__HOPEFULLY_NOT_DEFINED__` not defined
env!("CARGO__HOPEFULLY_NOT_DEFINED__");
//~^ ERROR: environment variable `CARGO__HOPEFULLY_NOT_DEFINED__` not defined
}

View File

@ -1,9 +1,10 @@
error: environment variable `__HOPEFULLY_NOT_DEFINED__` not defined
error: environment variable `CARGO__HOPEFULLY_NOT_DEFINED__` not defined at compile time
--> $DIR/extenv-not-defined-default.rs:2:5
|
LL | env!("__HOPEFULLY_NOT_DEFINED__");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | env!("CARGO__HOPEFULLY_NOT_DEFINED__");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: Cargo sets build script variables at run time. Use `std::env::var("CARGO__HOPEFULLY_NOT_DEFINED__")` instead
= note: this error originates in the macro `env` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to previous error

View File

@ -1,9 +1,10 @@
error: environment variable `NON_EXISTENT` not defined
error: environment variable `NON_EXISTENT` not defined at compile time
--> $DIR/issue-55897.rs:11:22
|
LL | include!(concat!(env!("NON_EXISTENT"), "/data.rs"));
| ^^^^^^^^^^^^^^^^^^^^
|
= help: Use `std::env::var("NON_EXISTENT")` to read the variable at run time
= note: this error originates in the macro `env` (in Nightly builds, run with -Z macro-backtrace for more info)
error: suffixes on string literals are invalid

View File

@ -150,18 +150,19 @@ error: expected string literal
LL | env!(invalid);
| ^^^^^^^
error: expected string literal
--> $DIR/macros-nonfatal-errors.rs:105:10
error: env! takes 1 or 2 arguments
--> $DIR/macros-nonfatal-errors.rs:105:5
|
LL | env!(foo, abr, baz);
| ^^^
| ^^^^^^^^^^^^^^^^^^^
error: environment variable `RUST_HOPEFULLY_THIS_DOESNT_EXIST` not defined
error: environment variable `RUST_HOPEFULLY_THIS_DOESNT_EXIST` not defined at compile time
--> $DIR/macros-nonfatal-errors.rs:106:5
|
LL | env!("RUST_HOPEFULLY_THIS_DOESNT_EXIST");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: Use `std::env::var("RUST_HOPEFULLY_THIS_DOESNT_EXIST")` to read the variable at run time
= note: this error originates in the macro `env` (in Nightly builds, run with -Z macro-backtrace for more info)
error: format argument must be a string literal