Skip to content

Commit e16a049

Browse files
committed
Auto merge of #137914 - matthiaskrgr:rollup-phaxe6f, r=matthiaskrgr
Rollup of 6 pull requests Successful merges: - #137103 ({json|html}docck: catch and error on deprecated syntax) - #137632 (rustdoc: when merging target features, keep the highest stability) - #137684 (Add rustdoc support for `--emit=dep-info[=path]`) - #137794 (make qnx pass a test) - #137801 (tests: Unignore target modifier tests on all platforms) - #137826 (test(codegen): add looping_over_ne_bytes test for #133528) r? `@ghost` `@rustbot` modify labels: rollup
2 parents d491662 + 15e97bd commit e16a049

26 files changed

+266
-151
lines changed

compiler/rustc_codegen_ssa/src/target_features.rs

+44-12
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use rustc_middle::query::Providers;
1010
use rustc_middle::ty::TyCtxt;
1111
use rustc_session::parse::feature_err;
1212
use rustc_span::{Span, Symbol, sym};
13-
use rustc_target::target_features;
13+
use rustc_target::target_features::{self, Stability};
1414

1515
use crate::errors;
1616

@@ -87,12 +87,17 @@ pub(crate) fn from_target_feature_attr(
8787
// But ensure the ABI does not forbid enabling this.
8888
// Here we do assume that LLVM doesn't add even more implied features
8989
// we don't know about, at least no features that would have ABI effects!
90-
if abi_feature_constraints.incompatible.contains(&name.as_str()) {
91-
tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr {
92-
span: item.span(),
93-
feature: name.as_str(),
94-
reason: "this feature is incompatible with the target ABI",
95-
});
90+
// We skip this logic in rustdoc, where we want to allow all target features of
91+
// all targets, so we can't check their ABI compatibility and anyway we are not
92+
// generating code so "it's fine".
93+
if !tcx.sess.opts.actually_rustdoc {
94+
if abi_feature_constraints.incompatible.contains(&name.as_str()) {
95+
tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr {
96+
span: item.span(),
97+
feature: name.as_str(),
98+
reason: "this feature is incompatible with the target ABI",
99+
});
100+
}
96101
}
97102
target_features.push(TargetFeature { name, implied: name != feature_sym })
98103
}
@@ -142,11 +147,38 @@ pub(crate) fn provide(providers: &mut Providers) {
142147
rust_target_features: |tcx, cnum| {
143148
assert_eq!(cnum, LOCAL_CRATE);
144149
if tcx.sess.opts.actually_rustdoc {
145-
// rustdoc needs to be able to document functions that use all the features, so
146-
// whitelist them all
147-
rustc_target::target_features::all_rust_features()
148-
.map(|(a, b)| (a.to_string(), b))
149-
.collect()
150+
// HACK: rustdoc would like to pretend that we have all the target features, so we
151+
// have to merge all the lists into one. To ensure an unstable target never prevents
152+
// a stable one from working, we merge the stability info of all instances of the
153+
// same target feature name, with the "most stable" taking precedence. And then we
154+
// hope that this doesn't cause issues anywhere else in the compiler...
155+
let mut result: UnordMap<String, Stability> = Default::default();
156+
for (name, stability) in rustc_target::target_features::all_rust_features() {
157+
use std::collections::hash_map::Entry;
158+
match result.entry(name.to_owned()) {
159+
Entry::Vacant(vacant_entry) => {
160+
vacant_entry.insert(stability);
161+
}
162+
Entry::Occupied(mut occupied_entry) => {
163+
// Merge the two stabilities, "more stable" taking precedence.
164+
match (occupied_entry.get(), stability) {
165+
(Stability::Stable, _)
166+
| (
167+
Stability::Unstable { .. },
168+
Stability::Unstable { .. } | Stability::Forbidden { .. },
169+
)
170+
| (Stability::Forbidden { .. }, Stability::Forbidden { .. }) => {
171+
// The stability in the entry is at least as good as the new one, just keep it.
172+
}
173+
_ => {
174+
// Overwrite stabilite.
175+
occupied_entry.insert(stability);
176+
}
177+
}
178+
}
179+
}
180+
}
181+
result
150182
} else {
151183
tcx.sess
152184
.target

src/etc/htmldocck.py

+14
Original file line numberDiff line numberDiff line change
@@ -297,10 +297,24 @@ def filter_line(line):
297297
re.X | re.UNICODE,
298298
)
299299

300+
DEPRECATED_LINE_PATTERN = re.compile(
301+
r"""
302+
//\s+@
303+
""",
304+
re.X | re.UNICODE,
305+
)
306+
300307

301308
def get_commands(template):
302309
with io.open(template, encoding="utf-8") as f:
303310
for lineno, line in concat_multi_lines(f):
311+
if DEPRECATED_LINE_PATTERN.search(line):
312+
print_err(
313+
lineno,
314+
line,
315+
"Deprecated command syntax, replace `// @` with `//@ `",
316+
)
317+
continue
304318
m = LINE_PATTERN.search(line)
305319
if not m:
306320
continue

src/librustdoc/config.rs

+22-6
Original file line numberDiff line numberDiff line change
@@ -315,23 +315,30 @@ pub(crate) enum ModuleSorting {
315315
Alphabetical,
316316
}
317317

318-
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
318+
#[derive(Clone, Debug, PartialEq, Eq)]
319319
pub(crate) enum EmitType {
320320
Unversioned,
321321
Toolchain,
322322
InvocationSpecific,
323+
DepInfo(Option<PathBuf>),
323324
}
324325

325326
impl FromStr for EmitType {
326327
type Err = ();
327328

328329
fn from_str(s: &str) -> Result<Self, Self::Err> {
329-
use EmitType::*;
330330
match s {
331-
"unversioned-shared-resources" => Ok(Unversioned),
332-
"toolchain-shared-resources" => Ok(Toolchain),
333-
"invocation-specific" => Ok(InvocationSpecific),
334-
_ => Err(()),
331+
"unversioned-shared-resources" => Ok(Self::Unversioned),
332+
"toolchain-shared-resources" => Ok(Self::Toolchain),
333+
"invocation-specific" => Ok(Self::InvocationSpecific),
334+
"dep-info" => Ok(Self::DepInfo(None)),
335+
option => {
336+
if let Some(file) = option.strip_prefix("dep-info=") {
337+
Ok(Self::DepInfo(Some(Path::new(file).into())))
338+
} else {
339+
Err(())
340+
}
341+
}
335342
}
336343
}
337344
}
@@ -340,6 +347,15 @@ impl RenderOptions {
340347
pub(crate) fn should_emit_crate(&self) -> bool {
341348
self.emit.is_empty() || self.emit.contains(&EmitType::InvocationSpecific)
342349
}
350+
351+
pub(crate) fn dep_info(&self) -> Option<Option<&Path>> {
352+
for emit in &self.emit {
353+
if let EmitType::DepInfo(file) = emit {
354+
return Some(file.as_deref());
355+
}
356+
}
357+
None
358+
}
343359
}
344360

345361
/// Create the input (string or file path)

src/librustdoc/core.rs

+18-6
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,12 @@ use rustc_hir::def::Res;
1515
use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LocalDefId};
1616
use rustc_hir::intravisit::{self, Visitor};
1717
use rustc_hir::{HirId, Path};
18-
use rustc_interface::interface;
1918
use rustc_lint::{MissingDoc, late_lint_mod};
2019
use rustc_middle::hir::nested_filter;
2120
use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt};
22-
use rustc_session::config::{self, CrateType, ErrorOutputType, Input, ResolveDocLinks};
21+
use rustc_session::config::{
22+
self, CrateType, ErrorOutputType, Input, OutFileName, OutputType, OutputTypes, ResolveDocLinks,
23+
};
2324
pub(crate) use rustc_session::config::{Options, UnstableOptions};
2425
use rustc_session::{Session, lint};
2526
use rustc_span::source_map;
@@ -219,7 +220,7 @@ pub(crate) fn create_config(
219220
remap_path_prefix,
220221
..
221222
}: RustdocOptions,
222-
RenderOptions { document_private, .. }: &RenderOptions,
223+
render_options: &RenderOptions,
223224
) -> rustc_interface::Config {
224225
// Add the doc cfg into the doc build.
225226
cfgs.push("doc".to_string());
@@ -245,8 +246,11 @@ pub(crate) fn create_config(
245246

246247
let crate_types =
247248
if proc_macro_crate { vec![CrateType::ProcMacro] } else { vec![CrateType::Rlib] };
248-
let resolve_doc_links =
249-
if *document_private { ResolveDocLinks::All } else { ResolveDocLinks::Exported };
249+
let resolve_doc_links = if render_options.document_private {
250+
ResolveDocLinks::All
251+
} else {
252+
ResolveDocLinks::Exported
253+
};
250254
let test = scrape_examples_options.map(|opts| opts.scrape_tests).unwrap_or(false);
251255
// plays with error output here!
252256
let sessopts = config::Options {
@@ -269,10 +273,18 @@ pub(crate) fn create_config(
269273
crate_name,
270274
test,
271275
remap_path_prefix,
276+
output_types: if let Some(file) = render_options.dep_info() {
277+
OutputTypes::new(&[(
278+
OutputType::DepInfo,
279+
file.map(|f| OutFileName::Real(f.to_path_buf())),
280+
)])
281+
} else {
282+
OutputTypes::new(&[])
283+
},
272284
..Options::default()
273285
};
274286

275-
interface::Config {
287+
rustc_interface::Config {
276288
opts: sessopts,
277289
crate_cfg: cfgs,
278290
crate_check_cfg: check_cfgs,

src/librustdoc/html/render/write_shared.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ fn write_rendered_cross_crate_info(
153153
include_sources: bool,
154154
) -> Result<(), Error> {
155155
let m = &opt.should_merge;
156-
if opt.emit.is_empty() || opt.emit.contains(&EmitType::InvocationSpecific) {
156+
if opt.should_emit_crate() {
157157
if include_sources {
158158
write_rendered_cci::<SourcesPart, _>(SourcesPart::blank, dst, crates, m)?;
159159
}

src/librustdoc/lib.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -561,7 +561,7 @@ fn opts() -> Vec<RustcOptGroup> {
561561
"",
562562
"emit",
563563
"Comma separated list of types of output for rustdoc to emit",
564-
"[unversioned-shared-resources,toolchain-shared-resources,invocation-specific]",
564+
"[unversioned-shared-resources,toolchain-shared-resources,invocation-specific,dep-info]",
565565
),
566566
opt(Unstable, FlagMulti, "", "no-run", "Compile doctests without running them", ""),
567567
opt(
@@ -890,7 +890,13 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) {
890890
// if we ran coverage, bail early, we don't need to also generate docs at this point
891891
// (also we didn't load in any of the useful passes)
892892
return;
893-
} else if run_check {
893+
}
894+
895+
if render_opts.dep_info().is_some() {
896+
rustc_interface::passes::write_dep_info(tcx);
897+
}
898+
899+
if run_check {
894900
// Since we're in "check" mode, no need to generate anything beyond this point.
895901
return;
896902
}

src/tools/jsondocck/src/main.rs

+28-15
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::borrow::Cow;
22
use std::process::ExitCode;
3-
use std::sync::OnceLock;
3+
use std::sync::LazyLock;
44
use std::{env, fs};
55

66
use regex::{Regex, RegexBuilder};
@@ -151,8 +151,7 @@ impl CommandKind {
151151
}
152152
}
153153

154-
static LINE_PATTERN: OnceLock<Regex> = OnceLock::new();
155-
fn line_pattern() -> Regex {
154+
static LINE_PATTERN: LazyLock<Regex> = LazyLock::new(|| {
156155
RegexBuilder::new(
157156
r#"
158157
//@\s+
@@ -165,7 +164,19 @@ fn line_pattern() -> Regex {
165164
.unicode(true)
166165
.build()
167166
.unwrap()
168-
}
167+
});
168+
169+
static DEPRECATED_LINE_PATTERN: LazyLock<Regex> = LazyLock::new(|| {
170+
RegexBuilder::new(
171+
r#"
172+
//\s+@
173+
"#,
174+
)
175+
.ignore_whitespace(true)
176+
.unicode(true)
177+
.build()
178+
.unwrap()
179+
});
169180

170181
fn print_err(msg: &str, lineno: usize) {
171182
eprintln!("Invalid command: {} on line {}", msg, lineno)
@@ -184,21 +195,23 @@ fn get_commands(template: &str) -> Result<Vec<Command>, ()> {
184195
for (lineno, line) in file.split('\n').enumerate() {
185196
let lineno = lineno + 1;
186197

187-
let cap = match LINE_PATTERN.get_or_init(line_pattern).captures(line) {
188-
Some(c) => c,
189-
None => continue,
198+
if DEPRECATED_LINE_PATTERN.is_match(line) {
199+
print_err("Deprecated command syntax, replace `// @` with `//@ `", lineno);
200+
errors = true;
201+
continue;
202+
}
203+
204+
let Some(cap) = LINE_PATTERN.captures(line) else {
205+
continue;
190206
};
191207

192-
let negated = cap.name("negated").unwrap().as_str() == "!";
208+
let negated = &cap["negated"] == "!";
193209

194210
let args_str = &cap["args"];
195-
let args = match shlex::split(args_str) {
196-
Some(args) => args,
197-
None => {
198-
print_err(&format!("Invalid arguments to shlex::split: `{args_str}`",), lineno);
199-
errors = true;
200-
continue;
201-
}
211+
let Some(args) = shlex::split(args_str) else {
212+
print_err(&format!("Invalid arguments to shlex::split: `{args_str}`",), lineno);
213+
errors = true;
214+
continue;
202215
};
203216

204217
if let Some((kind, path)) = CommandKind::parse(&cap["cmd"], negated, &args) {

src/tools/run-make-support/src/external_deps/rustdoc.rs

+7
Original file line numberDiff line numberDiff line change
@@ -132,4 +132,11 @@ impl Rustdoc {
132132
self.cmd.arg(format);
133133
self
134134
}
135+
136+
/// Specify type(s) of output files to generate.
137+
pub fn emit<S: AsRef<str>>(&mut self, kinds: S) -> &mut Self {
138+
let kinds = kinds.as_ref();
139+
self.cmd.arg(format!("--emit={kinds}"));
140+
self
141+
}
135142
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//@ compile-flags: -Copt-level=3
2+
//@ min-llvm-version: 20
3+
#![crate_type = "lib"]
4+
5+
/// Ensure the function is properly optimized
6+
/// In the issue #133528, the function was not getting optimized
7+
/// whereas, a version with `bytes` wrapped into a `black_box` was optimized
8+
/// It was probably a LLVM bug that was fixed in LLVM 20
9+
10+
// CHECK-LABEL: @looping_over_ne_bytes
11+
// CHECK: icmp eq i64 %input, -1
12+
// CHECK-NEXT: ret i1
13+
#[no_mangle]
14+
fn looping_over_ne_bytes(input: u64) -> bool {
15+
let bytes = input.to_ne_bytes();
16+
bytes.iter().all(|x| *x == !0)
17+
}

tests/run-make/rustdoc-default-output/output-default.stdout

+1-1
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ Options:
153153
--generate-redirect-map
154154
Generate JSON file at the top level instead of
155155
generating HTML redirection files
156-
--emit [unversioned-shared-resources,toolchain-shared-resources,invocation-specific]
156+
--emit [unversioned-shared-resources,toolchain-shared-resources,invocation-specific,dep-info]
157157
Comma separated list of types of output for rustdoc to
158158
emit
159159
--no-run Compile doctests without running them
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
include!("foo.rs");
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
blablabla
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub fn foo() {}
+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#![crate_name = "foo"]
2+
3+
#[cfg_attr(doc, doc = include_str!("doc.md"))]
4+
pub struct Bar;
5+
6+
mod bar;
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// This is a simple smoke test for rustdoc's `--emit dep-info` feature. It prints out
2+
// information about dependencies in a Makefile-compatible format, as a `.d` file.
3+
4+
use run_make_support::assertion_helpers::assert_contains;
5+
use run_make_support::{path, rfs, rustdoc};
6+
7+
fn main() {
8+
// We're only emitting dep info, so we shouldn't be running static analysis to
9+
// figure out that this program is erroneous.
10+
rustdoc().input("lib.rs").arg("-Zunstable-options").emit("dep-info").run();
11+
12+
let content = rfs::read_to_string("foo.d");
13+
assert_contains(&content, "lib.rs:");
14+
assert_contains(&content, "foo.rs:");
15+
assert_contains(&content, "bar.rs:");
16+
assert_contains(&content, "doc.md:");
17+
18+
// Now we check that we can provide a file name to the `dep-info` argument.
19+
rustdoc().input("lib.rs").arg("-Zunstable-options").emit("dep-info=bla.d").run();
20+
assert!(path("bla.d").exists());
21+
}

0 commit comments

Comments
 (0)