From 870f84875b073c7ad84fd943bdcbc0030512bc39 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 20 Jan 2025 16:57:30 +0100 Subject: [PATCH 01/40] Add support for trait associated items --- src/librustdoc/html/render/span_map.rs | 61 ++++++++++++------------ tests/rustdoc/jump-to-def-assoc-items.rs | 15 ++++++ 2 files changed, 46 insertions(+), 30 deletions(-) create mode 100644 tests/rustdoc/jump-to-def-assoc-items.rs diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs index 846d3ad310ce4..ebbc61db688f7 100644 --- a/src/librustdoc/html/render/span_map.rs +++ b/src/librustdoc/html/render/span_map.rs @@ -4,9 +4,7 @@ use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{ - ExprKind, HirId, Item, ItemKind, Mod, Node, Pat, PatExpr, PatExprKind, PatKind, QPath, -}; +use rustc_hir::{ExprKind, HirId, Item, ItemKind, Mod, Node, QPath}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::TyCtxt; use rustc_span::hygiene::MacroKind; @@ -189,31 +187,6 @@ impl SpanMapVisitor<'_> { self.matches.insert(span, link); } } - - fn handle_pat(&mut self, p: &Pat<'_>) { - let mut check_qpath = |qpath, hir_id| match qpath { - QPath::TypeRelative(_, path) if matches!(path.res, Res::Err) => { - self.infer_id(path.hir_id, Some(hir_id), qpath.span()); - } - QPath::Resolved(_, path) => self.handle_path(path), - _ => {} - }; - match p.kind { - PatKind::Binding(_, _, _, Some(p)) => self.handle_pat(p), - PatKind::Struct(qpath, _, _) | PatKind::TupleStruct(qpath, _, _) => { - check_qpath(qpath, p.hir_id) - } - PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), hir_id, .. }) => { - check_qpath(*qpath, *hir_id) - } - PatKind::Or(pats) => { - for pat in pats { - self.handle_pat(pat); - } - } - _ => {} - } - } } impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> { @@ -231,8 +204,36 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> { intravisit::walk_path(self, path); } - fn visit_pat(&mut self, p: &Pat<'tcx>) { - self.handle_pat(p); + fn visit_qpath(&mut self, qpath: &QPath<'tcx>, id: HirId, span: Span) { + match *qpath { + QPath::TypeRelative(qself, path) => { + if matches!(path.res, Res::Err) { + let tcx = self.tcx; + let hir = tcx.hir(); + let body_id = hir.enclosing_body_owner(id); + let typeck_results = tcx.typeck_body(hir.body_owned_by(body_id).id()); + let path = rustc_hir::Path { + // We change the span to not include parens. + span: span.with_hi(path.ident.span.hi()), + res: typeck_results.qpath_res(qpath, id), + segments: &[], + }; + self.handle_path(&path); + } else { + self.infer_id(path.hir_id, Some(id), span); + } + + rustc_ast::visit::try_visit!(self.visit_ty(qself)); + self.visit_path_segment(path); + } + QPath::Resolved(maybe_qself, path) => { + self.handle_path(path); + + rustc_ast::visit::visit_opt!(self, visit_ty, maybe_qself); + self.visit_path(path, id) + } + _ => {} + } } fn visit_mod(&mut self, m: &'tcx Mod<'tcx>, span: Span, id: HirId) { diff --git a/tests/rustdoc/jump-to-def-assoc-items.rs b/tests/rustdoc/jump-to-def-assoc-items.rs new file mode 100644 index 0000000000000..1229c36317253 --- /dev/null +++ b/tests/rustdoc/jump-to-def-assoc-items.rs @@ -0,0 +1,15 @@ +//@ compile-flags: -Zunstable-options --generate-link-to-definition + +#![crate_name = "foo"] + +pub enum Ty { + Var, +} + +//@ has 'src/foo/jump-to-def-assoc-items.rs.html' +//@ has - '//a[@href="#6"]' 'Self::Var' +impl Ty { + fn f() { + let _ = Self::Var; + } +} From ef39e42c17487d2af46d07759cea166b2a72221d Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 26 Jan 2025 21:24:36 +0100 Subject: [PATCH 02/40] Better handling of paths in link to def feature --- src/librustdoc/html/highlight.rs | 120 +++++++++++------- src/librustdoc/html/render/span_map.rs | 70 ++++++---- tests/rustdoc/jump-to-def-assoc-items.rs | 53 +++++++- .../jump-to-def-doc-links-calls.rs | 4 +- tests/rustdoc/jump-to-def/jump-to-def-pats.rs | 10 +- .../jump-to-def/jump-to-non-local-method.rs | 7 +- .../check-source-code-urls-to-def.rs | 2 +- 7 files changed, 175 insertions(+), 91 deletions(-) diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 2db1ea8450ce1..db51fced1ef29 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -1052,6 +1052,64 @@ fn string( } } +fn generate_link_to_def( + out: &mut impl Write, + text_s: &str, + klass: Class, + href_context: &Option>, + def_span: Span, + open_tag: bool, +) -> bool { + if let Some(href_context) = href_context + && let Some(href) = + href_context.context.shared.span_correspondence_map.get(&def_span).and_then(|href| { + let context = href_context.context; + // FIXME: later on, it'd be nice to provide two links (if possible) for all items: + // one to the documentation page and one to the source definition. + // FIXME: currently, external items only generate a link to their documentation, + // a link to their definition can be generated using this: + // https://github.com/rust-lang/rust/blob/60f1a2fc4b535ead9c85ce085fdce49b1b097531/src/librustdoc/html/render/context.rs#L315-L338 + match href { + LinkFromSrc::Local(span) => { + context.href_from_span_relative(*span, &href_context.current_href) + } + LinkFromSrc::External(def_id) => { + format::href_with_root_path(*def_id, context, Some(href_context.root_path)) + .ok() + .map(|(url, _, _)| url) + } + LinkFromSrc::Primitive(prim) => format::href_with_root_path( + PrimitiveType::primitive_locations(context.tcx())[prim], + context, + Some(href_context.root_path), + ) + .ok() + .map(|(url, _, _)| url), + LinkFromSrc::Doc(def_id) => { + format::href_with_root_path(*def_id, context, Some(href_context.root_path)) + .ok() + .map(|(doc_link, _, _)| doc_link) + } + } + }) + { + if !open_tag { + // We're already inside an element which has the same klass, no need to give it + // again. + write!(out, "{text_s}").unwrap(); + } else { + let klass_s = klass.as_html(); + if klass_s.is_empty() { + write!(out, "{text_s}").unwrap(); + } else { + write!(out, "{text_s}").unwrap(); + } + } + return true; + } + false +} + /// This function writes `text` into `out` with some modifications depending on `klass`: /// /// * If `klass` is `None`, `text` is written into `out` with no modification. @@ -1081,10 +1139,14 @@ fn string_without_closing_tag( return Some(""); }; + let mut added_links = false; let mut text_s = text.to_string(); if text_s.contains("::") { + let mut span = def_span.with_hi(def_span.lo()); text_s = text_s.split("::").intersperse("::").fold(String::new(), |mut path, t| { + span = span.with_hi(span.hi() + BytePos(t.len() as _)); match t { + "::" => write!(&mut path, "::"), "self" | "Self" => write!( &mut path, "{t}", @@ -1097,58 +1159,24 @@ fn string_without_closing_tag( klass = Class::KeyWord.as_html(), ) } - t => write!(&mut path, "{t}"), + t => { + if !t.is_empty() + && generate_link_to_def(&mut path, t, klass, href_context, span, open_tag) + { + added_links = true; + write!(&mut path, "") + } else { + write!(&mut path, "{t}") + } + } } .expect("Failed to build source HTML path"); + span = span.with_lo(span.lo() + BytePos(t.len() as _)); path }); } - if let Some(href_context) = href_context - && let Some(href) = href_context.context.shared.span_correspondence_map.get(&def_span) - && let Some(href) = { - let context = href_context.context; - // FIXME: later on, it'd be nice to provide two links (if possible) for all items: - // one to the documentation page and one to the source definition. - // FIXME: currently, external items only generate a link to their documentation, - // a link to their definition can be generated using this: - // https://github.com/rust-lang/rust/blob/60f1a2fc4b535ead9c85ce085fdce49b1b097531/src/librustdoc/html/render/context.rs#L315-L338 - match href { - LinkFromSrc::Local(span) => { - context.href_from_span_relative(*span, &href_context.current_href) - } - LinkFromSrc::External(def_id) => { - format::href_with_root_path(*def_id, context, Some(href_context.root_path)) - .ok() - .map(|(url, _, _)| url) - } - LinkFromSrc::Primitive(prim) => format::href_with_root_path( - PrimitiveType::primitive_locations(context.tcx())[prim], - context, - Some(href_context.root_path), - ) - .ok() - .map(|(url, _, _)| url), - LinkFromSrc::Doc(def_id) => { - format::href_with_root_path(*def_id, context, Some(href_context.root_path)) - .ok() - .map(|(doc_link, _, _)| doc_link) - } - } - } - { - if !open_tag { - // We're already inside an element which has the same klass, no need to give it - // again. - write!(out, "{text_s}").unwrap(); - } else { - let klass_s = klass.as_html(); - if klass_s.is_empty() { - write!(out, "{text_s}").unwrap(); - } else { - write!(out, "{text_s}").unwrap(); - } - } + if !added_links && generate_link_to_def(out, &text_s, klass, href_context, def_span, open_tag) { return Some(""); } if !open_tag { diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs index ebbc61db688f7..342cf5073f716 100644 --- a/src/librustdoc/html/render/span_map.rs +++ b/src/librustdoc/html/render/span_map.rs @@ -65,7 +65,7 @@ struct SpanMapVisitor<'tcx> { impl SpanMapVisitor<'_> { /// This function is where we handle `hir::Path` elements and add them into the "span map". - fn handle_path(&mut self, path: &rustc_hir::Path<'_>) { + fn handle_path(&mut self, path: &rustc_hir::Path<'_>, only_use_last_segment: bool) { match path.res { // FIXME: For now, we handle `DefKind` if it's not a `DefKind::TyParam`. // Would be nice to support them too alongside the other `DefKind` @@ -77,24 +77,36 @@ impl SpanMapVisitor<'_> { LinkFromSrc::External(def_id) }; // In case the path ends with generics, we remove them from the span. - let span = path - .segments - .last() - .map(|last| { - // In `use` statements, the included item is not in the path segments. - // However, it doesn't matter because you can't have generics on `use` - // statements. - if path.span.contains(last.ident.span) { - path.span.with_hi(last.ident.span.hi()) - } else { - path.span - } - }) - .unwrap_or(path.span); + let span = if only_use_last_segment + && let Some(path_span) = path.segments.last().map(|segment| segment.ident.span) + { + path_span + } else { + path.segments + .last() + .map(|last| { + // In `use` statements, the included item is not in the path segments. + // However, it doesn't matter because you can't have generics on `use` + // statements. + if path.span.contains(last.ident.span) { + path.span.with_hi(last.ident.span.hi()) + } else { + path.span + } + }) + .unwrap_or(path.span) + }; self.matches.insert(span, link); } Res::Local(_) if let Some(span) = self.tcx.hir_res_span(path.res) => { - self.matches.insert(path.span, LinkFromSrc::Local(clean::Span::new(span))); + let path_span = if only_use_last_segment + && let Some(path_span) = path.segments.last().map(|segment| segment.ident.span) + { + path_span + } else { + path.span + }; + self.matches.insert(path_span, LinkFromSrc::Local(clean::Span::new(span))); } Res::PrimTy(p) => { // FIXME: Doesn't handle "path-like" primitives like arrays or tuples. @@ -200,37 +212,41 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> { if self.handle_macro(path.span) { return; } - self.handle_path(path); + self.handle_path(path, false); intravisit::walk_path(self, path); } - fn visit_qpath(&mut self, qpath: &QPath<'tcx>, id: HirId, span: Span) { + fn visit_qpath(&mut self, qpath: &QPath<'tcx>, id: HirId, _span: Span) { match *qpath { QPath::TypeRelative(qself, path) => { if matches!(path.res, Res::Err) { let tcx = self.tcx; - let hir = tcx.hir(); - let body_id = hir.enclosing_body_owner(id); - let typeck_results = tcx.typeck_body(hir.body_owned_by(body_id).id()); + let body_id = tcx.hir_enclosing_body_owner(id); + let typeck_results = tcx.typeck_body(tcx.hir_body_owned_by(body_id).id()); let path = rustc_hir::Path { // We change the span to not include parens. - span: span.with_hi(path.ident.span.hi()), + span: path.ident.span, res: typeck_results.qpath_res(qpath, id), segments: &[], }; - self.handle_path(&path); + self.handle_path(&path, false); } else { - self.infer_id(path.hir_id, Some(id), span); + self.infer_id(path.hir_id, Some(id), path.ident.span); } - rustc_ast::visit::try_visit!(self.visit_ty(qself)); + if let Some(qself) = qself.try_as_ambig_ty() { + rustc_ast::visit::try_visit!(self.visit_ty(qself)); + } self.visit_path_segment(path); } QPath::Resolved(maybe_qself, path) => { - self.handle_path(path); + self.handle_path(path, true); + let maybe_qself = maybe_qself.and_then(|maybe_qself| maybe_qself.try_as_ambig_ty()); rustc_ast::visit::visit_opt!(self, visit_ty, maybe_qself); - self.visit_path(path, id) + if !self.handle_macro(path.span) { + intravisit::walk_path(self, path); + } } _ => {} } diff --git a/tests/rustdoc/jump-to-def-assoc-items.rs b/tests/rustdoc/jump-to-def-assoc-items.rs index 1229c36317253..8cbf990628386 100644 --- a/tests/rustdoc/jump-to-def-assoc-items.rs +++ b/tests/rustdoc/jump-to-def-assoc-items.rs @@ -1,15 +1,54 @@ +// This test ensures that patterns also get a link generated. + //@ compile-flags: -Zunstable-options --generate-link-to-definition #![crate_name = "foo"] -pub enum Ty { - Var, +//@ has 'src/foo/jump-to-def-assoc-items.rs.html' + +pub trait Trait { + type T; +} +pub trait Another { + type T; + const X: u32; } -//@ has 'src/foo/jump-to-def-assoc-items.rs.html' -//@ has - '//a[@href="#6"]' 'Self::Var' -impl Ty { - fn f() { - let _ = Self::Var; +pub struct Foo; + +impl Foo { + pub fn new() -> Self { Foo } +} + +pub struct C; + +impl C { + pub fn wat() {} +} + +pub struct Bar; +impl Trait for Bar { + type T = Foo; +} +impl Another for Bar { + type T = C; + const X: u32 = 12; +} + +pub fn bar() { + //@ has - '//a[@href="#20"]' 'new' + ::T::new(); + //@ has - '//a[@href="#26"]' 'wat' + ::T::wat(); + + match 12u32 { + //@ has - '//a[@href="#14"]' 'X' + ::X => {} + _ => {} } } + +pub struct Far { + //@ has - '//a[@href="#10"]' 'T' + x: ::T, +} diff --git a/tests/rustdoc/jump-to-def/jump-to-def-doc-links-calls.rs b/tests/rustdoc/jump-to-def/jump-to-def-doc-links-calls.rs index 618569787733c..55e59f23b6f28 100644 --- a/tests/rustdoc/jump-to-def/jump-to-def-doc-links-calls.rs +++ b/tests/rustdoc/jump-to-def/jump-to-def-doc-links-calls.rs @@ -8,7 +8,7 @@ pub struct Bar; impl std::default::Default for Bar { - //@ has - '//a[@href="#20-22"]' 'Self::new' + //@ has - '//a[@href="#20-22"]' 'new' fn default() -> Self { Self::new() } @@ -16,7 +16,7 @@ impl std::default::Default for Bar { //@ has - '//a[@href="#8"]' 'Bar' impl Bar { - //@ has - '//a[@href="#24-26"]' 'Self::bar' + //@ has - '//a[@href="#24-26"]' 'bar' pub fn new()-> Self { Self::bar() } diff --git a/tests/rustdoc/jump-to-def/jump-to-def-pats.rs b/tests/rustdoc/jump-to-def/jump-to-def-pats.rs index 147902b44cf5c..852eba208db01 100644 --- a/tests/rustdoc/jump-to-def/jump-to-def-pats.rs +++ b/tests/rustdoc/jump-to-def/jump-to-def-pats.rs @@ -30,13 +30,13 @@ pub fn foo() -> Result<(), ()> { impl fmt::Display for MyEnum { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - //@ has - '//a[@href="#12"]' 'Self::Ok' + //@ has - '//a[@href="#12"]' 'Ok' Self::Ok(_) => f.write_str("MyEnum::Ok"), - //@ has - '//a[@href="#13"]' 'MyEnum::Err' + //@ has - '//a[@href="#13"]' 'Err' MyEnum::Err(_) => f.write_str("MyEnum::Err"), - //@ has - '//a[@href="#14"]' 'Self::Some' + //@ has - '//a[@href="#14"]' 'Some' Self::Some(_) => f.write_str("MyEnum::Some"), - //@ has - '//a[@href="#15"]' 'Self::None' + //@ has - '//a[@href="#15"]' 'None' Self::None => f.write_str("MyEnum::None"), } } @@ -45,7 +45,7 @@ impl fmt::Display for MyEnum { impl X { fn p(&self) -> &str { match self { - //@ has - '//a[@href="#19"]' 'Self::A' + //@ has - '//a[@href="#19"]' 'A' Self::A => "X::A", } } diff --git a/tests/rustdoc/jump-to-def/jump-to-non-local-method.rs b/tests/rustdoc/jump-to-def/jump-to-non-local-method.rs index e2f530425f0db..1d6d6b8d18faa 100644 --- a/tests/rustdoc/jump-to-def/jump-to-non-local-method.rs +++ b/tests/rustdoc/jump-to-def/jump-to-non-local-method.rs @@ -21,9 +21,10 @@ pub fn bar2(readable: T) { } pub fn bar() { - //@ has - '//a[@href="{{channel}}/core/sync/atomic/struct.AtomicIsize.html#method.new"]' 'AtomicIsize::new' + //@ has - '//a[@href="{{channel}}/core/sync/atomic/struct.AtomicIsize.html"]' 'AtomicIsize' + //@ has - '//a[@href="{{channel}}/core/sync/atomic/struct.AtomicIsize.html#method.new"]' 'new' let _ = AtomicIsize::new(0); - //@ has - '//a[@href="#48"]' 'local_private' + //@ has - '//a[@href="#49"]' 'local_private' local_private(); } @@ -39,7 +40,7 @@ pub fn macro_call() -> Result<(), ()> { } pub fn variant() { - //@ has - '//a[@href="{{channel}}/core/cmp/enum.Ordering.html#variant.Less"]' 'Ordering::Less' + //@ has - '//a[@href="{{channel}}/core/cmp/enum.Ordering.html#variant.Less"]' 'Less' let _ = Ordering::Less; //@ has - '//a[@href="{{channel}}/core/marker/struct.PhantomData.html"]' 'PhantomData' let _: PhantomData:: = PhantomData; diff --git a/tests/rustdoc/source-code-pages/check-source-code-urls-to-def.rs b/tests/rustdoc/source-code-pages/check-source-code-urls-to-def.rs index d701b88bf9fd0..a7b944fa2f6fc 100644 --- a/tests/rustdoc/source-code-pages/check-source-code-urls-to-def.rs +++ b/tests/rustdoc/source-code-pages/check-source-code-urls-to-def.rs @@ -34,7 +34,7 @@ fn babar() {} // The 5 links to line 23 and the line 23 itself. //@ count - '//pre[@class="rust"]//a[@href="#23"]' 6 //@ has - '//pre[@class="rust"]//a[@href="../../source_code/struct.SourceCode.html"]' \ -// 'source_code::SourceCode' +// 'SourceCode' pub fn foo(a: u32, b: &str, c: String, d: Foo, e: bar::Bar, f: source_code::SourceCode) { let x = 12; let y: Foo = Foo; From 9457a324b4acd11eca0d95a518d173329a23b0c2 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 26 Jan 2025 21:50:50 +0100 Subject: [PATCH 03/40] Update to last rustc_hir Visitor changes --- src/librustdoc/html/render/span_map.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs index 342cf5073f716..505797ccc0b2f 100644 --- a/src/librustdoc/html/render/span_map.rs +++ b/src/librustdoc/html/render/span_map.rs @@ -3,7 +3,7 @@ use std::path::{Path, PathBuf}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; -use rustc_hir::intravisit::{self, Visitor}; +use rustc_hir::intravisit::{self, Visitor, VisitorExt}; use rustc_hir::{ExprKind, HirId, Item, ItemKind, Mod, Node, QPath}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::TyCtxt; @@ -234,16 +234,13 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> { self.infer_id(path.hir_id, Some(id), path.ident.span); } - if let Some(qself) = qself.try_as_ambig_ty() { - rustc_ast::visit::try_visit!(self.visit_ty(qself)); - } + rustc_ast::visit::try_visit!(self.visit_ty_unambig(qself)); self.visit_path_segment(path); } QPath::Resolved(maybe_qself, path) => { self.handle_path(path, true); - let maybe_qself = maybe_qself.and_then(|maybe_qself| maybe_qself.try_as_ambig_ty()); - rustc_ast::visit::visit_opt!(self, visit_ty, maybe_qself); + rustc_ast::visit::visit_opt!(self, visit_ty_unambig, maybe_qself); if !self.handle_macro(path.span) { intravisit::walk_path(self, path); } From 687ac3fdef85301ba49bbac5230901c35b541270 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 4 May 2025 19:08:23 +0200 Subject: [PATCH 04/40] Fix panic if an item does not have a body --- src/librustdoc/html/render/span_map.rs | 32 ++++++++++++++++++-------- tests/rustdoc/jump-to-def-ice.rs | 24 +++++++++++++++++++ 2 files changed, 46 insertions(+), 10 deletions(-) create mode 100644 tests/rustdoc/jump-to-def-ice.rs diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs index 505797ccc0b2f..6906e6e30ff86 100644 --- a/src/librustdoc/html/render/span_map.rs +++ b/src/librustdoc/html/render/span_map.rs @@ -2,7 +2,7 @@ use std::path::{Path, PathBuf}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::{DefId, LOCAL_CRATE}; +use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::intravisit::{self, Visitor, VisitorExt}; use rustc_hir::{ExprKind, HirId, Item, ItemKind, Mod, Node, QPath}; use rustc_middle::hir::nested_filter; @@ -201,6 +201,17 @@ impl SpanMapVisitor<'_> { } } +// This is a reimplementation of `hir_enclosing_body_owner` which allows to fail without +// panicking. +fn hir_enclosing_body_owner(tcx: TyCtxt<'_>, hir_id: HirId) -> Option { + for (_, node) in tcx.hir_parent_iter(hir_id) { + if let Some((def_id, _)) = node.associated_body() { + return Some(def_id); + } + } + None +} + impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> { type NestedFilter = nested_filter::All; @@ -221,15 +232,16 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> { QPath::TypeRelative(qself, path) => { if matches!(path.res, Res::Err) { let tcx = self.tcx; - let body_id = tcx.hir_enclosing_body_owner(id); - let typeck_results = tcx.typeck_body(tcx.hir_body_owned_by(body_id).id()); - let path = rustc_hir::Path { - // We change the span to not include parens. - span: path.ident.span, - res: typeck_results.qpath_res(qpath, id), - segments: &[], - }; - self.handle_path(&path, false); + if let Some(body_id) = hir_enclosing_body_owner(tcx, id) { + let typeck_results = tcx.typeck_body(tcx.hir_body_owned_by(body_id).id()); + let path = rustc_hir::Path { + // We change the span to not include parens. + span: path.ident.span, + res: typeck_results.qpath_res(qpath, id), + segments: &[], + }; + self.handle_path(&path, false); + } } else { self.infer_id(path.hir_id, Some(id), path.ident.span); } diff --git a/tests/rustdoc/jump-to-def-ice.rs b/tests/rustdoc/jump-to-def-ice.rs new file mode 100644 index 0000000000000..5578b9af3d74f --- /dev/null +++ b/tests/rustdoc/jump-to-def-ice.rs @@ -0,0 +1,24 @@ +// This test ensures that items with no body don't panic when generating +// jump to def links. + +//@ compile-flags: -Zunstable-options --generate-link-to-definition + +#![crate_name = "foo"] + +//@ has 'src/foo/jump-to-def-ice.rs.html' + +pub trait A { + type T; + type U; +} + +impl A for () { + type T = Self::U; + type U = (); +} + +pub trait C { + type X; +} + +pub struct F(pub T::X); From 5b2c61edbe508163ad11cde0f35278417d7c26b3 Mon Sep 17 00:00:00 2001 From: Alisa Sireneva Date: Sat, 19 Jul 2025 18:18:01 +0300 Subject: [PATCH 05/40] Document guarantees of poisoning This mostly documents the current behavior of `Mutex` and `RwLock` as imperfect. It's unlikely that the situation improves significantly in the future, and even if it does, the rules will probably be more complicated than "poisoning is completely reliable", so this is a conservative guarantee. We also explicitly specify that `OnceLock` never poisons, even though it has an API similar to mutexes. --- library/std/src/sync/once_lock.rs | 2 ++ library/std/src/sync/poison.rs | 24 +++++++------ library/std/src/sync/poison/mutex.rs | 52 ++++++++++++++++++++++----- library/std/src/sync/poison/once.rs | 6 +++- library/std/src/sync/poison/rwlock.rs | 8 ++--- 5 files changed, 67 insertions(+), 25 deletions(-) diff --git a/library/std/src/sync/once_lock.rs b/library/std/src/sync/once_lock.rs index a5c3a6c46a433..b224044cbe09c 100644 --- a/library/std/src/sync/once_lock.rs +++ b/library/std/src/sync/once_lock.rs @@ -16,6 +16,8 @@ use crate::sync::Once; /// A `OnceLock` can be thought of as a safe abstraction over uninitialized data that becomes /// initialized once written. /// +/// Unlike [`Mutex`](crate::sync::Mutex), `OnceLock` is never poisoned on panic. +/// /// [`OnceCell`]: crate::cell::OnceCell /// [`LazyLock`]: crate::sync::LazyLock /// [`LazyLock::new(|| ...)`]: crate::sync::LazyLock::new diff --git a/library/std/src/sync/poison.rs b/library/std/src/sync/poison.rs index 0c05f152ef84c..e928b6772cc81 100644 --- a/library/std/src/sync/poison.rs +++ b/library/std/src/sync/poison.rs @@ -2,15 +2,16 @@ //! //! # Poisoning //! -//! All synchronization objects in this module implement a strategy called "poisoning" -//! where if a thread panics while holding the exclusive access granted by the primitive, -//! the state of the primitive is set to "poisoned". -//! This information is then propagated to all other threads +//! All synchronization objects in this module implement a strategy called +//! "poisoning" where a primitive becomes poisoned if it recognizes that some +//! thread has panicked while holding the exclusive access granted by the +//! primitive. This information is then propagated to all other threads //! to signify that the data protected by this primitive is likely tainted //! (some invariant is not being upheld). //! -//! The specifics of how this "poisoned" state affects other threads -//! depend on the primitive. See [#Overview] below. +//! The specifics of how this "poisoned" state affects other threads and whether +//! the panics are recognized reliably or on a best-effort basis depend on the +//! primitive. See [#Overview] below. //! //! For the alternative implementations that do not employ poisoning, //! see `std::sync::nonpoisoning`. @@ -34,14 +35,15 @@ //! - [`Mutex`]: Mutual Exclusion mechanism, which ensures that at //! most one thread at a time is able to access some data. //! -//! [`Mutex::lock()`] returns a [`LockResult`], -//! providing a way to deal with the poisoned state. -//! See [`Mutex`'s documentation](Mutex#poisoning) for more. +//! Panicking while holding the lock typically poisons the mutex, but it is +//! not guaranteed to detect this condition in all circumstances. +//! [`Mutex::lock()`] returns a [`LockResult`], providing a way to deal with +//! the poisoned state. See [`Mutex`'s documentation](Mutex#poisoning) for more. //! //! - [`Once`]: A thread-safe way to run a piece of code only once. //! Mostly useful for implementing one-time global initialization. //! -//! [`Once`] is poisoned if the piece of code passed to +//! [`Once`] is reliably poisoned if the piece of code passed to //! [`Once::call_once()`] or [`Once::call_once_force()`] panics. //! When in poisoned state, subsequent calls to [`Once::call_once()`] will panic too. //! [`Once::call_once_force()`] can be used to clear the poisoned state. @@ -51,7 +53,7 @@ //! writer at a time. In some cases, this can be more efficient than //! a mutex. //! -//! This implementation, like [`Mutex`], will become poisoned on a panic. +//! This implementation, like [`Mutex`], usually becomes poisoned on a panic. //! Note, however, that an `RwLock` may only be poisoned if a panic occurs //! while it is locked exclusively (write mode). If a panic occurs in any reader, //! then the lock will not be poisoned. diff --git a/library/std/src/sync/poison/mutex.rs b/library/std/src/sync/poison/mutex.rs index 30325be685c32..15dfe116133ba 100644 --- a/library/std/src/sync/poison/mutex.rs +++ b/library/std/src/sync/poison/mutex.rs @@ -18,20 +18,54 @@ use crate::sys::sync as sys; /// # Poisoning /// /// The mutexes in this module implement a strategy called "poisoning" where a -/// mutex is considered poisoned whenever a thread panics while holding the -/// mutex. Once a mutex is poisoned, all other threads are unable to access the -/// data by default as it is likely tainted (some invariant is not being -/// upheld). +/// mutex becomes poisoned if it recognizes that the thread holding it has +/// panicked. /// -/// For a mutex, this means that the [`lock`] and [`try_lock`] methods return a +/// Once a mutex is poisoned, all other threads are unable to access the data by +/// default as it is likely tainted (some invariant is not being upheld). For a +/// mutex, this means that the [`lock`] and [`try_lock`] methods return a /// [`Result`] which indicates whether a mutex has been poisoned or not. Most /// usage of a mutex will simply [`unwrap()`] these results, propagating panics /// among threads to ensure that a possibly invalid invariant is not witnessed. /// -/// A poisoned mutex, however, does not prevent all access to the underlying -/// data. The [`PoisonError`] type has an [`into_inner`] method which will return -/// the guard that would have otherwise been returned on a successful lock. This -/// allows access to the data, despite the lock being poisoned. +/// Poisoning is only advisory: the [`PoisonError`] type has an [`into_inner`] +/// method which will return the guard that would have otherwise been returned +/// on a successful lock. This allows access to the data, despite the lock being +/// poisoned. +/// +/// In addition, the panic detection is not ideal, so even unpoisoned mutexes +/// need to be handled with care, since certain panics may have been skipped. +/// Therefore, `unsafe` code cannot rely on poisoning for soundness. Here's an +/// example of **incorrect** use of poisoning: +/// +/// ```rust +/// use std::sync::Mutex; +/// +/// struct MutexBox { +/// data: Mutex<*mut T>, +/// } +/// +/// impl MutexBox { +/// pub fn new(value: T) -> Self { +/// Self { +/// data: Mutex::new(Box::into_raw(Box::new(value))), +/// } +/// } +/// +/// pub fn replace_with(&self, f: impl FnOnce(T) -> T) { +/// let ptr = self.data.lock().expect("poisoned"); +/// // While `f` is running, the data is moved out of `*ptr`. If `f` +/// // panics, `*ptr` keeps pointing at a dropped value. The intention +/// // is that this will poison the mutex, so the following calls to +/// // `replace_with` will panic without reading `*ptr`. But since +/// // poisoning is not guaranteed to occur, this can lead to +/// // use-after-free. +/// unsafe { +/// (*ptr).write(f((*ptr).read())); +/// } +/// } +/// } +/// ``` /// /// [`new`]: Self::new /// [`lock`]: Self::lock diff --git a/library/std/src/sync/poison/once.rs b/library/std/src/sync/poison/once.rs index 103e519540795..faf2913c54730 100644 --- a/library/std/src/sync/poison/once.rs +++ b/library/std/src/sync/poison/once.rs @@ -136,7 +136,8 @@ impl Once { /// it will *poison* this [`Once`] instance, causing all future invocations of /// `call_once` to also panic. /// - /// This is similar to [poisoning with mutexes][poison]. + /// This is similar to [poisoning with mutexes][poison], but this mechanism + /// is guaranteed to never skip panics within `f`. /// /// [poison]: struct.Mutex.html#poisoning #[inline] @@ -293,6 +294,9 @@ impl Once { /// Blocks the current thread until initialization has completed, ignoring /// poisoning. + /// + /// If this [`Once`] has been poisoned, this function blocks until it + /// becomes completed, unlike [`Once::wait()`], which panics in this case. #[stable(feature = "once_wait", since = "1.86.0")] pub fn wait_force(&self) { if !self.inner.is_completed() { diff --git a/library/std/src/sync/poison/rwlock.rs b/library/std/src/sync/poison/rwlock.rs index 934a173425a81..0a50b6c2d8f10 100644 --- a/library/std/src/sync/poison/rwlock.rs +++ b/library/std/src/sync/poison/rwlock.rs @@ -46,10 +46,10 @@ use crate::sys::sync as sys; /// /// # Poisoning /// -/// An `RwLock`, like [`Mutex`], will become poisoned on a panic. Note, however, -/// that an `RwLock` may only be poisoned if a panic occurs while it is locked -/// exclusively (write mode). If a panic occurs in any reader, then the lock -/// will not be poisoned. +/// An `RwLock`, like [`Mutex`], will usually become poisoned on a panic. Note, +/// however, that an `RwLock` may only be poisoned if a panic occurs while it is +/// locked exclusively (write mode). If a panic occurs in any reader, then the +/// lock will not be poisoned. /// /// # Examples /// From 45231fa599583edc95843aaa4f23b12f762e01e7 Mon Sep 17 00:00:00 2001 From: Predrag Gruevski Date: Wed, 23 Jul 2025 00:00:01 +0000 Subject: [PATCH 06/40] [rustdoc] Display unsafe attrs with edition 2024 `unsafe()` wrappers. --- src/librustdoc/clean/types.rs | 6 +++--- tests/rustdoc/attribute-rendering.rs | 6 +++--- tests/rustdoc/attributes-2021-edition.rs | 14 ++++++++++++++ tests/rustdoc/attributes-re-export-2021-edition.rs | 13 +++++++++++++ tests/rustdoc/attributes-re-export.rs | 2 +- tests/rustdoc/attributes.rs | 13 +++++++++---- tests/rustdoc/reexport/reexport-attrs.rs | 6 +++--- 7 files changed, 46 insertions(+), 14 deletions(-) create mode 100644 tests/rustdoc/attributes-2021-edition.rs create mode 100644 tests/rustdoc/attributes-re-export-2021-edition.rs diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 5ac5da24299fa..64e707ad9d364 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -768,13 +768,13 @@ impl Item { .iter() .filter_map(|attr| match attr { hir::Attribute::Parsed(AttributeKind::LinkSection { name, .. }) => { - Some(format!("#[link_section = \"{name}\"]")) + Some(format!("#[unsafe(link_section = \"{name}\")]")) } hir::Attribute::Parsed(AttributeKind::NoMangle(..)) => { - Some("#[no_mangle]".to_string()) + Some("#[unsafe(no_mangle)]".to_string()) } hir::Attribute::Parsed(AttributeKind::ExportName { name, .. }) => { - Some(format!("#[export_name = \"{name}\"]")) + Some(format!("#[unsafe(export_name = \"{name}\")]")) } hir::Attribute::Parsed(AttributeKind::NonExhaustive(..)) => { Some("#[non_exhaustive]".to_string()) diff --git a/tests/rustdoc/attribute-rendering.rs b/tests/rustdoc/attribute-rendering.rs index 841533814c388..bf9b81077f356 100644 --- a/tests/rustdoc/attribute-rendering.rs +++ b/tests/rustdoc/attribute-rendering.rs @@ -1,7 +1,7 @@ #![crate_name = "foo"] //@ has 'foo/fn.f.html' -//@ has - //*[@'class="rust item-decl"]' '#[export_name = "f"] pub fn f()' -#[export_name = "\ -f"] +//@ has - //*[@'class="rust item-decl"]' '#[unsafe(export_name = "f")] pub fn f()' +#[unsafe(export_name = "\ +f")] pub fn f() {} diff --git a/tests/rustdoc/attributes-2021-edition.rs b/tests/rustdoc/attributes-2021-edition.rs new file mode 100644 index 0000000000000..b5028d8c85254 --- /dev/null +++ b/tests/rustdoc/attributes-2021-edition.rs @@ -0,0 +1,14 @@ +//@ edition: 2021 +#![crate_name = "foo"] + +//@ has foo/fn.f.html '//pre[@class="rust item-decl"]' '#[unsafe(no_mangle)]' +#[no_mangle] +pub extern "C" fn f() {} + +//@ has foo/fn.g.html '//pre[@class="rust item-decl"]' '#[unsafe(export_name = "bar")]' +#[export_name = "bar"] +pub extern "C" fn g() {} + +//@ has foo/fn.example.html '//pre[@class="rust item-decl"]' '#[unsafe(link_section = ".text")]' +#[link_section = ".text"] +pub extern "C" fn example() {} diff --git a/tests/rustdoc/attributes-re-export-2021-edition.rs b/tests/rustdoc/attributes-re-export-2021-edition.rs new file mode 100644 index 0000000000000..04ee6c273dd61 --- /dev/null +++ b/tests/rustdoc/attributes-re-export-2021-edition.rs @@ -0,0 +1,13 @@ +// Tests that attributes are correctly copied onto a re-exported item. +//@ edition:2024 +#![crate_name = "re_export"] + +//@ has 're_export/fn.thingy2.html' '//pre[@class="rust item-decl"]' '#[unsafe(no_mangle)]' +pub use thingymod::thingy as thingy2; + +mod thingymod { + #[unsafe(no_mangle)] + pub fn thingy() { + + } +} diff --git a/tests/rustdoc/attributes-re-export.rs b/tests/rustdoc/attributes-re-export.rs index 458826ea8a356..820276f83c091 100644 --- a/tests/rustdoc/attributes-re-export.rs +++ b/tests/rustdoc/attributes-re-export.rs @@ -2,7 +2,7 @@ //@ edition:2021 #![crate_name = "re_export"] -//@ has 're_export/fn.thingy2.html' '//pre[@class="rust item-decl"]' '#[no_mangle]' +//@ has 're_export/fn.thingy2.html' '//pre[@class="rust item-decl"]' '#[unsafe(no_mangle)]' pub use thingymod::thingy as thingy2; mod thingymod { diff --git a/tests/rustdoc/attributes.rs b/tests/rustdoc/attributes.rs index e34468a88b113..34487a8912778 100644 --- a/tests/rustdoc/attributes.rs +++ b/tests/rustdoc/attributes.rs @@ -1,13 +1,18 @@ +//@ edition: 2024 #![crate_name = "foo"] -//@ has foo/fn.f.html '//pre[@class="rust item-decl"]' '#[no_mangle]' -#[no_mangle] +//@ has foo/fn.f.html '//pre[@class="rust item-decl"]' '#[unsafe(no_mangle)]' +#[unsafe(no_mangle)] pub extern "C" fn f() {} -//@ has foo/fn.g.html '//pre[@class="rust item-decl"]' '#[export_name = "bar"]' -#[export_name = "bar"] +//@ has foo/fn.g.html '//pre[@class="rust item-decl"]' '#[unsafe(export_name = "bar")]' +#[unsafe(export_name = "bar")] pub extern "C" fn g() {} +//@ has foo/fn.example.html '//pre[@class="rust item-decl"]' '#[unsafe(link_section = ".text")]' +#[unsafe(link_section = ".text")] +pub extern "C" fn example() {} + //@ has foo/struct.Repr.html '//pre[@class="rust item-decl"]' '#[repr(C, align(8))]' #[repr(C, align(8))] pub struct Repr; diff --git a/tests/rustdoc/reexport/reexport-attrs.rs b/tests/rustdoc/reexport/reexport-attrs.rs index 0ec645841f0b8..aec0a11c0c6a0 100644 --- a/tests/rustdoc/reexport/reexport-attrs.rs +++ b/tests/rustdoc/reexport/reexport-attrs.rs @@ -4,13 +4,13 @@ extern crate reexports_attrs; -//@ has 'foo/fn.f0.html' '//pre[@class="rust item-decl"]' '#[no_mangle]' +//@ has 'foo/fn.f0.html' '//pre[@class="rust item-decl"]' '#[unsafe(no_mangle)]' pub use reexports_attrs::f0; -//@ has 'foo/fn.f1.html' '//pre[@class="rust item-decl"]' '#[link_section = ".here"]' +//@ has 'foo/fn.f1.html' '//pre[@class="rust item-decl"]' '#[unsafe(link_section = ".here")]' pub use reexports_attrs::f1; -//@ has 'foo/fn.f2.html' '//pre[@class="rust item-decl"]' '#[export_name = "f2export"]' +//@ has 'foo/fn.f2.html' '//pre[@class="rust item-decl"]' '#[unsafe(export_name = "f2export")]' pub use reexports_attrs::f2; //@ has 'foo/enum.T0.html' '//pre[@class="rust item-decl"]' '#[repr(u8)]' From 5ae2d42a8c80807c7e3ae7e8aff165fb4d8e046f Mon Sep 17 00:00:00 2001 From: binarycat Date: Thu, 7 Nov 2024 15:33:45 -0600 Subject: [PATCH 07/40] get rid of some false negatives in rustdoc::broken_intra_doc_links rustdoc will not try to do intra-doc linking if the "path" of a link looks too much like a "real url". however, only inline links ([text](url)) can actually contain a url, other types of links (reference links, shortcut links) contain a *reference* which is later resolved to an actual url. the "path" in this case cannot be a url, and therefore it should not be skipped due to looking like a url. Co-authored-by: Michael Howell --- .../passes/collect_intra_doc_links.rs | 13 ++++++-- tests/rustdoc-ui/bad-intra-doc.rs | 13 ++++++++ tests/rustdoc-ui/bad-intra-doc.stderr | 31 +++++++++++++++++++ 3 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 tests/rustdoc-ui/bad-intra-doc.rs create mode 100644 tests/rustdoc-ui/bad-intra-doc.stderr diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 5a9aa2a94c8b4..97f8a2be10b27 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -941,13 +941,20 @@ fn preprocess_link( ori_link: &MarkdownLink, dox: &str, ) -> Option> { + // certain link kinds cannot have their path be urls, + // so they should not be ignored, no matter how much they look like urls. + // e.g. [https://example.com/] is not a link to example.com. + let can_be_url = ori_link.kind != LinkType::ShortcutUnknown + && ori_link.kind != LinkType::CollapsedUnknown + && ori_link.kind != LinkType::ReferenceUnknown; + // [] is mostly likely not supposed to be a link if ori_link.link.is_empty() { return None; } // Bail early for real links. - if ori_link.link.contains('/') { + if can_be_url && ori_link.link.contains('/') { return None; } @@ -972,7 +979,7 @@ fn preprocess_link( Ok(None) => (None, link, link), Err((err_msg, relative_range)) => { // Only report error if we would not have ignored this link. See issue #83859. - if !should_ignore_link_with_disambiguators(link) { + if !(can_be_url && should_ignore_link_with_disambiguators(link)) { let disambiguator_range = match range_between_backticks(&ori_link.range, dox) { MarkdownLinkRange::Destination(no_backticks_range) => { MarkdownLinkRange::Destination( @@ -989,7 +996,7 @@ fn preprocess_link( } }; - if should_ignore_link(path_str) { + if can_be_url && should_ignore_link(path_str) { return None; } diff --git a/tests/rustdoc-ui/bad-intra-doc.rs b/tests/rustdoc-ui/bad-intra-doc.rs new file mode 100644 index 0000000000000..7bc55756e670b --- /dev/null +++ b/tests/rustdoc-ui/bad-intra-doc.rs @@ -0,0 +1,13 @@ +#![no_std] +#![deny(rustdoc::broken_intra_doc_links)] + +// regression test for https://github.com/rust-lang/rust/issues/54191 + +/// this is not a link to [`example.com`] //~ERROR unresolved link +/// +/// this link [`has spaces in it`]. +/// +/// attempted link to method: [`Foo.bar()`] //~ERROR unresolved link +/// +/// classic broken intra-doc link: [`Bar`] //~ERROR unresolved link +pub struct Foo; diff --git a/tests/rustdoc-ui/bad-intra-doc.stderr b/tests/rustdoc-ui/bad-intra-doc.stderr new file mode 100644 index 0000000000000..9533792b35b60 --- /dev/null +++ b/tests/rustdoc-ui/bad-intra-doc.stderr @@ -0,0 +1,31 @@ +error: unresolved link to `example.com` + --> $DIR/bad-intra-doc.rs:6:29 + | +LL | /// this is not a link to [`example.com`] + | ^^^^^^^^^^^ no item named `example.com` in scope + | + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` +note: the lint level is defined here + --> $DIR/bad-intra-doc.rs:2:9 + | +LL | #![deny(rustdoc::broken_intra_doc_links)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: unresolved link to `Foo.bar` + --> $DIR/bad-intra-doc.rs:10:33 + | +LL | /// attempted link to method: [`Foo.bar()`] + | ^^^^^^^^^ no item named `Foo.bar` in scope + | + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` + +error: unresolved link to `Bar` + --> $DIR/bad-intra-doc.rs:12:38 + | +LL | /// classic broken intra-doc link: [`Bar`] + | ^^^ no item named `Bar` in scope + | + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` + +error: aborting due to 3 previous errors + From 87d7d80cec060690c9055fa93cb29dfe5eb79ce9 Mon Sep 17 00:00:00 2001 From: binarycat Date: Fri, 8 Nov 2024 13:59:56 -0600 Subject: [PATCH 08/40] adjust more unit tests to reflect more aggressive intra-doc linting --- .../disambiguator-endswith-named-suffix.rs | 30 ++--- ...disambiguator-endswith-named-suffix.stderr | 122 +++++++++++++++++- tests/rustdoc-ui/intra-doc/weird-syntax.rs | 2 +- .../rustdoc-ui/intra-doc/weird-syntax.stderr | 24 ++++ .../lints/redundant_explicit_links-utf8.rs | 14 +- .../redundant_explicit_links-utf8.stderr | 59 +++++++++ 6 files changed, 230 insertions(+), 21 deletions(-) create mode 100644 tests/rustdoc-ui/lints/redundant_explicit_links-utf8.stderr diff --git a/tests/rustdoc-ui/disambiguator-endswith-named-suffix.rs b/tests/rustdoc-ui/disambiguator-endswith-named-suffix.rs index 1174e16dd53fd..cb277d529ce77 100644 --- a/tests/rustdoc-ui/disambiguator-endswith-named-suffix.rs +++ b/tests/rustdoc-ui/disambiguator-endswith-named-suffix.rs @@ -2,67 +2,67 @@ //@ normalize-stderr: "nightly|beta|1\.[0-9][0-9]\.[0-9]" -> "$$CHANNEL" //! [struct@m!()] //~ WARN: unmatched disambiguator `struct` and suffix `!()` -//! [struct@m!{}] +//! [struct@m!{}] //~ WARN: unmatched disambiguator `struct` and suffix `!{}` //! [struct@m![]] //! [struct@f()] //~ WARN: unmatched disambiguator `struct` and suffix `()` //! [struct@m!] //~ WARN: unmatched disambiguator `struct` and suffix `!` //! //! [enum@m!()] //~ WARN: unmatched disambiguator `enum` and suffix `!()` -//! [enum@m!{}] +//! [enum@m!{}] //~ WARN: unmatched disambiguator `enum` and suffix `!{}` //! [enum@m![]] //! [enum@f()] //~ WARN: unmatched disambiguator `enum` and suffix `()` //! [enum@m!] //~ WARN: unmatched disambiguator `enum` and suffix `!` //! //! [trait@m!()] //~ WARN: unmatched disambiguator `trait` and suffix `!()` -//! [trait@m!{}] +//! [trait@m!{}] //~ WARN: unmatched disambiguator `trait` and suffix `!{}` //! [trait@m![]] //! [trait@f()] //~ WARN: unmatched disambiguator `trait` and suffix `()` //! [trait@m!] //~ WARN: unmatched disambiguator `trait` and suffix `!` //! //! [module@m!()] //~ WARN: unmatched disambiguator `module` and suffix `!()` -//! [module@m!{}] +//! [module@m!{}] //~ WARN: unmatched disambiguator `module` and suffix `!{}` //! [module@m![]] //! [module@f()] //~ WARN: unmatched disambiguator `module` and suffix `()` //! [module@m!] //~ WARN: unmatched disambiguator `module` and suffix `!` //! //! [mod@m!()] //~ WARN: unmatched disambiguator `mod` and suffix `!()` -//! [mod@m!{}] +//! [mod@m!{}] //~ WARN: unmatched disambiguator `mod` and suffix `!{}` //! [mod@m![]] //! [mod@f()] //~ WARN: unmatched disambiguator `mod` and suffix `()` //! [mod@m!] //~ WARN: unmatched disambiguator `mod` and suffix `!` //! //! [const@m!()] //~ WARN: unmatched disambiguator `const` and suffix `!()` -//! [const@m!{}] +//! [const@m!{}] //~ WARN: unmatched disambiguator `const` and suffix `!{}` //! [const@m![]] //! [const@f()] //~ WARN: incompatible link kind for `f` //! [const@m!] //~ WARN: unmatched disambiguator `const` and suffix `!` //! //! [constant@m!()] //~ WARN: unmatched disambiguator `constant` and suffix `!()` -//! [constant@m!{}] +//! [constant@m!{}] //~ WARN: unmatched disambiguator `constant` and suffix `!{}` //! [constant@m![]] //! [constant@f()] //~ WARN: incompatible link kind for `f` //! [constant@m!] //~ WARN: unmatched disambiguator `constant` and suffix `!` //! //! [static@m!()] //~ WARN: unmatched disambiguator `static` and suffix `!()` -//! [static@m!{}] +//! [static@m!{}] //~ WARN: unmatched disambiguator `static` and suffix `!{}` //! [static@m![]] //! [static@f()] //~ WARN: incompatible link kind for `f` //! [static@m!] //~ WARN: unmatched disambiguator `static` and suffix `!` //! //! [function@m!()] //~ WARN: unmatched disambiguator `function` and suffix `!()` -//! [function@m!{}] +//! [function@m!{}] //~ WARN: unmatched disambiguator `function` and suffix `!{}` //! [function@m![]] //! [function@f()] //! [function@m!] //~ WARN: unmatched disambiguator `function` and suffix `!` //! //! [fn@m!()] //~ WARN: unmatched disambiguator `fn` and suffix `!()` -//! [fn@m!{}] +//! [fn@m!{}] //~ WARN: unmatched disambiguator `fn` and suffix `!{}` //! [fn@m![]] //! [fn@f()] //! [fn@m!] //~ WARN: unmatched disambiguator `fn` and suffix `!` //! //! [method@m!()] //~ WARN: unmatched disambiguator `method` and suffix `!()` -//! [method@m!{}] +//! [method@m!{}] //~ WARN: unmatched disambiguator `method` and suffix `!{}` //! [method@m![]] //! [method@f()] //! [method@m!] //~ WARN: unmatched disambiguator `method` and suffix `!` @@ -74,13 +74,13 @@ //! [derive@m!] //~ WARN: incompatible link kind for `m` //! //! [type@m!()] //~ WARN: unmatched disambiguator `type` and suffix `!()` -//! [type@m!{}] +//! [type@m!{}] //~ WARN: unmatched disambiguator `type` and suffix `!{}` //! [type@m![]] //! [type@f()] //~ WARN: unmatched disambiguator `type` and suffix `()` //! [type@m!] //~ WARN: unmatched disambiguator `type` and suffix `!` //! //! [value@m!()] //~ WARN: unmatched disambiguator `value` and suffix `!()` -//! [value@m!{}] +//! [value@m!{}] //~ WARN: unmatched disambiguator `value` and suffix `!{}` //! [value@m![]] //! [value@f()] //! [value@m!] //~ WARN: unmatched disambiguator `value` and suffix `!` @@ -92,13 +92,13 @@ //! [macro@m!] //! //! [prim@m!()] //~ WARN: unmatched disambiguator `prim` and suffix `!()` -//! [prim@m!{}] +//! [prim@m!{}] //~ WARN: unmatched disambiguator `prim` and suffix `!{}` //! [prim@m![]] //! [prim@f()] //~ WARN: unmatched disambiguator `prim` and suffix `()` //! [prim@m!] //~ WARN: unmatched disambiguator `prim` and suffix `!` //! //! [primitive@m!()] //~ WARN: unmatched disambiguator `primitive` and suffix `!()` -//! [primitive@m!{}] +//! [primitive@m!{}] //~ WARN: unmatched disambiguator `primitive` and suffix `!{}` //! [primitive@m![]] //! [primitive@f()] //~ WARN: unmatched disambiguator `primitive` and suffix `()` //! [primitive@m!] //~ WARN: unmatched disambiguator `primitive` and suffix `!` diff --git a/tests/rustdoc-ui/disambiguator-endswith-named-suffix.stderr b/tests/rustdoc-ui/disambiguator-endswith-named-suffix.stderr index f4e40a4887383..1840338596880 100644 --- a/tests/rustdoc-ui/disambiguator-endswith-named-suffix.stderr +++ b/tests/rustdoc-ui/disambiguator-endswith-named-suffix.stderr @@ -7,6 +7,14 @@ LL | //! [struct@m!()] = note: see https://doc.rust-lang.org/$CHANNEL/rustdoc/write-documentation/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators = note: `#[warn(rustdoc::broken_intra_doc_links)]` on by default +warning: unmatched disambiguator `struct` and suffix `!{}` + --> $DIR/disambiguator-endswith-named-suffix.rs:5:6 + | +LL | //! [struct@m!{}] + | ^^^^^^ + | + = note: see https://doc.rust-lang.org/$CHANNEL/rustdoc/write-documentation/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators + warning: unmatched disambiguator `struct` and suffix `()` --> $DIR/disambiguator-endswith-named-suffix.rs:7:6 | @@ -31,6 +39,14 @@ LL | //! [enum@m!()] | = note: see https://doc.rust-lang.org/$CHANNEL/rustdoc/write-documentation/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators +warning: unmatched disambiguator `enum` and suffix `!{}` + --> $DIR/disambiguator-endswith-named-suffix.rs:11:6 + | +LL | //! [enum@m!{}] + | ^^^^ + | + = note: see https://doc.rust-lang.org/$CHANNEL/rustdoc/write-documentation/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators + warning: unmatched disambiguator `enum` and suffix `()` --> $DIR/disambiguator-endswith-named-suffix.rs:13:6 | @@ -55,6 +71,14 @@ LL | //! [trait@m!()] | = note: see https://doc.rust-lang.org/$CHANNEL/rustdoc/write-documentation/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators +warning: unmatched disambiguator `trait` and suffix `!{}` + --> $DIR/disambiguator-endswith-named-suffix.rs:17:6 + | +LL | //! [trait@m!{}] + | ^^^^^ + | + = note: see https://doc.rust-lang.org/$CHANNEL/rustdoc/write-documentation/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators + warning: unmatched disambiguator `trait` and suffix `()` --> $DIR/disambiguator-endswith-named-suffix.rs:19:6 | @@ -79,6 +103,14 @@ LL | //! [module@m!()] | = note: see https://doc.rust-lang.org/$CHANNEL/rustdoc/write-documentation/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators +warning: unmatched disambiguator `module` and suffix `!{}` + --> $DIR/disambiguator-endswith-named-suffix.rs:23:6 + | +LL | //! [module@m!{}] + | ^^^^^^ + | + = note: see https://doc.rust-lang.org/$CHANNEL/rustdoc/write-documentation/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators + warning: unmatched disambiguator `module` and suffix `()` --> $DIR/disambiguator-endswith-named-suffix.rs:25:6 | @@ -103,6 +135,14 @@ LL | //! [mod@m!()] | = note: see https://doc.rust-lang.org/$CHANNEL/rustdoc/write-documentation/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators +warning: unmatched disambiguator `mod` and suffix `!{}` + --> $DIR/disambiguator-endswith-named-suffix.rs:29:6 + | +LL | //! [mod@m!{}] + | ^^^ + | + = note: see https://doc.rust-lang.org/$CHANNEL/rustdoc/write-documentation/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators + warning: unmatched disambiguator `mod` and suffix `()` --> $DIR/disambiguator-endswith-named-suffix.rs:31:6 | @@ -127,6 +167,14 @@ LL | //! [const@m!()] | = note: see https://doc.rust-lang.org/$CHANNEL/rustdoc/write-documentation/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators +warning: unmatched disambiguator `const` and suffix `!{}` + --> $DIR/disambiguator-endswith-named-suffix.rs:35:6 + | +LL | //! [const@m!{}] + | ^^^^^ + | + = note: see https://doc.rust-lang.org/$CHANNEL/rustdoc/write-documentation/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators + warning: incompatible link kind for `f` --> $DIR/disambiguator-endswith-named-suffix.rs:37:6 | @@ -155,6 +203,14 @@ LL | //! [constant@m!()] | = note: see https://doc.rust-lang.org/$CHANNEL/rustdoc/write-documentation/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators +warning: unmatched disambiguator `constant` and suffix `!{}` + --> $DIR/disambiguator-endswith-named-suffix.rs:41:6 + | +LL | //! [constant@m!{}] + | ^^^^^^^^ + | + = note: see https://doc.rust-lang.org/$CHANNEL/rustdoc/write-documentation/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators + warning: incompatible link kind for `f` --> $DIR/disambiguator-endswith-named-suffix.rs:43:6 | @@ -183,6 +239,14 @@ LL | //! [static@m!()] | = note: see https://doc.rust-lang.org/$CHANNEL/rustdoc/write-documentation/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators +warning: unmatched disambiguator `static` and suffix `!{}` + --> $DIR/disambiguator-endswith-named-suffix.rs:47:6 + | +LL | //! [static@m!{}] + | ^^^^^^ + | + = note: see https://doc.rust-lang.org/$CHANNEL/rustdoc/write-documentation/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators + warning: incompatible link kind for `f` --> $DIR/disambiguator-endswith-named-suffix.rs:49:6 | @@ -211,6 +275,14 @@ LL | //! [function@m!()] | = note: see https://doc.rust-lang.org/$CHANNEL/rustdoc/write-documentation/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators +warning: unmatched disambiguator `function` and suffix `!{}` + --> $DIR/disambiguator-endswith-named-suffix.rs:53:6 + | +LL | //! [function@m!{}] + | ^^^^^^^^ + | + = note: see https://doc.rust-lang.org/$CHANNEL/rustdoc/write-documentation/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators + warning: unmatched disambiguator `function` and suffix `!` --> $DIR/disambiguator-endswith-named-suffix.rs:56:6 | @@ -227,6 +299,14 @@ LL | //! [fn@m!()] | = note: see https://doc.rust-lang.org/$CHANNEL/rustdoc/write-documentation/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators +warning: unmatched disambiguator `fn` and suffix `!{}` + --> $DIR/disambiguator-endswith-named-suffix.rs:59:6 + | +LL | //! [fn@m!{}] + | ^^ + | + = note: see https://doc.rust-lang.org/$CHANNEL/rustdoc/write-documentation/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators + warning: unmatched disambiguator `fn` and suffix `!` --> $DIR/disambiguator-endswith-named-suffix.rs:62:6 | @@ -243,6 +323,14 @@ LL | //! [method@m!()] | = note: see https://doc.rust-lang.org/$CHANNEL/rustdoc/write-documentation/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators +warning: unmatched disambiguator `method` and suffix `!{}` + --> $DIR/disambiguator-endswith-named-suffix.rs:65:6 + | +LL | //! [method@m!{}] + | ^^^^^^ + | + = note: see https://doc.rust-lang.org/$CHANNEL/rustdoc/write-documentation/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators + warning: unmatched disambiguator `method` and suffix `!` --> $DIR/disambiguator-endswith-named-suffix.rs:68:6 | @@ -303,6 +391,14 @@ LL | //! [type@m!()] | = note: see https://doc.rust-lang.org/$CHANNEL/rustdoc/write-documentation/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators +warning: unmatched disambiguator `type` and suffix `!{}` + --> $DIR/disambiguator-endswith-named-suffix.rs:77:6 + | +LL | //! [type@m!{}] + | ^^^^ + | + = note: see https://doc.rust-lang.org/$CHANNEL/rustdoc/write-documentation/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators + warning: unmatched disambiguator `type` and suffix `()` --> $DIR/disambiguator-endswith-named-suffix.rs:79:6 | @@ -327,6 +423,14 @@ LL | //! [value@m!()] | = note: see https://doc.rust-lang.org/$CHANNEL/rustdoc/write-documentation/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators +warning: unmatched disambiguator `value` and suffix `!{}` + --> $DIR/disambiguator-endswith-named-suffix.rs:83:6 + | +LL | //! [value@m!{}] + | ^^^^^ + | + = note: see https://doc.rust-lang.org/$CHANNEL/rustdoc/write-documentation/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators + warning: unmatched disambiguator `value` and suffix `!` --> $DIR/disambiguator-endswith-named-suffix.rs:86:6 | @@ -351,6 +455,14 @@ LL | //! [prim@m!()] | = note: see https://doc.rust-lang.org/$CHANNEL/rustdoc/write-documentation/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators +warning: unmatched disambiguator `prim` and suffix `!{}` + --> $DIR/disambiguator-endswith-named-suffix.rs:95:6 + | +LL | //! [prim@m!{}] + | ^^^^ + | + = note: see https://doc.rust-lang.org/$CHANNEL/rustdoc/write-documentation/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators + warning: unmatched disambiguator `prim` and suffix `()` --> $DIR/disambiguator-endswith-named-suffix.rs:97:6 | @@ -375,6 +487,14 @@ LL | //! [primitive@m!()] | = note: see https://doc.rust-lang.org/$CHANNEL/rustdoc/write-documentation/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators +warning: unmatched disambiguator `primitive` and suffix `!{}` + --> $DIR/disambiguator-endswith-named-suffix.rs:101:6 + | +LL | //! [primitive@m!{}] + | ^^^^^^^^^ + | + = note: see https://doc.rust-lang.org/$CHANNEL/rustdoc/write-documentation/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators + warning: unmatched disambiguator `primitive` and suffix `()` --> $DIR/disambiguator-endswith-named-suffix.rs:103:6 | @@ -391,5 +511,5 @@ LL | //! [primitive@m!] | = note: see https://doc.rust-lang.org/$CHANNEL/rustdoc/write-documentation/linking-to-items-by-name.html#namespaces-and-disambiguators for more info about disambiguators -warning: 46 warnings emitted +warning: 61 warnings emitted diff --git a/tests/rustdoc-ui/intra-doc/weird-syntax.rs b/tests/rustdoc-ui/intra-doc/weird-syntax.rs index d2a922b2b6247..1c5977b1bc33d 100644 --- a/tests/rustdoc-ui/intra-doc/weird-syntax.rs +++ b/tests/rustdoc-ui/intra-doc/weird-syntax.rs @@ -65,7 +65,7 @@ pub struct XLinkToCloneWithStartSpace; /// [x][struct@Clone ] //~ERROR link pub struct XLinkToCloneWithEndSpace; -/// [x][Clone\(\)] not URL-shaped enough +/// [x][Clone\(\)] //~ERROR link pub struct XLinkToCloneWithEscapedParens; /// [x][`Clone`] not URL-shaped enough diff --git a/tests/rustdoc-ui/intra-doc/weird-syntax.stderr b/tests/rustdoc-ui/intra-doc/weird-syntax.stderr index ad813f0f9b623..3a567e409eff7 100644 --- a/tests/rustdoc-ui/intra-doc/weird-syntax.stderr +++ b/tests/rustdoc-ui/intra-doc/weird-syntax.stderr @@ -123,6 +123,14 @@ LL - /// [x][struct@Clone ] LL + /// [x][trait@Clone ] | +error: unresolved link to `Clone\(\)` + --> $DIR/weird-syntax.rs:68:9 + | +LL | /// [x][Clone\(\)] + | ^^^^^^^^^ no item named `Clone\(\)` in scope + | + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` + error: unresolved link to `Clone` --> $DIR/weird-syntax.rs:74:9 | @@ -299,5 +307,21 @@ LL | /// - [`SDL_PROP_WINDOW_CREATE_COCOA_WINDOW_POINTER`]: the | = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` +error: unresolved link to `Clone` + --> $DIR/weird-syntax.rs:132:9 + | +LL | /// The [cln][] link here will produce a plain text suggestion + | ^^^^^ this link resolves to the trait `Clone`, which is not a function + | + = help: to link to the trait, prefix with `trait@`: trait@Clone + +error: incompatible link kind for `Clone` + --> $DIR/weird-syntax.rs:137:9 + | +LL | /// The [cln][] link here will produce a plain text suggestion + | ^^^^^ this link resolved to a trait, which is not a struct + | + = help: to link to the trait, prefix with `trait@`: trait@Clone + error: aborting due to 27 previous errors diff --git a/tests/rustdoc-ui/lints/redundant_explicit_links-utf8.rs b/tests/rustdoc-ui/lints/redundant_explicit_links-utf8.rs index 4f4590d45fc88..81cd6c73d1c17 100644 --- a/tests/rustdoc-ui/lints/redundant_explicit_links-utf8.rs +++ b/tests/rustdoc-ui/lints/redundant_explicit_links-utf8.rs @@ -1,18 +1,24 @@ //@ check-pass -/// [`…foo`] [`…bar`] [`Err`] +/// [`…foo`] //~ WARN: unresolved link +/// [`…bar`] //~ WARN: unresolved link +/// [`Err`] pub struct Broken {} -/// [`…`] [`…`] [`Err`] +/// [`…`] //~ WARN: unresolved link +/// [`…`] //~ WARN: unresolved link +/// [`Err`] pub struct Broken2 {} -/// [`…`][…] [`…`][…] [`Err`] +/// [`…`][…] //~ WARN: unresolved link +/// [`…`][…] //~ WARN: unresolved link +/// [`Err`] pub struct Broken3 {} /// […………………………][Broken3] pub struct Broken4 {} -/// [Broken3][…………………………] +/// [Broken3][…………………………] //~ WARN: unresolved link pub struct Broken5 {} pub struct Err; diff --git a/tests/rustdoc-ui/lints/redundant_explicit_links-utf8.stderr b/tests/rustdoc-ui/lints/redundant_explicit_links-utf8.stderr new file mode 100644 index 0000000000000..0a5ff68b90814 --- /dev/null +++ b/tests/rustdoc-ui/lints/redundant_explicit_links-utf8.stderr @@ -0,0 +1,59 @@ +warning: unresolved link to `…foo` + --> $DIR/redundant_explicit_links-utf8.rs:3:7 + | +LL | /// [`…foo`] + | ^^^^ no item named `…foo` in scope + | + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` + = note: `#[warn(rustdoc::broken_intra_doc_links)]` on by default + +warning: unresolved link to `…bar` + --> $DIR/redundant_explicit_links-utf8.rs:4:7 + | +LL | /// [`…bar`] + | ^^^^ no item named `…bar` in scope + | + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` + +warning: unresolved link to `…` + --> $DIR/redundant_explicit_links-utf8.rs:8:7 + | +LL | /// [`…`] + | ^ no item named `…` in scope + | + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` + +warning: unresolved link to `…` + --> $DIR/redundant_explicit_links-utf8.rs:9:7 + | +LL | /// [`…`] + | ^ no item named `…` in scope + | + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` + +warning: unresolved link to `…` + --> $DIR/redundant_explicit_links-utf8.rs:13:11 + | +LL | /// [`…`][…] + | ^ no item named `…` in scope + | + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` + +warning: unresolved link to `…` + --> $DIR/redundant_explicit_links-utf8.rs:14:11 + | +LL | /// [`…`][…] + | ^ no item named `…` in scope + | + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` + +warning: unresolved link to `…………………………` + --> $DIR/redundant_explicit_links-utf8.rs:21:15 + | +LL | /// [Broken3][…………………………] + | ^^^^^^^^^^ no item named `…………………………` in scope + | + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` + +warning: 7 warnings emitted + From a7da4b829d328f129e342917908b70c5f4b98abc Mon Sep 17 00:00:00 2001 From: binarycat Date: Fri, 18 Apr 2025 15:32:56 -0500 Subject: [PATCH 09/40] rustdoc::broken_intra_doc_links: no backticks = use old behavior this is in an effort to reduce the amount of code churn caused by this lint triggering on text that was never meant to be a link. a more principled hierustic for ignoring lints is not possible without extensive changes, due to the lint emitting code being so far away from the link collecting code, and the fact that only the link collecting code has access to details about how the link appears in the unnormalized markdown. --- src/librustdoc/passes/collect_intra_doc_links.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 97f8a2be10b27..08f0d4015fcaf 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -996,7 +996,9 @@ fn preprocess_link( } }; - if can_be_url && should_ignore_link(path_str) { + // If there's no backticks, be lenient revert to old behavior. + // This is to prevent churn by linting on stuff that isn't meant to be a link. + if (can_be_url || !ori_link.link.contains('`')) && should_ignore_link(path_str) { return None; } From 041348110e2e526a80efd5adfc2909c716cbb37f Mon Sep 17 00:00:00 2001 From: binarycat Date: Fri, 18 Apr 2025 17:15:11 -0500 Subject: [PATCH 10/40] rustdoc: update tests to match new lint behavior --- tests/rustdoc-ui/bad-intra-doc.rs | 2 ++ tests/rustdoc-ui/intra-doc/weird-syntax.rs | 2 +- .../rustdoc-ui/intra-doc/weird-syntax.stderr | 24 ------------------- 3 files changed, 3 insertions(+), 25 deletions(-) diff --git a/tests/rustdoc-ui/bad-intra-doc.rs b/tests/rustdoc-ui/bad-intra-doc.rs index 7bc55756e670b..c24a848d8985c 100644 --- a/tests/rustdoc-ui/bad-intra-doc.rs +++ b/tests/rustdoc-ui/bad-intra-doc.rs @@ -10,4 +10,6 @@ /// attempted link to method: [`Foo.bar()`] //~ERROR unresolved link /// /// classic broken intra-doc link: [`Bar`] //~ERROR unresolved link +/// +/// no backticks, so we let this one slide: [Foo.bar()] pub struct Foo; diff --git a/tests/rustdoc-ui/intra-doc/weird-syntax.rs b/tests/rustdoc-ui/intra-doc/weird-syntax.rs index 1c5977b1bc33d..9f18f67fc44ab 100644 --- a/tests/rustdoc-ui/intra-doc/weird-syntax.rs +++ b/tests/rustdoc-ui/intra-doc/weird-syntax.rs @@ -65,7 +65,7 @@ pub struct XLinkToCloneWithStartSpace; /// [x][struct@Clone ] //~ERROR link pub struct XLinkToCloneWithEndSpace; -/// [x][Clone\(\)] //~ERROR link +/// [x][Clone\(\)] pub struct XLinkToCloneWithEscapedParens; /// [x][`Clone`] not URL-shaped enough diff --git a/tests/rustdoc-ui/intra-doc/weird-syntax.stderr b/tests/rustdoc-ui/intra-doc/weird-syntax.stderr index 3a567e409eff7..ad813f0f9b623 100644 --- a/tests/rustdoc-ui/intra-doc/weird-syntax.stderr +++ b/tests/rustdoc-ui/intra-doc/weird-syntax.stderr @@ -123,14 +123,6 @@ LL - /// [x][struct@Clone ] LL + /// [x][trait@Clone ] | -error: unresolved link to `Clone\(\)` - --> $DIR/weird-syntax.rs:68:9 - | -LL | /// [x][Clone\(\)] - | ^^^^^^^^^ no item named `Clone\(\)` in scope - | - = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` - error: unresolved link to `Clone` --> $DIR/weird-syntax.rs:74:9 | @@ -307,21 +299,5 @@ LL | /// - [`SDL_PROP_WINDOW_CREATE_COCOA_WINDOW_POINTER`]: the | = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` -error: unresolved link to `Clone` - --> $DIR/weird-syntax.rs:132:9 - | -LL | /// The [cln][] link here will produce a plain text suggestion - | ^^^^^ this link resolves to the trait `Clone`, which is not a function - | - = help: to link to the trait, prefix with `trait@`: trait@Clone - -error: incompatible link kind for `Clone` - --> $DIR/weird-syntax.rs:137:9 - | -LL | /// The [cln][] link here will produce a plain text suggestion - | ^^^^^ this link resolved to a trait, which is not a struct - | - = help: to link to the trait, prefix with `trait@`: trait@Clone - error: aborting due to 27 previous errors From 6a7d4882a207890c75ca09ce19810d90ac9cfde6 Mon Sep 17 00:00:00 2001 From: binarycat Date: Fri, 18 Apr 2025 20:23:05 -0500 Subject: [PATCH 11/40] rustdoc::broken_intra_doc_links: only be lenient with shortcut links MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit collapsed links and reference links have a pretty particular syntax, it seems unlikely they would show up on accident. Co-authored-by: León Orell Valerian Liehr --- .../passes/collect_intra_doc_links.rs | 27 +++++++++++++++---- tests/rustdoc-ui/intra-doc/weird-syntax.rs | 2 +- .../rustdoc-ui/intra-doc/weird-syntax.stderr | 10 ++++++- 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 08f0d4015fcaf..c9fa3a4837f29 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -944,9 +944,10 @@ fn preprocess_link( // certain link kinds cannot have their path be urls, // so they should not be ignored, no matter how much they look like urls. // e.g. [https://example.com/] is not a link to example.com. - let can_be_url = ori_link.kind != LinkType::ShortcutUnknown - && ori_link.kind != LinkType::CollapsedUnknown - && ori_link.kind != LinkType::ReferenceUnknown; + let can_be_url = !matches!( + ori_link.kind, + LinkType::ShortcutUnknown | LinkType::CollapsedUnknown | LinkType::ReferenceUnknown + ); // [] is mostly likely not supposed to be a link if ori_link.link.is_empty() { @@ -996,9 +997,25 @@ fn preprocess_link( } }; - // If there's no backticks, be lenient revert to old behavior. + // If there's no backticks, be lenient and revert to the old behavior. // This is to prevent churn by linting on stuff that isn't meant to be a link. - if (can_be_url || !ori_link.link.contains('`')) && should_ignore_link(path_str) { + // only shortcut links have simple enough syntax that they + // are likely to be written accidentally, collapsed and reference links + // need 4 metachars, and reference links will not usually use + // backticks in the reference name. + // therefore, only shortcut syntax gets the lenient behavior. + // + // here's a truth table for how link kinds that cannot be urls are handled: + // + // |-------------------------------------------------------| + // | | is shortcut link | not shortcut link | + // |--------------|--------------------|-------------------| + // | has backtick | never ignore | never ignore | + // | no backtick | ignore if url-like | never ignore | + // |-------------------------------------------------------| + let ignore_urllike = + can_be_url || (ori_link.kind == LinkType::ShortcutUnknown && !ori_link.link.contains('`')); + if ignore_urllike && should_ignore_link(path_str) { return None; } diff --git a/tests/rustdoc-ui/intra-doc/weird-syntax.rs b/tests/rustdoc-ui/intra-doc/weird-syntax.rs index 9f18f67fc44ab..1c5977b1bc33d 100644 --- a/tests/rustdoc-ui/intra-doc/weird-syntax.rs +++ b/tests/rustdoc-ui/intra-doc/weird-syntax.rs @@ -65,7 +65,7 @@ pub struct XLinkToCloneWithStartSpace; /// [x][struct@Clone ] //~ERROR link pub struct XLinkToCloneWithEndSpace; -/// [x][Clone\(\)] +/// [x][Clone\(\)] //~ERROR link pub struct XLinkToCloneWithEscapedParens; /// [x][`Clone`] not URL-shaped enough diff --git a/tests/rustdoc-ui/intra-doc/weird-syntax.stderr b/tests/rustdoc-ui/intra-doc/weird-syntax.stderr index ad813f0f9b623..7f2fc1fe62562 100644 --- a/tests/rustdoc-ui/intra-doc/weird-syntax.stderr +++ b/tests/rustdoc-ui/intra-doc/weird-syntax.stderr @@ -123,6 +123,14 @@ LL - /// [x][struct@Clone ] LL + /// [x][trait@Clone ] | +error: unresolved link to `Clone\(\)` + --> $DIR/weird-syntax.rs:68:9 + | +LL | /// [x][Clone\(\)] + | ^^^^^^^^^ no item named `Clone\(\)` in scope + | + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` + error: unresolved link to `Clone` --> $DIR/weird-syntax.rs:74:9 | @@ -299,5 +307,5 @@ LL | /// - [`SDL_PROP_WINDOW_CREATE_COCOA_WINDOW_POINTER`]: the | = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` -error: aborting due to 27 previous errors +error: aborting due to 28 previous errors From bd85df192d07660511b711cd7d4cae5b5a85577a Mon Sep 17 00:00:00 2001 From: binarycat Date: Sat, 19 Apr 2025 10:51:40 -0500 Subject: [PATCH 12/40] move bad-intra-doc test into intra-doc dir --- tests/rustdoc-ui/{ => intra-doc}/bad-intra-doc.rs | 0 tests/rustdoc-ui/{ => intra-doc}/bad-intra-doc.stderr | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename tests/rustdoc-ui/{ => intra-doc}/bad-intra-doc.rs (100%) rename tests/rustdoc-ui/{ => intra-doc}/bad-intra-doc.stderr (100%) diff --git a/tests/rustdoc-ui/bad-intra-doc.rs b/tests/rustdoc-ui/intra-doc/bad-intra-doc.rs similarity index 100% rename from tests/rustdoc-ui/bad-intra-doc.rs rename to tests/rustdoc-ui/intra-doc/bad-intra-doc.rs diff --git a/tests/rustdoc-ui/bad-intra-doc.stderr b/tests/rustdoc-ui/intra-doc/bad-intra-doc.stderr similarity index 100% rename from tests/rustdoc-ui/bad-intra-doc.stderr rename to tests/rustdoc-ui/intra-doc/bad-intra-doc.stderr From a73d7e3ab9cc29cb427c1463d1a02b89dd7728b6 Mon Sep 17 00:00:00 2001 From: binarycat Date: Thu, 24 Jul 2025 12:37:46 -0500 Subject: [PATCH 13/40] fix up issues with internal compiler docs revealed by stricter lint --- compiler/rustc_mir_transform/src/elaborate_drop.rs | 4 ++++ compiler/rustc_thread_pool/src/sleep/mod.rs | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_mir_transform/src/elaborate_drop.rs b/compiler/rustc_mir_transform/src/elaborate_drop.rs index de96b1f255a6e..de60772b17ea2 100644 --- a/compiler/rustc_mir_transform/src/elaborate_drop.rs +++ b/compiler/rustc_mir_transform/src/elaborate_drop.rs @@ -611,6 +611,7 @@ where /// /// For example, with 3 fields, the drop ladder is /// + /// ```text /// .d0: /// ELAB(drop location.0 [target=.d1, unwind=.c1]) /// .d1: @@ -621,8 +622,10 @@ where /// ELAB(drop location.1 [target=.c2]) /// .c2: /// ELAB(drop location.2 [target=`self.unwind`]) + /// ``` /// /// For possible-async drops in coroutines we also need dropline ladder + /// ```text /// .d0 (mainline): /// ELAB(drop location.0 [target=.d1, unwind=.c1, drop=.e1]) /// .d1 (mainline): @@ -637,6 +640,7 @@ where /// ELAB(drop location.1 [target=.e2, unwind=.c2]) /// .e2 (dropline): /// ELAB(drop location.2 [target=`self.drop`, unwind=`self.unwind`]) + /// ``` /// /// NOTE: this does not clear the master drop flag, so you need /// to point succ/unwind on a `drop_ladder_bottom`. diff --git a/compiler/rustc_thread_pool/src/sleep/mod.rs b/compiler/rustc_thread_pool/src/sleep/mod.rs index 31bf7184b42d5..aa6666092147a 100644 --- a/compiler/rustc_thread_pool/src/sleep/mod.rs +++ b/compiler/rustc_thread_pool/src/sleep/mod.rs @@ -44,7 +44,7 @@ impl SleepData { /// jobs are published, and it either blocks threads or wakes them in response to these /// events. See the [`README.md`] in this module for more details. /// -/// [`README.md`] README.md +/// [`README.md`]: README.md pub(super) struct Sleep { /// One "sleep state" per worker. Used to track if a worker is sleeping and to have /// them block. From da4687bafe4ff78a9816797c3b74b6bcc0c5a3bc Mon Sep 17 00:00:00 2001 From: Alisa Sireneva Date: Fri, 25 Jul 2025 16:09:29 +0300 Subject: [PATCH 14/40] Link to Mutex poisoning docs from RwLock docs --- library/std/src/sync/poison/rwlock.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/library/std/src/sync/poison/rwlock.rs b/library/std/src/sync/poison/rwlock.rs index 0a50b6c2d8f10..2c92602bc878f 100644 --- a/library/std/src/sync/poison/rwlock.rs +++ b/library/std/src/sync/poison/rwlock.rs @@ -46,11 +46,13 @@ use crate::sys::sync as sys; /// /// # Poisoning /// -/// An `RwLock`, like [`Mutex`], will usually become poisoned on a panic. Note, +/// An `RwLock`, like [`Mutex`], will [usually] become poisoned on a panic. Note, /// however, that an `RwLock` may only be poisoned if a panic occurs while it is /// locked exclusively (write mode). If a panic occurs in any reader, then the /// lock will not be poisoned. /// +/// [usually]: super::Mutex#poisoning +/// /// # Examples /// /// ``` From 5b8c61494f85a58759dad04194c8353de07d75e1 Mon Sep 17 00:00:00 2001 From: Alisa Sireneva Date: Fri, 25 Jul 2025 21:03:09 +0300 Subject: [PATCH 15/40] Add a list of failure conditions for poisoning --- library/std/src/sync/poison/mutex.rs | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/library/std/src/sync/poison/mutex.rs b/library/std/src/sync/poison/mutex.rs index 15dfe116133ba..317e5fc3bfb9f 100644 --- a/library/std/src/sync/poison/mutex.rs +++ b/library/std/src/sync/poison/mutex.rs @@ -35,8 +35,20 @@ use crate::sys::sync as sys; /// /// In addition, the panic detection is not ideal, so even unpoisoned mutexes /// need to be handled with care, since certain panics may have been skipped. -/// Therefore, `unsafe` code cannot rely on poisoning for soundness. Here's an -/// example of **incorrect** use of poisoning: +/// Here is a non-exhaustive list of situations where this might occur: +/// +/// - If a mutex is locked while a panic is underway, e.g. within a [`Drop`] +/// implementation or a [panic hook], panicking for the second time while the +/// lock is held will leave the mutex unpoisoned. Note that while double panic +/// usually aborts the program, [`catch_unwind`] can prevent this. +/// +/// - Locking and unlocking the mutex across different panic contexts, e.g. by +/// storing the guard to a [`Cell`] within [`Drop::drop`] and accessing it +/// outside, or vice versa, can affect poisoning status in an unexpected way. +/// +/// While this rarely happens in realistic code, `unsafe` code cannot rely on +/// poisoning for soundness, since the behavior of poisoning can depend on +/// outside context. Here's an example of **incorrect** use of poisoning: /// /// ```rust /// use std::sync::Mutex; @@ -58,8 +70,8 @@ use crate::sys::sync as sys; /// // panics, `*ptr` keeps pointing at a dropped value. The intention /// // is that this will poison the mutex, so the following calls to /// // `replace_with` will panic without reading `*ptr`. But since -/// // poisoning is not guaranteed to occur, this can lead to -/// // use-after-free. +/// // poisoning is not guaranteed to occur if this is run from a panic +/// // hook, this can lead to use-after-free. /// unsafe { /// (*ptr).write(f((*ptr).read())); /// } @@ -73,6 +85,9 @@ use crate::sys::sync as sys; /// [`unwrap()`]: Result::unwrap /// [`PoisonError`]: super::PoisonError /// [`into_inner`]: super::PoisonError::into_inner +/// [panic hook]: crate::panic::set_hook +/// [`catch_unwind`]: crate::panic::catch_unwind +/// [`Cell`]: crate::cell::Cell /// /// # Examples /// From c1d06cccaed537c799838dc5338dda28bbace52b Mon Sep 17 00:00:00 2001 From: Alisa Sireneva Date: Fri, 25 Jul 2025 22:14:02 +0300 Subject: [PATCH 16/40] Add a note on foreign exceptions --- library/std/src/sync/poison/mutex.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library/std/src/sync/poison/mutex.rs b/library/std/src/sync/poison/mutex.rs index 317e5fc3bfb9f..363220baf7b98 100644 --- a/library/std/src/sync/poison/mutex.rs +++ b/library/std/src/sync/poison/mutex.rs @@ -46,6 +46,9 @@ use crate::sys::sync as sys; /// storing the guard to a [`Cell`] within [`Drop::drop`] and accessing it /// outside, or vice versa, can affect poisoning status in an unexpected way. /// +/// - Foreign exceptions do not currently trigger poisoning even in absence of +/// other panics. +/// /// While this rarely happens in realistic code, `unsafe` code cannot rely on /// poisoning for soundness, since the behavior of poisoning can depend on /// outside context. Here's an example of **incorrect** use of poisoning: From 3352dfc4a232fa6f38db3ea3ed465f56e6c8f85b Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Fri, 25 Jul 2025 17:27:08 -0700 Subject: [PATCH 17/40] Skip formatting for some compiler documentation code These examples feature Rust code that's presented primarily to illustrate how its compilation would be handled, and these examples are formatted to highlight those aspects in ways that rustfmt wouldn't preserve. Turn formatting off in those examples. (Doc code isn't formatted yet, but this will make it easier to enable doc code formatting in the future.) --- compiler/rustc_borrowck/src/region_infer/mod.rs | 2 +- compiler/rustc_hir/src/def.rs | 2 +- compiler/rustc_mir_dataflow/src/impls/initialized.rs | 3 +++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 5f1b655c6b60d..d3bf9f0ec4a65 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -417,7 +417,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// minimum values. /// /// For example: - /// ``` + /// ```ignore (illustrative) /// fn foo<'a, 'b>( /* ... */ ) where 'a: 'b { /* ... */ } /// ``` /// would initialize two variables like so: diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index ca57c4f31641d..5024c85e2a8a2 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -356,7 +356,7 @@ impl DefKind { /// For example, everything prefixed with `/* Res */` in this example has /// an associated `Res`: /// -/// ``` +/// ```ignore (illustrative) /// fn str_to_string(s: & /* Res */ str) -> /* Res */ String { /// /* Res */ String::from(/* Res */ s) /// } diff --git a/compiler/rustc_mir_dataflow/src/impls/initialized.rs b/compiler/rustc_mir_dataflow/src/impls/initialized.rs index 085757f0fb6ee..ebf43300f98af 100644 --- a/compiler/rustc_mir_dataflow/src/impls/initialized.rs +++ b/compiler/rustc_mir_dataflow/src/impls/initialized.rs @@ -108,6 +108,7 @@ impl<'tcx> MaybePlacesSwitchIntData<'tcx> { /// /// ```rust /// struct S; +/// #[rustfmt::skip] /// fn foo(pred: bool) { // maybe-init: /// // {} /// let a = S; let mut b = S; let c; let d; // {a, b} @@ -197,6 +198,7 @@ impl<'a, 'tcx> HasMoveData<'tcx> for MaybeInitializedPlaces<'a, 'tcx> { /// /// ```rust /// struct S; +/// #[rustfmt::skip] /// fn foo(pred: bool) { // maybe-uninit: /// // {a, b, c, d} /// let a = S; let mut b = S; let c; let d; // { c, d} @@ -289,6 +291,7 @@ impl<'tcx> HasMoveData<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { /// /// ```rust /// struct S; +/// #[rustfmt::skip] /// fn foo(pred: bool) { // ever-init: /// // { } /// let a = S; let mut b = S; let c; let d; // {a, b } From 715088094c2e2ef31158b4767ceacfba62c8e6ef Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Fri, 25 Jul 2025 17:35:47 -0700 Subject: [PATCH 18/40] Improve and regularize comment placement in doc code Because doc code does not get automatically formatted, some doc code has creative placements of comments that automatic formatting can't handle. Reformat those comments to make the resulting code support standard Rust formatting without breaking; this is generally an improvement to readability as well. Some comments are not indented to the prevailing indent, and are instead aligned under some bit of code. Indent them to the prevailing indent, and put spaces *inside* the comments to align them with code. Some comments span several lines of code (which aren't the line the comment is about) and expect alignment. Reformat them into one comment not broken up by unrelated intervening code. Some comments are placed on the same line as an opening brace, placing them effectively inside the subsequent block, such that formatting would typically format them like a line of that block. Move those comments to attach them to what they apply to. Some comments are placed on the same line as a one-line braced block, effectively attaching them to the closing brace, even though they're about the code inside the block. Reformat to make sure the comment will stay on the same line as the code it's commenting. --- compiler/rustc_hir/src/def.rs | 2 +- .../src/check/compare_impl_item.rs | 2 +- compiler/rustc_hir_typeck/src/upvar.rs | 4 +--- .../src/infer/outlives/obligations.rs | 2 +- compiler/rustc_middle/src/hir/map.rs | 12 +++++++---- compiler/rustc_middle/src/mir/mod.rs | 3 ++- .../src/builder/expr/as_operand.rs | 4 +++- compiler/rustc_span/src/hygiene.rs | 4 +++- compiler/rustc_thread_pool/src/scope/mod.rs | 6 +++--- library/alloc/src/vec/mod.rs | 2 +- library/core/src/cell.rs | 6 +++--- library/core/src/hint.rs | 2 -- library/core/src/iter/mod.rs | 6 ++++-- library/core/src/macros/mod.rs | 10 +++++++-- library/core/src/marker.rs | 3 +-- library/core/src/mem/maybe_uninit.rs | 21 +++++++++---------- library/core/src/result.rs | 2 +- 17 files changed, 51 insertions(+), 40 deletions(-) diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index 5024c85e2a8a2..0ef70a6ecaa86 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -417,7 +417,7 @@ pub enum Res { /// } /// /// impl Foo for Bar { - /// fn foo() -> Box { // SelfTyAlias + /// fn foo() -> Box { /// let _: Self; // SelfTyAlias /// /// todo!() diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index e24426f9fedce..bd9125363fc48 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -425,7 +425,7 @@ impl<'tcx> TypeFolder> for RemapLateParam<'tcx> { /// /// trait Foo { /// fn bar() -> impl Deref; -/// // ^- RPITIT #1 ^- RPITIT #2 +/// // ^- RPITIT #1 ^- RPITIT #2 /// } /// /// impl Foo for () { diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index be774106abf8f..7ebdba9f80aa1 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -2391,13 +2391,11 @@ fn migration_suggestion_for_2229( /// let mut p = Point { x: 10, y: 10 }; /// /// let c = || { -/// p.x += 10; -/// // ^ E1 ^ +/// p.x += 10; // E1 /// // ... /// // More code /// // ... /// p.x += 10; // E2 -/// // ^ E2 ^ /// }; /// ``` /// `CaptureKind` associated with both `E1` and `E2` will be ByRef(MutBorrow), diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index a8520c0e71dd1..d6243d727aca9 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -36,7 +36,7 @@ //! fn bar(a: T, b: impl for<'a> Fn(&'a T)) {} //! fn foo(x: T) { //! bar(x, |y| { /* ... */}) -//! // ^ closure arg +//! // ^ closure arg //! } //! ``` //! diff --git a/compiler/rustc_middle/src/hir/map.rs b/compiler/rustc_middle/src/hir/map.rs index 42a1e7377f4ba..1b1735ab2bf5f 100644 --- a/compiler/rustc_middle/src/hir/map.rs +++ b/compiler/rustc_middle/src/hir/map.rs @@ -533,8 +533,10 @@ impl<'tcx> TyCtxt<'tcx> { /// ``` /// fn foo(x: usize) -> bool { /// if x == 1 { - /// true // If `get_fn_id_for_return_block` gets passed the `id` corresponding - /// } else { // to this, it will return `foo`'s `HirId`. + /// // If `get_fn_id_for_return_block` gets passed the `id` corresponding to this, it + /// // will return `foo`'s `HirId`. + /// true + /// } else { /// false /// } /// } @@ -543,8 +545,10 @@ impl<'tcx> TyCtxt<'tcx> { /// ```compile_fail,E0308 /// fn foo(x: usize) -> bool { /// loop { - /// true // If `get_fn_id_for_return_block` gets passed the `id` corresponding - /// } // to this, it will return `None`. + /// // If `get_fn_id_for_return_block` gets passed the `id` corresponding to this, it + /// // will return `None`. + /// true + /// } /// false /// } /// ``` diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index e819aa2d8f815..c55c7fc6002c7 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -1017,7 +1017,8 @@ pub struct LocalDecl<'tcx> { /// ``` /// fn foo(x: &str) { /// #[allow(unused_mut)] - /// let mut x: u32 = { // <- one unused mut + /// let mut x: u32 = { + /// //^ one unused mut /// let mut y: u32 = x.parse().unwrap(); /// y + 2 /// }; diff --git a/compiler/rustc_mir_build/src/builder/expr/as_operand.rs b/compiler/rustc_mir_build/src/builder/expr/as_operand.rs index 982e7aa8246b6..6a42222399041 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_operand.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_operand.rs @@ -57,7 +57,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// ``` /// #![feature(unsized_fn_params)] /// # use core::fmt::Debug; - /// fn foo(_p: dyn Debug) { /* ... */ } + /// fn foo(_p: dyn Debug) { + /// /* ... */ + /// } /// /// fn bar(box_p: Box) { foo(*box_p); } /// ``` diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index c3080875da803..6ea774eeccaa2 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -756,7 +756,9 @@ impl SyntaxContext { /// /// ```rust /// #![feature(decl_macro)] - /// mod foo { pub fn f() {} } // `f`'s `SyntaxContext` is empty. + /// mod foo { + /// pub fn f() {} // `f`'s `SyntaxContext` is empty. + /// } /// m!(f); /// macro m($f:ident) { /// mod bar { diff --git a/compiler/rustc_thread_pool/src/scope/mod.rs b/compiler/rustc_thread_pool/src/scope/mod.rs index 382303839650f..677009a9bc3da 100644 --- a/compiler/rustc_thread_pool/src/scope/mod.rs +++ b/compiler/rustc_thread_pool/src/scope/mod.rs @@ -501,9 +501,9 @@ impl<'scope> Scope<'scope> { /// let mut value_c = None; /// rayon::scope(|s| { /// s.spawn(|s1| { - /// // ^ this is the same scope as `s`; this handle `s1` - /// // is intended for use by the spawned task, - /// // since scope handles cannot cross thread boundaries. + /// // ^ this is the same scope as `s`; this handle `s1` + /// // is intended for use by the spawned task, + /// // since scope handles cannot cross thread boundaries. /// /// value_a = Some(22); /// diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 9856e9c18ec68..387adab70c688 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -3778,8 +3778,8 @@ impl Vec { /// while i < vec.len() - end_items { /// if some_predicate(&mut vec[i]) { /// let val = vec.remove(i); - /// # extracted.push(val); /// // your code here + /// # extracted.push(val); /// } else { /// i += 1; /// } diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index d6d1bf2effae2..d67408cae1b95 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -2068,9 +2068,9 @@ impl fmt::Display for RefMut<'_, T> { /// implies exclusive access to its `T`: /// /// ```rust -/// #![forbid(unsafe_code)] // with exclusive accesses, -/// // `UnsafeCell` is a transparent no-op wrapper, -/// // so no need for `unsafe` here. +/// #![forbid(unsafe_code)] +/// // with exclusive accesses, `UnsafeCell` is a transparent no-op wrapper, so no need for +/// // `unsafe` here. /// use std::cell::UnsafeCell; /// /// let mut x: UnsafeCell = 42.into(); diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs index 696d323c66d2e..c72eeb9a9c976 100644 --- a/library/core/src/hint.rs +++ b/library/core/src/hint.rs @@ -649,8 +649,6 @@ pub const fn must_use(value: T) -> T { /// } /// } /// ``` -/// -/// #[unstable(feature = "likely_unlikely", issue = "136873")] #[inline(always)] pub const fn likely(b: bool) -> bool { diff --git a/library/core/src/iter/mod.rs b/library/core/src/iter/mod.rs index 7ca41d224a0a6..56ca1305b601b 100644 --- a/library/core/src/iter/mod.rs +++ b/library/core/src/iter/mod.rs @@ -233,10 +233,12 @@ //! //! ``` //! let mut values = vec![41]; -//! for x in &mut values { // same as `values.iter_mut()` +//! for x in &mut values { +//! // ^ same as `values.iter_mut()` //! *x += 1; //! } -//! for x in &values { // same as `values.iter()` +//! for x in &values { +//! // ^ same as `values.iter()` //! assert_eq!(*x, 42); //! } //! assert_eq!(values.len(), 1); diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 8ac6ce2242d4e..ae75d166d0cf2 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -271,7 +271,10 @@ pub macro cfg_select($($tt:tt)*) { /// // expression given. /// debug_assert!(true); /// -/// fn some_expensive_computation() -> bool { true } // a very simple function +/// fn some_expensive_computation() -> bool { +/// // Some expensive computation here +/// true +/// } /// debug_assert!(some_expensive_computation()); /// /// // assert with a custom message @@ -1545,7 +1548,10 @@ pub(crate) mod builtin { /// // expression given. /// assert!(true); /// - /// fn some_computation() -> bool { true } // a very simple function + /// fn some_computation() -> bool { + /// // Some expensive computation here + /// true + /// } /// /// assert!(some_computation()); /// diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 45277a1c82d3a..ba00ee17b6528 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -138,8 +138,7 @@ unsafe impl Send for &T {} /// impl Bar for Impl { } /// /// let x: &dyn Foo = &Impl; // OK -/// // let y: &dyn Bar = &Impl; // error: the trait `Bar` cannot -/// // be made into an object +/// // let y: &dyn Bar = &Impl; // error: the trait `Bar` cannot be made into an object /// ``` /// /// [trait object]: ../../book/ch17-02-trait-objects.html diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index 34d8370da7ecb..c160360cfacf9 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -773,8 +773,7 @@ impl MaybeUninit { /// // Initialize the `MaybeUninit` using `Cell::set`: /// unsafe { /// b.assume_init_ref().set(true); - /// // ^^^^^^^^^^^^^^^ - /// // Reference to an uninitialized `Cell`: UB! + /// //^^^^^^^^^^^^^^^ Reference to an uninitialized `Cell`: UB! /// } /// ``` #[stable(feature = "maybe_uninit_ref", since = "1.55.0")] @@ -864,9 +863,9 @@ impl MaybeUninit { /// { /// let mut buffer = MaybeUninit::<[u8; 64]>::uninit(); /// reader.read_exact(unsafe { buffer.assume_init_mut() })?; - /// // ^^^^^^^^^^^^^^^^^^^^^^^^ - /// // (mutable) reference to uninitialized memory! - /// // This is undefined behavior. + /// // ^^^^^^^^^^^^^^^^^^^^^^^^ + /// // (mutable) reference to uninitialized memory! + /// // This is undefined behavior. /// Ok(unsafe { buffer.assume_init() }) /// } /// ``` @@ -884,13 +883,13 @@ impl MaybeUninit { /// let foo: Foo = unsafe { /// let mut foo = MaybeUninit::::uninit(); /// ptr::write(&mut foo.assume_init_mut().a as *mut u32, 1337); - /// // ^^^^^^^^^^^^^^^^^^^^^ - /// // (mutable) reference to uninitialized memory! - /// // This is undefined behavior. + /// // ^^^^^^^^^^^^^^^^^^^^^ + /// // (mutable) reference to uninitialized memory! + /// // This is undefined behavior. /// ptr::write(&mut foo.assume_init_mut().b as *mut u8, 42); - /// // ^^^^^^^^^^^^^^^^^^^^^ - /// // (mutable) reference to uninitialized memory! - /// // This is undefined behavior. + /// // ^^^^^^^^^^^^^^^^^^^^^ + /// // (mutable) reference to uninitialized memory! + /// // This is undefined behavior. /// foo.assume_init() /// }; /// ``` diff --git a/library/core/src/result.rs b/library/core/src/result.rs index f65257ff59b92..4db7e5021edb0 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -1544,7 +1544,7 @@ impl Result { /// /// ```no_run /// let x: Result = Err("emergency failure"); - /// unsafe { x.unwrap_unchecked(); } // Undefined behavior! + /// unsafe { x.unwrap_unchecked() }; // Undefined behavior! /// ``` #[inline] #[track_caller] From b6c54a97b3ce019b48a7e98f952532b9d1670834 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Fri, 25 Jul 2025 17:52:13 -0700 Subject: [PATCH 19/40] Avoid placing `// FIXME` comments inside doc code blocks This leads tools like rustfmt to get confused, because the doc code block effectively spans two doc comments. As a result, the tools think the first code block is unclosed, and the subsequent terminator opens a new block. Move the FIXME comments outside the doc code blocks, instead. --- library/alloc/src/string.rs | 4 ++-- library/alloc/src/vec/mod.rs | 8 ++++---- library/core/src/primitive_docs.rs | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index a189c00a6b61d..3bcc9920c99f3 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -265,12 +265,12 @@ use crate::vec::{self, Vec}; /// You can look at these with the [`as_ptr`], [`len`], and [`capacity`] /// methods: /// +// FIXME Update this when vec_into_raw_parts is stabilized /// ``` /// use std::mem; /// /// let story = String::from("Once upon a time..."); /// -// FIXME Update this when vec_into_raw_parts is stabilized /// // Prevent automatically dropping the String's data /// let mut story = mem::ManuallyDrop::new(story); /// @@ -970,13 +970,13 @@ impl String { /// /// # Examples /// + // FIXME Update this when vec_into_raw_parts is stabilized /// ``` /// use std::mem; /// /// unsafe { /// let s = String::from("hello"); /// - // FIXME Update this when vec_into_raw_parts is stabilized /// // Prevent automatically dropping the String's data /// let mut s = mem::ManuallyDrop::new(s); /// diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 387adab70c688..3a182fe341f3c 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -566,13 +566,13 @@ impl Vec { /// /// # Examples /// + // FIXME Update this when vec_into_raw_parts is stabilized /// ``` /// use std::ptr; /// use std::mem; /// /// let v = vec![1, 2, 3]; /// - // FIXME Update this when vec_into_raw_parts is stabilized /// // Prevent running `v`'s destructor so we are in complete control /// // of the allocation. /// let mut v = mem::ManuallyDrop::new(v); @@ -674,6 +674,7 @@ impl Vec { /// /// # Examples /// + // FIXME Update this when vec_into_raw_parts is stabilized /// ``` /// #![feature(box_vec_non_null)] /// @@ -682,7 +683,6 @@ impl Vec { /// /// let v = vec![1, 2, 3]; /// - // FIXME Update this when vec_into_raw_parts is stabilized /// // Prevent running `v`'s destructor so we are in complete control /// // of the allocation. /// let mut v = mem::ManuallyDrop::new(v); @@ -994,6 +994,7 @@ impl Vec { /// /// # Examples /// + // FIXME Update this when vec_into_raw_parts is stabilized /// ``` /// #![feature(allocator_api)] /// @@ -1007,7 +1008,6 @@ impl Vec { /// v.push(2); /// v.push(3); /// - // FIXME Update this when vec_into_raw_parts is stabilized /// // Prevent running `v`'s destructor so we are in complete control /// // of the allocation. /// let mut v = mem::ManuallyDrop::new(v); @@ -1114,6 +1114,7 @@ impl Vec { /// /// # Examples /// + // FIXME Update this when vec_into_raw_parts is stabilized /// ``` /// #![feature(allocator_api, box_vec_non_null)] /// @@ -1127,7 +1128,6 @@ impl Vec { /// v.push(2); /// v.push(3); /// - // FIXME Update this when vec_into_raw_parts is stabilized /// // Prevent running `v`'s destructor so we are in complete control /// // of the allocation. /// let mut v = mem::ManuallyDrop::new(v); diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs index 9a1ba7d1728cb..1c824e336bed7 100644 --- a/library/core/src/primitive_docs.rs +++ b/library/core/src/primitive_docs.rs @@ -271,6 +271,7 @@ mod prim_bool {} /// When the compiler sees a value of type `!` in a [coercion site], it implicitly inserts a /// coercion to allow the type checker to infer any type: /// +// FIXME: use `core::convert::absurd` here instead, once it's merged /// ```rust,ignore (illustrative-and-has-placeholders) /// // this /// let x: u8 = panic!(); @@ -281,7 +282,6 @@ mod prim_bool {} /// // where absurd is a function with the following signature /// // (it's sound, because `!` always marks unreachable code): /// fn absurd(_: !) -> T { ... } -// FIXME: use `core::convert::absurd` here instead, once it's merged /// ``` /// /// This can lead to compilation errors if the type cannot be inferred: From 56c5b1df941664567d3c1f820d5861fdb25a3ca4 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Fri, 25 Jul 2025 17:53:58 -0700 Subject: [PATCH 20/40] Add parentheses around expression arguments to `..` This makes it easier for humans to parse, and improves the result of potential future automatic formatting. --- library/core/src/iter/traits/iterator.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index 10f9d464f7d7f..29313867ff266 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -807,7 +807,7 @@ pub trait Iterator { /// might be preferable to keep a functional style with longer iterators: /// /// ``` - /// (0..5).flat_map(|x| x * 100 .. x * 110) + /// (0..5).flat_map(|x| (x * 100)..(x * 110)) /// .enumerate() /// .filter(|&(i, x)| (i + x) % 3 == 0) /// .for_each(|(i, x)| println!("{i}:{x}")); From 1e2d58798fab4aa6275acf3746e1aec5340fc97f Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Fri, 25 Jul 2025 17:55:45 -0700 Subject: [PATCH 21/40] Avoid making the start of a doc code block conditional Placing the opening triple-backquote inside a `cfg_attr` makes many tools confused, including syntax highlighters (e.g. vim's) and rustfmt. Instead, use a `cfg` inside the doc code block. --- library/std/src/path.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/library/std/src/path.rs b/library/std/src/path.rs index d9c34d4fa0451..055e7f814800d 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -3259,8 +3259,8 @@ impl Path { /// /// # Examples /// - #[cfg_attr(unix, doc = "```no_run")] - #[cfg_attr(not(unix), doc = "```ignore")] + /// ```rust,no_run + /// # #[cfg(unix)] { /// use std::path::Path; /// use std::os::unix::fs::symlink; /// @@ -3268,6 +3268,7 @@ impl Path { /// symlink("/origin_does_not_exist/", link_path).unwrap(); /// assert_eq!(link_path.is_symlink(), true); /// assert_eq!(link_path.exists(), false); + /// # } /// ``` /// /// # See Also From 91bccd6ba8861fd91bcf0f682f50f78fdb38a450 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Thu, 24 Jul 2025 14:06:58 +0200 Subject: [PATCH 22/40] use `minicore` for fortanix assembly tests --- ...4-fortanix-unknown-sgx-lvi-generic-load.rs | 31 ++++++++++++------- ...64-fortanix-unknown-sgx-lvi-generic-ret.rs | 14 +++++++-- ...ortanix-unknown-sgx-lvi-inline-assembly.rs | 28 ++++++++++------- 3 files changed, 46 insertions(+), 27 deletions(-) diff --git a/tests/assembly-llvm/x86_64-fortanix-unknown-sgx-lvi-generic-load.rs b/tests/assembly-llvm/x86_64-fortanix-unknown-sgx-lvi-generic-load.rs index f5e2f18e68e4c..2892ff2882a71 100644 --- a/tests/assembly-llvm/x86_64-fortanix-unknown-sgx-lvi-generic-load.rs +++ b/tests/assembly-llvm/x86_64-fortanix-unknown-sgx-lvi-generic-load.rs @@ -1,17 +1,24 @@ -// Test LVI load hardening on SGX enclave code +// Test LVI load hardening on SGX enclave code, specifically that `ret` is rewritten. +//@ add-core-stubs //@ assembly-output: emit-asm -//@ compile-flags: --crate-type staticlib -//@ only-x86_64-fortanix-unknown-sgx +//@ compile-flags: --target x86_64-fortanix-unknown-sgx -Copt-level=0 +//@ needs-llvm-components: x86 + +#![feature(no_core, lang_items, f16)] +#![crate_type = "lib"] +#![no_core] + +extern crate minicore; +use minicore::*; #[no_mangle] -pub extern "C" fn plus_one(r: &mut u64) { - *r = *r + 1; +pub extern "C" fn dereference(a: &mut u64) -> u64 { + // CHECK-LABEL: dereference + // CHECK: lfence + // CHECK: mov + // CHECK: popq [[REGISTER:%[a-z]+]] + // CHECK-NEXT: lfence + // CHECK-NEXT: jmpq *[[REGISTER]] + *a } - -// CHECK: plus_one -// CHECK: lfence -// CHECK-NEXT: incq -// CHECK: popq [[REGISTER:%[a-z]+]] -// CHECK-NEXT: lfence -// CHECK-NEXT: jmpq *[[REGISTER]] diff --git a/tests/assembly-llvm/x86_64-fortanix-unknown-sgx-lvi-generic-ret.rs b/tests/assembly-llvm/x86_64-fortanix-unknown-sgx-lvi-generic-ret.rs index f16d68fa2554b..a0cedc3bc2da1 100644 --- a/tests/assembly-llvm/x86_64-fortanix-unknown-sgx-lvi-generic-ret.rs +++ b/tests/assembly-llvm/x86_64-fortanix-unknown-sgx-lvi-generic-ret.rs @@ -1,12 +1,20 @@ // Test LVI ret hardening on generic rust code +//@ add-core-stubs //@ assembly-output: emit-asm -//@ compile-flags: --crate-type staticlib -//@ only-x86_64-fortanix-unknown-sgx +//@ compile-flags: --target x86_64-fortanix-unknown-sgx +//@ needs-llvm-components: x86 + +#![feature(no_core, lang_items, f16)] +#![crate_type = "lib"] +#![no_core] + +extern crate minicore; +use minicore::*; #[no_mangle] pub extern "C" fn myret() {} -// CHECK: myret: +// CHECK-LABEL: myret: // CHECK: popq [[REGISTER:%[a-z]+]] // CHECK-NEXT: lfence // CHECK-NEXT: jmpq *[[REGISTER]] diff --git a/tests/assembly-llvm/x86_64-fortanix-unknown-sgx-lvi-inline-assembly.rs b/tests/assembly-llvm/x86_64-fortanix-unknown-sgx-lvi-inline-assembly.rs index a729df8e1660f..215fb4b804ac2 100644 --- a/tests/assembly-llvm/x86_64-fortanix-unknown-sgx-lvi-inline-assembly.rs +++ b/tests/assembly-llvm/x86_64-fortanix-unknown-sgx-lvi-inline-assembly.rs @@ -1,13 +1,22 @@ // Test LVI load hardening on SGX inline assembly code +//@ add-core-stubs //@ assembly-output: emit-asm -//@ compile-flags: --crate-type staticlib -//@ only-x86_64-fortanix-unknown-sgx +//@ compile-flags: --target x86_64-fortanix-unknown-sgx +//@ needs-llvm-components: x86 -use std::arch::asm; +#![feature(no_core, lang_items, f16)] +#![crate_type = "lib"] +#![no_core] + +extern crate minicore; +use minicore::*; #[no_mangle] pub extern "C" fn get(ptr: *const u64) -> u64 { + // CHECK-LABEL: get + // CHECK: movq + // CHECK-NEXT: lfence let value: u64; unsafe { asm!("mov {}, [{}]", @@ -17,18 +26,13 @@ pub extern "C" fn get(ptr: *const u64) -> u64 { value } -// CHECK: get -// CHECK: movq -// CHECK-NEXT: lfence - #[no_mangle] pub extern "C" fn myret() { + // CHECK-LABEL: myret + // CHECK: shlq $0, (%rsp) + // CHECK-NEXT: lfence + // CHECK-NEXT: retq unsafe { asm!("ret"); } } - -// CHECK: myret -// CHECK: shlq $0, (%rsp) -// CHECK-NEXT: lfence -// CHECK-NEXT: retq From 8b90847416d5678d784942b46c05f8c97caef83d Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Thu, 24 Jul 2025 14:07:32 +0200 Subject: [PATCH 23/40] update fortanix run-make test Make it more idiomatic with the new run-make infra --- .../x86_64-fortanix-unknown-sgx-lvi/rmake.rs | 43 ++++++++++++------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/rmake.rs b/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/rmake.rs index e58762aeb6db7..89754cdaf905c 100644 --- a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/rmake.rs +++ b/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/rmake.rs @@ -13,42 +13,56 @@ //@ only-x86_64-fortanix-unknown-sgx -use run_make_support::{cmd, cwd, llvm_filecheck, llvm_objdump, regex, set_current_dir, target}; +use run_make_support::{ + cargo, cwd, llvm_filecheck, llvm_objdump, regex, run, set_current_dir, target, +}; fn main() { - let main_dir = cwd(); - set_current_dir("enclave"); - // HACK(eddyb) sets `RUSTC_BOOTSTRAP=1` so Cargo can accept nightly features. - // These come from the top-level Rust workspace, that this crate is not a - // member of, but Cargo tries to load the workspace `Cargo.toml` anyway. - cmd("cargo") - .env("RUSTC_BOOTSTRAP", "1") + cargo() .arg("-v") - .arg("run") + .arg("build") .arg("--target") .arg(target()) + .current_dir("enclave") + .env("CC_x86_64_fortanix_unknown_sgx", "clang") + .env( + "CFLAGS_x86_64_fortanix_unknown_sgx", + "-D__ELF__ -isystem/usr/include/x86_64-linux-gnu -mlvi-hardening -mllvm -x86-experimental-lvi-inline-asm-hardening", + ) + .env("CXX_x86_64_fortanix_unknown_sgx", "clang++") + .env( + "CXXFLAGS_x86_64_fortanix_unknown_sgx", + "-D__ELF__ -isystem/usr/include/x86_64-linux-gnu -mlvi-hardening -mllvm -x86-experimental-lvi-inline-asm-hardening", + ) .run(); - set_current_dir(&main_dir); - // Rust has various ways of adding code to a binary: + + // Rust has several ways of including machine code into a binary: + // // - Rust code // - Inline assembly // - Global assembly // - C/C++ code compiled as part of Rust crates - // For those different kinds, we do have very small code examples that should be - // mitigated in some way. Mostly we check that ret instructions should no longer be present. + // + // For each of those, check that the mitigations are applied. Mostly we check + // that ret instructions are no longer present. + + // Check that normal rust code has the right mitigations. check("unw_getcontext", "unw_getcontext.checks"); check("__libunwind_Registers_x86_64_jumpto", "jumpto.checks"); check("std::io::stdio::_print::[[:alnum:]]+", "print.with_frame_pointers.checks"); + // Check that rust global assembly has the right mitigations. check("rust_plus_one_global_asm", "rust_plus_one_global_asm.checks"); + // Check that C code compiled using the `cc` crate has the right mitigations. check("cc_plus_one_c", "cc_plus_one_c.checks"); check("cc_plus_one_c_asm", "cc_plus_one_c_asm.checks"); check("cc_plus_one_cxx", "cc_plus_one_cxx.checks"); check("cc_plus_one_cxx_asm", "cc_plus_one_cxx_asm.checks"); check("cc_plus_one_asm", "cc_plus_one_asm.checks"); + // Check that C++ code compiled using the `cc` crate has the right mitigations. check("cmake_plus_one_c", "cmake_plus_one_c.checks"); check("cmake_plus_one_c_asm", "cmake_plus_one_c_asm.checks"); check("cmake_plus_one_c_global_asm", "cmake_plus_one_c_global_asm.checks"); @@ -71,8 +85,7 @@ fn check(func_re: &str, mut checks: &str) { .input("enclave/target/x86_64-fortanix-unknown-sgx/debug/enclave") .args(&["--demangle", &format!("--disassemble-symbols={func}")]) .run() - .stdout_utf8(); - let dump = dump.as_bytes(); + .stdout(); // Unique case, must succeed at one of two possible tests. // This is because frame pointers are optional, and them being enabled requires From 0e53d8533bf0bb8d6bc59bb000fe7cc7a361902e Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Mon, 16 Jun 2025 15:25:31 +0000 Subject: [PATCH 24/40] Fortify RemoveUnneededDrops test. --- ...annot_opt_generic.RemoveUnneededDrops.diff | 15 +++++++ ...neric.RemoveUnneededDrops.panic-abort.diff | 26 ------------ ...eric.RemoveUnneededDrops.panic-unwind.diff | 30 -------------- ...ed_drops.dont_opt.RemoveUnneededDrops.diff | 15 +++++++ ...t_opt.RemoveUnneededDrops.panic-abort.diff | 26 ------------ ..._opt.RemoveUnneededDrops.panic-unwind.diff | 30 -------------- ...nneeded_drops.opt.RemoveUnneededDrops.diff | 15 +++++++ ...s.opt.RemoveUnneededDrops.panic-abort.diff | 26 ------------ ....opt.RemoveUnneededDrops.panic-unwind.diff | 26 ------------ ....opt_generic_copy.RemoveUnneededDrops.diff | 15 +++++++ ..._copy.RemoveUnneededDrops.panic-abort.diff | 26 ------------ ...copy.RemoveUnneededDrops.panic-unwind.diff | 26 ------------ tests/mir-opt/remove_unneeded_drops.rs | 40 ++++++++++++++++--- 13 files changed, 94 insertions(+), 222 deletions(-) create mode 100644 tests/mir-opt/remove_unneeded_drops.cannot_opt_generic.RemoveUnneededDrops.diff delete mode 100644 tests/mir-opt/remove_unneeded_drops.cannot_opt_generic.RemoveUnneededDrops.panic-abort.diff delete mode 100644 tests/mir-opt/remove_unneeded_drops.cannot_opt_generic.RemoveUnneededDrops.panic-unwind.diff create mode 100644 tests/mir-opt/remove_unneeded_drops.dont_opt.RemoveUnneededDrops.diff delete mode 100644 tests/mir-opt/remove_unneeded_drops.dont_opt.RemoveUnneededDrops.panic-abort.diff delete mode 100644 tests/mir-opt/remove_unneeded_drops.dont_opt.RemoveUnneededDrops.panic-unwind.diff create mode 100644 tests/mir-opt/remove_unneeded_drops.opt.RemoveUnneededDrops.diff delete mode 100644 tests/mir-opt/remove_unneeded_drops.opt.RemoveUnneededDrops.panic-abort.diff delete mode 100644 tests/mir-opt/remove_unneeded_drops.opt.RemoveUnneededDrops.panic-unwind.diff create mode 100644 tests/mir-opt/remove_unneeded_drops.opt_generic_copy.RemoveUnneededDrops.diff delete mode 100644 tests/mir-opt/remove_unneeded_drops.opt_generic_copy.RemoveUnneededDrops.panic-abort.diff delete mode 100644 tests/mir-opt/remove_unneeded_drops.opt_generic_copy.RemoveUnneededDrops.panic-unwind.diff diff --git a/tests/mir-opt/remove_unneeded_drops.cannot_opt_generic.RemoveUnneededDrops.diff b/tests/mir-opt/remove_unneeded_drops.cannot_opt_generic.RemoveUnneededDrops.diff new file mode 100644 index 0000000000000..52832e739051d --- /dev/null +++ b/tests/mir-opt/remove_unneeded_drops.cannot_opt_generic.RemoveUnneededDrops.diff @@ -0,0 +1,15 @@ +- // MIR for `cannot_opt_generic` before RemoveUnneededDrops ++ // MIR for `cannot_opt_generic` after RemoveUnneededDrops + + fn cannot_opt_generic(_1: T) -> () { + let mut _0: (); + + bb0: { + drop(_1) -> [return: bb1, unwind unreachable]; + } + + bb1: { + return; + } + } + diff --git a/tests/mir-opt/remove_unneeded_drops.cannot_opt_generic.RemoveUnneededDrops.panic-abort.diff b/tests/mir-opt/remove_unneeded_drops.cannot_opt_generic.RemoveUnneededDrops.panic-abort.diff deleted file mode 100644 index 0c73602bec84b..0000000000000 --- a/tests/mir-opt/remove_unneeded_drops.cannot_opt_generic.RemoveUnneededDrops.panic-abort.diff +++ /dev/null @@ -1,26 +0,0 @@ -- // MIR for `cannot_opt_generic` before RemoveUnneededDrops -+ // MIR for `cannot_opt_generic` after RemoveUnneededDrops - - fn cannot_opt_generic(_1: T) -> () { - debug x => _1; - let mut _0: (); - let _2: (); - let mut _3: T; - scope 1 (inlined std::mem::drop::) { - } - - bb0: { - nop; - StorageLive(_3); - _3 = move _1; - drop(_3) -> [return: bb1, unwind unreachable]; - } - - bb1: { - StorageDead(_3); - nop; - nop; - return; - } - } - diff --git a/tests/mir-opt/remove_unneeded_drops.cannot_opt_generic.RemoveUnneededDrops.panic-unwind.diff b/tests/mir-opt/remove_unneeded_drops.cannot_opt_generic.RemoveUnneededDrops.panic-unwind.diff deleted file mode 100644 index 59cce9fbcdd0a..0000000000000 --- a/tests/mir-opt/remove_unneeded_drops.cannot_opt_generic.RemoveUnneededDrops.panic-unwind.diff +++ /dev/null @@ -1,30 +0,0 @@ -- // MIR for `cannot_opt_generic` before RemoveUnneededDrops -+ // MIR for `cannot_opt_generic` after RemoveUnneededDrops - - fn cannot_opt_generic(_1: T) -> () { - debug x => _1; - let mut _0: (); - let _2: (); - let mut _3: T; - scope 1 (inlined std::mem::drop::) { - } - - bb0: { - nop; - StorageLive(_3); - _3 = move _1; - drop(_3) -> [return: bb2, unwind: bb1]; - } - - bb1 (cleanup): { - resume; - } - - bb2: { - StorageDead(_3); - nop; - nop; - return; - } - } - diff --git a/tests/mir-opt/remove_unneeded_drops.dont_opt.RemoveUnneededDrops.diff b/tests/mir-opt/remove_unneeded_drops.dont_opt.RemoveUnneededDrops.diff new file mode 100644 index 0000000000000..3d67cada5ddfd --- /dev/null +++ b/tests/mir-opt/remove_unneeded_drops.dont_opt.RemoveUnneededDrops.diff @@ -0,0 +1,15 @@ +- // MIR for `dont_opt` before RemoveUnneededDrops ++ // MIR for `dont_opt` after RemoveUnneededDrops + + fn dont_opt(_1: Vec) -> () { + let mut _0: (); + + bb0: { + drop(_1) -> [return: bb1, unwind unreachable]; + } + + bb1: { + return; + } + } + diff --git a/tests/mir-opt/remove_unneeded_drops.dont_opt.RemoveUnneededDrops.panic-abort.diff b/tests/mir-opt/remove_unneeded_drops.dont_opt.RemoveUnneededDrops.panic-abort.diff deleted file mode 100644 index 428b366b5a684..0000000000000 --- a/tests/mir-opt/remove_unneeded_drops.dont_opt.RemoveUnneededDrops.panic-abort.diff +++ /dev/null @@ -1,26 +0,0 @@ -- // MIR for `dont_opt` before RemoveUnneededDrops -+ // MIR for `dont_opt` after RemoveUnneededDrops - - fn dont_opt(_1: Vec) -> () { - debug x => _1; - let mut _0: (); - let _2: (); - let mut _3: std::vec::Vec; - scope 1 (inlined std::mem::drop::>) { - } - - bb0: { - nop; - StorageLive(_3); - _3 = move _1; - drop(_3) -> [return: bb1, unwind unreachable]; - } - - bb1: { - StorageDead(_3); - nop; - nop; - return; - } - } - diff --git a/tests/mir-opt/remove_unneeded_drops.dont_opt.RemoveUnneededDrops.panic-unwind.diff b/tests/mir-opt/remove_unneeded_drops.dont_opt.RemoveUnneededDrops.panic-unwind.diff deleted file mode 100644 index 445c1f82a96f8..0000000000000 --- a/tests/mir-opt/remove_unneeded_drops.dont_opt.RemoveUnneededDrops.panic-unwind.diff +++ /dev/null @@ -1,30 +0,0 @@ -- // MIR for `dont_opt` before RemoveUnneededDrops -+ // MIR for `dont_opt` after RemoveUnneededDrops - - fn dont_opt(_1: Vec) -> () { - debug x => _1; - let mut _0: (); - let _2: (); - let mut _3: std::vec::Vec; - scope 1 (inlined std::mem::drop::>) { - } - - bb0: { - nop; - StorageLive(_3); - _3 = move _1; - drop(_3) -> [return: bb2, unwind: bb1]; - } - - bb1 (cleanup): { - resume; - } - - bb2: { - StorageDead(_3); - nop; - nop; - return; - } - } - diff --git a/tests/mir-opt/remove_unneeded_drops.opt.RemoveUnneededDrops.diff b/tests/mir-opt/remove_unneeded_drops.opt.RemoveUnneededDrops.diff new file mode 100644 index 0000000000000..cb7e58ca1a1f9 --- /dev/null +++ b/tests/mir-opt/remove_unneeded_drops.opt.RemoveUnneededDrops.diff @@ -0,0 +1,15 @@ +- // MIR for `opt` before RemoveUnneededDrops ++ // MIR for `opt` after RemoveUnneededDrops + + fn opt(_1: bool) -> () { + let mut _0: (); + + bb0: { +- drop(_1) -> [return: bb1, unwind unreachable]; +- } +- +- bb1: { + return; + } + } + diff --git a/tests/mir-opt/remove_unneeded_drops.opt.RemoveUnneededDrops.panic-abort.diff b/tests/mir-opt/remove_unneeded_drops.opt.RemoveUnneededDrops.panic-abort.diff deleted file mode 100644 index 01eb6d4901f76..0000000000000 --- a/tests/mir-opt/remove_unneeded_drops.opt.RemoveUnneededDrops.panic-abort.diff +++ /dev/null @@ -1,26 +0,0 @@ -- // MIR for `opt` before RemoveUnneededDrops -+ // MIR for `opt` after RemoveUnneededDrops - - fn opt(_1: bool) -> () { - debug x => _1; - let mut _0: (); - let _2: (); - let mut _3: bool; - scope 1 (inlined std::mem::drop::) { - } - - bb0: { -- nop; - StorageLive(_3); - _3 = copy _1; -- drop(_3) -> [return: bb1, unwind unreachable]; -- } -- -- bb1: { - StorageDead(_3); -- nop; -- nop; - return; - } - } - diff --git a/tests/mir-opt/remove_unneeded_drops.opt.RemoveUnneededDrops.panic-unwind.diff b/tests/mir-opt/remove_unneeded_drops.opt.RemoveUnneededDrops.panic-unwind.diff deleted file mode 100644 index c2c3cb76e8328..0000000000000 --- a/tests/mir-opt/remove_unneeded_drops.opt.RemoveUnneededDrops.panic-unwind.diff +++ /dev/null @@ -1,26 +0,0 @@ -- // MIR for `opt` before RemoveUnneededDrops -+ // MIR for `opt` after RemoveUnneededDrops - - fn opt(_1: bool) -> () { - debug x => _1; - let mut _0: (); - let _2: (); - let mut _3: bool; - scope 1 (inlined std::mem::drop::) { - } - - bb0: { -- nop; - StorageLive(_3); - _3 = copy _1; -- drop(_3) -> [return: bb1, unwind continue]; -- } -- -- bb1: { - StorageDead(_3); -- nop; -- nop; - return; - } - } - diff --git a/tests/mir-opt/remove_unneeded_drops.opt_generic_copy.RemoveUnneededDrops.diff b/tests/mir-opt/remove_unneeded_drops.opt_generic_copy.RemoveUnneededDrops.diff new file mode 100644 index 0000000000000..1e166eee9fb07 --- /dev/null +++ b/tests/mir-opt/remove_unneeded_drops.opt_generic_copy.RemoveUnneededDrops.diff @@ -0,0 +1,15 @@ +- // MIR for `opt_generic_copy` before RemoveUnneededDrops ++ // MIR for `opt_generic_copy` after RemoveUnneededDrops + + fn opt_generic_copy(_1: T) -> () { + let mut _0: (); + + bb0: { +- drop(_1) -> [return: bb1, unwind unreachable]; +- } +- +- bb1: { + return; + } + } + diff --git a/tests/mir-opt/remove_unneeded_drops.opt_generic_copy.RemoveUnneededDrops.panic-abort.diff b/tests/mir-opt/remove_unneeded_drops.opt_generic_copy.RemoveUnneededDrops.panic-abort.diff deleted file mode 100644 index a82ede6196ed3..0000000000000 --- a/tests/mir-opt/remove_unneeded_drops.opt_generic_copy.RemoveUnneededDrops.panic-abort.diff +++ /dev/null @@ -1,26 +0,0 @@ -- // MIR for `opt_generic_copy` before RemoveUnneededDrops -+ // MIR for `opt_generic_copy` after RemoveUnneededDrops - - fn opt_generic_copy(_1: T) -> () { - debug x => _1; - let mut _0: (); - let _2: (); - let mut _3: T; - scope 1 (inlined std::mem::drop::) { - } - - bb0: { -- nop; - StorageLive(_3); - _3 = copy _1; -- drop(_3) -> [return: bb1, unwind unreachable]; -- } -- -- bb1: { - StorageDead(_3); -- nop; -- nop; - return; - } - } - diff --git a/tests/mir-opt/remove_unneeded_drops.opt_generic_copy.RemoveUnneededDrops.panic-unwind.diff b/tests/mir-opt/remove_unneeded_drops.opt_generic_copy.RemoveUnneededDrops.panic-unwind.diff deleted file mode 100644 index 6e7c9ead740f0..0000000000000 --- a/tests/mir-opt/remove_unneeded_drops.opt_generic_copy.RemoveUnneededDrops.panic-unwind.diff +++ /dev/null @@ -1,26 +0,0 @@ -- // MIR for `opt_generic_copy` before RemoveUnneededDrops -+ // MIR for `opt_generic_copy` after RemoveUnneededDrops - - fn opt_generic_copy(_1: T) -> () { - debug x => _1; - let mut _0: (); - let _2: (); - let mut _3: T; - scope 1 (inlined std::mem::drop::) { - } - - bb0: { -- nop; - StorageLive(_3); - _3 = copy _1; -- drop(_3) -> [return: bb1, unwind continue]; -- } -- -- bb1: { - StorageDead(_3); -- nop; -- nop; - return; - } - } - diff --git a/tests/mir-opt/remove_unneeded_drops.rs b/tests/mir-opt/remove_unneeded_drops.rs index cad79e0aa0cbc..49dc611838e38 100644 --- a/tests/mir-opt/remove_unneeded_drops.rs +++ b/tests/mir-opt/remove_unneeded_drops.rs @@ -1,28 +1,56 @@ -// skip-filecheck -// EMIT_MIR_FOR_EACH_PANIC_STRATEGY +//@ test-mir-pass: RemoveUnneededDrops + +#![feature(custom_mir, core_intrinsics)] +use std::intrinsics::mir::*; + // EMIT_MIR remove_unneeded_drops.opt.RemoveUnneededDrops.diff +#[custom_mir(dialect = "runtime")] fn opt(x: bool) { - drop(x); + // CHECK-LABEL: fn opt( + // CHECK-NOT: drop( + mir! { + { Drop(x, ReturnTo(bb1), UnwindUnreachable()) } + bb1 = { Return() } + } } // EMIT_MIR remove_unneeded_drops.dont_opt.RemoveUnneededDrops.diff +#[custom_mir(dialect = "runtime")] fn dont_opt(x: Vec) { - drop(x); + // CHECK-LABEL: fn dont_opt( + // CHECK: drop( + mir! { + { Drop(x, ReturnTo(bb1), UnwindUnreachable()) } + bb1 = { Return() } + } } // EMIT_MIR remove_unneeded_drops.opt_generic_copy.RemoveUnneededDrops.diff +#[custom_mir(dialect = "runtime")] fn opt_generic_copy(x: T) { - drop(x); + // CHECK-LABEL: fn opt_generic_copy( + // CHECK-NOT: drop( + mir! { + { Drop(x, ReturnTo(bb1), UnwindUnreachable()) } + bb1 = { Return() } + } } // EMIT_MIR remove_unneeded_drops.cannot_opt_generic.RemoveUnneededDrops.diff // since the pass is not running on monomorphisized code, // we can't (but probably should) optimize this +#[custom_mir(dialect = "runtime")] fn cannot_opt_generic(x: T) { - drop(x); + // CHECK-LABEL: fn cannot_opt_generic( + // CHECK: drop( + mir! { + { Drop(x, ReturnTo(bb1), UnwindUnreachable()) } + bb1 = { Return() } + } } fn main() { + // CHECK-LABEL: fn main( opt(true); opt_generic_copy(42); cannot_opt_generic(42); From ee0118f8a1dbfcad06b8c09644553a3fc3e070d4 Mon Sep 17 00:00:00 2001 From: David Tenty Date: Wed, 30 Jul 2025 16:45:17 -0400 Subject: [PATCH 25/40] [test][AIX] ignore extern_weak linkage test The AIX linkage model doesn't support ELF style extern_weak semantic, so just skip this test, like other platforms that don't have it. --- tests/ui/linkage-attr/propagate-generic-issue-18804/main.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/ui/linkage-attr/propagate-generic-issue-18804/main.rs b/tests/ui/linkage-attr/propagate-generic-issue-18804/main.rs index 8c194ec50df4b..ad7b067447811 100644 --- a/tests/ui/linkage-attr/propagate-generic-issue-18804/main.rs +++ b/tests/ui/linkage-attr/propagate-generic-issue-18804/main.rs @@ -4,6 +4,7 @@ //@ ignore-emscripten no weak symbol support //@ ignore-apple no extern_weak linkage +//@ ignore-aix no extern_weak linkage //@ aux-build:lib.rs From 5aec4379e304ef280ee9a470e7ab121e4a81fd17 Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Thu, 31 Jul 2025 23:59:06 +0200 Subject: [PATCH 26/40] detect infinite recursion with tail calls in ctfe --- compiler/rustc_mir_transform/src/ctfe_limit.rs | 2 +- .../infinite-recursion-in-ctfe.rs | 10 ++++++++++ .../infinite-recursion-in-ctfe.stderr | 17 +++++++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 tests/ui/explicit-tail-calls/infinite-recursion-in-ctfe.rs create mode 100644 tests/ui/explicit-tail-calls/infinite-recursion-in-ctfe.stderr diff --git a/compiler/rustc_mir_transform/src/ctfe_limit.rs b/compiler/rustc_mir_transform/src/ctfe_limit.rs index fb17cca30f4a5..ac46336b83473 100644 --- a/compiler/rustc_mir_transform/src/ctfe_limit.rs +++ b/compiler/rustc_mir_transform/src/ctfe_limit.rs @@ -18,7 +18,7 @@ impl<'tcx> crate::MirPass<'tcx> for CtfeLimit { .basic_blocks .iter_enumerated() .filter_map(|(node, node_data)| { - if matches!(node_data.terminator().kind, TerminatorKind::Call { .. }) + if matches!(node_data.terminator().kind, TerminatorKind::Call { .. } | TerminatorKind::TailCall { .. }) // Back edges in a CFG indicate loops || has_back_edge(doms, node, node_data) { diff --git a/tests/ui/explicit-tail-calls/infinite-recursion-in-ctfe.rs b/tests/ui/explicit-tail-calls/infinite-recursion-in-ctfe.rs new file mode 100644 index 0000000000000..0c55f13c16c22 --- /dev/null +++ b/tests/ui/explicit-tail-calls/infinite-recursion-in-ctfe.rs @@ -0,0 +1,10 @@ +#![feature(explicit_tail_calls)] +#![expect(incomplete_features)] + +const _: () = f(); + +const fn f() { + become f(); //~ error: constant evaluation is taking a long time +} + +fn main() {} diff --git a/tests/ui/explicit-tail-calls/infinite-recursion-in-ctfe.stderr b/tests/ui/explicit-tail-calls/infinite-recursion-in-ctfe.stderr new file mode 100644 index 0000000000000..b5a96114a5837 --- /dev/null +++ b/tests/ui/explicit-tail-calls/infinite-recursion-in-ctfe.stderr @@ -0,0 +1,17 @@ +error: constant evaluation is taking a long time + --> $DIR/infinite-recursion-in-ctfe.rs:7:5 + | +LL | become f(); + | ^^^^^^^^^^ + | + = note: this lint makes sure the compiler doesn't get stuck due to infinite loops in const eval. + If your compilation actually takes a long time, you can safely allow the lint. +help: the constant being evaluated + --> $DIR/infinite-recursion-in-ctfe.rs:4:1 + | +LL | const _: () = f(); + | ^^^^^^^^^^^ + = note: `#[deny(long_running_const_eval)]` on by default + +error: aborting due to 1 previous error + From 040f71e8123b5994177f787e64aecd9b06cdfa7e Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Fri, 25 Jul 2025 15:18:51 +0200 Subject: [PATCH 27/40] loop match: error on `#[const_continue]` outside `#[loop_match]` --- compiler/rustc_hir/src/hir.rs | 2 +- compiler/rustc_hir_typeck/src/loops.rs | 50 +++++++++++-------- compiler/rustc_mir_build/messages.ftl | 2 +- compiler/rustc_mir_build/src/errors.rs | 4 +- compiler/rustc_mir_build/src/thir/cx/expr.rs | 4 +- .../ui/loop-match/const-continue-to-block.rs | 21 ++++++++ .../loop-match/const-continue-to-block.stderr | 17 ++++++- tests/ui/loop-match/invalid.rs | 19 +++++++ tests/ui/loop-match/invalid.stderr | 22 ++++++-- 9 files changed, 107 insertions(+), 34 deletions(-) diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 1b1b3ced44db7..08361718108bf 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -3016,7 +3016,7 @@ impl fmt::Display for LoopIdError { } } -#[derive(Copy, Clone, Debug, HashStable_Generic)] +#[derive(Copy, Clone, Debug, PartialEq, HashStable_Generic)] pub struct Destination { /// This is `Some(_)` iff there is an explicit user-specified 'label pub label: Option