1
Fork 0

builtin_macros: raw str in diagnostic output

If a raw string was used in the `env!` invocation, then it should also
be shown in the diagnostic messages as a raw string.

Signed-off-by: David Wood <david@davidtw.co>
This commit is contained in:
David Wood 2023-07-25 11:12:52 +01:00
parent c06a7eb2a6
commit 75df62d4a2
No known key found for this signature in database
6 changed files with 73 additions and 49 deletions

View file

@ -109,8 +109,8 @@ builtin_macros_derive_path_args_value = traits in `#[derive(...)]` don't accept
.suggestion = remove the value .suggestion = remove the value
builtin_macros_env_not_defined = environment variable `{$var}` not defined at compile time builtin_macros_env_not_defined = environment variable `{$var}` not defined at compile time
.cargo = Cargo sets build script variables at run time. Use `std::env::var("{$var}")` instead .cargo = Cargo sets build script variables at run time. Use `std::env::var({$var_expr})` instead
.other = use `std::env::var("{$var}")` to read the variable at run time .custom = use `std::env::var({$var_expr})` to read the variable at run time
builtin_macros_env_takes_args = `env!()` takes 1 or 2 arguments builtin_macros_env_takes_args = `env!()` takes 1 or 2 arguments

View file

@ -4,7 +4,7 @@
// //
use rustc_ast::tokenstream::TokenStream; use rustc_ast::tokenstream::TokenStream;
use rustc_ast::{self as ast, GenericArg}; use rustc_ast::{self as ast, AstDeref, GenericArg};
use rustc_expand::base::{self, *}; use rustc_expand::base::{self, *};
use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::Span; use rustc_span::Span;
@ -76,27 +76,36 @@ pub fn expand_env<'cx>(
}, },
}; };
let sp = cx.with_def_site_ctxt(sp); let span = cx.with_def_site_ctxt(sp);
let value = env::var(var.as_str()).ok().as_deref().map(Symbol::intern); let value = env::var(var.as_str()).ok().as_deref().map(Symbol::intern);
cx.sess.parse_sess.env_depinfo.borrow_mut().insert((var, value)); cx.sess.parse_sess.env_depinfo.borrow_mut().insert((var, value));
let e = match value { let e = match value {
None => { None => {
// Use the string literal in the code in the diagnostic to avoid confusing diagnostics,
// e.g. when the literal contains escape sequences.
let ast::ExprKind::Lit(ast::token::Lit { let ast::ExprKind::Lit(ast::token::Lit {
kind: ast::token::LitKind::Str | ast::token::LitKind::StrRaw(..), kind: ast::token::LitKind::Str | ast::token::LitKind::StrRaw(..),
symbol: original_var, symbol,
.. ..
}) = &var_expr.kind }) = &var_expr.kind
else { else {
unreachable!("`expr_to_string` ensures this is a string lit") unreachable!("`expr_to_string` ensures this is a string lit")
}; };
cx.emit_err(errors::EnvNotDefined {
span: sp, if let Some(msg_from_user) = custom_msg {
msg: custom_msg, cx.emit_err(errors::EnvNotDefinedWithUserMessage { span, msg_from_user });
var: *original_var, } else if is_cargo_env_var(var.as_str()) {
help: custom_msg.is_none().then(|| help_for_missing_env_var(var.as_str())), cx.emit_err(errors::EnvNotDefined::CargoEnvVar {
}); span,
var: *symbol,
var_expr: var_expr.ast_deref(),
});
} else {
cx.emit_err(errors::EnvNotDefined::CustomEnvVar {
span,
var: *symbol,
var_expr: var_expr.ast_deref(),
});
}
return DummyResult::any(sp); return DummyResult::any(sp);
} }
Some(value) => cx.expr_str(sp, value), Some(value) => cx.expr_str(sp, value),
@ -104,13 +113,9 @@ pub fn expand_env<'cx>(
MacEager::expr(e) MacEager::expr(e)
} }
fn help_for_missing_env_var(var: &str) -> errors::EnvNotDefinedHelp { /// Returns `true` if an environment variable from `env!` is one used by Cargo.
if var.starts_with("CARGO_") fn is_cargo_env_var(var: &str) -> bool {
var.starts_with("CARGO_")
|| var.starts_with("DEP_") || var.starts_with("DEP_")
|| matches!(var, "OUT_DIR" | "OPT_LEVEL" | "PROFILE" | "HOST" | "TARGET") || matches!(var, "OUT_DIR" | "OPT_LEVEL" | "PROFILE" | "HOST" | "TARGET")
{
errors::EnvNotDefinedHelp::CargoVar
} else {
errors::EnvNotDefinedHelp::Other
}
} }

View file

@ -440,43 +440,43 @@ pub(crate) struct EnvTakesArgs {
pub(crate) span: Span, pub(crate) span: Span,
} }
//#[derive(Diagnostic)] pub(crate) struct EnvNotDefinedWithUserMessage {
//#[diag(builtin_macros_env_not_defined)]
pub(crate) struct EnvNotDefined {
pub(crate) span: Span, pub(crate) span: Span,
pub(crate) msg: Option<Symbol>, pub(crate) msg_from_user: Symbol,
pub(crate) var: Symbol,
pub(crate) help: Option<EnvNotDefinedHelp>,
} }
// Hand-written implementation to support custom user messages // Hand-written implementation to support custom user messages.
impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for EnvNotDefined { impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for EnvNotDefinedWithUserMessage {
#[track_caller] #[track_caller]
fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'a, G> { fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'a, G> {
let mut diag = if let Some(msg) = self.msg { #[expect(
#[expect( rustc::untranslatable_diagnostic,
rustc::untranslatable_diagnostic, reason = "cannot translate user-provided messages"
reason = "cannot translate user-provided messages" )]
)] let mut diag = handler.struct_diagnostic(self.msg_from_user.to_string());
handler.struct_diagnostic(msg.to_string())
} else {
handler.struct_diagnostic(crate::fluent_generated::builtin_macros_env_not_defined)
};
diag.set_arg("var", self.var);
diag.set_span(self.span); diag.set_span(self.span);
if let Some(help) = self.help {
diag.subdiagnostic(help);
}
diag diag
} }
} }
#[derive(Subdiagnostic)] #[derive(Diagnostic)]
pub(crate) enum EnvNotDefinedHelp { pub(crate) enum EnvNotDefined<'a> {
#[diag(builtin_macros_env_not_defined)]
#[help(builtin_macros_cargo)] #[help(builtin_macros_cargo)]
CargoVar, CargoEnvVar {
#[help(builtin_macros_other)] #[primary_span]
Other, span: Span,
var: Symbol,
var_expr: &'a rustc_ast::Expr,
},
#[diag(builtin_macros_env_not_defined)]
#[help(builtin_macros_custom)]
CustomEnvVar {
#[primary_span]
span: Span,
var: Symbol,
var_expr: &'a rustc_ast::Expr,
},
} }
#[derive(Diagnostic)] #[derive(Diagnostic)]

View file

@ -164,6 +164,12 @@ impl IntoDiagnosticArg for hir::ConstContext {
} }
} }
impl IntoDiagnosticArg for ast::Expr {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
DiagnosticArgValue::Str(Cow::Owned(pprust::expr_to_string(&self)))
}
}
impl IntoDiagnosticArg for ast::Path { impl IntoDiagnosticArg for ast::Path {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
DiagnosticArgValue::Str(Cow::Owned(pprust::path_to_string(&self))) DiagnosticArgValue::Str(Cow::Owned(pprust::path_to_string(&self)))

View file

@ -1,6 +1,10 @@
// unset-rustc-env:oopsie // unset-rustc-env:oopsie
// unset-rustc-env:a""a
env![r#"oopsie"#]; env![r#"oopsie"#];
//~^ ERROR environment variable `oopsie` not defined at compile time //~^ ERROR environment variable `oopsie` not defined at compile time
env![r#"a""a"#];
//~^ ERROR environment variable `a""a` not defined at compile time
fn main() {} fn main() {}

View file

@ -1,11 +1,20 @@
error: environment variable `oopsie` not defined at compile time error: environment variable `oopsie` not defined at compile time
--> $DIR/builtin-env-issue-114010.rs:3:1 --> $DIR/builtin-env-issue-114010.rs:4:1
| |
LL | env![r#"oopsie"#]; LL | env![r#"oopsie"#];
| ^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^
| |
= help: use `std::env::var("oopsie")` to read the variable at run time = help: use `std::env::var(r#"oopsie"#)` 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) = 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 error: environment variable `a""a` not defined at compile time
--> $DIR/builtin-env-issue-114010.rs:7:1
|
LL | env![r#"a""a"#];
| ^^^^^^^^^^^^^^^
|
= help: use `std::env::var(r#"a""a"#)` 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: aborting due to 2 previous errors