1
Fork 0

Auto merge of #79686 - Dylan-DPC:rollup-leama5f, r=Dylan-DPC

Rollup of 11 pull requests

Successful merges:

 - #77686 (Render Markdown in search results)
 - #79541 (Doc keyword lint pass)
 - #79602 (Fix SGX CI)
 - #79611 (Use more std:: instead of core:: in docs for consistency)
 - #79623 (Pass around Symbols instead of Idents in doctree)
 - #79627 (Update cargo)
 - #79631 (disable a ptr equality test on Miri)
 - #79638 (Use `item_name` instead of pretty printing for resolving `Self` on intra-doc links)
 - #79646 (rustc_metadata: Remove some dead code)
 - #79664 (move interpret::MemoryKind::Heap to const eval)
 - #79678 (Fix some clippy lints)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2020-12-04 04:51:49 +00:00
commit e9dd18ca74
49 changed files with 430 additions and 180 deletions

View file

@ -235,12 +235,10 @@ impl Annotatable {
pub fn derive_allowed(&self) -> bool {
match *self {
Annotatable::Stmt(ref stmt) => match stmt.kind {
ast::StmtKind::Item(ref item) => match item.kind {
ast::ItemKind::Struct(..)
| ast::ItemKind::Enum(..)
| ast::ItemKind::Union(..) => true,
_ => false,
},
ast::StmtKind::Item(ref item) => matches!(
item.kind,
ast::ItemKind::Struct(..) | ast::ItemKind::Enum(..) | ast::ItemKind::Union(..)
),
_ => false,
},
Annotatable::Item(ref item) => match item.kind {

View file

@ -1134,7 +1134,9 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
if let Some(attr) = self.take_first_attr_no_derive(&mut expr) {
// Collect the invoc regardless of whether or not attributes are permitted here
// expansion will eat the attribute so it won't error later.
attr.0.as_ref().map(|attr| self.cfg.maybe_emit_expr_attr_err(attr));
if let Some(attr) = attr.0.as_ref() {
self.cfg.maybe_emit_expr_attr_err(attr)
}
// AstFragmentKind::Expr requires the macro to emit an expression.
return self
@ -1231,7 +1233,9 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
self.cfg.configure_expr_kind(&mut expr.kind);
if let Some(attr) = self.take_first_attr_no_derive(&mut expr) {
attr.0.as_ref().map(|attr| self.cfg.maybe_emit_expr_attr_err(attr));
if let Some(attr) = attr.0.as_ref() {
self.cfg.maybe_emit_expr_attr_err(attr)
}
return self
.collect_attr(attr, Annotatable::Expr(P(expr)), AstFragmentKind::OptExpr)

View file

@ -2401,7 +2401,7 @@ impl StructField<'_> {
// Still necessary in couple of places
pub fn is_positional(&self) -> bool {
let first = self.ident.as_str().as_bytes()[0];
first >= b'0' && first <= b'9'
(b'0'..=b'9').contains(&first)
}
}

View file

@ -267,8 +267,8 @@ pub fn is_whitespace(c: char) -> bool {
pub fn is_id_start(c: char) -> bool {
// This is XID_Start OR '_' (which formally is not a XID_Start).
// We also add fast-path for ascii idents
('a' <= c && c <= 'z')
|| ('A' <= c && c <= 'Z')
('a'..='z').contains(&c)
|| ('A'..='Z').contains(&c)
|| c == '_'
|| (c > '\x7f' && unicode_xid::UnicodeXID::is_xid_start(c))
}
@ -279,9 +279,9 @@ pub fn is_id_start(c: char) -> bool {
pub fn is_id_continue(c: char) -> bool {
// This is exactly XID_Continue.
// We also add fast-path for ascii idents
('a' <= c && c <= 'z')
|| ('A' <= c && c <= 'Z')
|| ('0' <= c && c <= '9')
('a'..='z').contains(&c)
|| ('A'..='Z').contains(&c)
|| ('0'..='9').contains(&c)
|| c == '_'
|| (c > '\x7f' && unicode_xid::UnicodeXID::is_xid_continue(c))
}

View file

@ -10,7 +10,7 @@ use rustc_hir::{GenericArg, HirId, MutTy, Mutability, Path, PathSegment, QPath,
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
use rustc_span::hygiene::{ExpnKind, MacroKind};
use rustc_span::symbol::{sym, Ident, Symbol};
use rustc_span::symbol::{kw, sym, Ident, Symbol};
declare_tool_lint! {
pub rustc::DEFAULT_HASH_TYPES,
@ -267,3 +267,47 @@ impl EarlyLintPass for LintPassImpl {
}
}
}
declare_tool_lint! {
pub rustc::EXISTING_DOC_KEYWORD,
Allow,
"Check that documented keywords in std and core actually exist",
report_in_external_macro: true
}
declare_lint_pass!(ExistingDocKeyword => [EXISTING_DOC_KEYWORD]);
fn is_doc_keyword(s: Symbol) -> bool {
s <= kw::Union
}
impl<'tcx> LateLintPass<'tcx> for ExistingDocKeyword {
fn check_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::Item<'_>) {
for attr in item.attrs {
if !attr.has_name(sym::doc) {
continue;
}
if let Some(list) = attr.meta_item_list() {
for nested in list {
if nested.has_name(sym::keyword) {
let v = nested
.value_str()
.expect("#[doc(keyword = \"...\")] expected a value!");
if is_doc_keyword(v) {
return;
}
cx.struct_span_lint(EXISTING_DOC_KEYWORD, attr.span, |lint| {
lint.build(&format!(
"Found non-existing keyword `{}` used in \
`#[doc(keyword = \"...\")]`",
v,
))
.help("only existing keywords are allowed in core/std")
.emit();
});
}
}
}
}
}
}

View file

@ -463,6 +463,8 @@ fn register_internals(store: &mut LintStore) {
store.register_early_pass(|| box DefaultHashTypes::new());
store.register_lints(&LintPassImpl::get_lints());
store.register_early_pass(|| box LintPassImpl);
store.register_lints(&ExistingDocKeyword::get_lints());
store.register_late_pass(|| box ExistingDocKeyword);
store.register_lints(&TyTyKind::get_lints());
store.register_late_pass(|| box TyTyKind);
store.register_group(
@ -475,6 +477,7 @@ fn register_internals(store: &mut LintStore) {
LintId::of(LINT_PASS_IMPL_WITHOUT_MACRO),
LintId::of(TY_PASS_BY_REFERENCE),
LintId::of(USAGE_OF_QUALIFIED_TY),
LintId::of(EXISTING_DOC_KEYWORD),
],
);
}

View file

@ -1592,23 +1592,6 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
self.def_path_hash_unlocked(index, &mut def_path_hashes)
}
fn all_def_path_hashes_and_def_ids(&self) -> Vec<(DefPathHash, DefId)> {
let mut def_path_hashes = self.def_path_hash_cache.lock();
let mut def_index_to_data = |index| {
(self.def_path_hash_unlocked(index, &mut def_path_hashes), self.local_def_id(index))
};
if let Some(data) = &self.root.proc_macro_data {
std::iter::once(CRATE_DEF_INDEX)
.chain(data.macros.decode(self))
.map(def_index_to_data)
.collect()
} else {
(0..self.num_def_ids())
.map(|index| def_index_to_data(DefIndex::from_usize(index)))
.collect()
}
}
/// Get the `DepNodeIndex` corresponding this crate. The result of this
/// method is cached in the `dep_node_index` field.
fn get_crate_dep_node_index(&self, tcx: TyCtxt<'tcx>) -> DepNodeIndex {

View file

@ -456,6 +456,10 @@ impl CStore {
pub fn module_expansion_untracked(&self, def_id: DefId, sess: &Session) -> ExpnId {
self.get_crate_data(def_id.krate).module_expansion(def_id.index, sess)
}
pub fn num_def_ids(&self, cnum: CrateNum) -> usize {
self.get_crate_data(cnum).num_def_ids()
}
}
impl CrateStore for CStore {
@ -498,14 +502,6 @@ impl CrateStore for CStore {
self.get_crate_data(def.krate).def_path_hash(def.index)
}
fn all_def_path_hashes_and_def_ids(&self, cnum: CrateNum) -> Vec<(DefPathHash, DefId)> {
self.get_crate_data(cnum).all_def_path_hashes_and_def_ids()
}
fn num_def_ids(&self, cnum: CrateNum) -> usize {
self.get_crate_data(cnum).num_def_ids()
}
// See `CrateMetadataRef::def_path_hash_to_def_id` for more details
fn def_path_hash_to_def_id(
&self,

View file

@ -189,8 +189,6 @@ pub trait CrateStore {
fn def_kind(&self, def: DefId) -> DefKind;
fn def_path(&self, def: DefId) -> DefPath;
fn def_path_hash(&self, def: DefId) -> DefPathHash;
fn all_def_path_hashes_and_def_ids(&self, cnum: CrateNum) -> Vec<(DefPathHash, DefId)>;
fn num_def_ids(&self, cnum: CrateNum) -> usize;
fn def_path_hash_to_def_id(
&self,
cnum: CrateNum,

View file

@ -7,6 +7,7 @@ use std::collections::hash_map::Entry;
use std::hash::Hash;
use rustc_data_structures::fx::FxHashMap;
use std::fmt;
use rustc_ast::Mutability;
use rustc_hir::def_id::DefId;
@ -179,6 +180,28 @@ impl<K: Hash + Eq, V> interpret::AllocMap<K, V> for FxHashMap<K, V> {
crate type CompileTimeEvalContext<'mir, 'tcx> =
InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>;
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum MemoryKind {
Heap,
}
impl fmt::Display for MemoryKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
MemoryKind::Heap => write!(f, "heap allocation"),
}
}
}
impl interpret::MayLeak for MemoryKind {
#[inline(always)]
fn may_leak(self) -> bool {
match self {
MemoryKind::Heap => false,
}
}
}
impl interpret::MayLeak for ! {
#[inline(always)]
fn may_leak(self) -> bool {
@ -222,6 +245,8 @@ impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> {
impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, 'tcx> {
compile_time_machine!(<'mir, 'tcx>);
type MemoryKind = MemoryKind;
type MemoryExtra = MemoryExtra;
fn find_mir_or_eval_fn(
@ -317,7 +342,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
let ptr = ecx.memory.allocate(
Size::from_bytes(size as u64),
align,
interpret::MemoryKind::ConstHeap,
interpret::MemoryKind::Machine(MemoryKind::Heap),
);
ecx.write_scalar(Scalar::Ptr(ptr), dest)?;
}

View file

@ -25,19 +25,20 @@ use rustc_target::abi::Size;
use rustc_ast::Mutability;
use super::{AllocId, Allocation, InterpCx, MPlaceTy, Machine, MemoryKind, Scalar, ValueVisitor};
use crate::const_eval;
pub trait CompileTimeMachine<'mir, 'tcx> = Machine<
pub trait CompileTimeMachine<'mir, 'tcx, T> = Machine<
'mir,
'tcx,
MemoryKind = !,
MemoryKind = T,
PointerTag = (),
ExtraFnVal = !,
FrameExtra = (),
AllocExtra = (),
MemoryMap = FxHashMap<AllocId, (MemoryKind<!>, Allocation)>,
MemoryMap = FxHashMap<AllocId, (MemoryKind<T>, Allocation)>,
>;
struct InternVisitor<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx>> {
struct InternVisitor<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx, const_eval::MemoryKind>> {
/// The ectx from which we intern.
ecx: &'rt mut InterpCx<'mir, 'tcx, M>,
/// Previously encountered safe references.
@ -74,7 +75,7 @@ struct IsStaticOrFn;
/// `immutable` things might become mutable if `ty` is not frozen.
/// `ty` can be `None` if there is no potential interior mutability
/// to account for (e.g. for vtables).
fn intern_shallow<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx>>(
fn intern_shallow<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx, const_eval::MemoryKind>>(
ecx: &'rt mut InterpCx<'mir, 'tcx, M>,
leftover_allocations: &'rt mut FxHashSet<AllocId>,
alloc_id: AllocId,
@ -105,7 +106,7 @@ fn intern_shallow<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx>>(
// changes in this function.
match kind {
MemoryKind::Stack
| MemoryKind::ConstHeap
| MemoryKind::Machine(const_eval::MemoryKind::Heap)
| MemoryKind::Vtable
| MemoryKind::CallerLocation => {}
}
@ -141,7 +142,9 @@ fn intern_shallow<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx>>(
None
}
impl<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx>> InternVisitor<'rt, 'mir, 'tcx, M> {
impl<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx, const_eval::MemoryKind>>
InternVisitor<'rt, 'mir, 'tcx, M>
{
fn intern_shallow(
&mut self,
alloc_id: AllocId,
@ -152,8 +155,8 @@ impl<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx>> InternVisitor<'rt, 'mir
}
}
impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
for InternVisitor<'rt, 'mir, 'tcx, M>
impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx, const_eval::MemoryKind>>
ValueVisitor<'mir, 'tcx, M> for InternVisitor<'rt, 'mir, 'tcx, M>
{
type V = MPlaceTy<'tcx>;
@ -290,7 +293,7 @@ pub enum InternKind {
/// Any errors here would anyway be turned into `const_err` lints, whereas validation failures
/// are hard errors.
#[tracing::instrument(skip(ecx))]
pub fn intern_const_alloc_recursive<M: CompileTimeMachine<'mir, 'tcx>>(
pub fn intern_const_alloc_recursive<M: CompileTimeMachine<'mir, 'tcx, const_eval::MemoryKind>>(
ecx: &mut InterpCx<'mir, 'tcx, M>,
intern_kind: InternKind,
ret: MPlaceTy<'tcx>,
@ -421,7 +424,9 @@ where
Ok(())
}
impl<'mir, 'tcx: 'mir, M: super::intern::CompileTimeMachine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
impl<'mir, 'tcx: 'mir, M: super::intern::CompileTimeMachine<'mir, 'tcx, !>>
InterpCx<'mir, 'tcx, M>
{
/// A helper function that allocates memory for the layout given and gives you access to mutate
/// it. Once your own mutation code is done, the backing `Allocation` is removed from the
/// current `Memory` and returned.

View file

@ -366,9 +366,9 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) {
type PointerTag = ();
type ExtraFnVal = !;
type MemoryKind = !;
type MemoryMap = rustc_data_structures::fx::FxHashMap<AllocId, (MemoryKind<!>, Allocation)>;
const GLOBAL_KIND: Option<!> = None; // no copying of globals from `tcx` to machine memory
type MemoryMap =
rustc_data_structures::fx::FxHashMap<AllocId, (MemoryKind<Self::MemoryKind>, Allocation)>;
const GLOBAL_KIND: Option<Self::MemoryKind> = None; // no copying of globals from `tcx` to machine memory
type AllocExtra = ();
type FrameExtra = ();
@ -407,7 +407,7 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) {
_memory_extra: &Self::MemoryExtra,
_id: AllocId,
alloc: Cow<'b, Allocation>,
_kind: Option<MemoryKind<!>>,
_kind: Option<MemoryKind<Self::MemoryKind>>,
) -> (Cow<'b, Allocation<Self::PointerTag>>, Self::PointerTag) {
// We do not use a tag so we can just cheaply forward the allocation
(alloc, ())

View file

@ -27,9 +27,6 @@ use crate::util::pretty;
pub enum MemoryKind<T> {
/// Stack memory. Error if deallocated except during a stack pop.
Stack,
/// Heap memory.
/// FIXME: this variant should be in const_eval
ConstHeap,
/// Memory backing vtables. Error if ever deallocated.
Vtable,
/// Memory allocated by `caller_location` intrinsic. Error if ever deallocated.
@ -43,7 +40,6 @@ impl<T: MayLeak> MayLeak for MemoryKind<T> {
fn may_leak(self) -> bool {
match self {
MemoryKind::Stack => false,
MemoryKind::ConstHeap => false,
MemoryKind::Vtable => true,
MemoryKind::CallerLocation => true,
MemoryKind::Machine(k) => k.may_leak(),
@ -55,7 +51,6 @@ impl<T: fmt::Display> fmt::Display for MemoryKind<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
MemoryKind::Stack => write!(f, "stack variable"),
MemoryKind::ConstHeap => write!(f, "heap allocation"),
MemoryKind::Vtable => write!(f, "vtable"),
MemoryKind::CallerLocation => write!(f, "caller location"),
MemoryKind::Machine(m) => write!(f, "{}", m),

View file

@ -180,6 +180,8 @@ impl<'mir, 'tcx> ConstPropMachine<'mir, 'tcx> {
impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx> {
compile_time_machine!(<'mir, 'tcx>);
type MemoryKind = !;
type MemoryExtra = ();
fn find_mir_or_eval_fn(

View file

@ -1859,7 +1859,7 @@ impl<T: Iterator<Item = char>> Parser<T> {
}
let n2 = self.decode_hex_escape()?;
if n2 < 0xDC00 || n2 > 0xDFFF {
if !(0xDC00..=0xDFFF).contains(&n2) {
return self.error(LoneLeadingSurrogateInHexEscape);
}
let c =

View file

@ -97,7 +97,7 @@ cfg_if::cfg_if! {
let ptr = src_bytes.as_ptr() as *const __m128i;
// We don't know if the pointer is aligned to 16 bytes, so we
// use `loadu`, which supports unaligned loading.
let chunk = _mm_loadu_si128(ptr.offset(chunk_index as isize));
let chunk = _mm_loadu_si128(ptr.add(chunk_index));
// For character in the chunk, see if its byte value is < 0, which
// indicates that it's part of a UTF-8 char.
@ -253,7 +253,7 @@ fn analyze_source_file_generic(
let pos = BytePos::from_usize(i) + output_offset;
if char_len > 1 {
assert!(char_len >= 2 && char_len <= 4);
assert!((2..=4).contains(&char_len));
let mbc = MultiByteChar { pos, bytes: char_len as u8 };
multi_byte_chars.push(mbc);
}

View file

@ -1015,10 +1015,7 @@ pub enum ExternalSourceKind {
impl ExternalSource {
pub fn is_absent(&self) -> bool {
match self {
ExternalSource::Foreign { kind: ExternalSourceKind::Present(_), .. } => false,
_ => true,
}
!matches!(self, ExternalSource::Foreign { kind: ExternalSourceKind::Present(_), .. })
}
pub fn get_source(&self) -> Option<&Lrc<String>> {

View file

@ -623,7 +623,7 @@ impl SourceMap {
self.span_to_source(sp, |src, start_index, end_index| {
src.get(start_index..end_index)
.map(|s| s.to_string())
.ok_or_else(|| SpanSnippetError::IllFormedSpan(sp))
.ok_or(SpanSnippetError::IllFormedSpan(sp))
})
}
@ -640,9 +640,7 @@ impl SourceMap {
/// Returns the source snippet as `String` before the given `Span`.
pub fn span_to_prev_source(&self, sp: Span) -> Result<String, SpanSnippetError> {
self.span_to_source(sp, |src, start_index, _| {
src.get(..start_index)
.map(|s| s.to_string())
.ok_or_else(|| SpanSnippetError::IllFormedSpan(sp))
src.get(..start_index).map(|s| s.to_string()).ok_or(SpanSnippetError::IllFormedSpan(sp))
})
}

View file

@ -1363,15 +1363,13 @@ impl fmt::Display for IdentPrinter {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_raw {
f.write_str("r#")?;
} else {
if self.symbol == kw::DollarCrate {
if let Some(span) = self.convert_dollar_crate {
let converted = span.ctxt().dollar_crate_name();
if !converted.is_path_segment_keyword() {
f.write_str("::")?;
}
return fmt::Display::fmt(&converted, f);
} else if self.symbol == kw::DollarCrate {
if let Some(span) = self.convert_dollar_crate {
let converted = span.ctxt().dollar_crate_name();
if !converted.is_path_segment_keyword() {
f.write_str("::")?;
}
return fmt::Display::fmt(&converted, f);
}
}
fmt::Display::fmt(&self.symbol, f)

View file

@ -1978,9 +1978,14 @@ fn const_str_ptr() {
const B: &'static [u8; 2] = &A;
const C: *const u8 = B as *const u8;
unsafe {
// Miri does not deduplicate consts (https://github.com/rust-lang/miri/issues/131)
#[cfg(not(miri))]
{
let foo = &A as *const u8;
assert_eq!(foo, C);
}
unsafe {
assert_eq!(from_utf8_unchecked(&A), "hi");
assert_eq!(*C, A[0]);
assert_eq!(*(&B[0] as *const u8), A[0]);

View file

@ -403,7 +403,7 @@ impl<'a> Arguments<'a> {
/// ```rust
/// #![feature(fmt_as_str)]
///
/// use core::fmt::Arguments;
/// use std::fmt::Arguments;
///
/// fn write_str(_: &str) { /* ... */ }
///

View file

@ -21,7 +21,7 @@ pub struct Pending<T> {
/// # Examples
///
/// ```no_run
/// use core::future;
/// use std::future;
///
/// # async fn run() {
/// let future = future::pending();

View file

@ -3,7 +3,7 @@ use crate::future::Future;
use crate::pin::Pin;
use crate::task::{Context, Poll};
/// Creates a future that wraps a function returning `Poll`.
/// Creates a future that wraps a function returning [`Poll`].
///
/// Polling the future delegates to the wrapped function.
///
@ -13,7 +13,7 @@ use crate::task::{Context, Poll};
/// #![feature(future_poll_fn)]
/// # async fn run() {
/// use core::future::poll_fn;
/// use core::task::{Context, Poll};
/// use std::task::{Context, Poll};
///
/// fn read_line(_cx: &mut Context<'_>) -> Poll<String> {
/// Poll::Ready("Hello, World!".into())
@ -31,7 +31,7 @@ where
PollFn { f }
}
/// A Future that wraps a function returning `Poll`.
/// A Future that wraps a function returning [`Poll`].
///
/// This `struct` is created by [`poll_fn()`]. See its
/// documentation for more.

View file

@ -33,7 +33,7 @@ impl<T> Future for Ready<T> {
/// # Examples
///
/// ```
/// use core::future;
/// use std::future;
///
/// # async fn run() {
/// let a = future::ready(1);

View file

@ -189,7 +189,7 @@ impl<'a> Location<'a> {
/// # Examples
///
/// ```
/// use core::panic::Location;
/// use std::panic::Location;
///
/// /// Returns the [`Location`] at which it is called.
/// #[track_caller]

View file

@ -1,15 +1,18 @@
/// Extracts the successful type of a `Poll<T>`.
/// Extracts the successful type of a [`Poll<T>`].
///
/// This macro bakes in propagation of `Pending` signals by returning early.
/// This macro bakes in propagation of [`Pending`] signals by returning early.
///
/// [`Poll<T>`]: crate::task::Poll
/// [`Pending`]: crate::task::Poll::Pending
///
/// # Examples
///
/// ```
/// #![feature(ready_macro)]
///
/// use core::task::{ready, Context, Poll};
/// use core::future::{self, Future};
/// use core::pin::Pin;
/// use std::task::{ready, Context, Poll};
/// use std::future::{self, Future};
/// use std::pin::Pin;
///
/// pub fn do_poll(cx: &mut Context<'_>) -> Poll<()> {
/// let mut fut = future::ready(42);
@ -28,9 +31,9 @@
/// ```
/// # #![feature(ready_macro)]
/// #
/// # use core::task::{Context, Poll};
/// # use core::future::{self, Future};
/// # use core::pin::Pin;
/// # use std::task::{Context, Poll};
/// # use std::future::{self, Future};
/// # use std::pin::Pin;
/// #
/// # pub fn do_poll(cx: &mut Context<'_>) -> Poll<()> {
/// # let mut fut = future::ready(42);

View file

@ -212,6 +212,7 @@
all(target_vendor = "fortanix", target_env = "sgx"),
feature(slice_index_methods, coerce_unsized, sgx_platform)
)]
#![deny(rustc::existing_doc_keyword)]
#![cfg_attr(all(test, target_vendor = "fortanix", target_env = "sgx"), feature(fixed_size_array))]
// std is implemented with unstable features, many of which are internal
// compiler details that will never be stable

View file

@ -198,7 +198,7 @@ mod prim_bool {}
/// words, they can't return `!` from every code path. As an example, this code doesn't compile:
///
/// ```compile_fail
/// use core::ops::Add;
/// use std::ops::Add;
///
/// fn foo() -> impl Add<u32> {
/// unimplemented!()
@ -208,7 +208,7 @@ mod prim_bool {}
/// But this code does:
///
/// ```
/// use core::ops::Add;
/// use std::ops::Add;
///
/// fn foo() -> impl Add<u32> {
/// if true {

View file

@ -1,6 +1,5 @@
use std::env::*;
use std::ffi::{OsStr, OsString};
use std::path::PathBuf;
use rand::distributions::Alphanumeric;
use rand::{thread_rng, Rng};
@ -92,6 +91,8 @@ fn env_home_dir() {
cfg_if::cfg_if! {
if #[cfg(unix)] {
use std::path::PathBuf;
let oldhome = var_to_os_string(var("HOME"));
set_var("HOME", "/home/MountainView");
@ -109,6 +110,8 @@ fn env_home_dir() {
if let Some(oldhome) = oldhome { set_var("HOME", oldhome); }
} else if #[cfg(windows)] {
use std::path::PathBuf;
let oldhome = var_to_os_string(var("HOME"));
let olduserprofile = var_to_os_string(var("USERPROFILE"));

View file

@ -1000,7 +1000,7 @@ impl<'tcx> Clean<FnDecl> for (DefId, ty::PolyFnSig<'tcx>) {
.iter()
.map(|t| Argument {
type_: t.clean(cx),
name: names.next().map_or(String::new(), |name| name.to_string()),
name: names.next().map_or_else(|| String::new(), |name| name.to_string()),
})
.collect(),
},
@ -1974,16 +1974,13 @@ impl Clean<BareFunctionDecl> for hir::BareFnTy<'_> {
}
}
impl Clean<Vec<Item>> for (&hir::Item<'_>, Option<Ident>) {
impl Clean<Vec<Item>> for (&hir::Item<'_>, Option<Symbol>) {
fn clean(&self, cx: &DocContext<'_>) -> Vec<Item> {
use hir::ItemKind;
let (item, renamed) = self;
let def_id = cx.tcx.hir().local_def_id(item.hir_id).to_def_id();
let mut name = match renamed {
Some(ident) => ident.name,
None => cx.tcx.hir().name(item.hir_id),
};
let mut name = renamed.unwrap_or_else(|| cx.tcx.hir().name(item.hir_id));
cx.with_param_env(def_id, || {
let kind = match item.kind {
ItemKind::Static(ty, mutability, body_id) => StaticItem(Static {
@ -2276,7 +2273,7 @@ impl Clean<Vec<Item>> for doctree::Import<'_> {
}
}
impl Clean<Item> for (&hir::ForeignItem<'_>, Option<Ident>) {
impl Clean<Item> for (&hir::ForeignItem<'_>, Option<Symbol>) {
fn clean(&self, cx: &DocContext<'_>) -> Item {
let (item, renamed) = self;
cx.with_param_env(cx.tcx.hir().local_def_id(item.hir_id).to_def_id(), || {
@ -2310,7 +2307,7 @@ impl Clean<Item> for (&hir::ForeignItem<'_>, Option<Ident>) {
Item::from_hir_id_and_parts(
item.hir_id,
Some(renamed.unwrap_or(item.ident).name),
Some(renamed.unwrap_or(item.ident.name)),
kind,
cx,
)
@ -2318,10 +2315,10 @@ impl Clean<Item> for (&hir::ForeignItem<'_>, Option<Ident>) {
}
}
impl Clean<Item> for (&hir::MacroDef<'_>, Option<Ident>) {
impl Clean<Item> for (&hir::MacroDef<'_>, Option<Symbol>) {
fn clean(&self, cx: &DocContext<'_>) -> Item {
let (item, renamed) = self;
let name = renamed.unwrap_or(item.ident).name;
let name = renamed.unwrap_or(item.ident.name);
let tts = item.ast.body.inner_tokens().trees().collect::<Vec<_>>();
// Extract the spans of all matchers. They represent the "interface" of the macro.
let matchers = tts.chunks(4).map(|arm| arm[0].span()).collect::<Vec<_>>();

View file

@ -13,7 +13,6 @@ use rustc_hir::{
};
use rustc_interface::interface;
use rustc_middle::hir::map::Map;
use rustc_middle::middle::cstore::CrateStore;
use rustc_middle::middle::privacy::AccessLevels;
use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
use rustc_resolve as resolve;

View file

@ -3,7 +3,7 @@
crate use self::StructType::*;
use rustc_ast as ast;
use rustc_span::{self, symbol::Ident, Span, Symbol};
use rustc_span::{self, Span, Symbol};
use rustc_hir as hir;
@ -16,9 +16,9 @@ crate struct Module<'hir> {
crate mods: Vec<Module<'hir>>,
crate id: hir::HirId,
// (item, renamed)
crate items: Vec<(&'hir hir::Item<'hir>, Option<Ident>)>,
crate foreigns: Vec<(&'hir hir::ForeignItem<'hir>, Option<Ident>)>,
crate macros: Vec<(&'hir hir::MacroDef<'hir>, Option<Ident>)>,
crate items: Vec<(&'hir hir::Item<'hir>, Option<Symbol>)>,
crate foreigns: Vec<(&'hir hir::ForeignItem<'hir>, Option<Symbol>)>,
crate macros: Vec<(&'hir hir::MacroDef<'hir>, Option<Symbol>)>,
crate is_crate: bool,
}

View file

@ -14,13 +14,13 @@ use crate::config::RenderInfo;
use crate::fold::DocFolder;
use crate::formats::item_type::ItemType;
use crate::formats::Impl;
use crate::html::markdown::short_markdown_summary;
use crate::html::render::cache::{extern_location, get_index_search_type, ExternalLocation};
use crate::html::render::IndexItem;
use crate::html::render::{plain_text_summary, shorten};
thread_local!(crate static CACHE_KEY: RefCell<Arc<Cache>> = Default::default());
/// This cache is used to store information about the `clean::Crate` being
/// This cache is used to store information about the [`clean::Crate`] being
/// rendered in order to provide more useful documentation. This contains
/// information like all implementors of a trait, all traits a type implements,
/// documentation for all known traits, etc.
@ -313,7 +313,9 @@ impl DocFolder for Cache {
ty: item.type_(),
name: s.to_string(),
path: path.join("::"),
desc: shorten(plain_text_summary(item.doc_value())),
desc: item
.doc_value()
.map_or_else(|| String::new(), short_markdown_summary),
parent,
parent_idx: None,
search_type: get_index_search_type(&item),

View file

@ -17,8 +17,6 @@
//! // ... something using html
//! ```
#![allow(non_camel_case_types)]
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def_id::DefId;
use rustc_hir::HirId;
@ -1037,7 +1035,95 @@ impl MarkdownSummaryLine<'_> {
}
}
/// Renders a subset of Markdown in the first paragraph of the provided Markdown.
///
/// - *Italics*, **bold**, and `inline code` styles **are** rendered.
/// - Headings and links are stripped (though the text *is* rendered).
/// - HTML, code blocks, and everything else are ignored.
///
/// Returns a tuple of the rendered HTML string and whether the output was shortened
/// due to the provided `length_limit`.
fn markdown_summary_with_limit(md: &str, length_limit: usize) -> (String, bool) {
if md.is_empty() {
return (String::new(), false);
}
let mut s = String::with_capacity(md.len() * 3 / 2);
let mut text_length = 0;
let mut stopped_early = false;
fn push(s: &mut String, text_length: &mut usize, text: &str) {
s.push_str(text);
*text_length += text.len();
};
'outer: for event in Parser::new_ext(md, Options::ENABLE_STRIKETHROUGH) {
match &event {
Event::Text(text) => {
for word in text.split_inclusive(char::is_whitespace) {
if text_length + word.len() >= length_limit {
stopped_early = true;
break 'outer;
}
push(&mut s, &mut text_length, word);
}
}
Event::Code(code) => {
if text_length + code.len() >= length_limit {
stopped_early = true;
break;
}
s.push_str("<code>");
push(&mut s, &mut text_length, code);
s.push_str("</code>");
}
Event::Start(tag) => match tag {
Tag::Emphasis => s.push_str("<em>"),
Tag::Strong => s.push_str("<strong>"),
Tag::CodeBlock(..) => break,
_ => {}
},
Event::End(tag) => match tag {
Tag::Emphasis => s.push_str("</em>"),
Tag::Strong => s.push_str("</strong>"),
Tag::Paragraph => break,
_ => {}
},
Event::HardBreak | Event::SoftBreak => {
if text_length + 1 >= length_limit {
stopped_early = true;
break;
}
push(&mut s, &mut text_length, " ");
}
_ => {}
}
}
(s, stopped_early)
}
/// Renders a shortened first paragraph of the given Markdown as a subset of Markdown,
/// making it suitable for contexts like the search index.
///
/// Will shorten to 59 or 60 characters, including an ellipsis (…) if it was shortened.
///
/// See [`markdown_summary_with_limit`] for details about what is rendered and what is not.
crate fn short_markdown_summary(markdown: &str) -> String {
let (mut s, was_shortened) = markdown_summary_with_limit(markdown, 59);
if was_shortened {
s.push('…');
}
s
}
/// Renders the first paragraph of the provided markdown as plain text.
/// Useful for alt-text.
///
/// - Headings, links, and formatting are stripped.
/// - Inline code is rendered as-is, surrounded by backticks.

View file

@ -1,4 +1,4 @@
use super::plain_text_summary;
use super::{plain_text_summary, short_markdown_summary};
use super::{ErrorCodes, IdMap, Ignore, LangString, Markdown, MarkdownHtml};
use rustc_span::edition::{Edition, DEFAULT_EDITION};
use std::cell::RefCell;
@ -204,6 +204,33 @@ fn test_header_ids_multiple_blocks() {
);
}
#[test]
fn test_short_markdown_summary() {
fn t(input: &str, expect: &str) {
let output = short_markdown_summary(input);
assert_eq!(output, expect, "original: {}", input);
}
t("hello [Rust](https://www.rust-lang.org) :)", "hello Rust :)");
t("*italic*", "<em>italic</em>");
t("**bold**", "<strong>bold</strong>");
t("Multi-line\nsummary", "Multi-line summary");
t("Hard-break \nsummary", "Hard-break summary");
t("hello [Rust] :)\n\n[Rust]: https://www.rust-lang.org", "hello Rust :)");
t("hello [Rust](https://www.rust-lang.org \"Rust\") :)", "hello Rust :)");
t("code `let x = i32;` ...", "code <code>let x = i32;</code> ...");
t("type `Type<'static>` ...", "type <code>Type<'static></code> ...");
t("# top header", "top header");
t("## header", "header");
t("first paragraph\n\nsecond paragraph", "first paragraph");
t("```\nfn main() {}\n```", "");
t("<div>hello</div>", "");
t(
"a *very*, **very** long first paragraph. it has lots of `inline code: Vec<T>`. and it has a [link](https://www.rust-lang.org).\nthat was a soft line break! \nthat was a hard one\n\nsecond paragraph.",
"a <em>very</em>, <strong>very</strong> long first paragraph. it has lots of …",
);
}
#[test]
fn test_plain_text_summary() {
fn t(input: &str, expect: &str) {
@ -224,6 +251,10 @@ fn test_plain_text_summary() {
t("first paragraph\n\nsecond paragraph", "first paragraph");
t("```\nfn main() {}\n```", "");
t("<div>hello</div>", "");
t(
"a *very*, **very** long first paragraph. it has lots of `inline code: Vec<T>`. and it has a [link](https://www.rust-lang.org).\nthat was a soft line break! \nthat was a hard one\n\nsecond paragraph.",
"a very, very long first paragraph. it has lots of `inline code: Vec<T>`. and it has a link. that was a soft line break! that was a hard one",
);
}
#[test]

View file

@ -9,7 +9,7 @@ use crate::clean::types::GetDefId;
use crate::clean::{self, AttributesExt};
use crate::formats::cache::Cache;
use crate::formats::item_type::ItemType;
use crate::html::render::{plain_text_summary, shorten};
use crate::html::markdown::short_markdown_summary;
use crate::html::render::{Generic, IndexItem, IndexItemFunctionType, RenderType, TypeWithKind};
/// Indicates where an external crate can be found.
@ -78,7 +78,7 @@ crate fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String {
ty: item.type_(),
name: item.name.clone().unwrap(),
path: fqp[..fqp.len() - 1].join("::"),
desc: shorten(plain_text_summary(item.doc_value())),
desc: item.doc_value().map_or_else(|| String::new(), short_markdown_summary),
parent: Some(did),
parent_idx: None,
search_type: get_index_search_type(&item),
@ -127,7 +127,7 @@ crate fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String {
let crate_doc = krate
.module
.as_ref()
.map(|module| shorten(plain_text_summary(module.doc_value())))
.map(|module| module.doc_value().map_or_else(|| String::new(), short_markdown_summary))
.unwrap_or_default();
#[derive(Serialize)]

View file

@ -76,7 +76,9 @@ use crate::html::format::fmt_impl_for_trait_page;
use crate::html::format::Function;
use crate::html::format::{href, print_default_space, print_generic_bounds, WhereClause};
use crate::html::format::{print_abi_with_space, Buffer, PrintWithSpace};
use crate::html::markdown::{self, ErrorCodes, IdMap, Markdown, MarkdownHtml, MarkdownSummaryLine};
use crate::html::markdown::{
self, plain_text_summary, ErrorCodes, IdMap, Markdown, MarkdownHtml, MarkdownSummaryLine,
};
use crate::html::sources;
use crate::html::{highlight, layout, static_files};
use cache::{build_index, ExternalLocation};
@ -1604,9 +1606,10 @@ impl Context {
Some(ref s) => s.to_string(),
};
let short = short.to_string();
map.entry(short)
.or_default()
.push((myname, Some(plain_text_summary(item.doc_value()))));
map.entry(short).or_default().push((
myname,
Some(item.doc_value().map_or_else(|| String::new(), plain_text_summary)),
));
}
if self.shared.sort_modules_alphabetically {
@ -1810,36 +1813,6 @@ fn full_path(cx: &Context, item: &clean::Item) -> String {
s
}
/// Renders the first paragraph of the given markdown as plain text, making it suitable for
/// contexts like alt-text or the search index.
///
/// If no markdown is supplied, the empty string is returned.
///
/// See [`markdown::plain_text_summary`] for further details.
#[inline]
crate fn plain_text_summary(s: Option<&str>) -> String {
s.map(markdown::plain_text_summary).unwrap_or_default()
}
crate fn shorten(s: String) -> String {
if s.chars().count() > 60 {
let mut len = 0;
let mut ret = s
.split_whitespace()
.take_while(|p| {
// + 1 for the added character after the word.
len += p.chars().count() + 1;
len < 60
})
.collect::<Vec<_>>()
.join(" ");
ret.push('…');
ret
} else {
s
}
}
fn document(w: &mut Buffer, cx: &Context, item: &clean::Item, parent: Option<&clean::Item>) {
if let Some(ref name) = item.name {
info!("Documenting {}", name);

View file

@ -1611,7 +1611,7 @@ function defocusSearchBar() {
item.displayPath + "<span class=\"" + type + "\">" +
name + "</span></a></td><td>" +
"<a href=\"" + item.href + "\">" +
"<span class=\"desc\">" + escape(item.desc) +
"<span class=\"desc\">" + item.desc +
"&nbsp;</span></a></td></tr>";
});
output += "</table>";
@ -2013,7 +2013,9 @@ function defocusSearchBar() {
}
var link = document.createElement("a");
link.href = rootPath + crates[i] + "/index.html";
link.title = rawSearchIndex[crates[i]].doc;
// The summary in the search index has HTML, so we need to
// dynamically render it as plaintext.
link.title = convertHTMLToPlaintext(rawSearchIndex[crates[i]].doc);
link.className = klass;
link.textContent = crates[i];
@ -2026,6 +2028,23 @@ function defocusSearchBar() {
}
};
/**
* Convert HTML to plaintext:
*
* * Replace "<code>foo</code>" with "`foo`"
* * Strip all other HTML tags
*
* Used by the dynamic sidebar crate list renderer.
*
* @param {[string]} html [The HTML to convert]
* @return {[string]} [The resulting plaintext]
*/
function convertHTMLToPlaintext(html) {
var x = document.createElement("div");
x.innerHTML = html.replace('<code>', '`').replace('</code>', '`');
return x.innerText;
}
// delayed sidebar rendering.
window.initSidebarItems = function(items) {

View file

@ -15,6 +15,7 @@
#![feature(never_type)]
#![feature(once_cell)]
#![feature(type_ascription)]
#![feature(split_inclusive)]
#![recursion_limit = "256"]
#[macro_use]

View file

@ -847,12 +847,17 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
// FIXME(jynelson): this shouldn't go through stringification, rustdoc should just use the DefId directly
let self_name = self_id.and_then(|self_id| {
use ty::TyKind;
if matches!(self.cx.tcx.def_kind(self_id), DefKind::Impl) {
// using `ty.to_string()` directly has issues with shortening paths
// using `ty.to_string()` (or any variant) has issues with raw idents
let ty = self.cx.tcx.type_of(self_id);
let name = ty::print::with_crate_prefix(|| ty.to_string());
debug!("using type_of(): {}", name);
Some(name)
let name = match ty.kind() {
TyKind::Adt(def, _) => Some(self.cx.tcx.item_name(def.did).to_string()),
other if other.is_primitive() => Some(ty.to_string()),
_ => None,
};
debug!("using type_of(): {:?}", name);
name
} else {
let name = self.cx.tcx.opt_item_name(self_id).map(|sym| sym.to_string());
debug!("using item_name(): {:?}", name);

View file

@ -10,7 +10,7 @@ use rustc_hir::Node;
use rustc_middle::middle::privacy::AccessLevel;
use rustc_middle::ty::TyCtxt;
use rustc_span::source_map::Spanned;
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::symbol::{kw, sym, Symbol};
use rustc_span::{self, Span};
use std::mem;
@ -116,7 +116,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
&mut self,
id: hir::HirId,
res: Res,
renamed: Option<Ident>,
renamed: Option<Symbol>,
glob: bool,
om: &mut Module<'tcx>,
please_inline: bool,
@ -226,11 +226,11 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
fn visit_item(
&mut self,
item: &'tcx hir::Item<'_>,
renamed: Option<Ident>,
renamed: Option<Symbol>,
om: &mut Module<'tcx>,
) {
debug!("visiting item {:?}", item);
let ident = renamed.unwrap_or(item.ident);
let name = renamed.unwrap_or(item.ident.name);
if item.vis.node.is_pub() {
let def_id = self.cx.tcx.hir().local_def_id(item.hir_id);
@ -266,7 +266,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
}
_ => false,
});
let ident = if is_glob { None } else { Some(ident) };
let ident = if is_glob { None } else { Some(name) };
if self.maybe_inline_local(
item.hir_id,
path.res,
@ -280,7 +280,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
}
om.imports.push(Import {
name: ident.name,
name,
id: item.hir_id,
vis: &item.vis,
attrs: &item.attrs,
@ -296,7 +296,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
&item.vis,
item.hir_id,
m,
Some(ident.name),
Some(name),
));
}
hir::ItemKind::Fn(..)
@ -312,7 +312,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
hir::ItemKind::Const(..) => {
// Underscore constants do not correspond to a nameable item and
// so are never useful in documentation.
if ident.name != kw::Underscore {
if name != kw::Underscore {
om.items.push((item, renamed));
}
}
@ -329,7 +329,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
fn visit_foreign_item(
&mut self,
item: &'tcx hir::ForeignItem<'_>,
renamed: Option<Ident>,
renamed: Option<Symbol>,
om: &mut Module<'tcx>,
) {
// If inlining we only want to include public functions.

View file

@ -1,2 +1,2 @@
/// Foo
/// Docs for Foo
pub struct Foo;

View file

@ -0,0 +1,21 @@
// ignore-tidy-linelength
const QUERY = ['summaries', 'summaries::Sidebar', 'summaries::Sidebar2'];
const EXPECTED = [
{
'others': [
{ 'path': '', 'name': 'summaries', 'desc': 'This <em>summary</em> has a link and <code>code</code>.' },
],
},
{
'others': [
{ 'path': 'summaries', 'name': 'Sidebar', 'desc': 'This <code>code</code> will be rendered in a code tag.' },
],
},
{
'others': [
{ 'path': 'summaries', 'name': 'Sidebar2', 'desc': '' },
],
},
];

View file

@ -0,0 +1,18 @@
#![crate_type = "lib"]
#![crate_name = "summaries"]
//! This *summary* has a [link] and `code`.
//!
//! This is the second paragraph.
//!
//! [link]: https://example.com
/// This `code` will be rendered in a code tag.
///
/// This text should not be rendered.
pub struct Sidebar;
/// ```text
/// this block should not be rendered
/// ```
pub struct Sidebar2;

View file

@ -0,0 +1,13 @@
#![deny(broken_intra_doc_links)]
pub mod r#impl {
pub struct S;
impl S {
/// See [Self::b].
// @has raw_ident_self/impl/struct.S.html
// @has - '//a[@href="../../raw_ident_self/impl/struct.S.html#method.b"]' 'Self::b'
pub fn a() {}
pub fn b() {}
}
}

View file

@ -1,21 +1,22 @@
#![crate_type = "lib"]
#![crate_name = "summaries"]
//! This summary has a [link] and `code`.
//! This *summary* has a [link] and `code`.
//!
//! This is the second paragraph.
//!
//! [link]: https://example.com
// @has search-index.js 'This summary has a link and `code`.'
// @has search-index.js 'This <em>summary</em> has a link and <code>code</code>.'
// @!has - 'second paragraph'
/// This `code` should be in backticks.
/// This `code` will be rendered in a code tag.
///
/// This text should not be rendered.
pub struct Sidebar;
// @has summaries/sidebar-items.js 'This `code` should be in backticks.'
// @has search-index.js 'This <code>code</code> will be rendered in a code tag.'
// @has summaries/sidebar-items.js 'This `code` will be rendered in a code tag.'
// @!has - 'text should not be rendered'
/// ```text

View file

@ -0,0 +1,11 @@
// compile-flags: -Z unstable-options
#![feature(rustc_private)]
#![feature(doc_keyword)]
#![crate_type = "lib"]
#![deny(rustc::existing_doc_keyword)]
#[doc(keyword = "tadam")] //~ ERROR
mod tadam {}

View file

@ -0,0 +1,15 @@
error: Found non-existing keyword `tadam` used in `#[doc(keyword = "...")]`
--> $DIR/existing_doc_keyword.rs:10:1
|
LL | #[doc(keyword = "tadam")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: the lint level is defined here
--> $DIR/existing_doc_keyword.rs:8:9
|
LL | #![deny(rustc::existing_doc_keyword)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: only existing keywords are allowed in core/std
error: aborting due to previous error

@ -1 +1 @@
Subproject commit bfca1cd22bf514d5f2b6c1089b0ded0ba7dfaa6e
Subproject commit 63d0fe43449adcb316d34d98a982b597faca4178