Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
5ed5b26
Add Command::get_resolved_envs
schneems Nov 26, 2025
25a1eb2
add `must_use` tests for `Result<MustUse/Struct, !>`
WaffleLapkin Oct 28, 2025
eb0d2a8
consider `Result<T, !>`/`ControlFlow<!, T>` the same as `T` for must_…
WaffleLapkin Oct 28, 2025
4c4416b
remove dead code
WaffleLapkin Oct 28, 2025
d0ea88e
error on empty `export_name`
folkertdev Apr 19, 2026
c08b9ab
validate link name parameter
folkertdev Apr 26, 2026
fd2e542
Add regression test for issue 144329
DaniPopes Apr 12, 2026
e264a92
gracefully handle invalid string/vec
Walnut356 Apr 27, 2026
0a4f89e
move string read logic to standalone function
Walnut356 Apr 28, 2026
7eb6a20
Update a bunch of bootstrap dependencies to remove windows-target
bjorn3 Apr 30, 2026
a80af22
remove turbofish notation + use None / Some instead of Option::
Cheese-Space May 3, 2026
09dc7fc
mark some panicking methods around Duration as track_caller
RalfJung May 4, 2026
40da877
Add `str::word_to_titlecase()` to `alloc`
Jules-Bertholet May 9, 2026
f4adc40
Add mention of sendfile(2) and splice(2) to fs::copy() documentation.
tomilepp May 9, 2026
b06f83c
Rollup merge of #148214 - WaffleLapkin:never-worn-never-type, r=fee1-…
JonathanBrouwer May 9, 2026
095559e
Rollup merge of #149362 - schneems:schneems/get_resolved_envs, r=Mark…
JonathanBrouwer May 9, 2026
cdb34cb
Rollup merge of #155509 - Walnut356:string_crash, r=Mark-Simulacrum
JonathanBrouwer May 9, 2026
f15f605
Rollup merge of #155705 - Jules-Bertholet:word-to-titlecase, r=Mark-S…
JonathanBrouwer May 9, 2026
ec6e0ce
Rollup merge of #155970 - tomilepp:issue-155968-fix, r=joshtriplett
JonathanBrouwer May 9, 2026
4db2779
Rollup merge of #156006 - bjorn3:bootstrap_no_import_libs, r=Mark-Sim…
JonathanBrouwer May 9, 2026
65b5f57
Rollup merge of #155188 - DaniPopes:144329-test, r=Mark-Simulacrum
JonathanBrouwer May 9, 2026
be4263a
Rollup merge of #155515 - folkertdev:export-name-not-empty, r=jdonsze…
JonathanBrouwer May 9, 2026
b75714f
Rollup merge of #155817 - folkertdev:link-name-null-empty, r=mejrs
JonathanBrouwer May 9, 2026
fb9735b
Rollup merge of #156107 - Cheese-Space:patch-1, r=Mark-Simulacrum
JonathanBrouwer May 9, 2026
f4cb666
Rollup merge of #156133 - RalfJung:time-panic-track-caller, r=Mark-Si…
JonathanBrouwer May 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ use rustc_span::edition::Edition::Edition2024;
use super::prelude::*;
use crate::attributes::AttributeSafety;
use crate::session_diagnostics::{
NakedFunctionIncompatibleAttribute, NullOnExport, NullOnObjcClass, NullOnObjcSelector,
ObjcClassExpectedStringLiteral, ObjcSelectorExpectedStringLiteral,
EmptyExportName, NakedFunctionIncompatibleAttribute, NullOnExport, NullOnObjcClass,
NullOnObjcSelector, ObjcClassExpectedStringLiteral, ObjcSelectorExpectedStringLiteral,
};
use crate::target_checking::Policy::AllowSilent;

Expand Down Expand Up @@ -129,6 +129,12 @@ impl SingleAttributeParser for ExportNameParser {
cx.emit_err(NullOnExport { span: cx.attr_span });
return None;
}
if name.is_empty() {
// LLVM will make up a name if the empty string is given, but that name will be
// inconsistent between compilation units, causing linker errors.
cx.emit_err(EmptyExportName { span: cx.attr_span });
return None;
}
Some(AttributeKind::ExportName { name, span: cx.attr_span })
}
}
Expand Down
27 changes: 19 additions & 8 deletions compiler/rustc_attr_parsing/src/attributes/link_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use crate::session_diagnostics::{
AsNeededCompatibility, BundleNeedsStatic, EmptyLinkName, ExportSymbolsNeedsStatic,
ImportNameTypeRaw, ImportNameTypeX86, IncompatibleWasmLink, InvalidLinkModifier,
InvalidMachoSection, InvalidMachoSectionReason, LinkFrameworkApple, LinkOrdinalOutOfRange,
LinkRequiresName, MultipleModifiers, NullOnLinkSection, RawDylibNoNul, RawDylibOnlyWindows,
LinkRequiresName, MultipleModifiers, NullOnLinkName, NullOnLinkSection, RawDylibOnlyWindows,
WholeArchiveNeedsStatic,
};

Expand All @@ -42,6 +42,19 @@ impl SingleAttributeParser for LinkNameParser {
return None;
};

if name.as_str().contains('\0') {
// `#[link_name = ...]` will be converted to a null-terminated string,
// so it may not contain any null characters.
cx.emit_err(NullOnLinkName { span: nv.value_span });
return None;
}
if name.is_empty() {
// Otherwise LLVM will just make up a name and the linker will fail
// to find an empty symbol name.
cx.emit_err(EmptyLinkName { span: nv.value_span });
return None;
}

Some(LinkName { name, span: cx.attr_span })
}
}
Expand Down Expand Up @@ -218,7 +231,7 @@ impl CombineAttributeParser for LinkParser {
if wasm_import_module.is_some() {
(name, kind) = (wasm_import_module, Some(NativeLibKind::WasmImportModule));
}
let Some((name, name_span)) = name else {
let Some((name, _name_span)) = name else {
cx.emit_err(LinkRequiresName { span: cx.attr_span });
return None;
};
Expand All @@ -230,12 +243,6 @@ impl CombineAttributeParser for LinkParser {
}
}

if let Some(NativeLibKind::RawDylib { .. }) = kind
&& name.as_str().contains('\0')
{
cx.emit_err(RawDylibNoNul { span: name_span });
}

Some(LinkEntry {
span: cx.attr_span,
kind: kind.unwrap_or(NativeLibKind::Unspecified),
Expand Down Expand Up @@ -265,9 +272,13 @@ impl LinkParser {
return false;
};

if link_name.as_str().contains('\0') {
cx.emit_err(NullOnLinkName { span: nv.value_span });
}
if link_name.is_empty() {
cx.emit_err(EmptyLinkName { span: nv.value_span });
}

*name = Some((link_name, nv.value_span));
true
}
Expand Down
21 changes: 14 additions & 7 deletions compiler/rustc_attr_parsing/src/session_diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,13 @@ pub(crate) struct UnusedMultiple {
pub name: Symbol,
}

#[derive(Diagnostic)]
#[diag("`export_name` may not be empty")]
pub(crate) struct EmptyExportName {
#[primary_span]
pub span: Span,
}

#[derive(Diagnostic)]
#[diag("`export_name` may not contain null characters", code = E0648)]
pub(crate) struct NullOnExport {
Expand All @@ -410,6 +417,13 @@ pub(crate) struct NullOnLinkSection {
pub span: Span,
}

#[derive(Diagnostic)]
#[diag("link name may not contain null characters", code = E0648)]
pub(crate) struct NullOnLinkName {
#[primary_span]
pub span: Span,
}

#[derive(Diagnostic)]
#[diag("`objc::class!` may not contain null characters")]
pub(crate) struct NullOnObjcClass {
Expand Down Expand Up @@ -984,13 +998,6 @@ pub(crate) struct LinkRequiresName {
pub span: Span,
}

#[derive(Diagnostic)]
#[diag("link name must not contain NUL characters if link kind is `raw-dylib`")]
pub(crate) struct RawDylibNoNul {
#[primary_span]
pub span: Span,
}

#[derive(Diagnostic)]
#[diag("link kind `raw-dylib` is only supported on Windows targets", code = E0455)]
pub(crate) struct RawDylibOnlyWindows {
Expand Down
49 changes: 11 additions & 38 deletions compiler/rustc_lint/src/unused/must_use.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,18 +133,11 @@ pub enum MustUsePath {

/// Returns `Some(path)` if `ty` should be considered as "`must_use`" in the context of `expr`
/// (`expr` is used to get the parent module, which can affect which types are considered uninhabited).
///
/// If `simplify_uninhabited` is true, this function considers `Result<T, Uninhabited>` and
/// `ControlFlow<Uninhabited, T>` the same as `T` (we don't set this *yet* in rustc, but expose this
/// so clippy can use this).
//
// FIXME: remove `simplify_uninhabited` once clippy had a release with the new semantics.
#[instrument(skip(cx, expr), level = "debug", ret)]
pub fn is_ty_must_use<'tcx>(
cx: &LateContext<'tcx>,
ty: Ty<'tcx>,
expr: &hir::Expr<'_>,
simplify_uninhabited: bool,
) -> IsTyMustUse {
if ty.is_unit() {
return IsTyMustUse::Trivial;
Expand All @@ -157,50 +150,29 @@ pub fn is_ty_must_use<'tcx>(
match *ty.kind() {
_ if is_uninhabited(ty) => IsTyMustUse::Trivial,
ty::Adt(..) if let Some(boxed) = ty.boxed_ty() => {
is_ty_must_use(cx, boxed, expr, simplify_uninhabited)
.map(|inner| MustUsePath::Boxed(Box::new(inner)))
is_ty_must_use(cx, boxed, expr).map(|inner| MustUsePath::Boxed(Box::new(inner)))
}
ty::Adt(def, args) if cx.tcx.is_lang_item(def.did(), LangItem::Pin) => {
let pinned_ty = args.type_at(0);
is_ty_must_use(cx, pinned_ty, expr, simplify_uninhabited)
.map(|inner| MustUsePath::Pinned(Box::new(inner)))
is_ty_must_use(cx, pinned_ty, expr).map(|inner| MustUsePath::Pinned(Box::new(inner)))
}
// Consider `Result<T, Uninhabited>` (e.g. `Result<(), !>`) equivalent to `T`.
ty::Adt(def, args)
if simplify_uninhabited
&& cx.tcx.is_diagnostic_item(sym::Result, def.did())
if cx.tcx.is_diagnostic_item(sym::Result, def.did())
&& is_uninhabited(args.type_at(1)) =>
{
let ok_ty = args.type_at(0);
is_ty_must_use(cx, ok_ty, expr, simplify_uninhabited)
.map(|path| MustUsePath::Result(Box::new(path)))
is_ty_must_use(cx, ok_ty, expr).map(|path| MustUsePath::Result(Box::new(path)))
}
// Consider `ControlFlow<Uninhabited, T>` (e.g. `ControlFlow<!, ()>`) equivalent to `T`.
ty::Adt(def, args)
if simplify_uninhabited
&& cx.tcx.is_diagnostic_item(sym::ControlFlow, def.did())
if cx.tcx.is_diagnostic_item(sym::ControlFlow, def.did())
&& is_uninhabited(args.type_at(0)) =>
{
let continue_ty = args.type_at(1);
is_ty_must_use(cx, continue_ty, expr, simplify_uninhabited)
is_ty_must_use(cx, continue_ty, expr)
.map(|path| MustUsePath::ControlFlow(Box::new(path)))
}
// Suppress warnings on `Result<(), Uninhabited>` (e.g. `Result<(), !>`).
ty::Adt(def, args)
if cx.tcx.is_diagnostic_item(sym::Result, def.did())
&& args.type_at(0).is_unit()
&& is_uninhabited(args.type_at(1)) =>
{
IsTyMustUse::Trivial
}
// Suppress warnings on `ControlFlow<Uninhabited, ()>` (e.g. `ControlFlow<!, ()>`).
ty::Adt(def, args)
if cx.tcx.is_diagnostic_item(sym::ControlFlow, def.did())
&& args.type_at(1).is_unit()
&& is_uninhabited(args.type_at(0)) =>
{
IsTyMustUse::Trivial
}
ty::Adt(def, _) => {
is_def_must_use(cx, def.did(), expr.span).map_or(IsTyMustUse::No, IsTyMustUse::Yes)
}
Expand Down Expand Up @@ -258,7 +230,7 @@ pub fn is_ty_must_use<'tcx>(
let mut nested_must_use = Vec::new();

tys.iter().zip(elem_exprs).enumerate().for_each(|(i, (ty, expr))| {
let must_use = is_ty_must_use(cx, ty, expr, simplify_uninhabited);
let must_use = is_ty_must_use(cx, ty, expr);

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

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

let must_use_result = is_ty_must_use(cx, ty, expr, false);
let must_use_result = is_ty_must_use(cx, ty, expr);
let type_lint_emitted_or_trivial = match must_use_result {
IsTyMustUse::Yes(path) => {
emit_must_use_untranslated(cx, &path, "", "", 1, false, expr_is_from_block);
Expand Down
Loading
Loading