diff --git a/compiler/rustc_mir/src/util/graphviz.rs b/compiler/rustc_mir/src/util/graphviz.rs index 37498e50c0eb1..92c7a358c0a41 100644 --- a/compiler/rustc_mir/src/util/graphviz.rs +++ b/compiler/rustc_mir/src/util/graphviz.rs @@ -2,7 +2,7 @@ use gsgdt::GraphvizSettings; use rustc_graphviz as dot; use rustc_hir::def_id::DefId; use rustc_middle::mir::*; -use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::{self, TyCtxt}; use std::fmt::Debug; use std::io::{self, Write}; @@ -16,14 +16,27 @@ where { let def_ids = dump_mir_def_ids(tcx, single); - let use_subgraphs = def_ids.len() > 1; + let mirs = + def_ids + .iter() + .flat_map(|def_id| { + if tcx.is_const_fn_raw(*def_id) { + vec![tcx.optimized_mir(*def_id), tcx.mir_for_ctfe(*def_id)] + } else { + vec![tcx.instance_mir(ty::InstanceDef::Item(ty::WithOptConstParam::unknown( + *def_id, + )))] + } + }) + .collect::>(); + + let use_subgraphs = mirs.len() > 1; if use_subgraphs { writeln!(w, "digraph __crate__ {{")?; } - for def_id in def_ids { - let body = &tcx.optimized_mir(def_id); - write_mir_fn_graphviz(tcx, body, use_subgraphs, w)?; + for mir in mirs { + write_mir_fn_graphviz(tcx, mir, use_subgraphs, w)?; } if use_subgraphs { diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index d51b501f7ae3d..62a95aa57c29f 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -37,15 +37,6 @@ fn should_explore(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> bool { ) } -fn base_expr<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { - loop { - match expr.kind { - hir::ExprKind::Field(base, ..) => expr = base, - _ => return expr, - } - } -} - struct MarkSymbolVisitor<'tcx> { worklist: Vec, tcx: TyCtxt<'tcx>, @@ -143,6 +134,22 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { } } + fn handle_assign(&mut self, expr: &'tcx hir::Expr<'tcx>) { + if self + .typeck_results() + .expr_adjustments(expr) + .iter() + .any(|adj| matches!(adj.kind, ty::adjustment::Adjust::Deref(_))) + { + self.visit_expr(expr); + } else if let hir::ExprKind::Field(base, ..) = expr.kind { + // Ignore write to field + self.handle_assign(base); + } else { + self.visit_expr(expr); + } + } + fn handle_field_pattern_match( &mut self, lhs: &hir::Pat<'_>, @@ -272,8 +279,7 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> { self.lookup_and_handle_method(expr.hir_id); } hir::ExprKind::Assign(ref left, ref right, ..) => { - // Ignore write to field - self.visit_expr(base_expr(left)); + self.handle_assign(left); self.visit_expr(right); return; } diff --git a/library/core/tests/atomic.rs b/library/core/tests/atomic.rs index b735957666fc5..539982eb0e49f 100644 --- a/library/core/tests/atomic.rs +++ b/library/core/tests/atomic.rs @@ -61,6 +61,7 @@ fn uint_xor() { #[test] #[cfg(any(not(target_arch = "arm"), target_os = "linux"))] // Missing intrinsic in compiler-builtins +#[cfg_attr(miri, ignore)] // FIXME: Miri does not support atomic_min fn uint_min() { let x = AtomicUsize::new(0xf731); assert_eq!(x.fetch_min(0x137f, SeqCst), 0xf731); @@ -71,6 +72,7 @@ fn uint_min() { #[test] #[cfg(any(not(target_arch = "arm"), target_os = "linux"))] // Missing intrinsic in compiler-builtins +#[cfg_attr(miri, ignore)] // FIXME: Miri does not support atomic_max fn uint_max() { let x = AtomicUsize::new(0x137f); assert_eq!(x.fetch_max(0xf731, SeqCst), 0x137f); @@ -109,6 +111,7 @@ fn int_xor() { #[test] #[cfg(any(not(target_arch = "arm"), target_os = "linux"))] // Missing intrinsic in compiler-builtins +#[cfg_attr(miri, ignore)] // FIXME: Miri does not support atomic_min fn int_min() { let x = AtomicIsize::new(0xf731); assert_eq!(x.fetch_min(0x137f, SeqCst), 0xf731); @@ -119,6 +122,7 @@ fn int_min() { #[test] #[cfg(any(not(target_arch = "arm"), target_os = "linux"))] // Missing intrinsic in compiler-builtins +#[cfg_attr(miri, ignore)] // FIXME: Miri does not support atomic_max fn int_max() { let x = AtomicIsize::new(0x137f); assert_eq!(x.fetch_max(0xf731, SeqCst), 0x137f); diff --git a/src/doc/rustdoc/src/how-to-write-documentation.md b/src/doc/rustdoc/src/how-to-write-documentation.md index 9938cddc94106..688be7aedea38 100644 --- a/src/doc/rustdoc/src/how-to-write-documentation.md +++ b/src/doc/rustdoc/src/how-to-write-documentation.md @@ -101,7 +101,7 @@ what an item is, how it is used, and for what purpose it exists. Let's see an example coming from the [standard library] by taking a look at the [`std::env::args()`][env::args] function: -``````text +``````markdown Returns the arguments which this program was started with (normally passed via the command line). @@ -135,7 +135,7 @@ for argument in env::args() { Everything before the first empty line will be reused to describe the component in searches and module overviews. For example, the function `std::env::args()` -above will be shown on the [`std::env`] module documentation. It is good +above will be shown on the [`std::env`] module documentation. It is good practice to keep the summary to one line: concise writing is a goal of good documentation. @@ -153,9 +153,10 @@ and finally provides a code example. ## Markdown -`rustdoc` uses the [CommonMark markdown specification]. You might be -interested into taking a look at their website to see what's possible to do. - - [commonmark quick reference] +`rustdoc` uses the [CommonMark Markdown specification]. You might be +interested in taking a look at their website to see what's possible: + + - [CommonMark quick reference] - [current spec] In addition to the standard CommonMark syntax, `rustdoc` supports several @@ -240,6 +241,21 @@ This will render as See the specification for the [task list extension] for more details. +### Smart punctuation + +Some ASCII punctuation sequences will be automatically turned into fancy Unicode +characters: + +| ASCII sequence | Unicode | +|----------------|---------| +| `--` | – | +| `---` | — | +| `...` | … | +| `"` | “ or ”, depending on context | +| `'` | ‘ or ’, depending on context | + +So, no need to manually enter those Unicode characters! + [`backtrace`]: https://docs.rs/backtrace/0.3.50/backtrace/ [commonmark markdown specification]: https://commonmark.org/ [commonmark quick reference]: https://commonmark.org/help/ diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index b7854bbf82b17..cb11f22d0d859 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -52,11 +52,12 @@ pub(crate) fn opts() -> Options { | Options::ENABLE_FOOTNOTES | Options::ENABLE_STRIKETHROUGH | Options::ENABLE_TASKLISTS + | Options::ENABLE_SMART_PUNCTUATION } /// A subset of [`opts()`] used for rendering summaries. pub(crate) fn summary_opts() -> Options { - Options::ENABLE_STRIKETHROUGH + Options::ENABLE_STRIKETHROUGH | Options::ENABLE_SMART_PUNCTUATION } /// When `to_string` is called, this struct will emit the HTML corresponding to diff --git a/src/librustdoc/html/markdown/tests.rs b/src/librustdoc/html/markdown/tests.rs index 994fe8206e8d4..6b2cfe685752d 100644 --- a/src/librustdoc/html/markdown/tests.rs +++ b/src/librustdoc/html/markdown/tests.rs @@ -201,8 +201,8 @@ fn test_short_markdown_summary() { t("Hard-break \nsummary", "Hard-break summary"); t("hello [Rust] :)\n\n[Rust]: https://www.rust-lang.org", "hello Rust :)"); t("hello [Rust](https://www.rust-lang.org \"Rust\") :)", "hello Rust :)"); - t("code `let x = i32;` ...", "code let x = i32; ..."); - t("type `Type<'static>` ...", "type Type<'static> ..."); + t("code `let x = i32;` ...", "code let x = i32; …"); + t("type `Type<'static>` ...", "type Type<'static> …"); t("# top header", "top header"); t("## header", "header"); t("first paragraph\n\nsecond paragraph", "first paragraph"); @@ -227,8 +227,8 @@ fn test_plain_text_summary() { t("Hard-break \nsummary", "Hard-break summary"); t("hello [Rust] :)\n\n[Rust]: https://www.rust-lang.org", "hello Rust :)"); t("hello [Rust](https://www.rust-lang.org \"Rust\") :)", "hello Rust :)"); - t("code `let x = i32;` ...", "code `let x = i32;` ..."); - t("type `Type<'static>` ...", "type `Type<'static>` ..."); + t("code `let x = i32;` ...", "code `let x = i32;` …"); + t("type `Type<'static>` ...", "type `Type<'static>` …"); t("# top header", "top header"); t("# top header\n\nfollowed by some text", "top header"); t("## header", "header"); @@ -251,6 +251,6 @@ fn test_markdown_html_escape() { } t("`Struct<'a, T>`", "

Struct<'a, T>

\n"); - t("Struct<'a, T>", "

Struct<'a, T>

\n"); + t("Struct<'a, T>", "

Struct<’a, T>

\n"); t("Struct
", "

Struct<br>

\n"); } diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js index d6d3171afbffe..6da3b54289b2a 100644 --- a/src/librustdoc/html/static/main.js +++ b/src/librustdoc/html/static/main.js @@ -101,7 +101,7 @@ function focusSearchBar() { getSearchInput().focus(); } -// Removes the focus from the search bar +// Removes the focus from the search bar. function defocusSearchBar() { getSearchInput().blur(); } @@ -220,6 +220,11 @@ function defocusSearchBar() { addClass(search, "hidden"); removeClass(main, "hidden"); document.title = titleBeforeSearch; + // We also remove the query parameter from the URL. + if (browserSupportsHistoryApi()) { + history.replaceState("", window.currentCrate + " - Rust", + getNakedUrl() + window.location.hash); + } } // used for special search precedence diff --git a/src/test/rustdoc-json/nested.rs b/src/test/rustdoc-json/nested.rs index 7e705255d983d..a3d4935f49614 100644 --- a/src/test/rustdoc-json/nested.rs +++ b/src/test/rustdoc-json/nested.rs @@ -1,24 +1,28 @@ // edition:2018 -// @has nested.json "$.index[*][?(@.name=='nested')].kind" \"module\" -// @has - "$.index[*][?(@.name=='nested')].inner.is_crate" true +// @is nested.json "$.index[*][?(@.name=='nested')].kind" \"module\" +// @is - "$.index[*][?(@.name=='nested')].inner.is_crate" true // @count - "$.index[*][?(@.name=='nested')].inner.items[*]" 1 -// @has nested.json "$.index[*][?(@.name=='l1')].kind" \"module\" -// @has - "$.index[*][?(@.name=='l1')].inner.is_crate" false +// @is nested.json "$.index[*][?(@.name=='l1')].kind" \"module\" +// @is - "$.index[*][?(@.name=='l1')].inner.is_crate" false // @count - "$.index[*][?(@.name=='l1')].inner.items[*]" 2 pub mod l1 { - // @has nested.json "$.index[*][?(@.name=='l3')].kind" \"module\" - // @has - "$.index[*][?(@.name=='l3')].inner.is_crate" false + // @is nested.json "$.index[*][?(@.name=='l3')].kind" \"module\" + // @is - "$.index[*][?(@.name=='l3')].inner.is_crate" false // @count - "$.index[*][?(@.name=='l3')].inner.items[*]" 1 + // @set l3_id = - "$.index[*][?(@.name=='l3')].id" + // @has - "$.index[*][?(@.name=='l1')].inner.items[*]" $l3_id pub mod l3 { - // @has nested.json "$.index[*][?(@.name=='L4')].kind" \"struct\" - // @has - "$.index[*][?(@.name=='L4')].inner.struct_type" \"unit\" + // @is nested.json "$.index[*][?(@.name=='L4')].kind" \"struct\" + // @is - "$.index[*][?(@.name=='L4')].inner.struct_type" \"unit\" + // @set l4_id = - "$.index[*][?(@.name=='L4')].id" + // @has - "$.index[*][?(@.name=='l3')].inner.items[*]" $l4_id pub struct L4; } - // @has nested.json "$.index[*][?(@.inner.span=='l3::L4')].kind" \"import\" - // @has - "$.index[*][?(@.inner.span=='l3::L4')].inner.glob" false + // @is nested.json "$.index[*][?(@.inner.span=='l3::L4')].kind" \"import\" + // @is - "$.index[*][?(@.inner.span=='l3::L4')].inner.glob" false pub use l3::L4; } diff --git a/src/test/rustdoc/inline_cross/add-docs.rs b/src/test/rustdoc/inline_cross/add-docs.rs index 1af5e8f03b44e..8f0c4e5e64184 100644 --- a/src/test/rustdoc/inline_cross/add-docs.rs +++ b/src/test/rustdoc/inline_cross/add-docs.rs @@ -4,6 +4,6 @@ extern crate inner; // @has add_docs/struct.MyStruct.html -// @has add_docs/struct.MyStruct.html "Doc comment from 'pub use', Doc comment from definition" +// @has add_docs/struct.MyStruct.html "Doc comment from ‘pub use’, Doc comment from definition" /// Doc comment from 'pub use', pub use inner::MyStruct; diff --git a/src/test/rustdoc/smart-punct.rs b/src/test/rustdoc/smart-punct.rs new file mode 100644 index 0000000000000..a1ca2699554e5 --- /dev/null +++ b/src/test/rustdoc/smart-punct.rs @@ -0,0 +1,30 @@ +// ignore-tidy-linelength + +#![crate_name = "foo"] + +//! This is the "start" of the 'document'! How'd you know that "it's" the start? +//! +//! # Header with "smart punct'" +//! +//! [link with "smart punct'" -- yessiree!][] +//! +//! [link with "smart punct'" -- yessiree!]: https://www.rust-lang.org +//! +//! # Code should not be smart-punct'd +//! +//! `this inline code -- it shouldn't have "smart punct"` +//! +//! ``` +//! let x = "don't smart-punct me -- please!"; +//! ``` +//! +//! ```text +//! I say "don't smart-punct me -- please!" +//! ``` + +// @has "foo/index.html" "//p" "This is the “start” of the ‘document’! How’d you know that “it’s” the start?" +// @has "foo/index.html" "//h1" "Header with “smart punct’”" +// @has "foo/index.html" '//a[@href="https://www.rust-lang.org"]' "link with “smart punct’” – yessiree!" +// @has "foo/index.html" '//code' "this inline code -- it shouldn't have \"smart punct\"" +// @has "foo/index.html" '//pre' "let x = \"don't smart-punct me -- please!\";" +// @has "foo/index.html" '//pre' "I say \"don't smart-punct me -- please!\"" diff --git a/src/test/ui/issues/issue-81918.rs b/src/test/ui/issues/issue-81918.rs new file mode 100644 index 0000000000000..8938b8a6f2c52 --- /dev/null +++ b/src/test/ui/issues/issue-81918.rs @@ -0,0 +1,11 @@ +// check-pass +// dont-check-compiler-stdout +// compile-flags: -Z unpretty=mir-cfg + +// This checks that unpretty=mir-cfg does not panic. See #81918. + +const TAG: &'static str = "ABCD"; + +fn main() { + if TAG == "" {} +} diff --git a/src/test/ui/lint/dead-code/write-only-field.rs b/src/test/ui/lint/dead-code/write-only-field.rs index 78cfcfda8f971..7b3f1e9f5b6cb 100644 --- a/src/test/ui/lint/dead-code/write-only-field.rs +++ b/src/test/ui/lint/dead-code/write-only-field.rs @@ -17,4 +17,53 @@ fn field_write(s: &mut S) { fn main() { let mut s = S { f: 0, sub: Sub { f: 0 } }; field_write(&mut s); + + auto_deref(); + nested_boxes(); +} + +fn auto_deref() { + struct E { + x: bool, + y: bool, //~ ERROR: field is never read + } + + struct P<'a> { + e: &'a mut E + } + + impl P<'_> { + fn f(&mut self) { + self.e.x = true; + self.e.y = true; + } + } + + let mut e = E { x: false, y: false }; + let mut p = P { e: &mut e }; + p.f(); + assert!(e.x); +} + +fn nested_boxes() { + struct A { + b: Box, + } + + struct B { + c: Box, + } + + struct C { + u: u32, //~ ERROR: field is never read + v: u32, //~ ERROR: field is never read + } + + let mut a = A { + b: Box::new(B { + c: Box::new(C { u: 0, v: 0 }), + }), + }; + a.b.c.v = 10; + a.b.c = Box::new(C { u: 1, v: 2 }); } diff --git a/src/test/ui/lint/dead-code/write-only-field.stderr b/src/test/ui/lint/dead-code/write-only-field.stderr index 70d2149665b20..a191d22c8b94c 100644 --- a/src/test/ui/lint/dead-code/write-only-field.stderr +++ b/src/test/ui/lint/dead-code/write-only-field.stderr @@ -22,5 +22,23 @@ error: field is never read: `f` LL | f: i32, | ^^^^^^ -error: aborting due to 3 previous errors +error: field is never read: `y` + --> $DIR/write-only-field.rs:28:9 + | +LL | y: bool, + | ^^^^^^^ + +error: field is never read: `u` + --> $DIR/write-only-field.rs:58:9 + | +LL | u: u32, + | ^^^^^^ + +error: field is never read: `v` + --> $DIR/write-only-field.rs:59:9 + | +LL | v: u32, + | ^^^^^^ + +error: aborting due to 6 previous errors diff --git a/src/tools/jsondocck/src/cache.rs b/src/tools/jsondocck/src/cache.rs index b742f0eb3ee55..8a6a911321c34 100644 --- a/src/tools/jsondocck/src/cache.rs +++ b/src/tools/jsondocck/src/cache.rs @@ -9,6 +9,7 @@ pub struct Cache { root: PathBuf, files: HashMap, values: HashMap, + pub variables: HashMap, last_path: Option, } @@ -19,6 +20,7 @@ impl Cache { root: Path::new(doc_dir).to_owned(), files: HashMap::new(), values: HashMap::new(), + variables: HashMap::new(), last_path: None, } } diff --git a/src/tools/jsondocck/src/main.rs b/src/tools/jsondocck/src/main.rs index 6ec292aba6495..5020a4917a00a 100644 --- a/src/tools/jsondocck/src/main.rs +++ b/src/tools/jsondocck/src/main.rs @@ -2,6 +2,7 @@ use jsonpath_lib::select; use lazy_static::lazy_static; use regex::{Regex, RegexBuilder}; use serde_json::Value; +use std::borrow::Cow; use std::{env, fmt, fs}; mod cache; @@ -48,13 +49,16 @@ pub struct Command { pub enum CommandKind { Has, Count, + Is, + Set, } impl CommandKind { fn validate(&self, args: &[String], command_num: usize, lineno: usize) -> bool { let count = match self { CommandKind::Has => (1..=3).contains(&args.len()), - CommandKind::Count => 3 == args.len(), + CommandKind::Count | CommandKind::Is => 3 == args.len(), + CommandKind::Set => 4 == args.len(), }; if !count { @@ -83,6 +87,8 @@ impl fmt::Display for CommandKind { let text = match self { CommandKind::Has => "has", CommandKind::Count => "count", + CommandKind::Is => "is", + CommandKind::Set => "set", }; write!(f, "{}", text) } @@ -127,6 +133,8 @@ fn get_commands(template: &str) -> Result, ()> { let cmd = match cmd { "has" => CommandKind::Has, "count" => CommandKind::Count, + "is" => CommandKind::Is, + "set" => CommandKind::Set, _ => { print_err(&format!("Unrecognized command name `@{}`", cmd), lineno); errors = true; @@ -180,6 +188,7 @@ fn get_commands(template: &str) -> Result, ()> { /// Performs the actual work of ensuring a command passes. Generally assumes the command /// is syntactically valid. fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> { + // FIXME: Be more granular about why, (e.g. syntax error, count not equal) let result = match command.kind { CommandKind::Has => { match command.args.len() { @@ -188,23 +197,15 @@ fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> { // @has = check path exists 2 => { let val = cache.get_value(&command.args[0])?; - - match select(&val, &command.args[1]) { - Ok(results) => !results.is_empty(), - Err(_) => false, - } + let results = select(&val, &command.args[1]).unwrap(); + !results.is_empty() } // @has = check *any* item matched by path equals value 3 => { let val = cache.get_value(&command.args[0])?; - match select(&val, &command.args[1]) { - Ok(results) => { - let pat: Value = serde_json::from_str(&command.args[2]).unwrap(); - - !results.is_empty() && results.into_iter().any(|val| *val == pat) - } - Err(_) => false, - } + let results = select(&val, &command.args[1]).unwrap(); + let pat = string_to_value(&command.args[2], cache); + results.contains(&pat.as_ref()) } _ => unreachable!(), } @@ -215,9 +216,37 @@ fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> { let expected: usize = command.args[2].parse().unwrap(); let val = cache.get_value(&command.args[0])?; - match select(&val, &command.args[1]) { - Ok(results) => results.len() == expected, - Err(_) => false, + let results = select(&val, &command.args[1]).unwrap(); + results.len() == expected + } + CommandKind::Is => { + // @has = check *exactly one* item matched by path, and it equals value + assert_eq!(command.args.len(), 3); + let val = cache.get_value(&command.args[0])?; + let results = select(&val, &command.args[1]).unwrap(); + let pat = string_to_value(&command.args[2], cache); + results.len() == 1 && results[0] == pat.as_ref() + } + CommandKind::Set => { + // @set = + assert_eq!(command.args.len(), 4); + assert_eq!(command.args[1], "=", "Expected an `=`"); + let val = cache.get_value(&command.args[2])?; + let results = select(&val, &command.args[3]).unwrap(); + assert_eq!(results.len(), 1); + match results.len() { + 0 => false, + 1 => { + let r = cache.variables.insert(command.args[0].clone(), results[0].clone()); + assert!(r.is_none(), "Name collision: {} is duplicated", command.args[0]); + true + } + _ => { + panic!( + "Got multiple results in `@set` for `{}`: {:?}", + &command.args[3], results + ); + } } } }; @@ -247,3 +276,11 @@ fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> { Ok(()) } } + +fn string_to_value<'a>(s: &str, cache: &'a Cache) -> Cow<'a, Value> { + if s.starts_with("$") { + Cow::Borrowed(&cache.variables[&s[1..]]) + } else { + Cow::Owned(serde_json::from_str(s).unwrap()) + } +}