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