Auto merge of #57882 - euclio:unused-doc-attributes, r=estebank

overhaul unused doc comments lint

This PR contains a number of improvements to the `unused_doc_comments` lint.

- Extends the span to cover the entire comment when using sugared doc comments.
- Triggers the lint for all unused doc comments on a node, instead of just the first one.
- Triggers the lint on macro expansions, and provides a help note explaining that doc comments must be expanded by the macro.
- Adds a label pointing at the node that cannot be documented.

Furthermore, this PR fixes any instances in rustc where a macro expansion was erroneously documented.
This commit is contained in:
bors 2019-03-09 08:17:48 +00:00
commit e1b8898cfb
14 changed files with 339 additions and 160 deletions

View File

@ -4515,7 +4515,7 @@ macro_rules! rev {
)*}
}
/// intra-sign conversions
// intra-sign conversions
try_from_upper_bounded!(u16, u8);
try_from_upper_bounded!(u32, u16, u8);
try_from_upper_bounded!(u64, u32, u16, u8);

View File

@ -122,15 +122,15 @@ impl fmt::Display for HirId {
// hack to ensure that we don't try to access the private parts of `ItemLocalId` in this module
mod item_local_id_inner {
use rustc_data_structures::indexed_vec::Idx;
/// An `ItemLocalId` uniquely identifies something within a given "item-like",
/// that is within a hir::Item, hir::TraitItem, or hir::ImplItem. There is no
/// guarantee that the numerical value of a given `ItemLocalId` corresponds to
/// the node's position within the owning item in any way, but there is a
/// guarantee that the `LocalItemId`s within an owner occupy a dense range of
/// integers starting at zero, so a mapping that maps all or most nodes within
/// an "item-like" to something else can be implement by a `Vec` instead of a
/// tree or hash map.
newtype_index! {
/// An `ItemLocalId` uniquely identifies something within a given "item-like",
/// that is within a hir::Item, hir::TraitItem, or hir::ImplItem. There is no
/// guarantee that the numerical value of a given `ItemLocalId` corresponds to
/// the node's position within the owning item in any way, but there is a
/// guarantee that the `LocalItemId`s within an owner occupy a dense range of
/// integers starting at zero, so a mapping that maps all or most nodes within
/// an "item-like" to something else can be implement by a `Vec` instead of a
/// tree or hash map.
pub struct ItemLocalId { .. }
}
}

View File

@ -132,25 +132,24 @@ pub enum ScopeData {
Remainder(FirstStatementIndex)
}
/// Represents a subscope of `block` for a binding that is introduced
/// by `block.stmts[first_statement_index]`. Such subscopes represent
/// a suffix of the block. Note that each subscope does not include
/// the initializer expression, if any, for the statement indexed by
/// `first_statement_index`.
///
/// For example, given `{ let (a, b) = EXPR_1; let c = EXPR_2; ... }`:
///
/// * The subscope with `first_statement_index == 0` is scope of both
/// `a` and `b`; it does not include EXPR_1, but does include
/// everything after that first `let`. (If you want a scope that
/// includes EXPR_1 as well, then do not use `Scope::Remainder`,
/// but instead another `Scope` that encompasses the whole block,
/// e.g., `Scope::Node`.
///
/// * The subscope with `first_statement_index == 1` is scope of `c`,
/// and thus does not include EXPR_2, but covers the `...`.
newtype_index! {
/// Represents a subscope of `block` for a binding that is introduced
/// by `block.stmts[first_statement_index]`. Such subscopes represent
/// a suffix of the block. Note that each subscope does not include
/// the initializer expression, if any, for the statement indexed by
/// `first_statement_index`.
///
/// For example, given `{ let (a, b) = EXPR_1; let c = EXPR_2; ... }`:
///
/// * The subscope with `first_statement_index == 0` is scope of both
/// `a` and `b`; it does not include EXPR_1, but does include
/// everything after that first `let`. (If you want a scope that
/// includes EXPR_1 as well, then do not use `Scope::Remainder`,
/// but instead another `Scope` that encompasses the whole block,
/// e.g., `Scope::Node`.
///
/// * The subscope with `first_statement_index == 1` is scope of `c`,
/// and thus does not include EXPR_2, but covers the `...`.
pub struct FirstStatementIndex { .. }
}

View File

@ -1892,9 +1892,11 @@ pub mod tls {
rayon_core::tlv::get()
}
/// A thread local variable which stores a pointer to the current ImplicitCtxt
#[cfg(not(parallel_compiler))]
thread_local!(static TLV: Cell<usize> = Cell::new(0));
thread_local! {
/// A thread local variable which stores a pointer to the current ImplicitCtxt.
static TLV: Cell<usize> = Cell::new(0);
}
/// Sets TLV to `value` during the call to `f`.
/// It is restored to its previous value after.
@ -2011,10 +2013,11 @@ pub mod tls {
})
}
/// Stores a pointer to the GlobalCtxt if one is available.
/// This is used to access the GlobalCtxt in the deadlock handler
/// given to Rayon.
scoped_thread_local!(pub static GCX_PTR: Lock<usize>);
scoped_thread_local! {
/// Stores a pointer to the GlobalCtxt if one is available.
/// This is used to access the GlobalCtxt in the deadlock handler given to Rayon.
pub static GCX_PTR: Lock<usize>
}
/// Creates a TyCtxt and ImplicitCtxt based on the GCX_PTR thread local.
/// This is used in the deadlock handler.

View File

@ -1512,42 +1512,42 @@ impl<'tcx> InstantiatedPredicates<'tcx> {
}
}
/// "Universes" are used during type- and trait-checking in the
/// presence of `for<..>` binders to control what sets of names are
/// visible. Universes are arranged into a tree: the root universe
/// contains names that are always visible. Each child then adds a new
/// set of names that are visible, in addition to those of its parent.
/// We say that the child universe "extends" the parent universe with
/// new names.
///
/// To make this more concrete, consider this program:
///
/// ```
/// struct Foo { }
/// fn bar<T>(x: T) {
/// let y: for<'a> fn(&'a u8, Foo) = ...;
/// }
/// ```
///
/// The struct name `Foo` is in the root universe U0. But the type
/// parameter `T`, introduced on `bar`, is in an extended universe U1
/// -- i.e., within `bar`, we can name both `T` and `Foo`, but outside
/// of `bar`, we cannot name `T`. Then, within the type of `y`, the
/// region `'a` is in a universe U2 that extends U1, because we can
/// name it inside the fn type but not outside.
///
/// Universes are used to do type- and trait-checking around these
/// "forall" binders (also called **universal quantification**). The
/// idea is that when, in the body of `bar`, we refer to `T` as a
/// type, we aren't referring to any type in particular, but rather a
/// kind of "fresh" type that is distinct from all other types we have
/// actually declared. This is called a **placeholder** type, and we
/// use universes to talk about this. In other words, a type name in
/// universe 0 always corresponds to some "ground" type that the user
/// declared, but a type name in a non-zero universe is a placeholder
/// type -- an idealized representative of "types in general" that we
/// use for checking generic functions.
newtype_index! {
/// "Universes" are used during type- and trait-checking in the
/// presence of `for<..>` binders to control what sets of names are
/// visible. Universes are arranged into a tree: the root universe
/// contains names that are always visible. Each child then adds a new
/// set of names that are visible, in addition to those of its parent.
/// We say that the child universe "extends" the parent universe with
/// new names.
///
/// To make this more concrete, consider this program:
///
/// ```
/// struct Foo { }
/// fn bar<T>(x: T) {
/// let y: for<'a> fn(&'a u8, Foo) = ...;
/// }
/// ```
///
/// The struct name `Foo` is in the root universe U0. But the type
/// parameter `T`, introduced on `bar`, is in an extended universe U1
/// -- i.e., within `bar`, we can name both `T` and `Foo`, but outside
/// of `bar`, we cannot name `T`. Then, within the type of `y`, the
/// region `'a` is in a universe U2 that extends U1, because we can
/// name it inside the fn type but not outside.
///
/// Universes are used to do type- and trait-checking around these
/// "forall" binders (also called **universal quantification**). The
/// idea is that when, in the body of `bar`, we refer to `T` as a
/// type, we aren't referring to any type in particular, but rather a
/// kind of "fresh" type that is distinct from all other types we have
/// actually declared. This is called a **placeholder** type, and we
/// use universes to talk about this. In other words, a type name in
/// universe 0 always corresponds to some "ground" type that the user
/// declared, but a type name in a non-zero universe is a placeholder
/// type -- an idealized representative of "types in general" that we
/// use for checking generic functions.
pub struct UniverseIndex {
DEBUG_FORMAT = "U{}",
}

View File

@ -1082,46 +1082,46 @@ impl<'a, 'gcx, 'tcx> ParamConst {
}
}
/// A [De Bruijn index][dbi] is a standard means of representing
/// regions (and perhaps later types) in a higher-ranked setting. In
/// particular, imagine a type like this:
///
/// for<'a> fn(for<'b> fn(&'b isize, &'a isize), &'a char)
/// ^ ^ | | |
/// | | | | |
/// | +------------+ 0 | |
/// | | |
/// +--------------------------------+ 1 |
/// | |
/// +------------------------------------------+ 0
///
/// In this type, there are two binders (the outer fn and the inner
/// fn). We need to be able to determine, for any given region, which
/// fn type it is bound by, the inner or the outer one. There are
/// various ways you can do this, but a De Bruijn index is one of the
/// more convenient and has some nice properties. The basic idea is to
/// count the number of binders, inside out. Some examples should help
/// clarify what I mean.
///
/// Let's start with the reference type `&'b isize` that is the first
/// argument to the inner function. This region `'b` is assigned a De
/// Bruijn index of 0, meaning "the innermost binder" (in this case, a
/// fn). The region `'a` that appears in the second argument type (`&'a
/// isize`) would then be assigned a De Bruijn index of 1, meaning "the
/// second-innermost binder". (These indices are written on the arrays
/// in the diagram).
///
/// What is interesting is that De Bruijn index attached to a particular
/// variable will vary depending on where it appears. For example,
/// the final type `&'a char` also refers to the region `'a` declared on
/// the outermost fn. But this time, this reference is not nested within
/// any other binders (i.e., it is not an argument to the inner fn, but
/// rather the outer one). Therefore, in this case, it is assigned a
/// De Bruijn index of 0, because the innermost binder in that location
/// is the outer fn.
///
/// [dbi]: http://en.wikipedia.org/wiki/De_Bruijn_index
newtype_index! {
/// A [De Bruijn index][dbi] is a standard means of representing
/// regions (and perhaps later types) in a higher-ranked setting. In
/// particular, imagine a type like this:
///
/// for<'a> fn(for<'b> fn(&'b isize, &'a isize), &'a char)
/// ^ ^ | | |
/// | | | | |
/// | +------------+ 0 | |
/// | | |
/// +--------------------------------+ 1 |
/// | |
/// +------------------------------------------+ 0
///
/// In this type, there are two binders (the outer fn and the inner
/// fn). We need to be able to determine, for any given region, which
/// fn type it is bound by, the inner or the outer one. There are
/// various ways you can do this, but a De Bruijn index is one of the
/// more convenient and has some nice properties. The basic idea is to
/// count the number of binders, inside out. Some examples should help
/// clarify what I mean.
///
/// Let's start with the reference type `&'b isize` that is the first
/// argument to the inner function. This region `'b` is assigned a De
/// Bruijn index of 0, meaning "the innermost binder" (in this case, a
/// fn). The region `'a` that appears in the second argument type (`&'a
/// isize`) would then be assigned a De Bruijn index of 1, meaning "the
/// second-innermost binder". (These indices are written on the arrays
/// in the diagram).
///
/// What is interesting is that De Bruijn index attached to a particular
/// variable will vary depending on where it appears. For example,
/// the final type `&'a char` also refers to the region `'a` declared on
/// the outermost fn. But this time, this reference is not nested within
/// any other binders (i.e., it is not an argument to the inner fn, but
/// rather the outer one). Therefore, in this case, it is assigned a
/// De Bruijn index of 0, because the innermost binder in that location
/// is the outer fn.
///
/// [dbi]: http://en.wikipedia.org/wiki/De_Bruijn_index
pub struct DebruijnIndex {
DEBUG_FORMAT = "DebruijnIndex({})",
const INNERMOST = 0,

View File

@ -58,9 +58,10 @@ macro_rules! newtype_index {
// ---- public rules ----
// Use default constants
($v:vis struct $name:ident { .. }) => (
($(#[$attrs:meta])* $v:vis struct $name:ident { .. }) => (
newtype_index!(
// Leave out derives marker so we can use its absence to ensure it comes first
@attrs [$(#[$attrs])*]
@type [$name]
// shave off 256 indices at the end to allow space for packing these indices into enums
@max [0xFFFF_FF00]
@ -69,9 +70,10 @@ macro_rules! newtype_index {
);
// Define any constants
($v:vis struct $name:ident { $($tokens:tt)+ }) => (
($(#[$attrs:meta])* $v:vis struct $name:ident { $($tokens:tt)+ }) => (
newtype_index!(
// Leave out derives marker so we can use its absence to ensure it comes first
@attrs [$(#[$attrs])*]
@type [$name]
// shave off 256 indices at the end to allow space for packing these indices into enums
@max [0xFFFF_FF00]
@ -84,10 +86,12 @@ macro_rules! newtype_index {
// Base case, user-defined constants (if any) have already been defined
(@derives [$($derives:ident,)*]
@attrs [$(#[$attrs:meta])*]
@type [$type:ident]
@max [$max:expr]
@vis [$v:vis]
@debug_format [$debug_format:tt]) => (
$(#[$attrs])*
#[derive(Copy, PartialEq, Eq, Hash, PartialOrd, Ord, $($derives),*)]
#[rustc_layout_scalar_valid_range_end($max)]
$v struct $type {
@ -317,7 +321,8 @@ macro_rules! newtype_index {
// By not including the @derives marker in this list nor in the default args, we can force it
// to come first if it exists. When encodable isn't custom, add serialization traits by default.
(@type [$type:ident]
(@attrs [$(#[$attrs:meta])*]
@type [$type:ident]
@max [$max:expr]
@vis [$v:vis]
@debug_format [$debug_format:tt]
@ -325,6 +330,7 @@ macro_rules! newtype_index {
$($tokens:tt)*) => (
newtype_index!(
@derives [$($derives,)+ RustcEncodable,]
@attrs [$(#[$attrs])*]
@type [$type]
@max [$max]
@vis [$v]
@ -335,7 +341,8 @@ macro_rules! newtype_index {
// The case where no derives are added, but encodable is overridden. Don't
// derive serialization traits
(@type [$type:ident]
(@attrs [$(#[$attrs:meta])*]
@type [$type:ident]
@max [$max:expr]
@vis [$v:vis]
@debug_format [$debug_format:tt]
@ -343,6 +350,7 @@ macro_rules! newtype_index {
$($tokens:tt)*) => (
newtype_index!(
@derives []
@attrs [$(#[$attrs])*]
@type [$type]
@max [$max]
@vis [$v]
@ -351,13 +359,15 @@ macro_rules! newtype_index {
);
// The case where no derives are added, add serialization derives by default
(@type [$type:ident]
(@attrs [$(#[$attrs:meta])*]
@type [$type:ident]
@max [$max:expr]
@vis [$v:vis]
@debug_format [$debug_format:tt]
$($tokens:tt)*) => (
newtype_index!(
@derives [RustcEncodable,]
@attrs [$(#[$attrs])*]
@type [$type]
@max [$max]
@vis [$v]
@ -384,6 +394,7 @@ macro_rules! newtype_index {
// Rewrite final without comma to one that includes comma
(@derives [$($derives:ident,)*]
@attrs [$(#[$attrs:meta])*]
@type [$type:ident]
@max [$max:expr]
@vis [$v:vis]
@ -391,6 +402,7 @@ macro_rules! newtype_index {
$name:ident = $constant:expr) => (
newtype_index!(
@derives [$($derives,)*]
@attrs [$(#[$attrs])*]
@type [$type]
@max [$max]
@vis [$v]
@ -400,6 +412,7 @@ macro_rules! newtype_index {
// Rewrite final const without comma to one that includes comma
(@derives [$($derives:ident,)*]
@attrs [$(#[$attrs:meta])*]
@type [$type:ident]
@max [$_max:expr]
@vis [$v:vis]
@ -408,6 +421,7 @@ macro_rules! newtype_index {
const $name:ident = $constant:expr) => (
newtype_index!(
@derives [$($derives,)*]
@attrs [$(#[$attrs])*]
@type [$type]
@max [$max]
@vis [$v]
@ -417,6 +431,7 @@ macro_rules! newtype_index {
// Replace existing default for max
(@derives [$($derives:ident,)*]
@attrs [$(#[$attrs:meta])*]
@type [$type:ident]
@max [$_max:expr]
@vis [$v:vis]
@ -425,6 +440,7 @@ macro_rules! newtype_index {
$($tokens:tt)*) => (
newtype_index!(
@derives [$($derives,)*]
@attrs [$(#[$attrs])*]
@type [$type]
@max [$max]
@vis [$v]
@ -434,6 +450,7 @@ macro_rules! newtype_index {
// Replace existing default for debug_format
(@derives [$($derives:ident,)*]
@attrs [$(#[$attrs:meta])*]
@type [$type:ident]
@max [$max:expr]
@vis [$v:vis]
@ -442,6 +459,7 @@ macro_rules! newtype_index {
$($tokens:tt)*) => (
newtype_index!(
@derives [$($derives,)*]
@attrs [$(#[$attrs])*]
@type [$type]
@max [$max]
@vis [$v]
@ -451,6 +469,7 @@ macro_rules! newtype_index {
// Assign a user-defined constant
(@derives [$($derives:ident,)*]
@attrs [$(#[$attrs:meta])*]
@type [$type:ident]
@max [$max:expr]
@vis [$v:vis]
@ -462,6 +481,7 @@ macro_rules! newtype_index {
pub const $name: $type = $type::from_u32_const($constant);
newtype_index!(
@derives [$($derives,)*]
@attrs [$(#[$attrs])*]
@type [$type]
@max [$max]
@vis [$v]

View File

@ -36,7 +36,7 @@ use syntax::tokenstream::{TokenTree, TokenStream};
use syntax::ast;
use syntax::ptr::P;
use syntax::ast::Expr;
use syntax::attr;
use syntax::attr::{self, HasAttrs};
use syntax::source_map::Spanned;
use syntax::edition::Edition;
use syntax::feature_gate::{AttributeGate, AttributeTemplate, AttributeType};
@ -802,27 +802,81 @@ impl LintPass for UnusedDocComment {
}
impl UnusedDocComment {
fn warn_if_doc<'a, 'tcx,
I: Iterator<Item=&'a ast::Attribute>,
C: LintContext<'tcx>>(&self, mut attrs: I, cx: &C) {
if let Some(attr) = attrs.find(|a| a.is_value_str() && a.check_name("doc")) {
cx.struct_span_lint(UNUSED_DOC_COMMENTS, attr.span, "doc comment not used by rustdoc")
.emit();
fn warn_if_doc(
&self,
cx: &EarlyContext<'_>,
node_span: Span,
node_kind: &str,
is_macro_expansion: bool,
attrs: &[ast::Attribute]
) {
let mut attrs = attrs.into_iter().peekable();
// Accumulate a single span for sugared doc comments.
let mut sugared_span: Option<Span> = None;
while let Some(attr) = attrs.next() {
if attr.is_sugared_doc {
sugared_span = Some(
sugared_span.map_or_else(
|| attr.span,
|span| span.with_hi(attr.span.hi()),
),
);
}
if attrs.peek().map(|next_attr| next_attr.is_sugared_doc).unwrap_or_default() {
continue;
}
let span = sugared_span.take().unwrap_or_else(|| attr.span);
if attr.name() == "doc" {
let mut err = cx.struct_span_lint(UNUSED_DOC_COMMENTS, span, "unused doc comment");
err.span_label(
node_span,
format!("rustdoc does not generate documentation for {}", node_kind)
);
if is_macro_expansion {
err.help("to document an item produced by a macro, \
the macro must produce the documentation as part of its expansion");
}
err.emit();
}
}
}
}
impl EarlyLintPass for UnusedDocComment {
fn check_local(&mut self, cx: &EarlyContext<'_>, decl: &ast::Local) {
self.warn_if_doc(decl.attrs.iter(), cx);
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
if let ast::ItemKind::Mac(..) = item.node {
self.warn_if_doc(cx, item.span, "macro expansions", true, &item.attrs);
}
}
fn check_stmt(&mut self, cx: &EarlyContext<'_>, stmt: &ast::Stmt) {
let (kind, is_macro_expansion) = match stmt.node {
ast::StmtKind::Local(..) => ("statements", false),
ast::StmtKind::Item(..) => ("inner items", false),
ast::StmtKind::Mac(..) => ("macro expansions", true),
// expressions will be reported by `check_expr`.
ast::StmtKind::Semi(..) |
ast::StmtKind::Expr(..) => return,
};
self.warn_if_doc(cx, stmt.span, kind, is_macro_expansion, stmt.node.attrs());
}
fn check_arm(&mut self, cx: &EarlyContext<'_>, arm: &ast::Arm) {
self.warn_if_doc(arm.attrs.iter(), cx);
let arm_span = arm.pats[0].span.with_hi(arm.body.span.hi());
self.warn_if_doc(cx, arm_span, "match arms", false, &arm.attrs);
}
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
self.warn_if_doc(expr.attrs.iter(), cx);
self.warn_if_doc(cx, expr.span, "expressions", false, &expr.attrs);
}
}

View File

@ -66,6 +66,7 @@ macro_rules! pre_expansion_lint_passes {
($macro:path, $args:tt) => (
$macro!($args, [
KeywordIdents: KeywordIdents,
UnusedDocComment: UnusedDocComment,
]);
)
}
@ -77,7 +78,6 @@ macro_rules! early_lint_passes {
UnusedImportBraces: UnusedImportBraces,
UnsafeCode: UnsafeCode,
AnonymousParameters: AnonymousParameters,
UnusedDocComment: UnusedDocComment,
EllipsisInclusiveRangePatterns: EllipsisInclusiveRangePatterns,
NonCamelCaseTypes: NonCamelCaseTypes,
DeprecatedAttr: DeprecatedAttr::new(),

View File

@ -116,14 +116,14 @@ impl RegionValueElements {
}
}
/// A single integer representing a `Location` in the MIR control-flow
/// graph. Constructed efficiently from `RegionValueElements`.
newtype_index! {
/// A single integer representing a `Location` in the MIR control-flow
/// graph. Constructed efficiently from `RegionValueElements`.
pub struct PointIndex { DEBUG_FORMAT = "PointIndex({})" }
}
/// A single integer representing a `ty::Placeholder`.
newtype_index! {
/// A single integer representing a `ty::Placeholder`.
pub struct PlaceholderIndex { DEBUG_FORMAT = "PlaceholderIndex({})" }
}

View File

@ -23,7 +23,7 @@ pub(crate) mod indexes {
use rustc_data_structures::indexed_vec::Idx;
macro_rules! new_index {
($Index:ident, $debug_name:expr) => {
($(#[$attrs:meta])* $Index:ident, $debug_name:expr) => {
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct $Index(NonZeroUsize);
@ -44,17 +44,29 @@ pub(crate) mod indexes {
}
}
/// Index into MovePathData.move_paths
new_index!(MovePathIndex, "mp");
new_index!(
/// Index into MovePathData.move_paths
MovePathIndex,
"mp"
);
/// Index into MoveData.moves.
new_index!(MoveOutIndex, "mo");
new_index!(
/// Index into MoveData.moves.
MoveOutIndex,
"mo"
);
/// Index into MoveData.inits.
new_index!(InitIndex, "in");
new_index!(
/// Index into MoveData.inits.
InitIndex,
"in"
);
/// Index into Borrows.locations
new_index!(BorrowIndex, "bw");
new_index!(
/// Index into Borrows.locations
BorrowIndex,
"bw"
);
}
pub use self::indexes::MovePathIndex;

View File

@ -11,15 +11,15 @@ use crate::sys::stdio;
use crate::sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard};
use crate::thread::LocalKey;
/// Stdout used by print! and println! macros
thread_local! {
/// Stdout used by print! and println! macros
static LOCAL_STDOUT: RefCell<Option<Box<dyn Write + Send>>> = {
RefCell::new(None)
}
}
/// Stderr used by eprint! and eprintln! macros, and panics
thread_local! {
/// Stderr used by eprint! and eprintln! macros, and panics
static LOCAL_STDERR: RefCell<Option<Box<dyn Write + Send>>> = {
RefCell::new(None)
}

View File

@ -1,18 +1,43 @@
#![feature(stmt_expr_attributes)]
#![deny(unused_doc_comments)]
macro_rules! mac {
() => {}
}
/// foo //~ ERROR unused doc comment
mac!();
fn foo() {
/// a //~ ERROR doc comment not used by rustdoc
/// a //~ ERROR unused doc comment
let x = 12;
/// b //~ doc comment not used by rustdoc
/// multi-line //~ unused doc comment
/// doc comment
/// that is unused
match x {
/// c //~ ERROR doc comment not used by rustdoc
/// c //~ ERROR unused doc comment
1 => {},
_ => {}
}
/// foo //~ ERROR doc comment not used by rustdoc
/// foo //~ ERROR unused doc comment
unsafe {}
#[doc = "foo"] //~ ERROR unused doc comment
#[doc = "bar"] //~ ERROR unused doc comment
3;
/// bar //~ ERROR unused doc comment
mac!();
let x = /** comment */ 47; //~ ERROR unused doc comment
/// dox //~ ERROR unused doc comment
{
}
}
fn main() {

View File

@ -1,32 +1,98 @@
error: doc comment not used by rustdoc
--> $DIR/useless_comment.rs:4:5
error: unused doc comment
--> $DIR/useless_comment.rs:9:1
|
LL | /// a //~ ERROR doc comment not used by rustdoc
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | /// foo //~ ERROR unused doc comment
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | mac!();
| ------- rustdoc does not generate documentation for macro expansions
|
note: lint level defined here
--> $DIR/useless_comment.rs:1:9
--> $DIR/useless_comment.rs:3:9
|
LL | #![deny(unused_doc_comments)]
| ^^^^^^^^^^^^^^^^^^^
= help: to document an item produced by a macro, the macro must produce the documentation as part of its expansion
error: doc comment not used by rustdoc
--> $DIR/useless_comment.rs:7:5
error: unused doc comment
--> $DIR/useless_comment.rs:13:5
|
LL | /// b //~ doc comment not used by rustdoc
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | /// a //~ ERROR unused doc comment
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | let x = 12;
| ----------- rustdoc does not generate documentation for statements
error: doc comment not used by rustdoc
--> $DIR/useless_comment.rs:9:9
error: unused doc comment
--> $DIR/useless_comment.rs:16:5
|
LL | /// c //~ ERROR doc comment not used by rustdoc
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | / /// multi-line //~ unused doc comment
LL | | /// doc comment
LL | | /// that is unused
| |______________________^
LL | / match x {
LL | | /// c //~ ERROR unused doc comment
LL | | 1 => {},
LL | | _ => {}
LL | | }
| |_____- rustdoc does not generate documentation for expressions
error: doc comment not used by rustdoc
--> $DIR/useless_comment.rs:14:5
error: unused doc comment
--> $DIR/useless_comment.rs:20:9
|
LL | /// foo //~ ERROR doc comment not used by rustdoc
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | /// c //~ ERROR unused doc comment
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | 1 => {},
| ------- rustdoc does not generate documentation for match arms
error: aborting due to 4 previous errors
error: unused doc comment
--> $DIR/useless_comment.rs:25:5
|
LL | /// foo //~ ERROR unused doc comment
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | unsafe {}
| --------- rustdoc does not generate documentation for expressions
error: unused doc comment
--> $DIR/useless_comment.rs:28:5
|
LL | #[doc = "foo"] //~ ERROR unused doc comment
| ^^^^^^^^^^^^^^
LL | #[doc = "bar"] //~ ERROR unused doc comment
LL | 3;
| - rustdoc does not generate documentation for expressions
error: unused doc comment
--> $DIR/useless_comment.rs:29:5
|
LL | #[doc = "bar"] //~ ERROR unused doc comment
| ^^^^^^^^^^^^^^
LL | 3;
| - rustdoc does not generate documentation for expressions
error: unused doc comment
--> $DIR/useless_comment.rs:32:5
|
LL | /// bar //~ ERROR unused doc comment
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | mac!();
| ------- rustdoc does not generate documentation for macro expansions
|
= help: to document an item produced by a macro, the macro must produce the documentation as part of its expansion
error: unused doc comment
--> $DIR/useless_comment.rs:35:13
|
LL | let x = /** comment */ 47; //~ ERROR unused doc comment
| ^^^^^^^^^^^^^^ -- rustdoc does not generate documentation for expressions
error: unused doc comment
--> $DIR/useless_comment.rs:37:5
|
LL | /// dox //~ ERROR unused doc comment
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | / {
LL | |
LL | | }
| |_____- rustdoc does not generate documentation for expressions
error: aborting due to 10 previous errors