mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-11-17 10:18:20 +00:00
Auto merge of #9567 - rust-lang:doc-link-with-code, r=Alexendoo
avoid doc-link-with-quotes in code blocks This fixes #8961 by moving the lint into the docs code, thus being able to re-use the pulldown-cmark parser and simplifying the code. --- changelog: none
This commit is contained in:
commit
8da24295c6
7 changed files with 67 additions and 82 deletions
|
@ -198,6 +198,29 @@ declare_clippy_lint! {
|
|||
"presence of `fn main() {` in code examples"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Detects the syntax `['foo']` in documentation comments (notice quotes instead of backticks)
|
||||
/// outside of code blocks
|
||||
/// ### Why is this bad?
|
||||
/// It is likely a typo when defining an intra-doc link
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// /// See also: ['foo']
|
||||
/// fn bar() {}
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// /// See also: [`foo`]
|
||||
/// fn bar() {}
|
||||
/// ```
|
||||
#[clippy::version = "1.63.0"]
|
||||
pub DOC_LINK_WITH_QUOTES,
|
||||
pedantic,
|
||||
"possible typo for an intra-doc link"
|
||||
}
|
||||
|
||||
#[expect(clippy::module_name_repetitions)]
|
||||
#[derive(Clone)]
|
||||
pub struct DocMarkdown {
|
||||
|
@ -214,9 +237,14 @@ impl DocMarkdown {
|
|||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(DocMarkdown =>
|
||||
[DOC_MARKDOWN, MISSING_SAFETY_DOC, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, NEEDLESS_DOCTEST_MAIN]
|
||||
);
|
||||
impl_lint_pass!(DocMarkdown => [
|
||||
DOC_LINK_WITH_QUOTES,
|
||||
DOC_MARKDOWN,
|
||||
MISSING_SAFETY_DOC,
|
||||
MISSING_ERRORS_DOC,
|
||||
MISSING_PANICS_DOC,
|
||||
NEEDLESS_DOCTEST_MAIN
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for DocMarkdown {
|
||||
fn check_crate(&mut self, cx: &LateContext<'tcx>) {
|
||||
|
@ -432,7 +460,7 @@ pub fn strip_doc_comment_decoration(doc: &str, comment_kind: CommentKind, span:
|
|||
(no_stars, sizes)
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[derive(Copy, Clone, Default)]
|
||||
struct DocHeaders {
|
||||
safety: bool,
|
||||
errors: bool,
|
||||
|
@ -476,11 +504,7 @@ fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs
|
|||
}
|
||||
|
||||
if doc.is_empty() {
|
||||
return DocHeaders {
|
||||
safety: false,
|
||||
errors: false,
|
||||
panics: false,
|
||||
};
|
||||
return DocHeaders::default();
|
||||
}
|
||||
|
||||
let mut cb = fake_broken_link_callback;
|
||||
|
@ -521,11 +545,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
|
|||
use pulldown_cmark::Tag::{CodeBlock, Heading, Item, Link, Paragraph};
|
||||
use pulldown_cmark::{CodeBlockKind, CowStr};
|
||||
|
||||
let mut headers = DocHeaders {
|
||||
safety: false,
|
||||
errors: false,
|
||||
panics: false,
|
||||
};
|
||||
let mut headers = DocHeaders::default();
|
||||
let mut in_code = false;
|
||||
let mut in_link = None;
|
||||
let mut in_heading = false;
|
||||
|
@ -612,6 +632,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
|
|||
check_code(cx, &text, edition, span);
|
||||
}
|
||||
} else {
|
||||
check_link_quotes(cx, in_link.is_some(), trimmed_text, span, &range, begin, text.len());
|
||||
// Adjust for the beginning of the current `Event`
|
||||
let span = span.with_lo(span.lo() + BytePos::from_usize(range.start - begin));
|
||||
text_to_check.push((text, span));
|
||||
|
@ -622,6 +643,27 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
|
|||
headers
|
||||
}
|
||||
|
||||
fn check_link_quotes(
|
||||
cx: &LateContext<'_>,
|
||||
in_link: bool,
|
||||
trimmed_text: &str,
|
||||
span: Span,
|
||||
range: &Range<usize>,
|
||||
begin: usize,
|
||||
text_len: usize,
|
||||
) {
|
||||
if in_link && trimmed_text.starts_with('\'') && trimmed_text.ends_with('\'') {
|
||||
// fix the span to only point at the text within the link
|
||||
let lo = span.lo() + BytePos::from_usize(range.start - begin);
|
||||
span_lint(
|
||||
cx,
|
||||
DOC_LINK_WITH_QUOTES,
|
||||
span.with_lo(lo).with_hi(lo + BytePos::from_usize(text_len)),
|
||||
"possible intra-doc link using quotes instead of backticks",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_current_span(spans: &[(usize, Span)], idx: usize) -> (usize, Span) {
|
||||
let index = match spans.binary_search_by(|c| c.0.cmp(&idx)) {
|
||||
Ok(o) => o,
|
||||
|
|
|
@ -1,60 +0,0 @@
|
|||
use clippy_utils::diagnostics::span_lint;
|
||||
use itertools::Itertools;
|
||||
use rustc_ast::{AttrKind, Attribute};
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Detects the syntax `['foo']` in documentation comments (notice quotes instead of backticks)
|
||||
/// outside of code blocks
|
||||
/// ### Why is this bad?
|
||||
/// It is likely a typo when defining an intra-doc link
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// /// See also: ['foo']
|
||||
/// fn bar() {}
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// /// See also: [`foo`]
|
||||
/// fn bar() {}
|
||||
/// ```
|
||||
#[clippy::version = "1.63.0"]
|
||||
pub DOC_LINK_WITH_QUOTES,
|
||||
pedantic,
|
||||
"possible typo for an intra-doc link"
|
||||
}
|
||||
declare_lint_pass!(DocLinkWithQuotes => [DOC_LINK_WITH_QUOTES]);
|
||||
|
||||
impl EarlyLintPass for DocLinkWithQuotes {
|
||||
fn check_attribute(&mut self, ctx: &EarlyContext<'_>, attr: &Attribute) {
|
||||
if let AttrKind::DocComment(_, symbol) = attr.kind {
|
||||
if contains_quote_link(symbol.as_str()) {
|
||||
span_lint(
|
||||
ctx,
|
||||
DOC_LINK_WITH_QUOTES,
|
||||
attr.span,
|
||||
"possible intra-doc link using quotes instead of backticks",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn contains_quote_link(s: &str) -> bool {
|
||||
let mut in_backticks = false;
|
||||
let mut found_opening = false;
|
||||
|
||||
for c in s.chars().tuple_windows::<(char, char)>() {
|
||||
match c {
|
||||
('`', _) => in_backticks = !in_backticks,
|
||||
('[', '\'') if !in_backticks => found_opening = true,
|
||||
('\'', ']') if !in_backticks && found_opening => return true,
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
|
@ -118,12 +118,12 @@ store.register_lints(&[
|
|||
disallowed_names::DISALLOWED_NAMES,
|
||||
disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS,
|
||||
disallowed_types::DISALLOWED_TYPES,
|
||||
doc::DOC_LINK_WITH_QUOTES,
|
||||
doc::DOC_MARKDOWN,
|
||||
doc::MISSING_ERRORS_DOC,
|
||||
doc::MISSING_PANICS_DOC,
|
||||
doc::MISSING_SAFETY_DOC,
|
||||
doc::NEEDLESS_DOCTEST_MAIN,
|
||||
doc_link_with_quotes::DOC_LINK_WITH_QUOTES,
|
||||
double_parens::DOUBLE_PARENS,
|
||||
drop_forget_ref::DROP_COPY,
|
||||
drop_forget_ref::DROP_NON_DROP,
|
||||
|
|
|
@ -20,10 +20,10 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
|
|||
LintId::of(dereference::REF_BINDING_TO_REFERENCE),
|
||||
LintId::of(derive::EXPL_IMPL_CLONE_ON_COPY),
|
||||
LintId::of(derive::UNSAFE_DERIVE_DESERIALIZE),
|
||||
LintId::of(doc::DOC_LINK_WITH_QUOTES),
|
||||
LintId::of(doc::DOC_MARKDOWN),
|
||||
LintId::of(doc::MISSING_ERRORS_DOC),
|
||||
LintId::of(doc::MISSING_PANICS_DOC),
|
||||
LintId::of(doc_link_with_quotes::DOC_LINK_WITH_QUOTES),
|
||||
LintId::of(empty_enum::EMPTY_ENUM),
|
||||
LintId::of(enum_variants::MODULE_NAME_REPETITIONS),
|
||||
LintId::of(eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS),
|
||||
|
|
|
@ -204,7 +204,6 @@ mod disallowed_names;
|
|||
mod disallowed_script_idents;
|
||||
mod disallowed_types;
|
||||
mod doc;
|
||||
mod doc_link_with_quotes;
|
||||
mod double_parens;
|
||||
mod drop_forget_ref;
|
||||
mod duplicate_mod;
|
||||
|
@ -864,7 +863,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_early_pass(|| Box::new(single_char_lifetime_names::SingleCharLifetimeNames));
|
||||
store.register_late_pass(move |_| Box::new(manual_bits::ManualBits::new(msrv)));
|
||||
store.register_late_pass(|_| Box::new(default_union_representation::DefaultUnionRepresentation));
|
||||
store.register_early_pass(|| Box::new(doc_link_with_quotes::DocLinkWithQuotes));
|
||||
store.register_late_pass(|_| Box::<only_used_in_recursion::OnlyUsedInRecursion>::default());
|
||||
let allow_dbg_in_tests = conf.allow_dbg_in_tests;
|
||||
store.register_late_pass(move |_| Box::new(dbg_macro::DbgMacro::new(allow_dbg_in_tests)));
|
||||
|
|
|
@ -4,9 +4,14 @@ fn main() {
|
|||
foo()
|
||||
}
|
||||
|
||||
/// Calls ['bar']
|
||||
/// Calls ['bar'] uselessly
|
||||
pub fn foo() {
|
||||
bar()
|
||||
}
|
||||
|
||||
/// # Examples
|
||||
/// This demonstrates issue \#8961
|
||||
/// ```
|
||||
/// let _ = vec!['w', 'a', 't'];
|
||||
/// ```
|
||||
pub fn bar() {}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
error: possible intra-doc link using quotes instead of backticks
|
||||
--> $DIR/doc_link_with_quotes.rs:7:1
|
||||
--> $DIR/doc_link_with_quotes.rs:7:12
|
||||
|
|
||||
LL | /// Calls ['bar']
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
LL | /// Calls ['bar'] uselessly
|
||||
| ^^^^^
|
||||
|
|
||||
= note: `-D clippy::doc-link-with-quotes` implied by `-D warnings`
|
||||
|
||||
|
|
Loading…
Reference in a new issue