1
1
use clippy_utils:: diagnostics:: { span_lint, span_lint_and_note} ;
2
+ use clippy_utils:: source:: first_line_of_span;
2
3
use clippy_utils:: ty:: { implements_trait, is_type_diagnostic_item} ;
3
4
use clippy_utils:: { is_entrypoint_fn, is_expn_of, match_panic_def_id, method_chain_args, return_ty} ;
4
5
use if_chain:: if_chain;
@@ -37,7 +38,7 @@ declare_clippy_lint! {
37
38
/// consider that.
38
39
///
39
40
/// **Known problems:** Lots of bad docs won’t be fixed, what the lint checks
40
- /// for is limited, and there are still false positives.
41
+ /// for is limited, and there are still false positives. HTML is not linted.
41
42
///
42
43
/// In addition, when writing documentation comments, including `[]` brackets
43
44
/// inside a link text would trip the parser. Therfore, documenting link with
@@ -469,11 +470,11 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
469
470
spans : & [ ( usize , Span ) ] ,
470
471
) -> DocHeaders {
471
472
// true if a safety header was found
472
- use pulldown_cmark:: CodeBlockKind ;
473
473
use pulldown_cmark:: Event :: {
474
474
Code , End , FootnoteReference , HardBreak , Html , Rule , SoftBreak , Start , TaskListMarker , Text ,
475
475
} ;
476
- use pulldown_cmark:: Tag :: { CodeBlock , Heading , Link } ;
476
+ use pulldown_cmark:: Tag :: { CodeBlock , Heading , Link , Paragraph } ;
477
+ use pulldown_cmark:: { CodeBlockKind , CowStr } ;
477
478
478
479
let mut headers = DocHeaders {
479
480
safety : false ,
@@ -485,6 +486,9 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
485
486
let mut in_heading = false ;
486
487
let mut is_rust = false ;
487
488
let mut edition = None ;
489
+ let mut ticks_unbalanced = false ;
490
+ let mut text_to_check: Vec < ( CowStr < ' _ > , Span ) > = Vec :: new ( ) ;
491
+ let mut paragraph_span = spans[ 0 ] . 1 ; // First line
488
492
for ( event, range) in events {
489
493
match event {
490
494
Start ( CodeBlock ( ref kind) ) => {
@@ -512,24 +516,45 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
512
516
End ( Link ( ..) ) => in_link = None ,
513
517
Start ( Heading ( _) ) => in_heading = true ,
514
518
End ( Heading ( _) ) => in_heading = false ,
519
+ Start ( Paragraph ) => {
520
+ ticks_unbalanced = false ;
521
+ let ( _, span) = get_current_span ( spans, range. start ) ;
522
+ paragraph_span = first_line_of_span ( cx, span) ;
523
+ } ,
524
+ End ( Paragraph ) => {
525
+ if ticks_unbalanced {
526
+ span_lint (
527
+ cx,
528
+ DOC_MARKDOWN ,
529
+ paragraph_span,
530
+ "backticks are unbalanced; one may be missing a pair" ,
531
+ ) ;
532
+ } else {
533
+ for ( text, span) in text_to_check {
534
+ check_text ( cx, valid_idents, & text, span) ;
535
+ }
536
+ }
537
+ text_to_check = Vec :: new ( ) ;
538
+ } ,
515
539
Start ( _tag) | End ( _tag) => ( ) , // We don't care about other tags
516
540
Html ( _html) => ( ) , // HTML is weird, just ignore it
517
541
SoftBreak | HardBreak | TaskListMarker ( _) | Code ( _) | Rule => ( ) ,
518
542
FootnoteReference ( text) | Text ( text) => {
519
- if Some ( & text) == in_link. as_ref ( ) {
543
+ let ( begin, span) = get_current_span ( spans, range. start ) ;
544
+ paragraph_span = paragraph_span. with_hi ( span. hi ( ) ) ;
545
+ if Some ( & text) == in_link. as_ref ( ) || ticks_unbalanced {
520
546
// Probably a link of the form `<http://example.com>`
521
547
// Which are represented as a link to "http://example.com" with
522
548
// text "http://example.com" by pulldown-cmark
523
549
continue ;
524
550
}
551
+ if text. contains ( '`' ) && !in_code {
552
+ ticks_unbalanced = true ;
553
+ continue ;
554
+ }
525
555
headers. safety |= in_heading && text. trim ( ) == "Safety" ;
526
556
headers. errors |= in_heading && text. trim ( ) == "Errors" ;
527
557
headers. panics |= in_heading && text. trim ( ) == "Panics" ;
528
- let index = match spans. binary_search_by ( |c| c. 0 . cmp ( & range. start ) ) {
529
- Ok ( o) => o,
530
- Err ( e) => e - 1 ,
531
- } ;
532
- let ( begin, span) = spans[ index] ;
533
558
if in_code {
534
559
if is_rust {
535
560
let edition = edition. unwrap_or_else ( || cx. tcx . sess . edition ( ) ) ;
@@ -538,15 +563,22 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
538
563
} else {
539
564
// Adjust for the beginning of the current `Event`
540
565
let span = span. with_lo ( span. lo ( ) + BytePos :: from_usize ( range. start - begin) ) ;
541
-
542
- check_text ( cx, valid_idents, & text, span) ;
566
+ text_to_check. push ( ( text, span) ) ;
543
567
}
544
568
} ,
545
569
}
546
570
}
547
571
headers
548
572
}
549
573
574
+ fn get_current_span ( spans : & [ ( usize , Span ) ] , idx : usize ) -> ( usize , Span ) {
575
+ let index = match spans. binary_search_by ( |c| c. 0 . cmp ( & idx) ) {
576
+ Ok ( o) => o,
577
+ Err ( e) => e - 1 ,
578
+ } ;
579
+ spans[ index]
580
+ }
581
+
550
582
fn check_code ( cx : & LateContext < ' _ > , text : & str , edition : Edition , span : Span ) {
551
583
fn has_needless_main ( code : & str , edition : Edition ) -> bool {
552
584
rustc_driver:: catch_fatal_errors ( || {
0 commit comments