Skip to content

Commit 3757a0e

Browse files
committed
avoid doc-link-with-quotes in code blocks
1 parent a78551b commit 3757a0e

7 files changed

+67
-82
lines changed

clippy_lints/src/doc.rs

Lines changed: 56 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,29 @@ declare_clippy_lint! {
198198
"presence of `fn main() {` in code examples"
199199
}
200200

201+
declare_clippy_lint! {
202+
/// ### What it does
203+
/// Detects the syntax `['foo']` in documentation comments (notice quotes instead of backticks)
204+
/// outside of code blocks
205+
/// ### Why is this bad?
206+
/// It is likely a typo when defining an intra-doc link
207+
///
208+
/// ### Example
209+
/// ```rust
210+
/// /// See also: ['foo']
211+
/// fn bar() {}
212+
/// ```
213+
/// Use instead:
214+
/// ```rust
215+
/// /// See also: [`foo`]
216+
/// fn bar() {}
217+
/// ```
218+
#[clippy::version = "1.63.0"]
219+
pub DOC_LINK_WITH_QUOTES,
220+
pedantic,
221+
"possible typo for an intra-doc link"
222+
}
223+
201224
#[expect(clippy::module_name_repetitions)]
202225
#[derive(Clone)]
203226
pub struct DocMarkdown {
@@ -214,9 +237,14 @@ impl DocMarkdown {
214237
}
215238
}
216239

217-
impl_lint_pass!(DocMarkdown =>
218-
[DOC_MARKDOWN, MISSING_SAFETY_DOC, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, NEEDLESS_DOCTEST_MAIN]
219-
);
240+
impl_lint_pass!(DocMarkdown => [
241+
DOC_LINK_WITH_QUOTES,
242+
DOC_MARKDOWN,
243+
MISSING_SAFETY_DOC,
244+
MISSING_ERRORS_DOC,
245+
MISSING_PANICS_DOC,
246+
NEEDLESS_DOCTEST_MAIN
247+
]);
220248

221249
impl<'tcx> LateLintPass<'tcx> for DocMarkdown {
222250
fn check_crate(&mut self, cx: &LateContext<'tcx>) {
@@ -432,7 +460,7 @@ pub fn strip_doc_comment_decoration(doc: &str, comment_kind: CommentKind, span:
432460
(no_stars, sizes)
433461
}
434462

435-
#[derive(Copy, Clone)]
463+
#[derive(Copy, Clone, Default)]
436464
struct DocHeaders {
437465
safety: bool,
438466
errors: bool,
@@ -476,11 +504,7 @@ fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs
476504
}
477505

478506
if doc.is_empty() {
479-
return DocHeaders {
480-
safety: false,
481-
errors: false,
482-
panics: false,
483-
};
507+
return DocHeaders::default();
484508
}
485509

486510
let mut cb = fake_broken_link_callback;
@@ -521,11 +545,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
521545
use pulldown_cmark::Tag::{CodeBlock, Heading, Item, Link, Paragraph};
522546
use pulldown_cmark::{CodeBlockKind, CowStr};
523547

524-
let mut headers = DocHeaders {
525-
safety: false,
526-
errors: false,
527-
panics: false,
528-
};
548+
let mut headers = DocHeaders::default();
529549
let mut in_code = false;
530550
let mut in_link = None;
531551
let mut in_heading = false;
@@ -612,6 +632,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
612632
check_code(cx, &text, edition, span);
613633
}
614634
} else {
635+
check_link_quotes(cx, in_link.is_some(), trimmed_text, span, &range, begin, text.len());
615636
// Adjust for the beginning of the current `Event`
616637
let span = span.with_lo(span.lo() + BytePos::from_usize(range.start - begin));
617638
text_to_check.push((text, span));
@@ -622,6 +643,27 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
622643
headers
623644
}
624645

646+
fn check_link_quotes(
647+
cx: &LateContext<'_>,
648+
in_link: bool,
649+
trimmed_text: &str,
650+
span: Span,
651+
range: &Range<usize>,
652+
begin: usize,
653+
text_len: usize,
654+
) {
655+
if in_link && trimmed_text.starts_with('\'') && trimmed_text.ends_with('\'') {
656+
// fix the span to only point at the text within the link
657+
let lo = span.lo() + BytePos::from_usize(range.start - begin);
658+
span_lint(
659+
cx,
660+
DOC_LINK_WITH_QUOTES,
661+
span.with_lo(lo).with_hi(lo + BytePos::from_usize(text_len)),
662+
"possible intra-doc link using quotes instead of backticks",
663+
);
664+
}
665+
}
666+
625667
fn get_current_span(spans: &[(usize, Span)], idx: usize) -> (usize, Span) {
626668
let index = match spans.binary_search_by(|c| c.0.cmp(&idx)) {
627669
Ok(o) => o,

clippy_lints/src/doc_link_with_quotes.rs

Lines changed: 0 additions & 60 deletions
This file was deleted.

clippy_lints/src/lib.register_lints.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,12 @@ store.register_lints(&[
118118
disallowed_names::DISALLOWED_NAMES,
119119
disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS,
120120
disallowed_types::DISALLOWED_TYPES,
121+
doc::DOC_LINK_WITH_QUOTES,
121122
doc::DOC_MARKDOWN,
122123
doc::MISSING_ERRORS_DOC,
123124
doc::MISSING_PANICS_DOC,
124125
doc::MISSING_SAFETY_DOC,
125126
doc::NEEDLESS_DOCTEST_MAIN,
126-
doc_link_with_quotes::DOC_LINK_WITH_QUOTES,
127127
double_parens::DOUBLE_PARENS,
128128
drop_forget_ref::DROP_COPY,
129129
drop_forget_ref::DROP_NON_DROP,

clippy_lints/src/lib.register_pedantic.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
2020
LintId::of(dereference::REF_BINDING_TO_REFERENCE),
2121
LintId::of(derive::EXPL_IMPL_CLONE_ON_COPY),
2222
LintId::of(derive::UNSAFE_DERIVE_DESERIALIZE),
23+
LintId::of(doc::DOC_LINK_WITH_QUOTES),
2324
LintId::of(doc::DOC_MARKDOWN),
2425
LintId::of(doc::MISSING_ERRORS_DOC),
2526
LintId::of(doc::MISSING_PANICS_DOC),
26-
LintId::of(doc_link_with_quotes::DOC_LINK_WITH_QUOTES),
2727
LintId::of(empty_enum::EMPTY_ENUM),
2828
LintId::of(enum_variants::MODULE_NAME_REPETITIONS),
2929
LintId::of(eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS),

clippy_lints/src/lib.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,6 @@ mod disallowed_names;
204204
mod disallowed_script_idents;
205205
mod disallowed_types;
206206
mod doc;
207-
mod doc_link_with_quotes;
208207
mod double_parens;
209208
mod drop_forget_ref;
210209
mod duplicate_mod;
@@ -864,7 +863,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
864863
store.register_early_pass(|| Box::new(single_char_lifetime_names::SingleCharLifetimeNames));
865864
store.register_late_pass(move |_| Box::new(manual_bits::ManualBits::new(msrv)));
866865
store.register_late_pass(|_| Box::new(default_union_representation::DefaultUnionRepresentation));
867-
store.register_early_pass(|| Box::new(doc_link_with_quotes::DocLinkWithQuotes));
868866
store.register_late_pass(|_| Box::<only_used_in_recursion::OnlyUsedInRecursion>::default());
869867
let allow_dbg_in_tests = conf.allow_dbg_in_tests;
870868
store.register_late_pass(move |_| Box::new(dbg_macro::DbgMacro::new(allow_dbg_in_tests)));

tests/ui/doc_link_with_quotes.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,14 @@ fn main() {
44
foo()
55
}
66

7-
/// Calls ['bar']
7+
/// Calls ['bar'] uselessly
88
pub fn foo() {
99
bar()
1010
}
1111

12+
/// # Examples
13+
/// This demonstrates issue \#8961
14+
/// ```
15+
/// let _ = vec!['w', 'a', 't'];
16+
/// ```
1217
pub fn bar() {}

tests/ui/doc_link_with_quotes.stderr

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
error: possible intra-doc link using quotes instead of backticks
2-
--> $DIR/doc_link_with_quotes.rs:7:1
2+
--> $DIR/doc_link_with_quotes.rs:7:12
33
|
4-
LL | /// Calls ['bar']
5-
| ^^^^^^^^^^^^^^^^^
4+
LL | /// Calls ['bar'] uselessly
5+
| ^^^^^
66
|
77
= note: `-D clippy::doc-link-with-quotes` implied by `-D warnings`
88

0 commit comments

Comments
 (0)