Skip to content
2 changes: 1 addition & 1 deletion compiler/rustc_hir_analysis/src/check/region.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ fn resolve_block<'tcx>(
for (i, statement) in blk.stmts.iter().enumerate() {
match statement.kind {
hir::StmtKind::Let(LetStmt { els: Some(els), .. }) => {
// Let-else has a special lexical structure for variables.
// let-else has a special lexical structure for variables.
// First we take a checkpoint of the current scope context here.
let mut prev_cx = visitor.cx;

Expand Down
14 changes: 12 additions & 2 deletions compiler/rustc_lint/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2697,7 +2697,7 @@ declare_lint! {
///
/// ### Example
///
/// ```rust,no_run
/// ```rust,compile_fail
/// # #![allow(unused)]
/// use std::ptr;
/// unsafe {
Expand All @@ -2716,7 +2716,7 @@ declare_lint! {
///
/// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
pub DEREF_NULLPTR,
Warn,
Deny,
"detects when an null pointer is dereferenced"
}

Expand All @@ -2726,6 +2726,16 @@ impl<'tcx> LateLintPass<'tcx> for DerefNullPtr {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) {
/// test if expression is a null ptr
fn is_null_ptr(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
let pointer_ty = cx.typeck_results().expr_ty(expr);
let ty::RawPtr(pointee, _) = pointer_ty.kind() else {
return false;
};
if let Ok(layout) = cx.tcx.layout_of(cx.typing_env().as_query_input(*pointee)) {
if layout.layout.size() == rustc_abi::Size::ZERO {
return false;
}
}

match &expr.kind {
hir::ExprKind::Cast(expr, ty) => {
if let hir::TyKind::Ptr(_) = ty.kind {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir_build/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ mir_build_suggest_if_let = you might want to use `if let` to ignore the {$count
*[other] variants that aren't
} matched

mir_build_suggest_let_else = you might want to use `let else` to handle the {$count ->
mir_build_suggest_let_else = you might want to use `let...else` to handle the {$count ->
[one] variant that isn't
*[other] variants that aren't
} matched
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_parse/src/parser/stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -867,7 +867,7 @@ impl<'a> Parser<'a> {
if let_else || !if_let {
err.span_suggestion_verbose(
block_span.shrink_to_lo(),
format!("{alternatively}you might have meant to use `let else`"),
format!("{alternatively}you might have meant to use `let...else`"),
"else ".to_string(),
if let_else {
Applicability::MachineApplicable
Expand Down
31 changes: 31 additions & 0 deletions src/doc/rustdoc/src/unstable-features.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,37 @@ themselves marked as unstable. To use any of these options, pass `-Z unstable-op
the flag in question to Rustdoc on the command-line. To do this from Cargo, you can either use the
`RUSTDOCFLAGS` environment variable or the `cargo rustdoc` command.

### `--merge`, `--parts-out-dir`, and `--include-parts-dir`

These options control how rustdoc handles files that combine data from multiple crates.

By default, they act like `--merge=shared` is set, and `--parts-out-dir` and `--include-parts-dir`
are turned off. The `--merge=shared` mode causes rustdoc to load the existing data in the out-dir,
combine the new crate data into it, and write the result. This is very easy to use in scripts that
manually invoke rustdoc, but it's also slow, because it performs O(crates) work on
every crate, meaning it performs O(crates<sup>2</sup>) work.

```console
$ rustdoc crate1.rs --out-dir=doc
$ cat doc/search.index/crateNames/*
rd_("fcrate1")
$ rustdoc crate2.rs --out-dir=doc
$ cat doc/search.index/crateNames/*
rd_("fcrate1fcrate2")
```

To delay shared-data merging until the end of a build, so that you only have to perform O(crates)
work, use `--merge=none` on every crate except the last one, which will use `--merge=finalize`.

```console
$ rustdoc +nightly crate1.rs --merge=none --parts-out-dir=crate1.d -Zunstable-options
$ cat doc/search.index/crateNames/*
cat: 'doc/search.index/crateNames/*': No such file or directory
$ rustdoc +nightly crate2.rs --merge=finalize --include-parts-dir=crate1.d -Zunstable-options
$ cat doc/search.index/crateNames/*
rd_("fcrate1fcrate2")
```

### `--document-hidden-items`: Show items that are `#[doc(hidden)]`
<span id="document-hidden-items"></span>

Expand Down
17 changes: 10 additions & 7 deletions src/librustdoc/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -978,15 +978,16 @@ fn parse_extern_html_roots(
Ok(externs)
}

/// Path directly to crate-info file.
/// Path directly to crate-info directory.
///
/// For example, `/home/user/project/target/doc.parts/<crate>/crate-info`.
/// For example, `/home/user/project/target/doc.parts`.
/// Each crate has its info stored in a file called `CRATENAME.json`.
#[derive(Clone, Debug)]
pub(crate) struct PathToParts(pub(crate) PathBuf);

impl PathToParts {
fn from_flag(path: String) -> Result<PathToParts, String> {
let mut path = PathBuf::from(path);
let path = PathBuf::from(path);
// check here is for diagnostics
if path.exists() && !path.is_dir() {
Err(format!(
Expand All @@ -995,20 +996,22 @@ impl PathToParts {
))
} else {
// if it doesn't exist, we'll create it. worry about that in write_shared
path.push("crate-info");
Ok(PathToParts(path))
}
}
}

/// Reports error if --include-parts-dir / crate-info is not a file
/// Reports error if --include-parts-dir is not a directory
fn parse_include_parts_dir(m: &getopts::Matches) -> Result<Vec<PathToParts>, String> {
let mut ret = Vec::new();
for p in m.opt_strs("include-parts-dir") {
let p = PathToParts::from_flag(p)?;
// this is just for diagnostic
if !p.0.is_file() {
return Err(format!("--include-parts-dir expected {} to be a file", p.0.display()));
if !p.0.is_dir() {
return Err(format!(
"--include-parts-dir expected {} to be a directory",
p.0.display()
));
}
ret.push(p);
}
Expand Down
32 changes: 23 additions & 9 deletions src/librustdoc/html/render/write_shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
//! or contains "invocation-specific".

use std::cell::RefCell;
use std::ffi::OsString;
use std::ffi::{OsStr, OsString};
use std::fs::File;
use std::io::{self, Write as _};
use std::iter::once;
Expand Down Expand Up @@ -84,9 +84,11 @@ pub(crate) fn write_shared(
};

if let Some(parts_out_dir) = &opt.parts_out_dir {
create_parents(&parts_out_dir.0)?;
let mut parts_out_file = parts_out_dir.0.clone();
parts_out_file.push(&format!("{crate_name}.json"));
create_parents(&parts_out_file)?;
try_err!(
fs::write(&parts_out_dir.0, serde_json::to_string(&info).unwrap()),
fs::write(&parts_out_file, serde_json::to_string(&info).unwrap()),
&parts_out_dir.0
);
}
Expand Down Expand Up @@ -238,13 +240,25 @@ impl CrateInfo {
pub(crate) fn read_many(parts_paths: &[PathToParts]) -> Result<Vec<Self>, Error> {
parts_paths
.iter()
.map(|parts_path| {
let path = &parts_path.0;
let parts = try_err!(fs::read(path), &path);
let parts: CrateInfo = try_err!(serde_json::from_slice(&parts), &path);
Ok::<_, Error>(parts)
.fold(Ok(Vec::new()), |acc, parts_path| {
let mut acc = acc?;
let dir = &parts_path.0;
acc.append(&mut try_err!(std::fs::read_dir(dir), dir.as_path())
.filter_map(|file| {
let to_crate_info = |file: Result<std::fs::DirEntry, std::io::Error>| -> Result<Option<CrateInfo>, Error> {
let file = try_err!(file, dir.as_path());
if file.path().extension() != Some(OsStr::new("json")) {
return Ok(None);
}
let parts = try_err!(fs::read(file.path()), file.path());
let parts: CrateInfo = try_err!(serde_json::from_slice(&parts), file.path());
Ok(Some(parts))
};
to_crate_info(file).transpose()
})
.collect::<Result<Vec<CrateInfo>, Error>>()?);
Ok(acc)
})
.collect::<Result<Vec<CrateInfo>, Error>>()
}
}

Expand Down
4 changes: 4 additions & 0 deletions tests/run-make/rustdoc-merge-directory/dep1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
//@ hasraw crates.js 'dep1'
//@ hasraw search.index/name/*.js 'Dep1'
//@ has dep1/index.html
pub struct Dep1;
4 changes: 4 additions & 0 deletions tests/run-make/rustdoc-merge-directory/dep2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
//@ hasraw crates.js 'dep1'
//@ hasraw search.index/name/*.js 'Dep1'
//@ has dep2/index.html
pub struct Dep2;
4 changes: 4 additions & 0 deletions tests/run-make/rustdoc-merge-directory/dep_missing.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
//@ !hasraw crates.js 'dep_missing'
//@ !hasraw search.index/name/*.js 'DepMissing'
//@ has dep_missing/index.html
pub struct DepMissing;
46 changes: 46 additions & 0 deletions tests/run-make/rustdoc-merge-directory/rmake.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Running --merge=finalize without an input crate root should not trigger ICE.
// Issue: https://github.com/rust-lang/rust/issues/146646

//@ needs-target-std

use run_make_support::{htmldocck, path, rustdoc};

fn main() {
let out_dir = path("out");
let merged_dir = path("merged");
let parts_out_dir = path("parts");

rustdoc()
.input("dep1.rs")
.out_dir(&out_dir)
.arg("-Zunstable-options")
.arg(format!("--parts-out-dir={}", parts_out_dir.display()))
.arg("--merge=none")
.run();
assert!(parts_out_dir.join("dep1.json").exists());

rustdoc()
.input("dep2.rs")
.out_dir(&out_dir)
.arg("-Zunstable-options")
.arg(format!("--parts-out-dir={}", parts_out_dir.display()))
.arg("--merge=none")
.run();
assert!(parts_out_dir.join("dep2.json").exists());

// dep_missing is different, because --parts-out-dir is not supplied
rustdoc().input("dep_missing.rs").out_dir(&out_dir).run();
assert!(parts_out_dir.join("dep2.json").exists());

let output = rustdoc()
.arg("-Zunstable-options")
.out_dir(&out_dir)
.arg(format!("--include-parts-dir={}", parts_out_dir.display()))
.arg("--merge=finalize")
.run();
output.assert_stderr_not_contains("error: the compiler unexpectedly panicked. this is a bug.");

htmldocck().arg(&out_dir).arg("dep1.rs").run();
htmldocck().arg(&out_dir).arg("dep2.rs").run();
htmldocck().arg(out_dir).arg("dep_missing.rs").run();
}
2 changes: 1 addition & 1 deletion tests/run-make/rustdoc-merge-no-input-finalize/rmake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ fn main() {
.arg(format!("--parts-out-dir={}", parts_out_dir.display()))
.arg("--merge=none")
.run();
assert!(parts_out_dir.join("crate-info").exists());
assert!(parts_out_dir.join("sierra.json").exists());

let output = rustdoc()
.arg("-Zunstable-options")
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/empty/empty-never-array.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ LL | enum Helper<T, U> {
LL | T(T, [!; 0]),
| - not covered
= note: the matched value is of type `Helper<T, U>`
help: you might want to use `let else` to handle the variant that isn't matched
help: you might want to use `let...else` to handle the variant that isn't matched
|
LL | let Helper::U(u) = Helper::T(t, []) else { todo!() };
| ++++++++++++++++
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/error-codes/E0005.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ LL | let Some(y) = x;
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
= note: for more information, visit https://doc.rust-lang.org/book/ch19-02-refutability.html
= note: the matched value is of type `Option<i32>`
help: you might want to use `let else` to handle the variant that isn't matched
help: you might want to use `let...else` to handle the variant that isn't matched
|
LL | let Some(y) = x else { todo!() };
| ++++++++++++++++
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ LL | let Ok(_x) = &foo();
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
= note: for more information, visit https://doc.rust-lang.org/book/ch19-02-refutability.html
= note: the matched value is of type `&Result<u32, !>`
help: you might want to use `let else` to handle the variant that isn't matched
help: you might want to use `let...else` to handle the variant that isn't matched
|
LL | let Ok(_x) = &foo() else { todo!() };
| ++++++++++++++++
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ LL | let [a @ 3.., b @ ..3, c @ 4..6, ..] = xs;
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
= note: for more information, visit https://doc.rust-lang.org/book/ch19-02-refutability.html
= note: the matched value is of type `[i32; 8]`
help: you might want to use `let else` to handle the variant that isn't matched
help: you might want to use `let...else` to handle the variant that isn't matched
|
LL | let [a @ 3.., b @ ..3, c @ 4..6, ..] = xs else { todo!() };
| ++++++++++++++++
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ LL | let [a @ 3.., b @ ..3, c @ 4..6, ..] = xs;
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
= note: for more information, visit https://doc.rust-lang.org/book/ch19-02-refutability.html
= note: the matched value is of type `[i32; 8]`
help: you might want to use `let else` to handle the variant that isn't matched
help: you might want to use `let...else` to handle the variant that isn't matched
|
LL | let [a @ 3.., b @ ..3, c @ 4..6, ..] = xs else { todo!() };
| ++++++++++++++++
Expand Down
10 changes: 8 additions & 2 deletions tests/ui/lint/lint-deref-nullptr.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
// test the deref_nullptr lint

#![deny(deref_nullptr)]

use std::ptr;

struct Struct {
field: u8,
}

#[derive(Clone, Copy)]
struct Zst;

fn f() {
unsafe {
let a = 1;
Expand All @@ -32,6 +33,11 @@ fn f() {
// ^^ OKAY
let offset = ptr::addr_of!((*ptr::null::<Struct>()).field);
//~^ ERROR dereferencing a null pointer

// Make sure the lint permits derefs of null pointers to ZSTs
let ok: Zst = *ptr::null();
let ok: Zst = *ptr::null_mut();
let ok: Zst = *(0 as *const Zst);
}
}

Expand Down
Loading
Loading