From 75df62d4a2c45175b8cb1eb4bdc10fa423c11c4c Mon Sep 17 00:00:00 2001 From: David Wood Date: Tue, 25 Jul 2023 11:12:52 +0100 Subject: [PATCH] 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 --- compiler/rustc_builtin_macros/messages.ftl | 4 +- compiler/rustc_builtin_macros/src/env.rs | 41 ++++++++------- compiler/rustc_builtin_macros/src/errors.rs | 52 +++++++++---------- compiler/rustc_errors/src/diagnostic_impls.rs | 6 +++ tests/ui/macros/builtin-env-issue-114010.rs | 4 ++ .../ui/macros/builtin-env-issue-114010.stderr | 15 ++++-- 6 files changed, 73 insertions(+), 49 deletions(-) diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl index 322222ae33039..79fae7f92f1ee 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -109,8 +109,8 @@ builtin_macros_derive_path_args_value = traits in `#[derive(...)]` don't accept .suggestion = remove the value 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 - .other = use `std::env::var("{$var}")` to read the variable at run time + .cargo = Cargo sets build script variables at run time. Use `std::env::var({$var_expr})` instead + .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 diff --git a/compiler/rustc_builtin_macros/src/env.rs b/compiler/rustc_builtin_macros/src/env.rs index 30d74ba593c97..92da0c069e51a 100644 --- a/compiler/rustc_builtin_macros/src/env.rs +++ b/compiler/rustc_builtin_macros/src/env.rs @@ -4,7 +4,7 @@ // 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_span::symbol::{kw, sym, Ident, Symbol}; 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); cx.sess.parse_sess.env_depinfo.borrow_mut().insert((var, value)); let e = match value { 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 { kind: ast::token::LitKind::Str | ast::token::LitKind::StrRaw(..), - symbol: original_var, + symbol, .. }) = &var_expr.kind else { unreachable!("`expr_to_string` ensures this is a string lit") }; - cx.emit_err(errors::EnvNotDefined { - span: sp, - msg: custom_msg, - var: *original_var, - help: custom_msg.is_none().then(|| help_for_missing_env_var(var.as_str())), - }); + + if let Some(msg_from_user) = custom_msg { + cx.emit_err(errors::EnvNotDefinedWithUserMessage { span, msg_from_user }); + } else if is_cargo_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); } Some(value) => cx.expr_str(sp, value), @@ -104,13 +113,9 @@ pub fn expand_env<'cx>( MacEager::expr(e) } -fn help_for_missing_env_var(var: &str) -> errors::EnvNotDefinedHelp { - if var.starts_with("CARGO_") +/// Returns `true` if an environment variable from `env!` is one used by Cargo. +fn is_cargo_env_var(var: &str) -> bool { + var.starts_with("CARGO_") || var.starts_with("DEP_") || matches!(var, "OUT_DIR" | "OPT_LEVEL" | "PROFILE" | "HOST" | "TARGET") - { - errors::EnvNotDefinedHelp::CargoVar - } else { - errors::EnvNotDefinedHelp::Other - } } diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index 7b2a375a822bf..fbf0395bb05ac 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -440,43 +440,43 @@ pub(crate) struct EnvTakesArgs { pub(crate) span: Span, } -//#[derive(Diagnostic)] -//#[diag(builtin_macros_env_not_defined)] -pub(crate) struct EnvNotDefined { +pub(crate) struct EnvNotDefinedWithUserMessage { pub(crate) span: Span, - pub(crate) msg: Option, - pub(crate) var: Symbol, - pub(crate) help: Option, + pub(crate) msg_from_user: Symbol, } -// Hand-written implementation to support custom user messages -impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for EnvNotDefined { +// Hand-written implementation to support custom user messages. +impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for EnvNotDefinedWithUserMessage { #[track_caller] fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'a, G> { - let mut diag = if let Some(msg) = self.msg { - #[expect( - rustc::untranslatable_diagnostic, - reason = "cannot translate user-provided messages" - )] - handler.struct_diagnostic(msg.to_string()) - } else { - handler.struct_diagnostic(crate::fluent_generated::builtin_macros_env_not_defined) - }; - diag.set_arg("var", self.var); + #[expect( + rustc::untranslatable_diagnostic, + reason = "cannot translate user-provided messages" + )] + let mut diag = handler.struct_diagnostic(self.msg_from_user.to_string()); diag.set_span(self.span); - if let Some(help) = self.help { - diag.subdiagnostic(help); - } diag } } -#[derive(Subdiagnostic)] -pub(crate) enum EnvNotDefinedHelp { +#[derive(Diagnostic)] +pub(crate) enum EnvNotDefined<'a> { + #[diag(builtin_macros_env_not_defined)] #[help(builtin_macros_cargo)] - CargoVar, - #[help(builtin_macros_other)] - Other, + CargoEnvVar { + #[primary_span] + 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)] diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs index 10fe7fc74a873..fb2e84bb7a130 100644 --- a/compiler/rustc_errors/src/diagnostic_impls.rs +++ b/compiler/rustc_errors/src/diagnostic_impls.rs @@ -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 { fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { DiagnosticArgValue::Str(Cow::Owned(pprust::path_to_string(&self))) diff --git a/tests/ui/macros/builtin-env-issue-114010.rs b/tests/ui/macros/builtin-env-issue-114010.rs index eb97c767fa2e9..819b8b1e8abe0 100644 --- a/tests/ui/macros/builtin-env-issue-114010.rs +++ b/tests/ui/macros/builtin-env-issue-114010.rs @@ -1,6 +1,10 @@ // unset-rustc-env:oopsie +// unset-rustc-env:a""a env![r#"oopsie"#]; //~^ 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() {} diff --git a/tests/ui/macros/builtin-env-issue-114010.stderr b/tests/ui/macros/builtin-env-issue-114010.stderr index 7934ce1b79099..0da42089cce77 100644 --- a/tests/ui/macros/builtin-env-issue-114010.stderr +++ b/tests/ui/macros/builtin-env-issue-114010.stderr @@ -1,11 +1,20 @@ 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"#]; | ^^^^^^^^^^^^^^^^^ | - = 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) -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