From 5dd54fdfc48ecac5646c8ffc5a5a5321f0733f65 Mon Sep 17 00:00:00 2001 From: David Wood Date: Wed, 1 Mar 2023 15:52:29 +0000 Subject: [PATCH 01/11] lint/ctypes: ext. abi fn-ptr in internal abi fn Instead of skipping functions with internal ABIs, check that the signature doesn't contain any fn-ptr types with external ABIs that aren't FFI-safe. Signed-off-by: David Wood --- compiler/rustc_lint/src/types.rs | 75 +++++++++++++++---- tests/ui/abi/foreign/foreign-fn-with-byval.rs | 2 +- tests/ui/lint/lint-ctypes-94223.rs | 5 ++ tests/ui/lint/lint-ctypes-94223.stderr | 16 ++++ 4 files changed, 81 insertions(+), 17 deletions(-) create mode 100644 tests/ui/lint/lint-ctypes-94223.rs create mode 100644 tests/ui/lint/lint-ctypes-94223.stderr diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 85958c4170569..702d8c0cea334 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -1230,17 +1230,40 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } } - fn check_foreign_fn(&mut self, def_id: LocalDefId, decl: &hir::FnDecl<'_>) { + /// Check if a function's argument types and result type are "ffi-safe". + /// + /// Argument types and the result type are checked for functions with external ABIs. + /// For functions with internal ABIs, argument types and the result type are walked to find + /// fn-ptr types that have external ABIs, as these still need checked. + fn check_maybe_foreign_fn(&mut self, abi: SpecAbi, def_id: LocalDefId, decl: &hir::FnDecl<'_>) { let sig = self.cx.tcx.fn_sig(def_id).subst_identity(); let sig = self.cx.tcx.erase_late_bound_regions(sig); + let is_internal_abi = self.is_internal_abi(abi); + let check_ty = |this: &mut ImproperCTypesVisitor<'a, 'tcx>, + span: Span, + ty: Ty<'tcx>, + is_return_type: bool| { + // If this function has an external ABI, then its arguments and return type should be + // checked.. + if !is_internal_abi { + this.check_type_for_ffi_and_report_errors(span, ty, false, is_return_type); + return; + } + + // ..but if this function has an internal ABI, then search the argument or return type + // for any fn-ptr types with external ABI, which should be checked.. + if let Some(fn_ptr_ty) = this.find_fn_ptr_ty_with_external_abi(ty) { + this.check_type_for_ffi_and_report_errors(span, fn_ptr_ty, false, is_return_type); + } + }; + for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) { - self.check_type_for_ffi_and_report_errors(input_hir.span, *input_ty, false, false); + check_ty(self, input_hir.span, *input_ty, false); } if let hir::FnRetTy::Return(ref ret_hir) = decl.output { - let ret_ty = sig.output(); - self.check_type_for_ffi_and_report_errors(ret_hir.span, ret_ty, false, true); + check_ty(self, ret_hir.span, sig.output(), true); } } @@ -1255,6 +1278,30 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { SpecAbi::Rust | SpecAbi::RustCall | SpecAbi::RustIntrinsic | SpecAbi::PlatformIntrinsic ) } + + /// Find any fn-ptr types with external ABIs in `ty`. + /// + /// For example, `Option` returns `extern "C" fn()` + fn find_fn_ptr_ty_with_external_abi(&self, ty: Ty<'tcx>) -> Option> { + struct FnPtrFinder<'parent, 'a, 'tcx>(&'parent ImproperCTypesVisitor<'a, 'tcx>); + impl<'vis, 'a, 'tcx> ty::visit::TypeVisitor> for FnPtrFinder<'vis, 'a, 'tcx> { + type BreakTy = Ty<'tcx>; + + fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow { + if let ty::FnPtr(sig) = ty.kind() && !self.0.is_internal_abi(sig.abi()) { + ControlFlow::Break(ty) + } else { + ty.super_visit_with(self) + } + } + } + + self.cx + .tcx + .normalize_erasing_regions(self.cx.param_env, ty) + .visit_with(&mut FnPtrFinder(&*self)) + .break_value() + } } impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDeclarations { @@ -1262,16 +1309,14 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDeclarations { let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Declaration }; let abi = cx.tcx.hir().get_foreign_abi(it.hir_id()); - if !vis.is_internal_abi(abi) { - match it.kind { - hir::ForeignItemKind::Fn(ref decl, _, _) => { - vis.check_foreign_fn(it.owner_id.def_id, decl); - } - hir::ForeignItemKind::Static(ref ty, _) => { - vis.check_foreign_static(it.owner_id, ty.span); - } - hir::ForeignItemKind::Type => (), + match it.kind { + hir::ForeignItemKind::Fn(ref decl, _, _) => { + vis.check_maybe_foreign_fn(abi, it.owner_id.def_id, decl); + } + hir::ForeignItemKind::Static(ref ty, _) if !vis.is_internal_abi(abi) => { + vis.check_foreign_static(it.owner_id, ty.span); } + hir::ForeignItemKind::Static(..) | hir::ForeignItemKind::Type => (), } } } @@ -1295,9 +1340,7 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDefinitions { }; let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Definition }; - if !vis.is_internal_abi(abi) { - vis.check_foreign_fn(id, decl); - } + vis.check_maybe_foreign_fn(abi, id, decl); } } diff --git a/tests/ui/abi/foreign/foreign-fn-with-byval.rs b/tests/ui/abi/foreign/foreign-fn-with-byval.rs index f366b6ee1bdd5..e20ee0da45d9d 100644 --- a/tests/ui/abi/foreign/foreign-fn-with-byval.rs +++ b/tests/ui/abi/foreign/foreign-fn-with-byval.rs @@ -1,5 +1,5 @@ // run-pass -#![allow(improper_ctypes)] +#![allow(improper_ctypes, improper_ctypes_definitions)] // ignore-wasm32-bare no libc to test ffi with diff --git a/tests/ui/lint/lint-ctypes-94223.rs b/tests/ui/lint/lint-ctypes-94223.rs new file mode 100644 index 0000000000000..ca7f3b73f66c5 --- /dev/null +++ b/tests/ui/lint/lint-ctypes-94223.rs @@ -0,0 +1,5 @@ +#![crate_type = "lib"] +#![deny(improper_ctypes_definitions)] + +pub fn bad(f: extern "C" fn([u8])) {} +//~^ ERROR `extern` fn uses type `[u8]`, which is not FFI-safe diff --git a/tests/ui/lint/lint-ctypes-94223.stderr b/tests/ui/lint/lint-ctypes-94223.stderr new file mode 100644 index 0000000000000..f8afed34ea458 --- /dev/null +++ b/tests/ui/lint/lint-ctypes-94223.stderr @@ -0,0 +1,16 @@ +error: `extern` fn uses type `[u8]`, which is not FFI-safe + --> $DIR/lint-ctypes-94223.rs:4:15 + | +LL | pub fn bad(f: extern "C" fn([u8])) {} + | ^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider using a raw pointer instead + = note: slices have no C equivalent +note: the lint level is defined here + --> $DIR/lint-ctypes-94223.rs:2:9 + | +LL | #![deny(improper_ctypes_definitions)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + From c1dcf2649e8a11b1990e999f3edf762c101d2b97 Mon Sep 17 00:00:00 2001 From: David Wood Date: Wed, 1 Mar 2023 15:55:09 +0000 Subject: [PATCH 02/11] abi: avoid ice for non-ffi-safe fn ptrs Remove an `unwrap` that assumed FFI-safe types in foreign fn-ptr types. Signed-off-by: David Wood --- compiler/rustc_target/src/abi/call/x86_64.rs | 10 ++++++---- tests/ui/abi/issue-94223.rs | 8 ++++++++ 2 files changed, 14 insertions(+), 4 deletions(-) create mode 100644 tests/ui/abi/issue-94223.rs diff --git a/compiler/rustc_target/src/abi/call/x86_64.rs b/compiler/rustc_target/src/abi/call/x86_64.rs index 9427f27d1b7bb..2470cdfebe225 100644 --- a/compiler/rustc_target/src/abi/call/x86_64.rs +++ b/compiler/rustc_target/src/abi/call/x86_64.rs @@ -153,9 +153,9 @@ fn reg_component(cls: &[Option], i: &mut usize, size: Size) -> Option], size: Size) -> CastTarget { +fn cast_target(cls: &[Option], size: Size) -> Option { let mut i = 0; - let lo = reg_component(cls, &mut i, size).unwrap(); + let lo = reg_component(cls, &mut i, size)?; let offset = Size::from_bytes(8) * (i as u64); let mut target = CastTarget::from(lo); if size > offset { @@ -164,7 +164,7 @@ fn cast_target(cls: &[Option], size: Size) -> CastTarget { } } assert_eq!(reg_component(cls, &mut i, Size::ZERO), None); - target + Some(target) } const MAX_INT_REGS: usize = 6; // RDI, RSI, RDX, RCX, R8, R9 @@ -227,7 +227,9 @@ where // split into sized chunks passed individually if arg.layout.is_aggregate() { let size = arg.layout.size; - arg.cast_to(cast_target(cls, size)) + if let Some(cast_target) = cast_target(cls, size) { + arg.cast_to(cast_target); + } } else { arg.extend_integer_width_to(32); } diff --git a/tests/ui/abi/issue-94223.rs b/tests/ui/abi/issue-94223.rs new file mode 100644 index 0000000000000..79d6b94031bc2 --- /dev/null +++ b/tests/ui/abi/issue-94223.rs @@ -0,0 +1,8 @@ +// check-pass +#![allow(improper_ctypes_definitions)] +#![crate_type = "lib"] + +// Check that computing the fn abi for `bad`, with a external ABI fn ptr that is not FFI-safe, does +// not ICE. + +pub fn bad(f: extern "C" fn([u8])) {} From 92ae35f53d73bcd006a99f22e7103f03ebdccc2e Mon Sep 17 00:00:00 2001 From: David Wood Date: Thu, 2 Mar 2023 11:23:44 +0000 Subject: [PATCH 03/11] lint/ctypes: multiple external fn-ptrs in ty Extend previous commit's support for checking for external fn-ptrs in internal fn types to report errors for multiple found fn-ptrs. Signed-off-by: David Wood --- compiler/rustc_lint/src/types.rs | 63 ++++++++++++++++++-------- tests/ui/lint/lint-ctypes-94223.rs | 4 ++ tests/ui/lint/lint-ctypes-94223.stderr | 20 +++++++- 3 files changed, 68 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 702d8c0cea334..62d56931426ef 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -1235,35 +1235,40 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { /// Argument types and the result type are checked for functions with external ABIs. /// For functions with internal ABIs, argument types and the result type are walked to find /// fn-ptr types that have external ABIs, as these still need checked. - fn check_maybe_foreign_fn(&mut self, abi: SpecAbi, def_id: LocalDefId, decl: &hir::FnDecl<'_>) { + fn check_maybe_foreign_fn( + &mut self, + abi: SpecAbi, + def_id: LocalDefId, + decl: &'tcx hir::FnDecl<'_>, + ) { let sig = self.cx.tcx.fn_sig(def_id).subst_identity(); let sig = self.cx.tcx.erase_late_bound_regions(sig); let is_internal_abi = self.is_internal_abi(abi); let check_ty = |this: &mut ImproperCTypesVisitor<'a, 'tcx>, - span: Span, + hir_ty: &'tcx hir::Ty<'_>, ty: Ty<'tcx>, is_return_type: bool| { // If this function has an external ABI, then its arguments and return type should be // checked.. if !is_internal_abi { - this.check_type_for_ffi_and_report_errors(span, ty, false, is_return_type); + this.check_type_for_ffi_and_report_errors(hir_ty.span, ty, false, is_return_type); return; } // ..but if this function has an internal ABI, then search the argument or return type // for any fn-ptr types with external ABI, which should be checked.. - if let Some(fn_ptr_ty) = this.find_fn_ptr_ty_with_external_abi(ty) { + for (fn_ptr_ty, span) in this.find_fn_ptr_ty_with_external_abi(hir_ty, ty) { this.check_type_for_ffi_and_report_errors(span, fn_ptr_ty, false, is_return_type); } }; for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) { - check_ty(self, input_hir.span, *input_ty, false); + check_ty(self, input_hir, *input_ty, false); } if let hir::FnRetTy::Return(ref ret_hir) = decl.output { - check_ty(self, ret_hir.span, sig.output(), true); + check_ty(self, ret_hir, sig.output(), true); } } @@ -1282,30 +1287,52 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { /// Find any fn-ptr types with external ABIs in `ty`. /// /// For example, `Option` returns `extern "C" fn()` - fn find_fn_ptr_ty_with_external_abi(&self, ty: Ty<'tcx>) -> Option> { - struct FnPtrFinder<'parent, 'a, 'tcx>(&'parent ImproperCTypesVisitor<'a, 'tcx>); + fn find_fn_ptr_ty_with_external_abi( + &self, + hir_ty: &hir::Ty<'tcx>, + ty: Ty<'tcx>, + ) -> Vec<(Ty<'tcx>, Span)> { + struct FnPtrFinder<'parent, 'a, 'tcx> { + visitor: &'parent ImproperCTypesVisitor<'a, 'tcx>, + spans: Vec, + tys: Vec>, + } + + impl<'parent, 'a, 'tcx> hir::intravisit::Visitor<'_> for FnPtrFinder<'parent, 'a, 'tcx> { + fn visit_ty(&mut self, ty: &'_ hir::Ty<'_>) { + debug!(?ty); + if let hir::TyKind::BareFn(hir::BareFnTy { abi, .. }) = ty.kind + && !self.visitor.is_internal_abi(*abi) + { + self.spans.push(ty.span); + } + + hir::intravisit::walk_ty(self, ty) + } + } + impl<'vis, 'a, 'tcx> ty::visit::TypeVisitor> for FnPtrFinder<'vis, 'a, 'tcx> { type BreakTy = Ty<'tcx>; fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow { - if let ty::FnPtr(sig) = ty.kind() && !self.0.is_internal_abi(sig.abi()) { - ControlFlow::Break(ty) - } else { - ty.super_visit_with(self) + if let ty::FnPtr(sig) = ty.kind() && !self.visitor.is_internal_abi(sig.abi()) { + self.tys.push(ty); } + + ty.super_visit_with(self) } } - self.cx - .tcx - .normalize_erasing_regions(self.cx.param_env, ty) - .visit_with(&mut FnPtrFinder(&*self)) - .break_value() + let mut visitor = FnPtrFinder { visitor: &*self, spans: Vec::new(), tys: Vec::new() }; + self.cx.tcx.normalize_erasing_regions(self.cx.param_env, ty).visit_with(&mut visitor); + hir::intravisit::Visitor::visit_ty(&mut visitor, hir_ty); + + iter::zip(visitor.tys.drain(..), visitor.spans.drain(..)).collect() } } impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDeclarations { - fn check_foreign_item(&mut self, cx: &LateContext<'_>, it: &hir::ForeignItem<'_>) { + fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, it: &hir::ForeignItem<'tcx>) { let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Declaration }; let abi = cx.tcx.hir().get_foreign_abi(it.hir_id()); diff --git a/tests/ui/lint/lint-ctypes-94223.rs b/tests/ui/lint/lint-ctypes-94223.rs index ca7f3b73f66c5..98ccbd23a9ed8 100644 --- a/tests/ui/lint/lint-ctypes-94223.rs +++ b/tests/ui/lint/lint-ctypes-94223.rs @@ -3,3 +3,7 @@ pub fn bad(f: extern "C" fn([u8])) {} //~^ ERROR `extern` fn uses type `[u8]`, which is not FFI-safe + +pub fn bad_twice(f: Result) {} +//~^ ERROR `extern` fn uses type `[u8]`, which is not FFI-safe +//~^^ ERROR `extern` fn uses type `[u8]`, which is not FFI-safe diff --git a/tests/ui/lint/lint-ctypes-94223.stderr b/tests/ui/lint/lint-ctypes-94223.stderr index f8afed34ea458..e05d6197cb426 100644 --- a/tests/ui/lint/lint-ctypes-94223.stderr +++ b/tests/ui/lint/lint-ctypes-94223.stderr @@ -12,5 +12,23 @@ note: the lint level is defined here LL | #![deny(improper_ctypes_definitions)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to previous error +error: `extern` fn uses type `[u8]`, which is not FFI-safe + --> $DIR/lint-ctypes-94223.rs:7:28 + | +LL | pub fn bad_twice(f: Result) {} + | ^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider using a raw pointer instead + = note: slices have no C equivalent + +error: `extern` fn uses type `[u8]`, which is not FFI-safe + --> $DIR/lint-ctypes-94223.rs:7:49 + | +LL | pub fn bad_twice(f: Result) {} + | ^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider using a raw pointer instead + = note: slices have no C equivalent + +error: aborting due to 3 previous errors From 3e7ca3e8518c9a2c8837a07a505cd85e82c01c81 Mon Sep 17 00:00:00 2001 From: David Wood Date: Thu, 2 Mar 2023 11:54:37 +0000 Subject: [PATCH 04/11] lint/ctypes: check other types for ext. fn-ptr ty Extend previous checks for external ABI fn-ptrs to use in internal statics, constants, type aliases and algebraic data types. Signed-off-by: David Wood --- compiler/rustc_lint/src/types.rs | 64 +++++++++++++++++- tests/ui/hashmap/hashmap-memory.rs | 1 + tests/ui/lint/lint-ctypes-94223.rs | 33 +++++++++ tests/ui/lint/lint-ctypes-94223.stderr | 94 +++++++++++++++++++++++++- 4 files changed, 190 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 62d56931426ef..defd5206bc025 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -1324,7 +1324,11 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } let mut visitor = FnPtrFinder { visitor: &*self, spans: Vec::new(), tys: Vec::new() }; - self.cx.tcx.normalize_erasing_regions(self.cx.param_env, ty).visit_with(&mut visitor); + self.cx + .tcx + .try_normalize_erasing_regions(self.cx.param_env, ty) + .unwrap_or(ty) + .visit_with(&mut visitor); hir::intravisit::Visitor::visit_ty(&mut visitor, hir_ty); iter::zip(visitor.tys.drain(..), visitor.spans.drain(..)).collect() @@ -1348,7 +1352,65 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDeclarations { } } +impl ImproperCTypesDefinitions { + fn check_ty_maybe_containing_foreign_fnptr<'tcx>( + &mut self, + cx: &LateContext<'tcx>, + hir_ty: &'tcx hir::Ty<'_>, + ty: Ty<'tcx>, + ) { + let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Definition }; + for (fn_ptr_ty, span) in vis.find_fn_ptr_ty_with_external_abi(hir_ty, ty) { + vis.check_type_for_ffi_and_report_errors(span, fn_ptr_ty, true, false); + } + } +} + +/// `ImproperCTypesDefinitions` checks items outside of foreign items (e.g. stuff that isn't in +/// `extern "C" { }` blocks): +/// +/// - `extern "" fn` definitions are checked in the same way as the +/// `ImproperCtypesDeclarations` visitor checks functions if `` is external (e.g. "C"). +/// - All other items which contain types (e.g. other functions, struct definitions, etc) are +/// checked for extern fn-ptrs with external ABIs. impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDefinitions { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { + match item.kind { + hir::ItemKind::Static(ty, ..) + | hir::ItemKind::Const(ty, ..) + | hir::ItemKind::TyAlias(ty, ..) => { + self.check_ty_maybe_containing_foreign_fnptr( + cx, + ty, + cx.tcx.type_of(item.owner_id).subst_identity(), + ); + } + // See `check_fn`.. + hir::ItemKind::Fn(..) => {} + // See `check_field_def`.. + hir::ItemKind::Union(..) | hir::ItemKind::Struct(..) | hir::ItemKind::Enum(..) => {} + // Doesn't define something that can contain a external type to be checked. + hir::ItemKind::Impl(..) + | hir::ItemKind::TraitAlias(..) + | hir::ItemKind::Trait(..) + | hir::ItemKind::OpaqueTy(..) + | hir::ItemKind::GlobalAsm(..) + | hir::ItemKind::ForeignMod { .. } + | hir::ItemKind::Mod(..) + | hir::ItemKind::Macro(..) + | hir::ItemKind::Use(..) + | hir::ItemKind::ExternCrate(..) => {} + } + } + + fn check_field_def(&mut self, cx: &LateContext<'tcx>, field: &'tcx hir::FieldDef<'tcx>) { + self.check_ty_maybe_containing_foreign_fnptr( + cx, + field.ty, + cx.tcx.type_of(field.def_id).subst_identity(), + ); + } + fn check_fn( &mut self, cx: &LateContext<'tcx>, diff --git a/tests/ui/hashmap/hashmap-memory.rs b/tests/ui/hashmap/hashmap-memory.rs index 87f8b6ad5730b..bd364b349e263 100644 --- a/tests/ui/hashmap/hashmap-memory.rs +++ b/tests/ui/hashmap/hashmap-memory.rs @@ -1,5 +1,6 @@ // run-pass +#![allow(improper_ctypes_definitions)] #![allow(non_camel_case_types)] #![allow(dead_code)] #![allow(unused_mut)] diff --git a/tests/ui/lint/lint-ctypes-94223.rs b/tests/ui/lint/lint-ctypes-94223.rs index 98ccbd23a9ed8..70dd2a71f2675 100644 --- a/tests/ui/lint/lint-ctypes-94223.rs +++ b/tests/ui/lint/lint-ctypes-94223.rs @@ -7,3 +7,36 @@ pub fn bad(f: extern "C" fn([u8])) {} pub fn bad_twice(f: Result) {} //~^ ERROR `extern` fn uses type `[u8]`, which is not FFI-safe //~^^ ERROR `extern` fn uses type `[u8]`, which is not FFI-safe + +struct BadStruct(extern "C" fn([u8])); +//~^ ERROR `extern` fn uses type `[u8]`, which is not FFI-safe + +enum BadEnum { + A(extern "C" fn([u8])), + //~^ ERROR `extern` fn uses type `[u8]`, which is not FFI-safe +} + +enum BadUnion { + A(extern "C" fn([u8])), + //~^ ERROR `extern` fn uses type `[u8]`, which is not FFI-safe +} + +type Foo = extern "C" fn([u8]); +//~^ ERROR `extern` fn uses type `[u8]`, which is not FFI-safe + +pub struct FfiUnsafe; + +#[allow(improper_ctypes_definitions)] +extern "C" fn f(_: FfiUnsafe) { + unimplemented!() +} + +pub static BAD: extern "C" fn(FfiUnsafe) = f; +//~^ ERROR `extern` fn uses type `FfiUnsafe`, which is not FFI-safe + +pub static BAD_TWICE: Result = Ok(f); +//~^ ERROR `extern` fn uses type `FfiUnsafe`, which is not FFI-safe +//~^^ ERROR `extern` fn uses type `FfiUnsafe`, which is not FFI-safe + +pub const BAD_CONST: extern "C" fn(FfiUnsafe) = f; +//~^ ERROR `extern` fn uses type `FfiUnsafe`, which is not FFI-safe diff --git a/tests/ui/lint/lint-ctypes-94223.stderr b/tests/ui/lint/lint-ctypes-94223.stderr index e05d6197cb426..49e64ed5140c2 100644 --- a/tests/ui/lint/lint-ctypes-94223.stderr +++ b/tests/ui/lint/lint-ctypes-94223.stderr @@ -30,5 +30,97 @@ LL | pub fn bad_twice(f: Result) {} = help: consider using a raw pointer instead = note: slices have no C equivalent -error: aborting due to 3 previous errors +error: `extern` fn uses type `[u8]`, which is not FFI-safe + --> $DIR/lint-ctypes-94223.rs:11:18 + | +LL | struct BadStruct(extern "C" fn([u8])); + | ^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider using a raw pointer instead + = note: slices have no C equivalent + +error: `extern` fn uses type `[u8]`, which is not FFI-safe + --> $DIR/lint-ctypes-94223.rs:15:7 + | +LL | A(extern "C" fn([u8])), + | ^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider using a raw pointer instead + = note: slices have no C equivalent + +error: `extern` fn uses type `[u8]`, which is not FFI-safe + --> $DIR/lint-ctypes-94223.rs:20:7 + | +LL | A(extern "C" fn([u8])), + | ^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider using a raw pointer instead + = note: slices have no C equivalent + +error: `extern` fn uses type `[u8]`, which is not FFI-safe + --> $DIR/lint-ctypes-94223.rs:24:12 + | +LL | type Foo = extern "C" fn([u8]); + | ^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider using a raw pointer instead + = note: slices have no C equivalent + +error: `extern` fn uses type `FfiUnsafe`, which is not FFI-safe + --> $DIR/lint-ctypes-94223.rs:34:17 + | +LL | pub static BAD: extern "C" fn(FfiUnsafe) = f; + | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct + = note: this struct has unspecified layout +note: the type is defined here + --> $DIR/lint-ctypes-94223.rs:27:1 + | +LL | pub struct FfiUnsafe; + | ^^^^^^^^^^^^^^^^^^^^ + +error: `extern` fn uses type `FfiUnsafe`, which is not FFI-safe + --> $DIR/lint-ctypes-94223.rs:37:30 + | +LL | pub static BAD_TWICE: Result = Ok(f); + | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct + = note: this struct has unspecified layout +note: the type is defined here + --> $DIR/lint-ctypes-94223.rs:27:1 + | +LL | pub struct FfiUnsafe; + | ^^^^^^^^^^^^^^^^^^^^ + +error: `extern` fn uses type `FfiUnsafe`, which is not FFI-safe + --> $DIR/lint-ctypes-94223.rs:37:56 + | +LL | pub static BAD_TWICE: Result = Ok(f); + | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct + = note: this struct has unspecified layout +note: the type is defined here + --> $DIR/lint-ctypes-94223.rs:27:1 + | +LL | pub struct FfiUnsafe; + | ^^^^^^^^^^^^^^^^^^^^ + +error: `extern` fn uses type `FfiUnsafe`, which is not FFI-safe + --> $DIR/lint-ctypes-94223.rs:41:22 + | +LL | pub const BAD_CONST: extern "C" fn(FfiUnsafe) = f; + | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct + = note: this struct has unspecified layout +note: the type is defined here + --> $DIR/lint-ctypes-94223.rs:27:1 + | +LL | pub struct FfiUnsafe; + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 11 previous errors From 5fa82092aed07351757fddeaf3cff062b96067d1 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 19 Apr 2023 23:04:01 +0000 Subject: [PATCH 05/11] Clear response values for overflow in new solver --- .../src/solve/eval_ctxt.rs | 19 ++++++---- .../src/solve/eval_ctxt/canonical.rs | 35 +++++++++++++++++-- .../new-solver/exponential-trait-goals.rs | 20 +++++++++++ .../new-solver/exponential-trait-goals.stderr | 23 ++++++++++++ 4 files changed, 88 insertions(+), 9 deletions(-) create mode 100644 tests/ui/traits/new-solver/exponential-trait-goals.rs create mode 100644 tests/ui/traits/new-solver/exponential-trait-goals.stderr diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs index bd52957d162f2..63a73f8d50d93 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs @@ -3,7 +3,8 @@ use rustc_infer::infer::at::ToTrace; use rustc_infer::infer::canonical::CanonicalVarValues; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::{ - DefineOpaqueTypes, InferCtxt, InferOk, LateBoundRegionConversionTime, TyCtxtInferExt, + DefineOpaqueTypes, InferCtxt, InferOk, LateBoundRegionConversionTime, RegionVariableOrigin, + TyCtxtInferExt, }; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::ObligationCause; @@ -223,18 +224,20 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { { debug!("rerunning goal to check result is stable"); let (_orig_values, canonical_goal) = self.canonicalize_goal(goal); - let canonical_response = + let new_canonical_response = EvalCtxt::evaluate_canonical_goal(self.tcx(), self.search_graph, canonical_goal)?; - if !canonical_response.value.var_values.is_identity() { + if !new_canonical_response.value.var_values.is_identity() { bug!( "unstable result: re-canonicalized goal={canonical_goal:#?} \ - response={canonical_response:#?}" + first_response={canonical_response:#?} \ + second_response={new_canonical_response:#?}" ); } - if certainty != canonical_response.value.certainty { + if certainty != new_canonical_response.value.certainty { bug!( "unstable certainty: {certainty:#?} re-canonicalized goal={canonical_goal:#?} \ - response={canonical_response:#?}" + first_response={canonical_response:#?} \ + second_response={new_canonical_response:#?}" ); } } @@ -434,6 +437,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { }) } + pub(super) fn next_region_infer(&self) -> ty::Region<'tcx> { + self.infcx.next_region_var(RegionVariableOrigin::MiscVariable(DUMMY_SP)) + } + pub(super) fn next_const_infer(&self, ty: Ty<'tcx>) -> ty::Const<'tcx> { self.infcx.next_const_var( ty, diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs index 226d29687e3a8..5b94709a5a6ff 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs @@ -16,7 +16,7 @@ use rustc_infer::infer::canonical::query_response::make_query_region_constraints use rustc_infer::infer::canonical::CanonicalVarValues; use rustc_infer::infer::canonical::{CanonicalExt, QueryRegionConstraints}; use rustc_middle::traits::query::NoSolution; -use rustc_middle::traits::solve::{ExternalConstraints, ExternalConstraintsData}; +use rustc_middle::traits::solve::{ExternalConstraints, ExternalConstraintsData, MaybeCause}; use rustc_middle::ty::{self, BoundVar, GenericArgKind}; use rustc_span::DUMMY_SP; use std::iter; @@ -60,9 +60,38 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { let certainty = certainty.unify_with(goals_certainty); - let external_constraints = self.compute_external_query_constraints()?; + let response = match certainty { + Certainty::Yes | Certainty::Maybe(MaybeCause::Ambiguity) => { + let external_constraints = self.compute_external_query_constraints()?; + Response { var_values: self.var_values, external_constraints, certainty } + } + Certainty::Maybe(MaybeCause::Overflow) => { + // If we have overflow, it's probable that we're substituting a type + // into itself infinitely and any partial substitutions in the query + // response are probably not useful anyways, so just return an empty + // query response. + Response { + var_values: CanonicalVarValues { + var_values: self.tcx().mk_substs_from_iter( + self.var_values.var_values.iter().map(|arg| -> ty::GenericArg<'tcx> { + match arg.unpack() { + GenericArgKind::Lifetime(_) => self.next_region_infer().into(), + GenericArgKind::Type(_) => self.next_ty_infer().into(), + GenericArgKind::Const(ct) => { + self.next_const_infer(ct.ty()).into() + } + } + }), + ), + }, + external_constraints: self + .tcx() + .mk_external_constraints(ExternalConstraintsData::default()), + certainty, + } + } + }; - let response = Response { var_values: self.var_values, external_constraints, certainty }; let canonical = Canonicalizer::canonicalize( self.infcx, CanonicalizeMode::Response { max_input_universe: self.max_input_universe }, diff --git a/tests/ui/traits/new-solver/exponential-trait-goals.rs b/tests/ui/traits/new-solver/exponential-trait-goals.rs new file mode 100644 index 0000000000000..b37f09ee185e9 --- /dev/null +++ b/tests/ui/traits/new-solver/exponential-trait-goals.rs @@ -0,0 +1,20 @@ +// compile-flags: -Ztrait-solver=next + +trait Trait {} + +struct W(T); + +impl Trait for W<(W, W)> +where + W: Trait, + W: Trait, +{ +} + +fn impls() {} + +fn main() { + impls::>(); + //~^ ERROR type annotations needed + //~| ERROR overflow evaluating the requirement `W<_>: Trait` +} diff --git a/tests/ui/traits/new-solver/exponential-trait-goals.stderr b/tests/ui/traits/new-solver/exponential-trait-goals.stderr new file mode 100644 index 0000000000000..28a99cbbca6a1 --- /dev/null +++ b/tests/ui/traits/new-solver/exponential-trait-goals.stderr @@ -0,0 +1,23 @@ +error[E0282]: type annotations needed + --> $DIR/exponential-trait-goals.rs:17:5 + | +LL | impls::>(); + | ^^^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `impls` + +error[E0275]: overflow evaluating the requirement `W<_>: Trait` + --> $DIR/exponential-trait-goals.rs:17:5 + | +LL | impls::>(); + | ^^^^^^^^^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`exponential_trait_goals`) +note: required by a bound in `impls` + --> $DIR/exponential-trait-goals.rs:14:13 + | +LL | fn impls() {} + | ^^^^^ required by this bound in `impls` + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0275, E0282. +For more information about an error, try `rustc --explain E0275`. From ee8942138a9ff0dedbe8575f0aacaea2ec78a51f Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 26 Apr 2023 22:33:33 +0000 Subject: [PATCH 06/11] Split out make_ambiguous_response_no_constraints --- .../src/solve/eval_ctxt/canonical.rs | 61 +++++++++++++------ .../rustc_trait_selection/src/solve/mod.rs | 22 +++---- 2 files changed, 53 insertions(+), 30 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs index 5b94709a5a6ff..67ad7fb4bd21d 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs @@ -70,25 +70,14 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // into itself infinitely and any partial substitutions in the query // response are probably not useful anyways, so just return an empty // query response. - Response { - var_values: CanonicalVarValues { - var_values: self.tcx().mk_substs_from_iter( - self.var_values.var_values.iter().map(|arg| -> ty::GenericArg<'tcx> { - match arg.unpack() { - GenericArgKind::Lifetime(_) => self.next_region_infer().into(), - GenericArgKind::Type(_) => self.next_ty_infer().into(), - GenericArgKind::Const(ct) => { - self.next_const_infer(ct.ty()).into() - } - } - }), - ), - }, - external_constraints: self - .tcx() - .mk_external_constraints(ExternalConstraintsData::default()), - certainty, - } + // + // This may prevent us from potentially useful inference, e.g. + // 2 candidates, one ambiguous and one overflow, which both + // have the same inference constraints. + // + // Changing this to retain some constraints in the future + // won't be a breaking change, so this is good enough for now. + return Ok(self.make_ambiguous_response_no_constraints(MaybeCause::Overflow)); } }; @@ -101,6 +90,40 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { Ok(canonical) } + /// Constructs a totally unconstrained, ambiguous response to a goal. + /// + /// Take care when using this, since often it's useful to respond with + /// ambiguity but return constrained variables to guide inference. + pub(in crate::solve) fn make_ambiguous_response_no_constraints( + &self, + maybe_cause: MaybeCause, + ) -> CanonicalResponse<'tcx> { + let unconstrained_response = Response { + var_values: CanonicalVarValues { + var_values: self.tcx().mk_substs_from_iter(self.var_values.var_values.iter().map( + |arg| -> ty::GenericArg<'tcx> { + match arg.unpack() { + GenericArgKind::Lifetime(_) => self.next_region_infer().into(), + GenericArgKind::Type(_) => self.next_ty_infer().into(), + GenericArgKind::Const(ct) => self.next_const_infer(ct.ty()).into(), + } + }, + )), + }, + external_constraints: self + .tcx() + .mk_external_constraints(ExternalConstraintsData::default()), + certainty: Certainty::Maybe(maybe_cause), + }; + + Canonicalizer::canonicalize( + self.infcx, + CanonicalizeMode::Response { max_input_universe: self.max_input_universe }, + &mut Default::default(), + unconstrained_response, + ) + } + #[instrument(level = "debug", skip(self), ret)] fn compute_external_query_constraints(&self) -> Result, NoSolution> { // Cannot use `take_registered_region_obligations` as we may compute the response diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index 19bcbd461447d..d94679fef2833 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -340,17 +340,17 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { if responses.is_empty() { return Err(NoSolution); } - let certainty = responses.iter().fold(Certainty::AMBIGUOUS, |certainty, response| { - certainty.unify_with(response.value.certainty) - }); - - let response = self.evaluate_added_goals_and_make_canonical_response(certainty); - if let Ok(response) = response { - assert!(response.has_no_inference_or_external_constraints()); - Ok(response) - } else { - bug!("failed to make floundered response: {responses:?}"); - } + + let Certainty::Maybe(maybe_cause) = responses.iter().fold( + Certainty::AMBIGUOUS, + |certainty, response| { + certainty.unify_with(response.value.certainty) + }, + ) else { + bug!("expected flounder response to be ambiguous") + }; + + Ok(self.make_ambiguous_response_no_constraints(maybe_cause)) } } From adee37a080c7415f7ba496726557531ddf950919 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 27 Apr 2023 16:40:59 +0200 Subject: [PATCH 07/11] repr attribute needs to be stored to be used in doc(inline) by rustdoc --- compiler/rustc_feature/src/builtin_attrs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index c77292fdd1647..103e0f344070d 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -344,7 +344,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ), ungated!(link_name, Normal, template!(NameValueStr: "name"), FutureWarnPreceding), ungated!(no_link, Normal, template!(Word), WarnFollowing), - ungated!(repr, Normal, template!(List: "C"), DuplicatesOk, @only_local: true), + ungated!(repr, Normal, template!(List: "C"), DuplicatesOk), ungated!(export_name, Normal, template!(NameValueStr: "name"), FutureWarnPreceding), ungated!(link_section, Normal, template!(NameValueStr: "name"), FutureWarnPreceding), ungated!(no_mangle, Normal, template!(Word), WarnFollowing, @only_local: true), From 3f082843aa19d2bb51531688770eeae401249ceb Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 27 Apr 2023 16:41:22 +0200 Subject: [PATCH 08/11] Add regression test for #110698 --- tests/rustdoc/inline_cross/auxiliary/repr.rs | 4 ++++ tests/rustdoc/inline_cross/repr.rs | 13 +++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 tests/rustdoc/inline_cross/auxiliary/repr.rs create mode 100644 tests/rustdoc/inline_cross/repr.rs diff --git a/tests/rustdoc/inline_cross/auxiliary/repr.rs b/tests/rustdoc/inline_cross/auxiliary/repr.rs new file mode 100644 index 0000000000000..64a98f1814626 --- /dev/null +++ b/tests/rustdoc/inline_cross/auxiliary/repr.rs @@ -0,0 +1,4 @@ +#[repr(C)] +pub struct Foo { + field: u8, +} diff --git a/tests/rustdoc/inline_cross/repr.rs b/tests/rustdoc/inline_cross/repr.rs new file mode 100644 index 0000000000000..7e1f2799af1cd --- /dev/null +++ b/tests/rustdoc/inline_cross/repr.rs @@ -0,0 +1,13 @@ +// Regression test for . +// This test ensures that the re-exported items still have the `#[repr(...)]` attribute. + +// aux-build:repr.rs + +#![crate_name = "foo"] + +extern crate repr; + +// @has 'foo/struct.Foo.html' +// @has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(C)]' +#[doc(inline)] +pub use repr::Foo; From 206c481414828aadce2ec05d55e95b27943e0d12 Mon Sep 17 00:00:00 2001 From: Lukas Markeffsky <@> Date: Sat, 17 Dec 2022 23:22:48 +0100 Subject: [PATCH 09/11] Add `rustdoc::unescaped_backtick` lint --- src/doc/rustdoc/src/lints.md | 38 + src/librustdoc/lib.rs | 9 +- src/librustdoc/lint.rs | 12 + src/librustdoc/passes/lint.rs | 2 + .../passes/lint/unescaped_backticks.rs | 416 ++++++++ tests/rustdoc-ui/unescaped_backticks.rs | 309 ++++++ tests/rustdoc-ui/unescaped_backticks.stderr | 885 ++++++++++++++++++ 7 files changed, 1667 insertions(+), 4 deletions(-) create mode 100644 src/librustdoc/passes/lint/unescaped_backticks.rs create mode 100644 tests/rustdoc-ui/unescaped_backticks.rs create mode 100644 tests/rustdoc-ui/unescaped_backticks.stderr diff --git a/src/doc/rustdoc/src/lints.md b/src/doc/rustdoc/src/lints.md index 45db3bb9b007b..fd57b07964481 100644 --- a/src/doc/rustdoc/src/lints.md +++ b/src/doc/rustdoc/src/lints.md @@ -374,3 +374,41 @@ warning: this URL is not a hyperlink warning: 2 warnings emitted ``` + +## `unescaped_backticks` + +This lint is **allowed by default**. It detects backticks (\`) that are not escaped. +This usually means broken inline code. For example: + +```rust +#![warn(rustdoc::unescaped_backticks)] + +/// `add(a, b) is the same as `add(b, a)`. +pub fn add(a: i32, b: i32) -> i32 { a + b } +``` + +Which will give: + +```text +warning: unescaped backtick + --> src/lib.rs:3:41 + | +3 | /// `add(a, b) is the same as `add(b, a)`. + | ^ + | +note: the lint level is defined here + --> src/lib.rs:1:9 + | +1 | #![warn(rustdoc::unescaped_backticks)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: a previous inline code might be longer than expected + | +3 | /// `add(a, b)` is the same as `add(b, a)`. + | + +help: if you meant to use a literal backtick, escape it + | +3 | /// `add(a, b) is the same as `add(b, a)\`. + | + + +warning: 1 warning emitted +``` diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 4a88dc5254de6..ee2749d4eb969 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -7,13 +7,14 @@ #![feature(assert_matches)] #![feature(box_patterns)] #![feature(drain_filter)] +#![feature(iter_intersperse)] +#![feature(lazy_cell)] #![feature(let_chains)] -#![feature(test)] #![feature(never_type)] -#![feature(lazy_cell)] -#![feature(type_ascription)] -#![feature(iter_intersperse)] +#![feature(round_char_boundary)] +#![feature(test)] #![feature(type_alias_impl_trait)] +#![feature(type_ascription)] #![cfg_attr(not(bootstrap), feature(impl_trait_in_assoc_type))] #![recursion_limit = "256"] #![warn(rustc::internal)] diff --git a/src/librustdoc/lint.rs b/src/librustdoc/lint.rs index 6d289eb996de7..749c1ff51bfc5 100644 --- a/src/librustdoc/lint.rs +++ b/src/librustdoc/lint.rs @@ -174,6 +174,17 @@ declare_rustdoc_lint! { "codeblock could not be parsed as valid Rust or is empty" } +declare_rustdoc_lint! { + /// The `unescaped_backticks` lint detects unescaped backticks (\`), which usually + /// mean broken inline code. This is a `rustdoc` only lint, see the documentation + /// in the [rustdoc book]. + /// + /// [rustdoc book]: ../../../rustdoc/lints.html#unescaped_backticks + UNESCAPED_BACKTICKS, + Allow, + "detects unescaped backticks in doc comments" +} + pub(crate) static RUSTDOC_LINTS: Lazy> = Lazy::new(|| { vec![ BROKEN_INTRA_DOC_LINKS, @@ -185,6 +196,7 @@ pub(crate) static RUSTDOC_LINTS: Lazy> = Lazy::new(|| { INVALID_HTML_TAGS, BARE_URLS, MISSING_CRATE_LEVEL_DOCS, + UNESCAPED_BACKTICKS, ] }); diff --git a/src/librustdoc/passes/lint.rs b/src/librustdoc/passes/lint.rs index 97031c4f028f4..e653207b9b6d4 100644 --- a/src/librustdoc/passes/lint.rs +++ b/src/librustdoc/passes/lint.rs @@ -4,6 +4,7 @@ mod bare_urls; mod check_code_block_syntax; mod html_tags; +mod unescaped_backticks; use super::Pass; use crate::clean::*; @@ -27,6 +28,7 @@ impl<'a, 'tcx> DocVisitor for Linter<'a, 'tcx> { bare_urls::visit_item(self.cx, item); check_code_block_syntax::visit_item(self.cx, item); html_tags::visit_item(self.cx, item); + unescaped_backticks::visit_item(self.cx, item); self.visit_item_recur(item) } diff --git a/src/librustdoc/passes/lint/unescaped_backticks.rs b/src/librustdoc/passes/lint/unescaped_backticks.rs new file mode 100644 index 0000000000000..33cef82a60cbb --- /dev/null +++ b/src/librustdoc/passes/lint/unescaped_backticks.rs @@ -0,0 +1,416 @@ +//! Detects unescaped backticks (\`) in doc comments. + +use crate::clean::Item; +use crate::core::DocContext; +use crate::html::markdown::main_body_opts; +use crate::passes::source_span_for_markdown_range; +use pulldown_cmark::{BrokenLink, Event, Parser}; +use rustc_errors::DiagnosticBuilder; +use rustc_lint_defs::Applicability; +use std::ops::Range; + +pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item) { + let tcx = cx.tcx; + let Some(hir_id) = DocContext::as_local_hir_id(tcx, item.item_id) else { + // If non-local, no need to check anything. + return; + }; + + let dox = item.attrs.collapsed_doc_value().unwrap_or_default(); + if dox.is_empty() { + return; + } + + let link_names = item.link_names(&cx.cache); + let mut replacer = |broken_link: BrokenLink<'_>| { + link_names + .iter() + .find(|link| *link.original_text == *broken_link.reference) + .map(|link| ((*link.href).into(), (*link.new_text).into())) + }; + let parser = Parser::new_with_broken_link_callback(&dox, main_body_opts(), Some(&mut replacer)) + .into_offset_iter(); + + let mut element_stack = Vec::new(); + + let mut prev_text_end = 0; + for (event, event_range) in parser { + match event { + Event::Start(_) => { + element_stack.push(Element::new(event_range)); + } + Event::End(_) => { + let element = element_stack.pop().unwrap(); + + let Some(backtick_index) = element.backtick_index else { + continue; + }; + + // If we can't get a span of the backtick, because it is in a `#[doc = ""]` attribute, + // use the span of the entire attribute as a fallback. + let span = source_span_for_markdown_range( + tcx, + &dox, + &(backtick_index..backtick_index + 1), + &item.attrs, + ) + .unwrap_or_else(|| item.attr_span(tcx)); + + cx.tcx.struct_span_lint_hir(crate::lint::UNESCAPED_BACKTICKS, hir_id, span, "unescaped backtick", |lint| { + let mut help_emitted = false; + + match element.prev_code_guess { + PrevCodeGuess::None => {} + PrevCodeGuess::Start { guess, .. } => { + // "foo` `bar`" -> "`foo` `bar`" + if let Some(suggest_index) = clamp_start(guess, &element.suggestible_ranges) + && can_suggest_backtick(&dox, suggest_index) + { + suggest_insertion(cx, item, &dox, lint, suggest_index, '`', "the opening backtick of a previous inline code may be missing"); + help_emitted = true; + } + } + PrevCodeGuess::End { guess, .. } => { + // "`foo `bar`" -> "`foo` `bar`" + // Don't `clamp_end` here, because the suggestion is guaranteed to be inside + // an inline code node and we intentionally "break" the inline code here. + let suggest_index = guess; + if can_suggest_backtick(&dox, suggest_index) { + suggest_insertion(cx, item, &dox, lint, suggest_index, '`', "a previous inline code might be longer than expected"); + help_emitted = true; + } + } + } + + if !element.prev_code_guess.is_confident() { + // "`foo` bar`" -> "`foo` `bar`" + if let Some(guess) = guess_start_of_code(&dox, element.element_range.start..backtick_index) + && let Some(suggest_index) = clamp_start(guess, &element.suggestible_ranges) + && can_suggest_backtick(&dox, suggest_index) + { + suggest_insertion(cx, item, &dox, lint, suggest_index, '`', "the opening backtick of an inline code may be missing"); + help_emitted = true; + } + + // "`foo` `bar" -> "`foo` `bar`" + // Don't suggest closing backtick after single trailing char, + // if we already suggested opening backtick. For example: + // "foo`." -> "`foo`." or "foo`s" -> "`foo`s". + if let Some(guess) = guess_end_of_code(&dox, backtick_index + 1..element.element_range.end) + && let Some(suggest_index) = clamp_end(guess, &element.suggestible_ranges) + && can_suggest_backtick(&dox, suggest_index) + && (!help_emitted || suggest_index - backtick_index > 2) + { + suggest_insertion(cx, item, &dox, lint, suggest_index, '`', "the closing backtick of an inline code may be missing"); + help_emitted = true; + } + } + + if !help_emitted { + lint.help("the opening or closing backtick of an inline code may be missing"); + } + + suggest_insertion(cx, item, &dox, lint, backtick_index, '\\', "if you meant to use a literal backtick, escape it"); + + lint + }); + } + Event::Code(_) => { + let element = element_stack + .last_mut() + .expect("expected inline code node to be inside of an element"); + assert!( + event_range.start >= element.element_range.start + && event_range.end <= element.element_range.end + ); + + // This inline code might be longer than it's supposed to be. + // Only check single backtick inline code for now. + if !element.prev_code_guess.is_confident() + && dox.as_bytes().get(event_range.start) == Some(&b'`') + && dox.as_bytes().get(event_range.start + 1) != Some(&b'`') + { + let range_inside = event_range.start + 1..event_range.end - 1; + let text_inside = &dox[range_inside.clone()]; + + let is_confident = text_inside.starts_with(char::is_whitespace) + || text_inside.ends_with(char::is_whitespace); + + if let Some(guess) = guess_end_of_code(&dox, range_inside) { + // Find earlier end of code. + element.prev_code_guess = PrevCodeGuess::End { guess, is_confident }; + } else { + // Find alternate start of code. + let range_before = element.element_range.start..event_range.start; + if let Some(guess) = guess_start_of_code(&dox, range_before) { + element.prev_code_guess = PrevCodeGuess::Start { guess, is_confident }; + } + } + } + } + Event::Text(text) => { + let element = element_stack + .last_mut() + .expect("expected inline text node to be inside of an element"); + assert!( + event_range.start >= element.element_range.start + && event_range.end <= element.element_range.end + ); + + // The first char is escaped if the prev char is \ and not part of a text node. + let is_escaped = prev_text_end < event_range.start + && dox.as_bytes()[event_range.start - 1] == b'\\'; + + // Don't lint backslash-escaped (\`) or html-escaped (`) backticks. + if *text == *"`" && !is_escaped && *text == dox[event_range.clone()] { + // We found a stray backtick. + assert!( + element.backtick_index.is_none(), + "expected at most one unescaped backtick per element", + ); + element.backtick_index = Some(event_range.start); + } + + prev_text_end = event_range.end; + + if is_escaped { + // Ensure that we suggest "`\x" and not "\`x". + element.suggestible_ranges.push(event_range.start - 1..event_range.end); + } else { + element.suggestible_ranges.push(event_range); + } + } + _ => {} + } + } +} + +/// A previous inline code node, that looks wrong. +/// +/// `guess` is the position, where we want to suggest a \` and the guess `is_confident` if an +/// inline code starts or ends with a whitespace. +#[derive(Debug)] +enum PrevCodeGuess { + None, + + /// Missing \` at start. + /// + /// ```markdown + /// foo` `bar` + /// ``` + Start { + guess: usize, + is_confident: bool, + }, + + /// Missing \` at end. + /// + /// ```markdown + /// `foo `bar` + /// ``` + End { + guess: usize, + is_confident: bool, + }, +} + +impl PrevCodeGuess { + fn is_confident(&self) -> bool { + match *self { + PrevCodeGuess::None => false, + PrevCodeGuess::Start { is_confident, .. } | PrevCodeGuess::End { is_confident, .. } => { + is_confident + } + } + } +} + +/// A markdown [tagged element], which may or may not contain an unescaped backtick. +/// +/// [tagged element]: https://docs.rs/pulldown-cmark/0.9/pulldown_cmark/enum.Tag.html +#[derive(Debug)] +struct Element { + /// The full range (span) of the element in the doc string. + element_range: Range, + + /// The ranges where we're allowed to put backticks. + /// This is used to prevent breaking markdown elements like links or lists. + suggestible_ranges: Vec>, + + /// The unescaped backtick. + backtick_index: Option, + + /// Suggest a different start or end of an inline code. + prev_code_guess: PrevCodeGuess, +} + +impl Element { + const fn new(element_range: Range) -> Self { + Self { + element_range, + suggestible_ranges: Vec::new(), + backtick_index: None, + prev_code_guess: PrevCodeGuess::None, + } + } +} + +/// Given a potentially unclosed inline code, attempt to find the start. +fn guess_start_of_code(dox: &str, range: Range) -> Option { + assert!(dox.as_bytes()[range.end] == b'`'); + + let mut braces = 0; + let mut guess = 0; + for (idx, ch) in dox[range.clone()].char_indices().rev() { + match ch { + ')' | ']' | '}' => braces += 1, + '(' | '[' | '{' => { + if braces == 0 { + guess = idx + 1; + break; + } + braces -= 1; + } + ch if ch.is_whitespace() && braces == 0 => { + guess = idx + 1; + break; + } + _ => (), + } + } + + guess += range.start; + + // Don't suggest empty inline code or duplicate backticks. + can_suggest_backtick(dox, guess).then_some(guess) +} + +/// Given a potentially unclosed inline code, attempt to find the end. +fn guess_end_of_code(dox: &str, range: Range) -> Option { + // Punctuation that should be outside of the inline code. + const TRAILING_PUNCTUATION: &[u8] = b".,"; + + assert!(dox.as_bytes()[range.start - 1] == b'`'); + + let text = dox[range.clone()].trim_end(); + let mut braces = 0; + let mut guess = text.len(); + for (idx, ch) in text.char_indices() { + match ch { + '(' | '[' | '{' => braces += 1, + ')' | ']' | '}' => { + if braces == 0 { + guess = idx; + break; + } + braces -= 1; + } + ch if ch.is_whitespace() && braces == 0 => { + guess = idx; + break; + } + _ => (), + } + } + + // Strip a single trailing punctuation. + if guess >= 1 + && TRAILING_PUNCTUATION.contains(&text.as_bytes()[guess - 1]) + && (guess < 2 || !TRAILING_PUNCTUATION.contains(&text.as_bytes()[guess - 2])) + { + guess -= 1; + } + + guess += range.start; + + // Don't suggest empty inline code or duplicate backticks. + can_suggest_backtick(dox, guess).then_some(guess) +} + +/// Returns whether inserting a backtick at `dox[index]` will not produce double backticks. +fn can_suggest_backtick(dox: &str, index: usize) -> bool { + (index == 0 || dox.as_bytes()[index - 1] != b'`') + && (index == dox.len() || dox.as_bytes()[index] != b'`') +} + +/// Increase the index until it is inside or one past the end of one of the ranges. +/// +/// The ranges must be sorted for this to work correctly. +fn clamp_start(index: usize, ranges: &[Range]) -> Option { + for range in ranges { + if range.start >= index { + return Some(range.start); + } + if index <= range.end { + return Some(index); + } + } + None +} + +/// Decrease the index until it is inside or one past the end of one of the ranges. +/// +/// The ranges must be sorted for this to work correctly. +fn clamp_end(index: usize, ranges: &[Range]) -> Option { + for range in ranges.iter().rev() { + if range.end <= index { + return Some(range.end); + } + if index >= range.start { + return Some(index); + } + } + None +} + +/// Try to emit a span suggestion and fall back to help messages if we can't find a suitable span. +/// +/// This helps finding backticks in huge macro-generated docs. +fn suggest_insertion( + cx: &DocContext<'_>, + item: &Item, + dox: &str, + lint: &mut DiagnosticBuilder<'_, ()>, + insert_index: usize, + suggestion: char, + message: &str, +) { + /// Maximum bytes of context to show around the insertion. + const CONTEXT_MAX_LEN: usize = 80; + + if let Some(span) = + source_span_for_markdown_range(cx.tcx, &dox, &(insert_index..insert_index), &item.attrs) + { + lint.span_suggestion(span, message, suggestion, Applicability::MaybeIncorrect); + } else { + let line_start = dox[..insert_index].rfind('\n').map_or(0, |idx| idx + 1); + let line_end = dox[insert_index..].find('\n').map_or(dox.len(), |idx| idx + insert_index); + + let context_before_max_len = if insert_index - line_start < CONTEXT_MAX_LEN / 2 { + insert_index - line_start + } else if line_end - insert_index < CONTEXT_MAX_LEN / 2 { + CONTEXT_MAX_LEN - (line_end - insert_index) + } else { + CONTEXT_MAX_LEN / 2 + }; + let context_after_max_len = CONTEXT_MAX_LEN - context_before_max_len; + + let (prefix, context_start) = if insert_index - line_start <= context_before_max_len { + ("", line_start) + } else { + ("...", dox.ceil_char_boundary(insert_index - context_before_max_len)) + }; + let (suffix, context_end) = if line_end - insert_index <= context_after_max_len { + ("", line_end) + } else { + ("...", dox.floor_char_boundary(insert_index + context_after_max_len)) + }; + + let context_full = &dox[context_start..context_end].trim_end(); + let context_before = &dox[context_start..insert_index]; + let context_after = &dox[insert_index..context_end].trim_end(); + lint.help(format!( + "{message}\n change: {prefix}{context_full}{suffix}\nto this: {prefix}{context_before}{suggestion}{context_after}{suffix}" + )); + } +} diff --git a/tests/rustdoc-ui/unescaped_backticks.rs b/tests/rustdoc-ui/unescaped_backticks.rs new file mode 100644 index 0000000000000..e7d34221916bf --- /dev/null +++ b/tests/rustdoc-ui/unescaped_backticks.rs @@ -0,0 +1,309 @@ +#![deny(rustdoc::unescaped_backticks)] +#![allow(rustdoc::broken_intra_doc_links)] +#![allow(rustdoc::invalid_html_tags)] + +/// ` +//~^ ERROR unescaped backtick +pub fn single() {} + +/// \` +pub fn escaped() {} + +/// \\` +//~^ ERROR unescaped backtick +pub fn not_escaped() {} + +/// \\\` +pub fn not_not_escaped() {} + +/// [`link1] +//~^ ERROR unescaped backtick +pub fn link1() {} + +/// [link2`] +//~^ ERROR unescaped backtick +pub fn link2() {} + +/// [`link_long](link_long) +//~^ ERROR unescaped backtick +pub fn link_long() {} + +/// [`broken-link] +//~^ ERROR unescaped backtick +pub fn broken_link() {} + +/// +pub fn url() {} + +/// +//~^ ERROR unescaped backtick +pub fn not_url() {} + +///

`

+pub fn html_tag() {} + +/// ` +pub fn html_escape() {} + +/// 🦀`🦀 +//~^ ERROR unescaped backtick +pub fn unicode() {} + +/// `foo( +//~^ ERROR unescaped backtick +/// +/// paragraph +pub fn paragraph() {} + +/// `foo `bar` +//~^ ERROR unescaped backtick +/// +/// paragraph +pub fn paragraph2() {} + +/// `foo( +//~^ ERROR unescaped backtick +/// not paragraph +pub fn not_paragraph() {} + +/// Addition is commutative, which means that add(a, b)` is the same as `add(b, a)`. +//~^ ERROR unescaped backtick +/// +/// You could use this function to add 42 to a number `n` (add(n, 42)`), +/// or even to add a number `n` to 42 (`add(42, b)`)! +//~^ ERROR unescaped backtick +pub fn add1(a: i32, b: i32) -> i32 { a + b } + +/// Addition is commutative, which means that `add(a, b) is the same as `add(b, a)`. +//~^ ERROR unescaped backtick +/// +/// You could use this function to add 42 to a number `n` (`add(n, 42)), +/// or even to add a number `n` to 42 (`add(42, n)`)! +//~^ ERROR unescaped backtick +pub fn add2(a: i32, b: i32) -> i32 { a + b } + +/// Addition is commutative, which means that `add(a, b)` is the same as add(b, a)`. +//~^ ERROR unescaped backtick +/// +/// You could use this function to add 42 to a number `n` (`add(n, 42)`), +/// or even to add a number `n` to 42 (add(42, n)`)! +//~^ ERROR unescaped backtick +pub fn add3(a: i32, b: i32) -> i32 { a + b } + +/// Addition is commutative, which means that `add(a, b)` is the same as `add(b, a). +//~^ ERROR unescaped backtick +/// +/// You could use this function to add 42 to a number `n` (`add(n, 42)), +/// or even to add a number `n` to 42 (`add(42, n)`)! +//~^ ERROR unescaped backtick +pub fn add4(a: i32, b: i32) -> i32 { a + b } + +#[doc = "`"] +//~^ ERROR unescaped backtick +pub fn attr() {} + +#[doc = concat!("\\", "`")] +pub fn attr_escaped() {} + +#[doc = concat!("\\\\", "`")] +//~^ ERROR unescaped backtick +pub fn attr_not_escaped() {} + +#[doc = "Addition is commutative, which means that add(a, b)` is the same as `add(b, a)`."] +//~^ ERROR unescaped backtick +pub fn attr_add1(a: i32, b: i32) -> i32 { a + b } + +#[doc = "Addition is commutative, which means that `add(a, b) is the same as `add(b, a)`."] +//~^ ERROR unescaped backtick +pub fn attr_add2(a: i32, b: i32) -> i32 { a + b } + +#[doc = "Addition is commutative, which means that `add(a, b)` is the same as add(b, a)`."] +//~^ ERROR unescaped backtick +pub fn attr_add3(a: i32, b: i32) -> i32 { a + b } + +#[doc = "Addition is commutative, which means that `add(a, b)` is the same as `add(b, a)."] +//~^ ERROR unescaped backtick +pub fn attr_add4(a: i32, b: i32) -> i32 { a + b } + +/// ``double backticks`` +/// `foo +//~^ ERROR unescaped backtick +pub fn double_backticks() {} + +/// # `(heading +//~^ ERROR unescaped backtick +/// ## heading2)` +//~^ ERROR unescaped backtick +/// +/// multi `( +//~^ ERROR unescaped backtick +/// line +/// ) heading +/// = +/// +/// para)`(graph +//~^ ERROR unescaped backtick +/// +/// para)`(graph2 +//~^ ERROR unescaped backtick +/// +/// 1. foo)` +//~^ ERROR unescaped backtick +/// 2. `(bar +//~^ ERROR unescaped backtick +/// * baz)` +//~^ ERROR unescaped backtick +/// * `(quux +//~^ ERROR unescaped backtick +/// +/// `#![this_is_actually_an_image(and(not), an = "attribute")] +//~^ ERROR unescaped backtick +/// +/// #![this_is_actually_an_image(and(not), an = "attribute")]` +//~^ ERROR unescaped backtick +/// +/// [this_is_actually_an_image(and(not), an = "attribute")]: `.png +/// +/// | `table( | )head` | +//~^ ERROR unescaped backtick +//~| ERROR unescaped backtick +/// |---------|--------| +/// | table`( | )`body | +//~^ ERROR unescaped backtick +//~| ERROR unescaped backtick +pub fn complicated_markdown() {} + +/// The `custom_mir` attribute tells the compiler to treat the function as being custom MIR. This +/// attribute only works on functions - there is no way to insert custom MIR into the middle of +/// another function. The `dialect` and `phase` parameters indicate which [version of MIR][dialect +/// docs] you are inserting here. Generally you'll want to use `#![custom_mir(dialect = "built")]` +/// if you want your MIR to be modified by the full MIR pipeline, or `#![custom_mir(dialect = +//~^ ERROR unescaped backtick +/// "runtime", phase = "optimized")] if you don't. +pub mod mir {} + +pub mod rustc { + /// Constructs a `TyKind::Error` type and registers a `delay_span_bug` with the given `msg to + //~^ ERROR unescaped backtick + /// ensure it gets used. + pub fn ty_error_with_message() {} + + pub struct WhereClause { + /// `true` if we ate a `where` token: this can happen + /// if we parsed no predicates (e.g. `struct Foo where {} + /// This allows us to accurately pretty-print + /// in `nt_to_tokenstream` + //~^ ERROR unescaped backtick + pub has_where_token: bool, + } + + /// A symbol is an interned or gensymed string. The use of `newtype_index!` means + /// that `Option` only takes up 4 bytes, because `newtype_index! reserves + //~^ ERROR unescaped backtick + /// the last 256 values for tagging purposes. + pub struct Symbol(); + + /// It is equivalent to `OpenOptions::new()` but allows you to write more + /// readable code. Instead of `OpenOptions::new().read(true).open("foo.txt")` + /// you can write `File::with_options().read(true).open("foo.txt"). This + /// also avoids the need to import `OpenOptions`. + //~^ ERROR unescaped backtick + pub fn with_options() {} + + /// Subtracts `set from `row`. `set` can be either `BitSet` or + /// `HybridBitSet`. Has no effect if `row` does not exist. + //~^ ERROR unescaped backtick + /// + /// Returns true if the row was changed. + pub fn subtract_row() {} + + pub mod assert_module_sources { + //! The reason that we use `cfg=...` and not `#[cfg_attr]` is so that + //! the HIR doesn't change as a result of the annotations, which might + //! perturb the reuse results. + //! + //! `#![rustc_expected_cgu_reuse(module="spike", cfg="rpass2", kind="post-lto")] + //~^ ERROR unescaped backtick + //! allows for doing a more fine-grained check to see if pre- or post-lto data + //! was re-used. + + /// `cfg=... + //~^ ERROR unescaped backtick + pub fn foo() {} + + /// `cfg=... and not `#[cfg_attr]` + //~^ ERROR unescaped backtick + pub fn bar() {} + } + + /// Conceptually, this is like a `Vec>`. But the number of + /// RWU`s can get very large, so it uses a more compact representation. + //~^ ERROR unescaped backtick + pub struct RWUTable {} + + /// Like [Self::canonicalize_query], but preserves distinct universes. For + /// example, canonicalizing `&'?0: Trait<'?1>`, where `'?0` is in `U1` and + /// `'?1` is in `U3` would be canonicalized to have ?0` in `U1` and `'?1` + /// in `U2`. + //~^ ERROR unescaped backtick + /// + /// This is used for Chalk integration. + pub fn canonicalize_query_preserving_universes() {} + + /// Note that we used to return `Error` here, but that was quite + /// dubious -- the premise was that an error would *eventually* be + /// reported, when the obligation was processed. But in general once + /// you see an `Error` you are supposed to be able to assume that an + /// error *has been* reported, so that you can take whatever heuristic + /// paths you want to take. To make things worse, it was possible for + /// cycles to arise, where you basically had a setup like ` + /// as Trait>::Foo == $0`. Here, normalizing ` as + /// Trait>::Foo> to `[type error]` would lead to an obligation of + /// ` as Trait>::Foo`. We are supposed to report + /// an error for this obligation, but we legitimately should not, + /// because it contains `[type error]`. Yuck! (See issue #29857 for + //~^ ERROR unescaped backtick + /// one case where this arose.) + pub fn normalize_to_error() {} + + /// you don't want to cache that `B: AutoTrait` or `A: AutoTrait` + /// is `EvaluatedToOk`; this is because they were only considered + /// ok on the premise that if `A: AutoTrait` held, but we indeed + /// encountered a problem (later on) with `A: AutoTrait. So we + /// currently set a flag on the stack node for `B: AutoTrait` (as + /// well as the second instance of `A: AutoTrait`) to suppress + //~^ ERROR unescaped backtick + /// caching. + pub struct TraitObligationStack; + + /// Extend `scc` so that it can outlive some placeholder region + /// from a universe it can't name; at present, the only way for + /// this to be true is if `scc` outlives `'static`. This is + /// actually stricter than necessary: ideally, we'd support bounds + /// like `for<'a: 'b`>` that might then allow us to approximate + /// `'a` with `'b` and not `'static`. But it will have to do for + //~^ ERROR unescaped backtick + /// now. + pub fn add_incompatible_universe(){} +} + +/// The Subscriber` may be accessed by calling [`WeakDispatch::upgrade`], +/// which returns an `Option`. If all [`Dispatch`] clones that point +/// at the `Subscriber` have been dropped, [`WeakDispatch::upgrade`] will return +/// `None`. Otherwise, it will return `Some(Dispatch)`. +//~^ ERROR unescaped backtick +/// +/// Returns some reference to this `[`Subscriber`] value if it is of type `T`, +/// or `None` if it isn't. +//~^ ERROR unescaped backtick +/// +/// Called before the filtered [`Layer]'s [`on_event`], to determine if +/// `on_event` should be called. +//~^ ERROR unescaped backtick +/// +/// Therefore, if the `Filter will change the value returned by this +/// method, it is responsible for ensuring that +/// [`rebuild_interest_cache`][rebuild] is called after the value of the max +//~^ ERROR unescaped backtick +/// level changes. +pub mod tracing {} diff --git a/tests/rustdoc-ui/unescaped_backticks.stderr b/tests/rustdoc-ui/unescaped_backticks.stderr new file mode 100644 index 0000000000000..db4ab4b3b7ca8 --- /dev/null +++ b/tests/rustdoc-ui/unescaped_backticks.stderr @@ -0,0 +1,885 @@ +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:180:70 + | +LL | /// if you want your MIR to be modified by the full MIR pipeline, or `#![custom_mir(dialect = + | ^ + | +note: the lint level is defined here + --> $DIR/unescaped_backticks.rs:1:9 + | +LL | #![deny(rustdoc::unescaped_backticks)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: the closing backtick of an inline code may be missing + | +LL | /// "runtime", phase = "optimized")]` if you don't. + | + +help: if you meant to use a literal backtick, escape it + | +LL | /// if you want your MIR to be modified by the full MIR pipeline, or \`#![custom_mir(dialect = + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:225:13 + | +LL | //! `#![rustc_expected_cgu_reuse(module="spike", cfg="rpass2", kind="post-lto")] + | ^ + | +help: the closing backtick of an inline code may be missing + | +LL | //! `#![rustc_expected_cgu_reuse(module="spike", cfg="rpass2", kind="post-lto")]` + | + +help: if you meant to use a literal backtick, escape it + | +LL | //! \`#![rustc_expected_cgu_reuse(module="spike", cfg="rpass2", kind="post-lto")] + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:230:13 + | +LL | /// `cfg=... + | ^ + | +help: the closing backtick of an inline code may be missing + | +LL | /// `cfg=...` + | + +help: if you meant to use a literal backtick, escape it + | +LL | /// \`cfg=... + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:234:42 + | +LL | /// `cfg=... and not `#[cfg_attr]` + | ^ + | +help: a previous inline code might be longer than expected + | +LL | /// `cfg=...` and not `#[cfg_attr]` + | + +help: if you meant to use a literal backtick, escape it + | +LL | /// `cfg=... and not `#[cfg_attr]\` + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:186:91 + | +LL | /// Constructs a `TyKind::Error` type and registers a `delay_span_bug` with the given `msg to + | ^ + | +help: the closing backtick of an inline code may be missing + | +LL | /// Constructs a `TyKind::Error` type and registers a `delay_span_bug` with the given `msg` to + | + +help: if you meant to use a literal backtick, escape it + | +LL | /// Constructs a `TyKind::Error` type and registers a `delay_span_bug` with the given \`msg to + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:195:34 + | +LL | /// in `nt_to_tokenstream` + | ^ + | +help: a previous inline code might be longer than expected + | +LL | /// if we parsed no predicates (e.g. `struct` Foo where {} + | + +help: if you meant to use a literal backtick, escape it + | +LL | /// in `nt_to_tokenstream\` + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:201:62 + | +LL | /// that `Option` only takes up 4 bytes, because `newtype_index! reserves + | ^ + | +help: the closing backtick of an inline code may be missing + | +LL | /// that `Option` only takes up 4 bytes, because `newtype_index!` reserves + | + +help: if you meant to use a literal backtick, escape it + | +LL | /// that `Option` only takes up 4 bytes, because \`newtype_index! reserves + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:209:52 + | +LL | /// also avoids the need to import `OpenOptions`. + | ^ + | +help: a previous inline code might be longer than expected + | +LL | /// you can write `File::with_options().read(true).open("foo.txt")`. This + | + +help: if you meant to use a literal backtick, escape it + | +LL | /// also avoids the need to import `OpenOptions\`. + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:214:46 + | +LL | /// `HybridBitSet`. Has no effect if `row` does not exist. + | ^ + | +help: a previous inline code might be longer than expected + | +LL | /// Subtracts `set` from `row`. `set` can be either `BitSet` or + | + +help: if you meant to use a literal backtick, escape it + | +LL | /// `HybridBitSet`. Has no effect if `row\` does not exist. + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:240:12 + | +LL | /// RWU`s can get very large, so it uses a more compact representation. + | ^ + | +help: the opening backtick of an inline code may be missing + | +LL | /// `RWU`s can get very large, so it uses a more compact representation. + | + +help: if you meant to use a literal backtick, escape it + | +LL | /// RWU\`s can get very large, so it uses a more compact representation. + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:247:15 + | +LL | /// in `U2`. + | ^ + | +help: the opening backtick of a previous inline code may be missing + | +LL | /// `'?1` is in `U3` would be canonicalized to have `?0` in `U1` and `'?1` + | + +help: if you meant to use a literal backtick, escape it + | +LL | /// in `U2\`. + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:264:42 + | +LL | /// because it contains `[type error]`. Yuck! (See issue #29857 for + | ^ + | +help: a previous inline code might be longer than expected + | +LL | /// as Trait>::Foo == $0`. Here, normalizing `` as + | + +help: if you meant to use a literal backtick, escape it + | +LL | /// because it contains `[type error]\`. Yuck! (See issue #29857 for + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:274:53 + | +LL | /// well as the second instance of `A: AutoTrait`) to suppress + | ^ + | +help: a previous inline code might be longer than expected + | +LL | /// encountered a problem (later on) with `A:` AutoTrait. So we + | + +help: if you meant to use a literal backtick, escape it + | +LL | /// well as the second instance of `A: AutoTrait\`) to suppress + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:284:40 + | +LL | /// `'a` with `'b` and not `'static`. But it will have to do for + | ^ + | + = help: the opening or closing backtick of an inline code may be missing +help: if you meant to use a literal backtick, escape it + | +LL | /// `'a` with `'b` and not `'static\`. But it will have to do for + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:293:54 + | +LL | /// `None`. Otherwise, it will return `Some(Dispatch)`. + | ^ + | +help: the opening backtick of a previous inline code may be missing + | +LL | /// The `Subscriber` may be accessed by calling [`WeakDispatch::upgrade`], + | + +help: if you meant to use a literal backtick, escape it + | +LL | /// `None`. Otherwise, it will return `Some(Dispatch)\`. + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:297:13 + | +LL | /// or `None` if it isn't. + | ^ + | + = help: the opening or closing backtick of an inline code may be missing +help: if you meant to use a literal backtick, escape it + | +LL | /// or `None\` if it isn't. + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:301:14 + | +LL | /// `on_event` should be called. + | ^ + | +help: a previous inline code might be longer than expected + | +LL | /// Called before the filtered [`Layer`]'s [`on_event`], to determine if + | + +help: if you meant to use a literal backtick, escape it + | +LL | /// `on_event\` should be called. + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:306:29 + | +LL | /// [`rebuild_interest_cache`][rebuild] is called after the value of the max + | ^ + | +help: a previous inline code might be longer than expected + | +LL | /// Therefore, if the `Filter` will change the value returned by this + | + +help: if you meant to use a literal backtick, escape it + | +LL | /// [`rebuild_interest_cache\`][rebuild] is called after the value of the max + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:5:5 + | +LL | /// ` + | ^ + | + = help: the opening or closing backtick of an inline code may be missing +help: if you meant to use a literal backtick, escape it + | +LL | /// \` + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:12:7 + | +LL | /// \` + | ^ + | +help: the opening backtick of an inline code may be missing + | +LL | /// `\` + | + +help: if you meant to use a literal backtick, escape it + | +LL | /// \\` + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:19:6 + | +LL | /// [`link1] + | ^ + | +help: the closing backtick of an inline code may be missing + | +LL | /// [`link1`] + | + +help: if you meant to use a literal backtick, escape it + | +LL | /// [\`link1] + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:23:11 + | +LL | /// [link2`] + | ^ + | +help: the opening backtick of an inline code may be missing + | +LL | /// [`link2`] + | + +help: if you meant to use a literal backtick, escape it + | +LL | /// [link2\`] + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:27:6 + | +LL | /// [`link_long](link_long) + | ^ + | +help: the closing backtick of an inline code may be missing + | +LL | /// [`link_long`](link_long) + | + +help: if you meant to use a literal backtick, escape it + | +LL | /// [\`link_long](link_long) + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:31:6 + | +LL | /// [`broken-link] + | ^ + | +help: the closing backtick of an inline code may be missing + | +LL | /// [`broken-link`] + | + +help: if you meant to use a literal backtick, escape it + | +LL | /// [\`broken-link] + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:38:8 + | +LL | /// + | ^ + | +help: the opening backtick of an inline code may be missing + | +LL | /// ` + | + +help: if you meant to use a literal backtick, escape it + | +LL | /// + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:48:6 + | +LL | /// 🦀`🦀 + | ^ + | +help: the opening backtick of an inline code may be missing + | +LL | /// `🦀`🦀 + | + +help: the closing backtick of an inline code may be missing + | +LL | /// 🦀`🦀` + | + +help: if you meant to use a literal backtick, escape it + | +LL | /// 🦀\`🦀 + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:52:5 + | +LL | /// `foo( + | ^ + | +help: the closing backtick of an inline code may be missing + | +LL | /// `foo(` + | + +help: if you meant to use a literal backtick, escape it + | +LL | /// \`foo( + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:58:14 + | +LL | /// `foo `bar` + | ^ + | +help: a previous inline code might be longer than expected + | +LL | /// `foo` `bar` + | + +help: if you meant to use a literal backtick, escape it + | +LL | /// `foo `bar\` + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:64:5 + | +LL | /// `foo( + | ^ + | +help: the closing backtick of an inline code may be missing + | +LL | /// not paragraph` + | + +help: if you meant to use a literal backtick, escape it + | +LL | /// \`foo( + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:69:83 + | +LL | /// Addition is commutative, which means that add(a, b)` is the same as `add(b, a)`. + | ^ + | +help: the opening backtick of a previous inline code may be missing + | +LL | /// Addition is commutative, which means that `add(a, b)` is the same as `add(b, a)`. + | + +help: if you meant to use a literal backtick, escape it + | +LL | /// Addition is commutative, which means that add(a, b)` is the same as `add(b, a)\`. + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:73:51 + | +LL | /// or even to add a number `n` to 42 (`add(42, b)`)! + | ^ + | +help: the opening backtick of a previous inline code may be missing + | +LL | /// You could use this function to add 42 to a number `n` (`add(n, 42)`), + | + +help: if you meant to use a literal backtick, escape it + | +LL | /// or even to add a number `n` to 42 (`add(42, b)\`)! + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:77:83 + | +LL | /// Addition is commutative, which means that `add(a, b) is the same as `add(b, a)`. + | ^ + | +help: a previous inline code might be longer than expected + | +LL | /// Addition is commutative, which means that `add(a, b)` is the same as `add(b, a)`. + | + +help: if you meant to use a literal backtick, escape it + | +LL | /// Addition is commutative, which means that `add(a, b) is the same as `add(b, a)\`. + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:81:51 + | +LL | /// or even to add a number `n` to 42 (`add(42, n)`)! + | ^ + | +help: a previous inline code might be longer than expected + | +LL | /// You could use this function to add 42 to a number `n` (`add(n, 42)`), + | + +help: if you meant to use a literal backtick, escape it + | +LL | /// or even to add a number `n` to 42 (`add(42, n)\`)! + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:85:83 + | +LL | /// Addition is commutative, which means that `add(a, b)` is the same as add(b, a)`. + | ^ + | +help: the opening backtick of an inline code may be missing + | +LL | /// Addition is commutative, which means that `add(a, b)` is the same as `add(b, a)`. + | + +help: if you meant to use a literal backtick, escape it + | +LL | /// Addition is commutative, which means that `add(a, b)` is the same as add(b, a)\`. + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:89:50 + | +LL | /// or even to add a number `n` to 42 (add(42, n)`)! + | ^ + | +help: the opening backtick of an inline code may be missing + | +LL | /// or even to add a number `n` to 42 (`add(42, n)`)! + | + +help: if you meant to use a literal backtick, escape it + | +LL | /// or even to add a number `n` to 42 (add(42, n)\`)! + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:93:74 + | +LL | /// Addition is commutative, which means that `add(a, b)` is the same as `add(b, a). + | ^ + | +help: the closing backtick of an inline code may be missing + | +LL | /// Addition is commutative, which means that `add(a, b)` is the same as `add(b, a)`. + | + +help: if you meant to use a literal backtick, escape it + | +LL | /// Addition is commutative, which means that `add(a, b)` is the same as \`add(b, a). + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:97:51 + | +LL | /// or even to add a number `n` to 42 (`add(42, n)`)! + | ^ + | +help: a previous inline code might be longer than expected + | +LL | /// You could use this function to add 42 to a number `n` (`add(n, 42)`), + | + +help: if you meant to use a literal backtick, escape it + | +LL | /// or even to add a number `n` to 42 (`add(42, n)\`)! + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:101:1 + | +LL | #[doc = "`"] + | ^^^^^^^^^^^^ + | + = help: the opening or closing backtick of an inline code may be missing + = help: if you meant to use a literal backtick, escape it + change: ` + to this: \` + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:108:1 + | +LL | #[doc = concat!("\\", "`")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: the opening backtick of an inline code may be missing + change: \` + to this: `\` + = help: if you meant to use a literal backtick, escape it + change: \` + to this: \\` + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:112:1 + | +LL | #[doc = "Addition is commutative, which means that add(a, b)` is the same as `add(b, a)`."] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: the opening backtick of a previous inline code may be missing + change: Addition is commutative, which means that add(a, b)` is the same as `add(b, a)`. + to this: Addition is commutative, which means that `add(a, b)` is the same as `add(b, a)`. + = help: if you meant to use a literal backtick, escape it + change: Addition is commutative, which means that add(a, b)` is the same as `add(b, a)`. + to this: Addition is commutative, which means that add(a, b)` is the same as `add(b, a)\`. + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:116:1 + | +LL | #[doc = "Addition is commutative, which means that `add(a, b) is the same as `add(b, a)`."] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: a previous inline code might be longer than expected + change: Addition is commutative, which means that `add(a, b) is the same as `add(b, a)`. + to this: Addition is commutative, which means that `add(a, b)` is the same as `add(b, a)`. + = help: if you meant to use a literal backtick, escape it + change: Addition is commutative, which means that `add(a, b) is the same as `add(b, a)`. + to this: Addition is commutative, which means that `add(a, b) is the same as `add(b, a)\`. + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:120:1 + | +LL | #[doc = "Addition is commutative, which means that `add(a, b)` is the same as add(b, a)`."] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: the opening backtick of an inline code may be missing + change: Addition is commutative, which means that `add(a, b)` is the same as add(b, a)`. + to this: Addition is commutative, which means that `add(a, b)` is the same as `add(b, a)`. + = help: if you meant to use a literal backtick, escape it + change: Addition is commutative, which means that `add(a, b)` is the same as add(b, a)`. + to this: Addition is commutative, which means that `add(a, b)` is the same as add(b, a)\`. + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:124:1 + | +LL | #[doc = "Addition is commutative, which means that `add(a, b)` is the same as `add(b, a)."] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: the closing backtick of an inline code may be missing + change: Addition is commutative, which means that `add(a, b)` is the same as `add(b, a). + to this: Addition is commutative, which means that `add(a, b)` is the same as `add(b, a)`. + = help: if you meant to use a literal backtick, escape it + change: Addition is commutative, which means that `add(a, b)` is the same as `add(b, a). + to this: Addition is commutative, which means that `add(a, b)` is the same as \`add(b, a). + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:129:5 + | +LL | /// `foo + | ^ + | +help: the closing backtick of an inline code may be missing + | +LL | /// `foo` + | + +help: if you meant to use a literal backtick, escape it + | +LL | /// \`foo + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:133:7 + | +LL | /// # `(heading + | ^ + | +help: the closing backtick of an inline code may be missing + | +LL | /// # `(heading` + | + +help: if you meant to use a literal backtick, escape it + | +LL | /// # \`(heading + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:135:17 + | +LL | /// ## heading2)` + | ^ + | +help: the opening backtick of an inline code may be missing + | +LL | /// ## `heading2)` + | + +help: if you meant to use a literal backtick, escape it + | +LL | /// ## heading2)\` + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:138:11 + | +LL | /// multi `( + | ^ + | +help: the closing backtick of an inline code may be missing + | +LL | /// )` heading + | + +help: if you meant to use a literal backtick, escape it + | +LL | /// multi \`( + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:144:10 + | +LL | /// para)`(graph + | ^ + | +help: the opening backtick of an inline code may be missing + | +LL | /// `para)`(graph + | + +help: the closing backtick of an inline code may be missing + | +LL | /// para)`(graph` + | + +help: if you meant to use a literal backtick, escape it + | +LL | /// para)\`(graph + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:147:10 + | +LL | /// para)`(graph2 + | ^ + | +help: the opening backtick of an inline code may be missing + | +LL | /// `para)`(graph2 + | + +help: the closing backtick of an inline code may be missing + | +LL | /// para)`(graph2` + | + +help: if you meant to use a literal backtick, escape it + | +LL | /// para)\`(graph2 + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:150:12 + | +LL | /// 1. foo)` + | ^ + | +help: the opening backtick of an inline code may be missing + | +LL | /// 1. `foo)` + | + +help: if you meant to use a literal backtick, escape it + | +LL | /// 1. foo)\` + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:152:8 + | +LL | /// 2. `(bar + | ^ + | +help: the closing backtick of an inline code may be missing + | +LL | /// 2. `(bar` + | + +help: if you meant to use a literal backtick, escape it + | +LL | /// 2. \`(bar + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:154:11 + | +LL | /// * baz)` + | ^ + | +help: the opening backtick of an inline code may be missing + | +LL | /// * `baz)` + | + +help: if you meant to use a literal backtick, escape it + | +LL | /// * baz)\` + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:156:7 + | +LL | /// * `(quux + | ^ + | +help: the closing backtick of an inline code may be missing + | +LL | /// * `(quux` + | + +help: if you meant to use a literal backtick, escape it + | +LL | /// * \`(quux + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:159:5 + | +LL | /// `#![this_is_actually_an_image(and(not), an = "attribute")] + | ^ + | +help: the closing backtick of an inline code may be missing + | +LL | /// `#`![this_is_actually_an_image(and(not), an = "attribute")] + | + +help: if you meant to use a literal backtick, escape it + | +LL | /// \`#![this_is_actually_an_image(and(not), an = "attribute")] + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:162:62 + | +LL | /// #![this_is_actually_an_image(and(not), an = "attribute")]` + | ^ + | +help: the opening backtick of an inline code may be missing + | +LL | /// `#![this_is_actually_an_image(and(not), an = "attribute")]` + | + +help: if you meant to use a literal backtick, escape it + | +LL | /// #![this_is_actually_an_image(and(not), an = "attribute")]\` + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:167:7 + | +LL | /// | `table( | )head` | + | ^ + | +help: the closing backtick of an inline code may be missing + | +LL | /// | `table(` | )head` | + | + +help: if you meant to use a literal backtick, escape it + | +LL | /// | \`table( | )head` | + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:167:22 + | +LL | /// | `table( | )head` | + | ^ + | +help: the opening backtick of an inline code may be missing + | +LL | /// | `table( | `)head` | + | + +help: if you meant to use a literal backtick, escape it + | +LL | /// | `table( | )head\` | + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:171:12 + | +LL | /// | table`( | )`body | + | ^ + | +help: the opening backtick of an inline code may be missing + | +LL | /// | `table`( | )`body | + | + +help: if you meant to use a literal backtick, escape it + | +LL | /// | table\`( | )`body | + | + + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:171:18 + | +LL | /// | table`( | )`body | + | ^ + | +help: the opening backtick of an inline code may be missing + | +LL | /// | table`( | `)`body | + | + +help: the closing backtick of an inline code may be missing + | +LL | /// | table`( | )`body` | + | + +help: if you meant to use a literal backtick, escape it + | +LL | /// | table`( | )\`body | + | + + +error: aborting due to 59 previous errors + From f5e16d53465ff22be6eb030e6ecc2fd11d8cb974 Mon Sep 17 00:00:00 2001 From: Lukas Markeffsky <@> Date: Thu, 27 Apr 2023 19:10:05 +0200 Subject: [PATCH 10/11] add more test cases --- tests/rustdoc-ui/unescaped_backticks.rs | 33 ++++ tests/rustdoc-ui/unescaped_backticks.stderr | 194 ++++++++++++++------ 2 files changed, 167 insertions(+), 60 deletions(-) diff --git a/tests/rustdoc-ui/unescaped_backticks.rs b/tests/rustdoc-ui/unescaped_backticks.rs index e7d34221916bf..f1ad7c8d4c784 100644 --- a/tests/rustdoc-ui/unescaped_backticks.rs +++ b/tests/rustdoc-ui/unescaped_backticks.rs @@ -2,6 +2,12 @@ #![allow(rustdoc::broken_intra_doc_links)] #![allow(rustdoc::invalid_html_tags)] +/// +pub fn empty() {} + +#[doc = ""] +pub fn empty2() {} + /// ` //~^ ERROR unescaped backtick pub fn single() {} @@ -307,3 +313,30 @@ pub mod rustc { //~^ ERROR unescaped backtick /// level changes. pub mod tracing {} + +macro_rules! id { + ($($tt:tt)*) => { $($tt)* } +} + +id! { + /// The Subscriber` may be accessed by calling [`WeakDispatch::upgrade`], + //~^ ERROR unescaped backtick + //~| ERROR unescaped backtick + //~| ERROR unescaped backtick + //~| ERROR unescaped backtick + /// which returns an `Option`. If all [`Dispatch`] clones that point + /// at the `Subscriber` have been dropped, [`WeakDispatch::upgrade`] will return + /// `None`. Otherwise, it will return `Some(Dispatch)`. + /// + /// Returns some reference to this `[`Subscriber`] value if it is of type `T`, + /// or `None` if it isn't. + /// + /// Called before the filtered [`Layer]'s [`on_event`], to determine if + /// `on_event` should be called. + /// + /// Therefore, if the `Filter will change the value returned by this + /// method, it is responsible for ensuring that + /// [`rebuild_interest_cache`][rebuild] is called after the value of the max + /// level changes. + pub mod tracing_macro {} +} diff --git a/tests/rustdoc-ui/unescaped_backticks.stderr b/tests/rustdoc-ui/unescaped_backticks.stderr index db4ab4b3b7ca8..e629dbc34e9b4 100644 --- a/tests/rustdoc-ui/unescaped_backticks.stderr +++ b/tests/rustdoc-ui/unescaped_backticks.stderr @@ -1,5 +1,5 @@ error: unescaped backtick - --> $DIR/unescaped_backticks.rs:180:70 + --> $DIR/unescaped_backticks.rs:186:70 | LL | /// if you want your MIR to be modified by the full MIR pipeline, or `#![custom_mir(dialect = | ^ @@ -19,7 +19,7 @@ LL | /// if you want your MIR to be modified by the full MIR pipeline, or \`#![c | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:225:13 + --> $DIR/unescaped_backticks.rs:231:13 | LL | //! `#![rustc_expected_cgu_reuse(module="spike", cfg="rpass2", kind="post-lto")] | ^ @@ -34,7 +34,7 @@ LL | //! \`#![rustc_expected_cgu_reuse(module="spike", cfg="rpass2", kin | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:230:13 + --> $DIR/unescaped_backticks.rs:236:13 | LL | /// `cfg=... | ^ @@ -49,7 +49,7 @@ LL | /// \`cfg=... | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:234:42 + --> $DIR/unescaped_backticks.rs:240:42 | LL | /// `cfg=... and not `#[cfg_attr]` | ^ @@ -64,7 +64,7 @@ LL | /// `cfg=... and not `#[cfg_attr]\` | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:186:91 + --> $DIR/unescaped_backticks.rs:192:91 | LL | /// Constructs a `TyKind::Error` type and registers a `delay_span_bug` with the given `msg to | ^ @@ -79,7 +79,7 @@ LL | /// Constructs a `TyKind::Error` type and registers a `delay_span_bug` | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:195:34 + --> $DIR/unescaped_backticks.rs:201:34 | LL | /// in `nt_to_tokenstream` | ^ @@ -94,7 +94,7 @@ LL | /// in `nt_to_tokenstream\` | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:201:62 + --> $DIR/unescaped_backticks.rs:207:62 | LL | /// that `Option` only takes up 4 bytes, because `newtype_index! reserves | ^ @@ -109,7 +109,7 @@ LL | /// that `Option` only takes up 4 bytes, because \`newtype_inde | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:209:52 + --> $DIR/unescaped_backticks.rs:215:52 | LL | /// also avoids the need to import `OpenOptions`. | ^ @@ -124,7 +124,7 @@ LL | /// also avoids the need to import `OpenOptions\`. | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:214:46 + --> $DIR/unescaped_backticks.rs:220:46 | LL | /// `HybridBitSet`. Has no effect if `row` does not exist. | ^ @@ -139,7 +139,7 @@ LL | /// `HybridBitSet`. Has no effect if `row\` does not exist. | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:240:12 + --> $DIR/unescaped_backticks.rs:246:12 | LL | /// RWU`s can get very large, so it uses a more compact representation. | ^ @@ -154,7 +154,7 @@ LL | /// RWU\`s can get very large, so it uses a more compact representation | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:247:15 + --> $DIR/unescaped_backticks.rs:253:15 | LL | /// in `U2`. | ^ @@ -169,7 +169,7 @@ LL | /// in `U2\`. | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:264:42 + --> $DIR/unescaped_backticks.rs:270:42 | LL | /// because it contains `[type error]`. Yuck! (See issue #29857 for | ^ @@ -184,7 +184,7 @@ LL | /// because it contains `[type error]\`. Yuck! (See issue #29857 for | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:274:53 + --> $DIR/unescaped_backticks.rs:280:53 | LL | /// well as the second instance of `A: AutoTrait`) to suppress | ^ @@ -199,7 +199,7 @@ LL | /// well as the second instance of `A: AutoTrait\`) to suppress | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:284:40 + --> $DIR/unescaped_backticks.rs:290:40 | LL | /// `'a` with `'b` and not `'static`. But it will have to do for | ^ @@ -211,7 +211,7 @@ LL | /// `'a` with `'b` and not `'static\`. But it will have to do for | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:293:54 + --> $DIR/unescaped_backticks.rs:299:54 | LL | /// `None`. Otherwise, it will return `Some(Dispatch)`. | ^ @@ -226,7 +226,7 @@ LL | /// `None`. Otherwise, it will return `Some(Dispatch)\`. | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:297:13 + --> $DIR/unescaped_backticks.rs:303:13 | LL | /// or `None` if it isn't. | ^ @@ -238,7 +238,7 @@ LL | /// or `None\` if it isn't. | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:301:14 + --> $DIR/unescaped_backticks.rs:307:14 | LL | /// `on_event` should be called. | ^ @@ -253,7 +253,7 @@ LL | /// `on_event\` should be called. | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:306:29 + --> $DIR/unescaped_backticks.rs:312:29 | LL | /// [`rebuild_interest_cache`][rebuild] is called after the value of the max | ^ @@ -268,7 +268,81 @@ LL | /// [`rebuild_interest_cache\`][rebuild] is called after the value of the m | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:5:5 + --> $DIR/unescaped_backticks.rs:322:5 + | +LL | / /// The Subscriber` may be accessed by calling [`WeakDispatch::upgrade`], +LL | | +LL | | +LL | | +... | +LL | | /// [`rebuild_interest_cache`][rebuild] is called after the value of the max +LL | | /// level changes. + | |______________________^ + | + = help: the opening backtick of a previous inline code may be missing + change: The Subscriber` may be accessed by calling [`WeakDispatch::upgrade`], + to this: The `Subscriber` may be accessed by calling [`WeakDispatch::upgrade`], + = help: if you meant to use a literal backtick, escape it + change: `None`. Otherwise, it will return `Some(Dispatch)`. + to this: `None`. Otherwise, it will return `Some(Dispatch)\`. + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:322:5 + | +LL | / /// The Subscriber` may be accessed by calling [`WeakDispatch::upgrade`], +LL | | +LL | | +LL | | +... | +LL | | /// [`rebuild_interest_cache`][rebuild] is called after the value of the max +LL | | /// level changes. + | |______________________^ + | + = help: the opening or closing backtick of an inline code may be missing + = help: if you meant to use a literal backtick, escape it + change: or `None` if it isn't. + to this: or `None\` if it isn't. + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:322:5 + | +LL | / /// The Subscriber` may be accessed by calling [`WeakDispatch::upgrade`], +LL | | +LL | | +LL | | +... | +LL | | /// [`rebuild_interest_cache`][rebuild] is called after the value of the max +LL | | /// level changes. + | |______________________^ + | + = help: a previous inline code might be longer than expected + change: Called before the filtered [`Layer]'s [`on_event`], to determine if + to this: Called before the filtered [`Layer`]'s [`on_event`], to determine if + = help: if you meant to use a literal backtick, escape it + change: `on_event` should be called. + to this: `on_event\` should be called. + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:322:5 + | +LL | / /// The Subscriber` may be accessed by calling [`WeakDispatch::upgrade`], +LL | | +LL | | +LL | | +... | +LL | | /// [`rebuild_interest_cache`][rebuild] is called after the value of the max +LL | | /// level changes. + | |______________________^ + | + = help: a previous inline code might be longer than expected + change: Therefore, if the `Filter will change the value returned by this + to this: Therefore, if the `Filter` will change the value returned by this + = help: if you meant to use a literal backtick, escape it + change: [`rebuild_interest_cache`][rebuild] is called after the value of the max + to this: [`rebuild_interest_cache\`][rebuild] is called after the value of the max + +error: unescaped backtick + --> $DIR/unescaped_backticks.rs:11:5 | LL | /// ` | ^ @@ -280,7 +354,7 @@ LL | /// \` | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:12:7 + --> $DIR/unescaped_backticks.rs:18:7 | LL | /// \` | ^ @@ -295,7 +369,7 @@ LL | /// \\` | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:19:6 + --> $DIR/unescaped_backticks.rs:25:6 | LL | /// [`link1] | ^ @@ -310,7 +384,7 @@ LL | /// [\`link1] | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:23:11 + --> $DIR/unescaped_backticks.rs:29:11 | LL | /// [link2`] | ^ @@ -325,7 +399,7 @@ LL | /// [link2\`] | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:27:6 + --> $DIR/unescaped_backticks.rs:33:6 | LL | /// [`link_long](link_long) | ^ @@ -340,7 +414,7 @@ LL | /// [\`link_long](link_long) | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:31:6 + --> $DIR/unescaped_backticks.rs:37:6 | LL | /// [`broken-link] | ^ @@ -355,7 +429,7 @@ LL | /// [\`broken-link] | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:38:8 + --> $DIR/unescaped_backticks.rs:44:8 | LL | /// | ^ @@ -370,7 +444,7 @@ LL | /// | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:48:6 + --> $DIR/unescaped_backticks.rs:54:6 | LL | /// 🦀`🦀 | ^ @@ -389,7 +463,7 @@ LL | /// 🦀\`🦀 | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:52:5 + --> $DIR/unescaped_backticks.rs:58:5 | LL | /// `foo( | ^ @@ -404,7 +478,7 @@ LL | /// \`foo( | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:58:14 + --> $DIR/unescaped_backticks.rs:64:14 | LL | /// `foo `bar` | ^ @@ -419,7 +493,7 @@ LL | /// `foo `bar\` | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:64:5 + --> $DIR/unescaped_backticks.rs:70:5 | LL | /// `foo( | ^ @@ -434,7 +508,7 @@ LL | /// \`foo( | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:69:83 + --> $DIR/unescaped_backticks.rs:75:83 | LL | /// Addition is commutative, which means that add(a, b)` is the same as `add(b, a)`. | ^ @@ -449,7 +523,7 @@ LL | /// Addition is commutative, which means that add(a, b)` is the same as `ad | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:73:51 + --> $DIR/unescaped_backticks.rs:79:51 | LL | /// or even to add a number `n` to 42 (`add(42, b)`)! | ^ @@ -464,7 +538,7 @@ LL | /// or even to add a number `n` to 42 (`add(42, b)\`)! | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:77:83 + --> $DIR/unescaped_backticks.rs:83:83 | LL | /// Addition is commutative, which means that `add(a, b) is the same as `add(b, a)`. | ^ @@ -479,7 +553,7 @@ LL | /// Addition is commutative, which means that `add(a, b) is the same as `ad | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:81:51 + --> $DIR/unescaped_backticks.rs:87:51 | LL | /// or even to add a number `n` to 42 (`add(42, n)`)! | ^ @@ -494,7 +568,7 @@ LL | /// or even to add a number `n` to 42 (`add(42, n)\`)! | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:85:83 + --> $DIR/unescaped_backticks.rs:91:83 | LL | /// Addition is commutative, which means that `add(a, b)` is the same as add(b, a)`. | ^ @@ -509,7 +583,7 @@ LL | /// Addition is commutative, which means that `add(a, b)` is the same as ad | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:89:50 + --> $DIR/unescaped_backticks.rs:95:50 | LL | /// or even to add a number `n` to 42 (add(42, n)`)! | ^ @@ -524,7 +598,7 @@ LL | /// or even to add a number `n` to 42 (add(42, n)\`)! | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:93:74 + --> $DIR/unescaped_backticks.rs:99:74 | LL | /// Addition is commutative, which means that `add(a, b)` is the same as `add(b, a). | ^ @@ -539,7 +613,7 @@ LL | /// Addition is commutative, which means that `add(a, b)` is the same as \` | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:97:51 + --> $DIR/unescaped_backticks.rs:103:51 | LL | /// or even to add a number `n` to 42 (`add(42, n)`)! | ^ @@ -554,7 +628,7 @@ LL | /// or even to add a number `n` to 42 (`add(42, n)\`)! | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:101:1 + --> $DIR/unescaped_backticks.rs:107:1 | LL | #[doc = "`"] | ^^^^^^^^^^^^ @@ -565,7 +639,7 @@ LL | #[doc = "`"] to this: \` error: unescaped backtick - --> $DIR/unescaped_backticks.rs:108:1 + --> $DIR/unescaped_backticks.rs:114:1 | LL | #[doc = concat!("\\", "`")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -578,7 +652,7 @@ LL | #[doc = concat!("\\", "`")] to this: \\` error: unescaped backtick - --> $DIR/unescaped_backticks.rs:112:1 + --> $DIR/unescaped_backticks.rs:118:1 | LL | #[doc = "Addition is commutative, which means that add(a, b)` is the same as `add(b, a)`."] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -591,7 +665,7 @@ LL | #[doc = "Addition is commutative, which means that add(a, b)` is the same a to this: Addition is commutative, which means that add(a, b)` is the same as `add(b, a)\`. error: unescaped backtick - --> $DIR/unescaped_backticks.rs:116:1 + --> $DIR/unescaped_backticks.rs:122:1 | LL | #[doc = "Addition is commutative, which means that `add(a, b) is the same as `add(b, a)`."] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -604,7 +678,7 @@ LL | #[doc = "Addition is commutative, which means that `add(a, b) is the same a to this: Addition is commutative, which means that `add(a, b) is the same as `add(b, a)\`. error: unescaped backtick - --> $DIR/unescaped_backticks.rs:120:1 + --> $DIR/unescaped_backticks.rs:126:1 | LL | #[doc = "Addition is commutative, which means that `add(a, b)` is the same as add(b, a)`."] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -617,7 +691,7 @@ LL | #[doc = "Addition is commutative, which means that `add(a, b)` is the same to this: Addition is commutative, which means that `add(a, b)` is the same as add(b, a)\`. error: unescaped backtick - --> $DIR/unescaped_backticks.rs:124:1 + --> $DIR/unescaped_backticks.rs:130:1 | LL | #[doc = "Addition is commutative, which means that `add(a, b)` is the same as `add(b, a)."] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -630,7 +704,7 @@ LL | #[doc = "Addition is commutative, which means that `add(a, b)` is the same to this: Addition is commutative, which means that `add(a, b)` is the same as \`add(b, a). error: unescaped backtick - --> $DIR/unescaped_backticks.rs:129:5 + --> $DIR/unescaped_backticks.rs:135:5 | LL | /// `foo | ^ @@ -645,7 +719,7 @@ LL | /// \`foo | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:133:7 + --> $DIR/unescaped_backticks.rs:139:7 | LL | /// # `(heading | ^ @@ -660,7 +734,7 @@ LL | /// # \`(heading | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:135:17 + --> $DIR/unescaped_backticks.rs:141:17 | LL | /// ## heading2)` | ^ @@ -675,7 +749,7 @@ LL | /// ## heading2)\` | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:138:11 + --> $DIR/unescaped_backticks.rs:144:11 | LL | /// multi `( | ^ @@ -690,7 +764,7 @@ LL | /// multi \`( | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:144:10 + --> $DIR/unescaped_backticks.rs:150:10 | LL | /// para)`(graph | ^ @@ -709,7 +783,7 @@ LL | /// para)\`(graph | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:147:10 + --> $DIR/unescaped_backticks.rs:153:10 | LL | /// para)`(graph2 | ^ @@ -728,7 +802,7 @@ LL | /// para)\`(graph2 | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:150:12 + --> $DIR/unescaped_backticks.rs:156:12 | LL | /// 1. foo)` | ^ @@ -743,7 +817,7 @@ LL | /// 1. foo)\` | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:152:8 + --> $DIR/unescaped_backticks.rs:158:8 | LL | /// 2. `(bar | ^ @@ -758,7 +832,7 @@ LL | /// 2. \`(bar | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:154:11 + --> $DIR/unescaped_backticks.rs:160:11 | LL | /// * baz)` | ^ @@ -773,7 +847,7 @@ LL | /// * baz)\` | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:156:7 + --> $DIR/unescaped_backticks.rs:162:7 | LL | /// * `(quux | ^ @@ -788,7 +862,7 @@ LL | /// * \`(quux | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:159:5 + --> $DIR/unescaped_backticks.rs:165:5 | LL | /// `#![this_is_actually_an_image(and(not), an = "attribute")] | ^ @@ -803,7 +877,7 @@ LL | /// \`#![this_is_actually_an_image(and(not), an = "attribute")] | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:162:62 + --> $DIR/unescaped_backticks.rs:168:62 | LL | /// #![this_is_actually_an_image(and(not), an = "attribute")]` | ^ @@ -818,7 +892,7 @@ LL | /// #![this_is_actually_an_image(and(not), an = "attribute")]\` | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:167:7 + --> $DIR/unescaped_backticks.rs:173:7 | LL | /// | `table( | )head` | | ^ @@ -833,7 +907,7 @@ LL | /// | \`table( | )head` | | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:167:22 + --> $DIR/unescaped_backticks.rs:173:22 | LL | /// | `table( | )head` | | ^ @@ -848,7 +922,7 @@ LL | /// | `table( | )head\` | | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:171:12 + --> $DIR/unescaped_backticks.rs:177:12 | LL | /// | table`( | )`body | | ^ @@ -863,7 +937,7 @@ LL | /// | table\`( | )`body | | + error: unescaped backtick - --> $DIR/unescaped_backticks.rs:171:18 + --> $DIR/unescaped_backticks.rs:177:18 | LL | /// | table`( | )`body | | ^ @@ -881,5 +955,5 @@ help: if you meant to use a literal backtick, escape it LL | /// | table`( | )\`body | | + -error: aborting due to 59 previous errors +error: aborting due to 63 previous errors From 618841b8156a85ff937b755626eae0983c05da25 Mon Sep 17 00:00:00 2001 From: John Bobbo Date: Thu, 27 Apr 2023 19:48:37 -0700 Subject: [PATCH 11/11] Use `NonNull::new_unchecked` and `NonNull::len` in `rustc_arena`. --- compiler/rustc_arena/src/lib.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_arena/src/lib.rs b/compiler/rustc_arena/src/lib.rs index 345e058e1134a..236bdb99709e8 100644 --- a/compiler/rustc_arena/src/lib.rs +++ b/compiler/rustc_arena/src/lib.rs @@ -74,7 +74,7 @@ impl ArenaChunk { #[inline] unsafe fn new(capacity: usize) -> ArenaChunk { ArenaChunk { - storage: NonNull::new(Box::into_raw(Box::new_uninit_slice(capacity))).unwrap(), + storage: NonNull::new_unchecked(Box::into_raw(Box::new_uninit_slice(capacity))), entries: 0, } } @@ -85,7 +85,7 @@ impl ArenaChunk { // The branch on needs_drop() is an -O1 performance optimization. // Without the branch, dropping TypedArena takes linear time. if mem::needs_drop::() { - let slice = &mut *(self.storage.as_mut()); + let slice = self.storage.as_mut(); ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(&mut slice[..len])); } } @@ -104,7 +104,7 @@ impl ArenaChunk { // A pointer as large as possible for zero-sized elements. ptr::invalid_mut(!0) } else { - self.start().add((*self.storage.as_ptr()).len()) + self.start().add(self.storage.len()) } } } @@ -288,7 +288,7 @@ impl TypedArena { // If the previous chunk's len is less than HUGE_PAGE // bytes, then this chunk will be least double the previous // chunk's size. - new_cap = (*last_chunk.storage.as_ptr()).len().min(HUGE_PAGE / elem_size / 2); + new_cap = last_chunk.storage.len().min(HUGE_PAGE / elem_size / 2); new_cap *= 2; } else { new_cap = PAGE / elem_size; @@ -396,7 +396,7 @@ impl DroplessArena { // If the previous chunk's len is less than HUGE_PAGE // bytes, then this chunk will be least double the previous // chunk's size. - new_cap = (*last_chunk.storage.as_ptr()).len().min(HUGE_PAGE / 2); + new_cap = last_chunk.storage.len().min(HUGE_PAGE / 2); new_cap *= 2; } else { new_cap = PAGE;