-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Auto merge of #10497 - y21:outer_doc_comment_bang, r=dswij
new lint: suspicious_doc_comments Fixes #10485. This PR adds a new lint (`suspicious_doc_comments`) that triggers when the user writes `///!` or `/**!`. This is almost certainly a mistake and the user probably meant to write an inner doc comment (`//!`, `/*!`) to document the module or crate that this comment is contained in. changelog: [`suspicious_doc_comments`]: new lint
- Loading branch information
Showing
9 changed files
with
427 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and_then}; | ||
use if_chain::if_chain; | ||
use rustc_ast::{token::CommentKind, AttrKind, AttrStyle, Attribute, Item}; | ||
use rustc_errors::Applicability; | ||
use rustc_lint::{EarlyContext, EarlyLintPass}; | ||
use rustc_session::{declare_lint_pass, declare_tool_lint}; | ||
use rustc_span::Span; | ||
|
||
declare_clippy_lint! { | ||
/// ### What it does | ||
/// Detects the use of outer doc comments (`///`, `/**`) followed by a bang (`!`): `///!` | ||
/// | ||
/// ### Why is this bad? | ||
/// Triple-slash comments (known as "outer doc comments") apply to items that follow it. | ||
/// An outer doc comment followed by a bang (i.e. `///!`) has no specific meaning. | ||
/// | ||
/// The user most likely meant to write an inner doc comment (`//!`, `/*!`), which | ||
/// applies to the parent item (i.e. the item that the comment is contained in, | ||
/// usually a module or crate). | ||
/// | ||
/// ### Known problems | ||
/// Inner doc comments can only appear before items, so there are certain cases where the suggestion | ||
/// made by this lint is not valid code. For example: | ||
/// ```rs | ||
/// fn foo() {} | ||
/// ///! | ||
/// fn bar() {} | ||
/// ``` | ||
/// This lint detects the doc comment and suggests changing it to `//!`, but an inner doc comment | ||
/// is not valid at that position. | ||
/// | ||
/// ### Example | ||
/// In this example, the doc comment is attached to the *function*, rather than the *module*. | ||
/// ```rust | ||
/// pub mod util { | ||
/// ///! This module contains utility functions. | ||
/// | ||
/// pub fn dummy() {} | ||
/// } | ||
/// ``` | ||
/// | ||
/// Use instead: | ||
/// ```rust | ||
/// pub mod util { | ||
/// //! This module contains utility functions. | ||
/// | ||
/// pub fn dummy() {} | ||
/// } | ||
/// ``` | ||
#[clippy::version = "1.70.0"] | ||
pub SUSPICIOUS_DOC_COMMENTS, | ||
suspicious, | ||
"suspicious usage of (outer) doc comments" | ||
} | ||
declare_lint_pass!(SuspiciousDocComments => [SUSPICIOUS_DOC_COMMENTS]); | ||
|
||
const WARNING: &str = "this is an outer doc comment and does not apply to the parent module or crate"; | ||
const HELP: &str = "use an inner doc comment to document the parent module or crate"; | ||
|
||
impl EarlyLintPass for SuspiciousDocComments { | ||
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { | ||
let replacements = collect_doc_comment_replacements(&item.attrs); | ||
|
||
if let Some(((lo_span, _), (hi_span, _))) = replacements.first().zip(replacements.last()) { | ||
let span = lo_span.to(*hi_span); | ||
|
||
span_lint_and_then(cx, SUSPICIOUS_DOC_COMMENTS, span, WARNING, |diag| { | ||
multispan_sugg_with_applicability(diag, HELP, Applicability::MaybeIncorrect, replacements); | ||
}); | ||
} | ||
} | ||
} | ||
|
||
fn collect_doc_comment_replacements(attrs: &[Attribute]) -> Vec<(Span, String)> { | ||
attrs | ||
.iter() | ||
.filter_map(|attr| { | ||
if_chain! { | ||
if let AttrKind::DocComment(com_kind, sym) = attr.kind; | ||
if let AttrStyle::Outer = attr.style; | ||
if let Some(com) = sym.as_str().strip_prefix('!'); | ||
then { | ||
let sugg = match com_kind { | ||
CommentKind::Line => format!("//!{com}"), | ||
CommentKind::Block => format!("/*!{com}*/") | ||
}; | ||
Some((attr.span, sugg)) | ||
} else { | ||
None | ||
} | ||
} | ||
}) | ||
.collect() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
// run-rustfix | ||
#![allow(unused)] | ||
#![warn(clippy::suspicious_doc_comments)] | ||
|
||
//! Real module documentation. | ||
//! Fake module documentation. | ||
fn baz() {} | ||
|
||
pub mod singleline_outer_doc { | ||
//! This module contains useful functions. | ||
pub fn bar() {} | ||
} | ||
|
||
pub mod singleline_inner_doc { | ||
//! This module contains useful functions. | ||
pub fn bar() {} | ||
} | ||
|
||
pub mod multiline_outer_doc { | ||
/*! This module contains useful functions. | ||
*/ | ||
|
||
pub fn bar() {} | ||
} | ||
|
||
pub mod multiline_inner_doc { | ||
/*! This module contains useful functions. | ||
*/ | ||
|
||
pub fn bar() {} | ||
} | ||
|
||
pub mod multiline_outer_doc2 { | ||
//! This module | ||
//! contains | ||
//! useful functions. | ||
pub fn bar() {} | ||
} | ||
|
||
pub mod multiline_outer_doc3 { | ||
//! a | ||
//! b | ||
/// c | ||
pub fn bar() {} | ||
} | ||
|
||
pub mod multiline_outer_doc4 { | ||
//! a | ||
/// b | ||
pub fn bar() {} | ||
} | ||
|
||
pub mod multiline_outer_doc_gap { | ||
//! a | ||
//! b | ||
pub fn bar() {} | ||
} | ||
|
||
pub mod multiline_outer_doc_commented { | ||
/////! This outer doc comment was commented out. | ||
pub fn bar() {} | ||
} | ||
|
||
pub mod outer_doc_macro { | ||
//! Very cool macro | ||
macro_rules! x { | ||
() => {}; | ||
} | ||
} | ||
|
||
pub mod useless_outer_doc { | ||
//! Huh. | ||
use std::mem; | ||
} | ||
|
||
fn main() {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
// run-rustfix | ||
#![allow(unused)] | ||
#![warn(clippy::suspicious_doc_comments)] | ||
|
||
//! Real module documentation. | ||
///! Fake module documentation. | ||
fn baz() {} | ||
|
||
pub mod singleline_outer_doc { | ||
///! This module contains useful functions. | ||
pub fn bar() {} | ||
} | ||
|
||
pub mod singleline_inner_doc { | ||
//! This module contains useful functions. | ||
pub fn bar() {} | ||
} | ||
|
||
pub mod multiline_outer_doc { | ||
/**! This module contains useful functions. | ||
*/ | ||
|
||
pub fn bar() {} | ||
} | ||
|
||
pub mod multiline_inner_doc { | ||
/*! This module contains useful functions. | ||
*/ | ||
|
||
pub fn bar() {} | ||
} | ||
|
||
pub mod multiline_outer_doc2 { | ||
///! This module | ||
///! contains | ||
///! useful functions. | ||
pub fn bar() {} | ||
} | ||
|
||
pub mod multiline_outer_doc3 { | ||
///! a | ||
///! b | ||
/// c | ||
pub fn bar() {} | ||
} | ||
|
||
pub mod multiline_outer_doc4 { | ||
///! a | ||
/// b | ||
pub fn bar() {} | ||
} | ||
|
||
pub mod multiline_outer_doc_gap { | ||
///! a | ||
///! b | ||
pub fn bar() {} | ||
} | ||
|
||
pub mod multiline_outer_doc_commented { | ||
/////! This outer doc comment was commented out. | ||
pub fn bar() {} | ||
} | ||
|
||
pub mod outer_doc_macro { | ||
///! Very cool macro | ||
macro_rules! x { | ||
() => {}; | ||
} | ||
} | ||
|
||
pub mod useless_outer_doc { | ||
///! Huh. | ||
use std::mem; | ||
} | ||
|
||
fn main() {} |
Oops, something went wrong.