From de94c5cd347a18699b9cc8e838b204616ed569a2 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Tue, 10 Mar 2026 23:28:32 +0100 Subject: [PATCH 1/2] improve target feature diagnostic --- compiler/rustc_codegen_ssa/src/errors.rs | 17 ++++- .../rustc_codegen_ssa/src/target_features.rs | 5 +- tests/auxiliary/minicore.rs | 2 + tests/ui/target-feature/invalid-attribute.rs | 17 ++++- .../target-feature/invalid-attribute.stderr | 64 +++++++++++-------- 5 files changed, 71 insertions(+), 34 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index be1965f674911..41337fc21a7b7 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -1248,8 +1248,21 @@ pub(crate) struct FeatureNotValid<'a> { #[primary_span] #[label("`{$feature}` is not valid for this target")] pub span: Span, - #[help("consider removing the leading `+` in the feature name")] - pub plus_hint: bool, + #[subdiagnostic] + pub plus_hint: Option>, +} + +#[derive(Subdiagnostic)] +#[suggestion( + "consider removing the leading `+` in the feature name", + code = "enable = \"{stripped}\"", + applicability = "maybe-incorrect", + style = "verbose" +)] +pub struct RemovePlusFromFeatureName<'a> { + #[primary_span] + pub span: Span, + pub stripped: &'a str, } #[derive(Diagnostic)] diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index e9209657984e0..08a02831312c7 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -14,7 +14,7 @@ use rustc_target::spec::Arch; use rustc_target::target_features::{RUSTC_SPECIFIC_FEATURES, Stability}; use smallvec::SmallVec; -use crate::errors::FeatureNotValid; +use crate::errors::{FeatureNotValid, RemovePlusFromFeatureName}; use crate::{errors, target_features}; /// Compute the enabled target features from the `#[target_feature]` function attribute. @@ -34,7 +34,8 @@ pub(crate) fn from_target_feature_attr( let Some(stability) = rust_target_features.get(feature_str) else { let plus_hint = feature_str .strip_prefix('+') - .is_some_and(|stripped| rust_target_features.contains_key(stripped)); + .filter(|stripped| rust_target_features.contains_key(*stripped)) + .map(|stripped| RemovePlusFromFeatureName { span: feature_span, stripped }); tcx.dcx().emit_err(FeatureNotValid { feature: feature_str, span: feature_span, diff --git a/tests/auxiliary/minicore.rs b/tests/auxiliary/minicore.rs index 17f325d582785..c91c5dfbeb157 100644 --- a/tests/auxiliary/minicore.rs +++ b/tests/auxiliary/minicore.rs @@ -236,6 +236,8 @@ impl_marker_trait!( ] ); +impl Sync for () {} + #[lang = "drop_in_place"] fn drop_in_place(_: *mut T) {} diff --git a/tests/ui/target-feature/invalid-attribute.rs b/tests/ui/target-feature/invalid-attribute.rs index a958700231a37..47068fbb25d49 100644 --- a/tests/ui/target-feature/invalid-attribute.rs +++ b/tests/ui/target-feature/invalid-attribute.rs @@ -1,14 +1,20 @@ -//@ only-x86_64 +//@ add-minicore +//@ compile-flags: --target=x86_64-unknown-linux-gnu +//@ needs-llvm-components: x86 #![warn(unused_attributes)] +#![feature(no_core)] +#![no_core] + +use minicore::*; #[target_feature(enable = "sse2")] //~^ ERROR attribute cannot be used on -extern crate alloc; +extern crate minicore; #[target_feature(enable = "sse2")] //~^ ERROR attribute cannot be used on -use alloc::alloc::alloc; +use minicore::mem::transmute; #[target_feature(enable = "sse2")] //~^ ERROR attribute cannot be used on @@ -112,3 +118,8 @@ fn main() { //~^ ERROR `+sse2` is not valid for this target //~| NOTE `+sse2` is not valid for this target unsafe fn hey() {} + +#[target_feature(enable = "+sse5")] +//~^ ERROR `+sse5` is not valid for this target +//~| NOTE `+sse5` is not valid for this target +unsafe fn typo() {} diff --git a/tests/ui/target-feature/invalid-attribute.stderr b/tests/ui/target-feature/invalid-attribute.stderr index 05a836b01af5d..fbc35cfdc325e 100644 --- a/tests/ui/target-feature/invalid-attribute.stderr +++ b/tests/ui/target-feature/invalid-attribute.stderr @@ -1,5 +1,5 @@ error: `#[target_feature]` attribute cannot be used on extern crates - --> $DIR/invalid-attribute.rs:5:1 + --> $DIR/invalid-attribute.rs:11:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | #[target_feature(enable = "sse2")] = help: `#[target_feature]` can only be applied to functions error: `#[target_feature]` attribute cannot be used on use statements - --> $DIR/invalid-attribute.rs:9:1 + --> $DIR/invalid-attribute.rs:15:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -15,7 +15,7 @@ LL | #[target_feature(enable = "sse2")] = help: `#[target_feature]` can only be applied to functions error: `#[target_feature]` attribute cannot be used on foreign modules - --> $DIR/invalid-attribute.rs:13:1 + --> $DIR/invalid-attribute.rs:19:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -23,7 +23,7 @@ LL | #[target_feature(enable = "sse2")] = help: `#[target_feature]` can only be applied to functions error[E0539]: malformed `target_feature` attribute input - --> $DIR/invalid-attribute.rs:17:1 + --> $DIR/invalid-attribute.rs:23:1 | LL | #[target_feature = "+sse2"] | ^^^^^^^^^^^^^^^^^---------^ @@ -32,7 +32,7 @@ LL | #[target_feature = "+sse2"] | help: must be of the form: `#[target_feature(enable = "feat1, feat2")]` error[E0539]: malformed `target_feature` attribute input - --> $DIR/invalid-attribute.rs:23:1 + --> $DIR/invalid-attribute.rs:29:1 | LL | #[target_feature(bar)] | ^^^^^^^^^^^^^^^^^---^^ @@ -41,7 +41,7 @@ LL | #[target_feature(bar)] | help: must be of the form: `#[target_feature(enable = "feat1, feat2")]` error[E0539]: malformed `target_feature` attribute input - --> $DIR/invalid-attribute.rs:26:1 + --> $DIR/invalid-attribute.rs:32:1 | LL | #[target_feature(disable = "baz")] | ^^^^^^^^^^^^^^^^^-------^^^^^^^^^^ @@ -50,7 +50,7 @@ LL | #[target_feature(disable = "baz")] | help: must be of the form: `#[target_feature(enable = "feat1, feat2")]` error: `#[target_feature]` attribute cannot be used on modules - --> $DIR/invalid-attribute.rs:31:1 + --> $DIR/invalid-attribute.rs:37:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -58,7 +58,7 @@ LL | #[target_feature(enable = "sse2")] = help: `#[target_feature]` can only be applied to functions error: `#[target_feature]` attribute cannot be used on constants - --> $DIR/invalid-attribute.rs:35:1 + --> $DIR/invalid-attribute.rs:41:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -66,7 +66,7 @@ LL | #[target_feature(enable = "sse2")] = help: `#[target_feature]` can only be applied to functions error: `#[target_feature]` attribute cannot be used on structs - --> $DIR/invalid-attribute.rs:39:1 + --> $DIR/invalid-attribute.rs:45:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -74,7 +74,7 @@ LL | #[target_feature(enable = "sse2")] = help: `#[target_feature]` can only be applied to functions error: `#[target_feature]` attribute cannot be used on enums - --> $DIR/invalid-attribute.rs:43:1 + --> $DIR/invalid-attribute.rs:49:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -82,7 +82,7 @@ LL | #[target_feature(enable = "sse2")] = help: `#[target_feature]` can only be applied to functions error: `#[target_feature]` attribute cannot be used on unions - --> $DIR/invalid-attribute.rs:47:1 + --> $DIR/invalid-attribute.rs:53:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -90,7 +90,7 @@ LL | #[target_feature(enable = "sse2")] = help: `#[target_feature]` can only be applied to functions error: `#[target_feature]` attribute cannot be used on type aliases - --> $DIR/invalid-attribute.rs:54:1 + --> $DIR/invalid-attribute.rs:60:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -98,7 +98,7 @@ LL | #[target_feature(enable = "sse2")] = help: `#[target_feature]` can only be applied to functions error: `#[target_feature]` attribute cannot be used on traits - --> $DIR/invalid-attribute.rs:58:1 + --> $DIR/invalid-attribute.rs:64:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -106,7 +106,7 @@ LL | #[target_feature(enable = "sse2")] = help: `#[target_feature]` can only be applied to functions error: `#[target_feature]` attribute cannot be used on statics - --> $DIR/invalid-attribute.rs:69:1 + --> $DIR/invalid-attribute.rs:75:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -114,7 +114,7 @@ LL | #[target_feature(enable = "sse2")] = help: `#[target_feature]` can only be applied to functions error: `#[target_feature]` attribute cannot be used on trait impl blocks - --> $DIR/invalid-attribute.rs:73:1 + --> $DIR/invalid-attribute.rs:79:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -122,7 +122,7 @@ LL | #[target_feature(enable = "sse2")] = help: `#[target_feature]` can only be applied to functions error: `#[target_feature]` attribute cannot be used on inherent impl blocks - --> $DIR/invalid-attribute.rs:79:1 + --> $DIR/invalid-attribute.rs:85:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -130,7 +130,7 @@ LL | #[target_feature(enable = "sse2")] = help: `#[target_feature]` can only be applied to functions error: `#[target_feature]` attribute cannot be used on expressions - --> $DIR/invalid-attribute.rs:100:5 + --> $DIR/invalid-attribute.rs:106:5 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -138,7 +138,7 @@ LL | #[target_feature(enable = "sse2")] = help: `#[target_feature]` can only be applied to functions error: `#[target_feature]` attribute cannot be used on closures - --> $DIR/invalid-attribute.rs:106:5 + --> $DIR/invalid-attribute.rs:112:5 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -146,7 +146,7 @@ LL | #[target_feature(enable = "sse2")] = help: `#[target_feature]` can be applied to functions and methods error[E0658]: cannot use `#[inline(always)]` with `#[target_feature]` - --> $DIR/invalid-attribute.rs:62:1 + --> $DIR/invalid-attribute.rs:68:1 | LL | #[inline(always)] | ^^^^^^^^^^^^^^^^^ @@ -156,13 +156,13 @@ LL | #[inline(always)] = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error: the feature named `foo` is not valid for this target - --> $DIR/invalid-attribute.rs:20:18 + --> $DIR/invalid-attribute.rs:26:18 | LL | #[target_feature(enable = "foo")] | ^^^^^^^^^^^^^^ `foo` is not valid for this target error[E0046]: not all trait items implemented, missing: `foo` - --> $DIR/invalid-attribute.rs:75:1 + --> $DIR/invalid-attribute.rs:81:1 | LL | impl Quux for u8 {} | ^^^^^^^^^^^^^^^^ missing `foo` in implementation @@ -171,7 +171,7 @@ LL | fn foo(); | --------- `foo` from trait error: `#[target_feature(..)]` cannot be applied to safe trait method - --> $DIR/invalid-attribute.rs:89:5 + --> $DIR/invalid-attribute.rs:95:5 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot be applied to safe trait method @@ -180,13 +180,13 @@ LL | fn foo() {} | -------- not an `unsafe` function error[E0053]: method `foo` has an incompatible type for trait - --> $DIR/invalid-attribute.rs:92:5 + --> $DIR/invalid-attribute.rs:98:5 | LL | fn foo() {} | ^^^^^^^^ expected safe fn, found unsafe fn | note: type in trait - --> $DIR/invalid-attribute.rs:84:5 + --> $DIR/invalid-attribute.rs:90:5 | LL | fn foo(); | ^^^^^^^^^ @@ -194,14 +194,24 @@ LL | fn foo(); found signature `#[target_features] fn()` error: the feature named `+sse2` is not valid for this target - --> $DIR/invalid-attribute.rs:111:18 + --> $DIR/invalid-attribute.rs:117:18 | LL | #[target_feature(enable = "+sse2")] | ^^^^^^^^^^^^^^^^ `+sse2` is not valid for this target | - = help: consider removing the leading `+` in the feature name +help: consider removing the leading `+` in the feature name + | +LL - #[target_feature(enable = "+sse2")] +LL + #[target_feature(enable = "sse2")] + | + +error: the feature named `+sse5` is not valid for this target + --> $DIR/invalid-attribute.rs:122:18 + | +LL | #[target_feature(enable = "+sse5")] + | ^^^^^^^^^^^^^^^^ `+sse5` is not valid for this target -error: aborting due to 24 previous errors +error: aborting due to 25 previous errors Some errors have detailed explanations: E0046, E0053, E0539, E0658. For more information about an error, try `rustc --explain E0046`. From c6b7f630a5365ccfe2d4f24c7f5a4cc8499c5a55 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Thu, 5 Mar 2026 12:25:24 -0700 Subject: [PATCH 2/2] rustdoc: rename `--emit` names These new names are pithier and match up with the rest of our terminology: - `--emit=html-static-files` matches the default name of the directory that it actually emits, which is `static.files` (the hyphen is used for emit because every other emit option uses hyphens, but the directory uses a dot because we don't want its name to conflict with a crate). - `--emit=html-non-static-files` matches the convention that emit is a noun, not an adjective. It also matches up with static-files, and it logically groups with other data formats. This commit changes the docs, but leaves in support for the old names, to break the cycle with cargo and docs.rs. This commit needs merged, then cargo and docs.rs will be updated to use the new names, then, finally, the old names will be removed. --- src/librustdoc/config.rs | 14 +++++++++----- src/librustdoc/html/render/write_shared.rs | 2 +- src/librustdoc/lib.rs | 2 +- tests/run-make/emit-shared-files/rmake.rs | 4 ++-- .../rustdoc-default-output/output-default.stdout | 2 +- 5 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index 3d4b4e969157c..f1fdce48181c2 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -322,8 +322,8 @@ pub(crate) enum ModuleSorting { #[derive(Clone, Debug, PartialEq, Eq)] pub(crate) enum EmitType { - Toolchain, - InvocationSpecific, + HtmlStaticFiles, + HtmlNonStaticFiles, DepInfo(Option), } @@ -332,8 +332,12 @@ impl FromStr for EmitType { fn from_str(s: &str) -> Result { match s { - "toolchain-shared-resources" => Ok(Self::Toolchain), - "invocation-specific" => Ok(Self::InvocationSpecific), + // old nightly-only choices that are going away soon + "toolchain-shared-resources" => Ok(Self::HtmlStaticFiles), + "invocation-specific" => Ok(Self::HtmlNonStaticFiles), + // modern choices + "html-static-files" => Ok(Self::HtmlStaticFiles), + "html-non-static-files" => Ok(Self::HtmlNonStaticFiles), "dep-info" => Ok(Self::DepInfo(None)), option => match option.strip_prefix("dep-info=") { Some("-") => Ok(Self::DepInfo(Some(OutFileName::Stdout))), @@ -346,7 +350,7 @@ impl FromStr for EmitType { impl RenderOptions { pub(crate) fn should_emit_crate(&self) -> bool { - self.emit.is_empty() || self.emit.contains(&EmitType::InvocationSpecific) + self.emit.is_empty() || self.emit.contains(&EmitType::HtmlNonStaticFiles) } pub(crate) fn dep_info(&self) -> Option> { diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index 1b5dbeed8de80..b1c77063ca94e 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -218,7 +218,7 @@ fn write_static_files( try_err!(fs::write(&dst_path, buffer), &dst_path); } - if opt.emit.is_empty() || opt.emit.contains(&EmitType::Toolchain) { + if opt.emit.is_empty() || opt.emit.contains(&EmitType::HtmlStaticFiles) { static_files::for_each(|f: &static_files::StaticFile| { let filename = static_dir.join(f.output_filename()); let contents: &[u8] = diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 8cad0210ea346..3445221e4dcee 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -537,7 +537,7 @@ fn opts() -> Vec { "", "emit", "Comma separated list of types of output for rustdoc to emit", - "[toolchain-shared-resources,invocation-specific,dep-info]", + "[html-static-files,html-non-static-files,dep-info]", ), opt(Unstable, FlagMulti, "", "no-run", "Compile doctests without running them", ""), opt( diff --git a/tests/run-make/emit-shared-files/rmake.rs b/tests/run-make/emit-shared-files/rmake.rs index bd868d4fd19e2..9841dce27fa2f 100644 --- a/tests/run-make/emit-shared-files/rmake.rs +++ b/tests/run-make/emit-shared-files/rmake.rs @@ -34,7 +34,7 @@ fn main() { rustdoc() .arg("-Zunstable-options") - .arg("--emit=toolchain-shared-resources") + .arg("--emit=html-static-files") .out_dir("toolchain-only") .arg("--resource-suffix=-xxx") .args(&["--extend-css", "z.css"]) @@ -68,7 +68,7 @@ fn main() { rustdoc() .arg("-Zunstable-options") - .arg("--emit=toolchain-shared-resources") + .arg("--emit=html-static-files") .out_dir("all-shared") .arg("--resource-suffix=-xxx") .args(&["--extend-css", "z.css"]) diff --git a/tests/run-make/rustdoc-default-output/output-default.stdout b/tests/run-make/rustdoc-default-output/output-default.stdout index 4e28be347cbb1..bc3a67a1ebcdc 100644 --- a/tests/run-make/rustdoc-default-output/output-default.stdout +++ b/tests/run-make/rustdoc-default-output/output-default.stdout @@ -150,7 +150,7 @@ Options: --generate-redirect-map Generate JSON file at the top level instead of generating HTML redirection files - --emit [toolchain-shared-resources,invocation-specific,dep-info] + --emit [html-static-files,html-non-static-files,dep-info] Comma separated list of types of output for rustdoc to emit --no-run Compile doctests without running them