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. get ( 0 ) . expect ( "function isn't called if doc comment is empty" ) . 1 ;
488
493
for ( event, range) in events {
489
494
match event {
490
495
Start ( CodeBlock ( ref kind) ) => {
@@ -510,13 +515,42 @@ 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
+ ticks_unbalanced |= text. contains ( '`' ) ;
553
+ if Some ( & text) == in_link. as_ref ( ) || ticks_unbalanced {
520
554
// Probably a link of the form `<http://example.com>`
521
555
// Which are represented as a link to "http://example.com" with
522
556
// text "http://example.com" by pulldown-cmark
@@ -525,11 +559,6 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
525
559
headers. safety |= in_heading && text. trim ( ) == "Safety" ;
526
560
headers. errors |= in_heading && text. trim ( ) == "Errors" ;
527
561
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
562
if in_code {
534
563
if is_rust {
535
564
let edition = edition. unwrap_or_else ( || cx. tcx . sess . edition ( ) ) ;
@@ -538,15 +567,22 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
538
567
} else {
539
568
// Adjust for the beginning of the current `Event`
540
569
let span = span. with_lo ( span. lo ( ) + BytePos :: from_usize ( range. start - begin) ) ;
541
-
542
- check_text ( cx, valid_idents, & text, span) ;
570
+ text_to_check. push ( ( text, span) ) ;
543
571
}
544
572
} ,
545
573
}
546
574
}
547
575
headers
548
576
}
549
577
578
+ fn get_current_span ( spans : & [ ( usize , Span ) ] , idx : usize ) -> ( usize , Span ) {
579
+ let index = match spans. binary_search_by ( |c| c. 0 . cmp ( & idx) ) {
580
+ Ok ( o) => o,
581
+ Err ( e) => e - 1 ,
582
+ } ;
583
+ spans[ index]
584
+ }
585
+
550
586
fn check_code ( cx : & LateContext < ' _ > , text : & str , edition : Edition , span : Span ) {
551
587
fn has_needless_main ( code : & str , edition : Edition ) -> bool {
552
588
rustc_driver:: catch_fatal_errors ( || {
0 commit comments