Skip to content

Commit 6ebe0bf

Browse files
committed
Auto merge of #156369 - matthiaskrgr:rollup-HcIYWx8, r=matthiaskrgr
Rollup of 5 pull requests Successful merges: - #148214 (Consider `Result<T, Uninhabited>` and `ControlFlow<Uninhabited, T>` to be equivalent to `T` for must use lint) - #149362 (Add Command::get_resolved_envs) - #155188 (Add regression test for issue 144329) - #155515 (error on empty `export_name`) - #155817 (validate `#[link_name = "..."]` & `#[link(name = "...")]` parameters)
2 parents 29155a4 + 93c6b09 commit 6ebe0bf

21 files changed

Lines changed: 419 additions & 78 deletions

compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ use rustc_span::edition::Edition::Edition2024;
55
use super::prelude::*;
66
use crate::attributes::AttributeSafety;
77
use crate::session_diagnostics::{
8-
NakedFunctionIncompatibleAttribute, NullOnExport, NullOnObjcClass, NullOnObjcSelector,
9-
ObjcClassExpectedStringLiteral, ObjcSelectorExpectedStringLiteral,
8+
EmptyExportName, NakedFunctionIncompatibleAttribute, NullOnExport, NullOnObjcClass,
9+
NullOnObjcSelector, ObjcClassExpectedStringLiteral, ObjcSelectorExpectedStringLiteral,
1010
};
1111
use crate::target_checking::Policy::AllowSilent;
1212

@@ -129,6 +129,12 @@ impl SingleAttributeParser for ExportNameParser {
129129
cx.emit_err(NullOnExport { span: cx.attr_span });
130130
return None;
131131
}
132+
if name.is_empty() {
133+
// LLVM will make up a name if the empty string is given, but that name will be
134+
// inconsistent between compilation units, causing linker errors.
135+
cx.emit_err(EmptyExportName { span: cx.attr_span });
136+
return None;
137+
}
132138
Some(AttributeKind::ExportName { name, span: cx.attr_span })
133139
}
134140
}

compiler/rustc_attr_parsing/src/attributes/link_attrs.rs

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use crate::session_diagnostics::{
1717
AsNeededCompatibility, BundleNeedsStatic, EmptyLinkName, ExportSymbolsNeedsStatic,
1818
ImportNameTypeRaw, ImportNameTypeX86, IncompatibleWasmLink, InvalidLinkModifier,
1919
InvalidMachoSection, InvalidMachoSectionReason, LinkFrameworkApple, LinkOrdinalOutOfRange,
20-
LinkRequiresName, MultipleModifiers, NullOnLinkSection, RawDylibNoNul, RawDylibOnlyWindows,
20+
LinkRequiresName, MultipleModifiers, NullOnLinkName, NullOnLinkSection, RawDylibOnlyWindows,
2121
WholeArchiveNeedsStatic,
2222
};
2323

@@ -42,6 +42,19 @@ impl SingleAttributeParser for LinkNameParser {
4242
return None;
4343
};
4444

45+
if name.as_str().contains('\0') {
46+
// `#[link_name = ...]` will be converted to a null-terminated string,
47+
// so it may not contain any null characters.
48+
cx.emit_err(NullOnLinkName { span: nv.value_span });
49+
return None;
50+
}
51+
if name.is_empty() {
52+
// Otherwise LLVM will just make up a name and the linker will fail
53+
// to find an empty symbol name.
54+
cx.emit_err(EmptyLinkName { span: nv.value_span });
55+
return None;
56+
}
57+
4558
Some(LinkName { name, span: cx.attr_span })
4659
}
4760
}
@@ -218,7 +231,7 @@ impl CombineAttributeParser for LinkParser {
218231
if wasm_import_module.is_some() {
219232
(name, kind) = (wasm_import_module, Some(NativeLibKind::WasmImportModule));
220233
}
221-
let Some((name, name_span)) = name else {
234+
let Some((name, _name_span)) = name else {
222235
cx.emit_err(LinkRequiresName { span: cx.attr_span });
223236
return None;
224237
};
@@ -230,12 +243,6 @@ impl CombineAttributeParser for LinkParser {
230243
}
231244
}
232245

233-
if let Some(NativeLibKind::RawDylib { .. }) = kind
234-
&& name.as_str().contains('\0')
235-
{
236-
cx.emit_err(RawDylibNoNul { span: name_span });
237-
}
238-
239246
Some(LinkEntry {
240247
span: cx.attr_span,
241248
kind: kind.unwrap_or(NativeLibKind::Unspecified),
@@ -265,9 +272,13 @@ impl LinkParser {
265272
return false;
266273
};
267274

275+
if link_name.as_str().contains('\0') {
276+
cx.emit_err(NullOnLinkName { span: nv.value_span });
277+
}
268278
if link_name.is_empty() {
269279
cx.emit_err(EmptyLinkName { span: nv.value_span });
270280
}
281+
271282
*name = Some((link_name, nv.value_span));
272283
true
273284
}

compiler/rustc_attr_parsing/src/session_diagnostics.rs

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,13 @@ pub(crate) struct UnusedMultiple {
396396
pub name: Symbol,
397397
}
398398

399+
#[derive(Diagnostic)]
400+
#[diag("`export_name` may not be empty")]
401+
pub(crate) struct EmptyExportName {
402+
#[primary_span]
403+
pub span: Span,
404+
}
405+
399406
#[derive(Diagnostic)]
400407
#[diag("`export_name` may not contain null characters", code = E0648)]
401408
pub(crate) struct NullOnExport {
@@ -410,6 +417,13 @@ pub(crate) struct NullOnLinkSection {
410417
pub span: Span,
411418
}
412419

420+
#[derive(Diagnostic)]
421+
#[diag("link name may not contain null characters", code = E0648)]
422+
pub(crate) struct NullOnLinkName {
423+
#[primary_span]
424+
pub span: Span,
425+
}
426+
413427
#[derive(Diagnostic)]
414428
#[diag("`objc::class!` may not contain null characters")]
415429
pub(crate) struct NullOnObjcClass {
@@ -984,13 +998,6 @@ pub(crate) struct LinkRequiresName {
984998
pub span: Span,
985999
}
9861000

987-
#[derive(Diagnostic)]
988-
#[diag("link name must not contain NUL characters if link kind is `raw-dylib`")]
989-
pub(crate) struct RawDylibNoNul {
990-
#[primary_span]
991-
pub span: Span,
992-
}
993-
9941001
#[derive(Diagnostic)]
9951002
#[diag("link kind `raw-dylib` is only supported on Windows targets", code = E0455)]
9961003
pub(crate) struct RawDylibOnlyWindows {

compiler/rustc_lint/src/unused/must_use.rs

Lines changed: 11 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -133,18 +133,11 @@ pub enum MustUsePath {
133133

134134
/// Returns `Some(path)` if `ty` should be considered as "`must_use`" in the context of `expr`
135135
/// (`expr` is used to get the parent module, which can affect which types are considered uninhabited).
136-
///
137-
/// If `simplify_uninhabited` is true, this function considers `Result<T, Uninhabited>` and
138-
/// `ControlFlow<Uninhabited, T>` the same as `T` (we don't set this *yet* in rustc, but expose this
139-
/// so clippy can use this).
140-
//
141-
// FIXME: remove `simplify_uninhabited` once clippy had a release with the new semantics.
142136
#[instrument(skip(cx, expr), level = "debug", ret)]
143137
pub fn is_ty_must_use<'tcx>(
144138
cx: &LateContext<'tcx>,
145139
ty: Ty<'tcx>,
146140
expr: &hir::Expr<'_>,
147-
simplify_uninhabited: bool,
148141
) -> IsTyMustUse {
149142
if ty.is_unit() {
150143
return IsTyMustUse::Trivial;
@@ -157,50 +150,29 @@ pub fn is_ty_must_use<'tcx>(
157150
match *ty.kind() {
158151
_ if is_uninhabited(ty) => IsTyMustUse::Trivial,
159152
ty::Adt(..) if let Some(boxed) = ty.boxed_ty() => {
160-
is_ty_must_use(cx, boxed, expr, simplify_uninhabited)
161-
.map(|inner| MustUsePath::Boxed(Box::new(inner)))
153+
is_ty_must_use(cx, boxed, expr).map(|inner| MustUsePath::Boxed(Box::new(inner)))
162154
}
163155
ty::Adt(def, args) if cx.tcx.is_lang_item(def.did(), LangItem::Pin) => {
164156
let pinned_ty = args.type_at(0);
165-
is_ty_must_use(cx, pinned_ty, expr, simplify_uninhabited)
166-
.map(|inner| MustUsePath::Pinned(Box::new(inner)))
157+
is_ty_must_use(cx, pinned_ty, expr).map(|inner| MustUsePath::Pinned(Box::new(inner)))
167158
}
168159
// Consider `Result<T, Uninhabited>` (e.g. `Result<(), !>`) equivalent to `T`.
169160
ty::Adt(def, args)
170-
if simplify_uninhabited
171-
&& cx.tcx.is_diagnostic_item(sym::Result, def.did())
161+
if cx.tcx.is_diagnostic_item(sym::Result, def.did())
172162
&& is_uninhabited(args.type_at(1)) =>
173163
{
174164
let ok_ty = args.type_at(0);
175-
is_ty_must_use(cx, ok_ty, expr, simplify_uninhabited)
176-
.map(|path| MustUsePath::Result(Box::new(path)))
165+
is_ty_must_use(cx, ok_ty, expr).map(|path| MustUsePath::Result(Box::new(path)))
177166
}
178167
// Consider `ControlFlow<Uninhabited, T>` (e.g. `ControlFlow<!, ()>`) equivalent to `T`.
179168
ty::Adt(def, args)
180-
if simplify_uninhabited
181-
&& cx.tcx.is_diagnostic_item(sym::ControlFlow, def.did())
169+
if cx.tcx.is_diagnostic_item(sym::ControlFlow, def.did())
182170
&& is_uninhabited(args.type_at(0)) =>
183171
{
184172
let continue_ty = args.type_at(1);
185-
is_ty_must_use(cx, continue_ty, expr, simplify_uninhabited)
173+
is_ty_must_use(cx, continue_ty, expr)
186174
.map(|path| MustUsePath::ControlFlow(Box::new(path)))
187175
}
188-
// Suppress warnings on `Result<(), Uninhabited>` (e.g. `Result<(), !>`).
189-
ty::Adt(def, args)
190-
if cx.tcx.is_diagnostic_item(sym::Result, def.did())
191-
&& args.type_at(0).is_unit()
192-
&& is_uninhabited(args.type_at(1)) =>
193-
{
194-
IsTyMustUse::Trivial
195-
}
196-
// Suppress warnings on `ControlFlow<Uninhabited, ()>` (e.g. `ControlFlow<!, ()>`).
197-
ty::Adt(def, args)
198-
if cx.tcx.is_diagnostic_item(sym::ControlFlow, def.did())
199-
&& args.type_at(1).is_unit()
200-
&& is_uninhabited(args.type_at(0)) =>
201-
{
202-
IsTyMustUse::Trivial
203-
}
204176
ty::Adt(def, _) => {
205177
is_def_must_use(cx, def.did(), expr.span).map_or(IsTyMustUse::No, IsTyMustUse::Yes)
206178
}
@@ -258,7 +230,7 @@ pub fn is_ty_must_use<'tcx>(
258230
let mut nested_must_use = Vec::new();
259231

260232
tys.iter().zip(elem_exprs).enumerate().for_each(|(i, (ty, expr))| {
261-
let must_use = is_ty_must_use(cx, ty, expr, simplify_uninhabited);
233+
let must_use = is_ty_must_use(cx, ty, expr);
262234

263235
all_trivial &= matches!(must_use, IsTyMustUse::Trivial);
264236
if let IsTyMustUse::Yes(path) = must_use {
@@ -280,8 +252,9 @@ pub fn is_ty_must_use<'tcx>(
280252
// If the array is empty we don't lint, to avoid false positives
281253
Some(0) | None => IsTyMustUse::No,
282254
// If the array is definitely non-empty, we can do `#[must_use]` checking.
283-
Some(len) => is_ty_must_use(cx, ty, expr, simplify_uninhabited)
284-
.map(|inner| MustUsePath::Array(Box::new(inner), len)),
255+
Some(len) => {
256+
is_ty_must_use(cx, ty, expr).map(|inner| MustUsePath::Array(Box::new(inner), len))
257+
}
285258
},
286259
ty::Closure(..) | ty::CoroutineClosure(..) => {
287260
IsTyMustUse::Yes(MustUsePath::Closure(expr.span))
@@ -345,7 +318,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
345318

346319
let ty = cx.typeck_results().expr_ty(expr);
347320

348-
let must_use_result = is_ty_must_use(cx, ty, expr, false);
321+
let must_use_result = is_ty_must_use(cx, ty, expr);
349322
let type_lint_emitted_or_trivial = match must_use_result {
350323
IsTyMustUse::Yes(path) => {
351324
emit_must_use_untranslated(cx, &path, "", "", 1, false, expr_is_from_block);

library/std/src/process.rs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1185,7 +1185,8 @@ impl Command {
11851185
/// [`Command::env_remove`] can be retrieved with this method.
11861186
///
11871187
/// Note that this output does not include environment variables inherited from the parent
1188-
/// process.
1188+
/// process. To see the full list of environment variables, including those inherited from the
1189+
/// parent process, use [`Command::get_resolved_envs`].
11891190
///
11901191
/// Each element is a tuple key/value pair `(&OsStr, Option<&OsStr>)`. A [`None`] value
11911192
/// indicates its key was explicitly removed via [`Command::env_remove`]. The associated key for
@@ -1214,6 +1215,42 @@ impl Command {
12141215
CommandEnvs { iter: self.inner.get_envs() }
12151216
}
12161217

1218+
/// Returns an iterator of the environment variables that will be set when the process is spawned.
1219+
///
1220+
/// This returns the environment as it would be if the command were executed at the time of calling
1221+
/// this method. The returned environment includes:
1222+
/// - All inherited environment variables from the parent process (unless [`Command::env_clear`] was called)
1223+
/// - All environment variables explicitly set via [`Command::env`] or [`Command::envs`]
1224+
/// - Excluding any environment variables removed via [`Command::env_remove`]
1225+
///
1226+
/// Note that the returned environment is a snapshot at the time this method is called and will not
1227+
/// reflect any subsequent changes to the `Command` or the parent process's environment. Additionally,
1228+
/// it will not reflect changes made in a `pre_exec` hook (on Unix platforms).
1229+
///
1230+
/// Each element is a tuple `(OsString, OsString)` representing an environment variable key and value.
1231+
///
1232+
/// # Examples
1233+
///
1234+
/// ```
1235+
/// #![feature(command_resolved_envs)]
1236+
/// use std::process::Command;
1237+
/// use std::ffi::{OsString, OsStr};
1238+
/// use std::env;
1239+
/// use std::collections::HashMap;
1240+
///
1241+
/// let mut cmd = Command::new("ls");
1242+
/// cmd.env("TZ", "UTC");
1243+
/// unsafe { env::set_var("EDITOR", "vim"); }
1244+
///
1245+
/// let resolved: HashMap<OsString, OsString> = cmd.get_resolved_envs().collect();
1246+
/// assert_eq!(resolved.get(OsStr::new("TZ")), Some(&OsString::from("UTC")));
1247+
/// assert_eq!(resolved.get(OsStr::new("EDITOR")), Some(&OsString::from("vim")));
1248+
/// ```
1249+
#[unstable(feature = "command_resolved_envs", issue = "149070")]
1250+
pub fn get_resolved_envs(&self) -> CommandResolvedEnvs {
1251+
self.inner.get_resolved_envs()
1252+
}
1253+
12171254
/// Returns the working directory for the child process.
12181255
///
12191256
/// This returns [`None`] if the working directory will not be changed.
@@ -1367,6 +1404,9 @@ impl<'a> fmt::Debug for CommandEnvs<'a> {
13671404
}
13681405
}
13691406

1407+
#[unstable(feature = "command_resolved_envs", issue = "149070")]
1408+
pub use imp::CommandResolvedEnvs;
1409+
13701410
/// The output of a finished process.
13711411
///
13721412
/// This is returned in a Result by either the [`output`] method of a

library/std/src/sys/process/env.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,3 +113,34 @@ impl<'a> ExactSizeIterator for CommandEnvs<'a> {
113113
self.iter.is_empty()
114114
}
115115
}
116+
117+
/// An iterator over the fully resolved environment variables.
118+
///
119+
/// This struct is created by
120+
/// [`Command::get_resolved_envs`][crate::process::Command::get_resolved_envs]. See its
121+
/// documentation for more.
122+
#[derive(Debug)]
123+
#[must_use = "iterators are lazy and do nothing unless consumed"]
124+
#[unstable(feature = "command_resolved_envs", issue = "149070")]
125+
pub struct CommandResolvedEnvs {
126+
inner: crate::collections::btree_map::IntoIter<EnvKey, OsString>,
127+
}
128+
129+
impl CommandResolvedEnvs {
130+
pub(crate) fn new(map: BTreeMap<EnvKey, OsString>) -> Self {
131+
Self { inner: map.into_iter() }
132+
}
133+
}
134+
135+
#[unstable(feature = "command_resolved_envs", issue = "149070")]
136+
impl Iterator for CommandResolvedEnvs {
137+
type Item = (OsString, OsString);
138+
139+
fn next(&mut self) -> Option<Self::Item> {
140+
self.inner.next().map(|(key, value)| (key.into(), value))
141+
}
142+
143+
fn size_hint(&self) -> (usize, Option<usize>) {
144+
self.inner.size_hint()
145+
}
146+
}

library/std/src/sys/process/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ cfg_select! {
2727
mod env;
2828

2929
pub use env::CommandEnvs;
30+
#[unstable(feature = "command_resolved_envs", issue = "149070")]
31+
pub use env::CommandResolvedEnvs;
3032
#[cfg(target_family = "unix")]
3133
pub use imp::getppid;
3234
pub use imp::{

library/std/src/sys/process/motor.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use super::CommandEnvs;
2-
use super::env::CommandEnv;
2+
use super::env::{CommandEnv, CommandResolvedEnvs};
33
use crate::ffi::OsStr;
44
pub use crate::ffi::OsString as EnvKey;
55
use crate::num::NonZeroI32;
@@ -100,6 +100,10 @@ impl Command {
100100
self.env.does_clear()
101101
}
102102

103+
pub fn get_resolved_envs(&self) -> CommandResolvedEnvs {
104+
CommandResolvedEnvs::new(self.env.capture())
105+
}
106+
103107
pub fn get_current_dir(&self) -> Option<&Path> {
104108
self.cwd.as_ref().map(Path::new)
105109
}

library/std/src/sys/process/uefi.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use r_efi::protocols::{simple_text_input, simple_text_output};
22

3-
use super::env::{CommandEnv, CommandEnvs};
3+
use super::env::{CommandEnv, CommandEnvs, CommandResolvedEnvs};
44
use crate::collections::BTreeMap;
55
pub use crate::ffi::OsString as EnvKey;
66
use crate::ffi::{OsStr, OsString};
@@ -86,6 +86,10 @@ impl Command {
8686
self.env.does_clear()
8787
}
8888

89+
pub fn get_resolved_envs(&self) -> CommandResolvedEnvs {
90+
CommandResolvedEnvs::new(self.env.capture())
91+
}
92+
8993
pub fn get_current_dir(&self) -> Option<&Path> {
9094
None
9195
}

0 commit comments

Comments
 (0)