@@ -44,6 +44,7 @@ use rustc_hir::Node;
44
44
use rustc_hir:: * ;
45
45
use rustc_lint:: { LateContext , Level , Lint , LintContext } ;
46
46
use rustc_span:: hygiene:: { ExpnKind , MacroKind } ;
47
+ use rustc_span:: source_map:: original_sp;
47
48
use rustc_span:: symbol:: { self , kw, Symbol } ;
48
49
use rustc_span:: { BytePos , Pos , Span , DUMMY_SP } ;
49
50
use smallvec:: SmallVec ;
@@ -541,11 +542,17 @@ pub fn snippet_opt<T: LintContext>(cx: &T, span: Span) -> Option<String> {
541
542
///
542
543
/// # Example
543
544
/// ```rust,ignore
544
- /// snippet_block(cx, expr.span, "..")
545
+ /// snippet_block(cx, expr.span, "..", None )
545
546
/// ```
546
- pub fn snippet_block < ' a , T : LintContext > ( cx : & T , span : Span , default : & ' a str ) -> Cow < ' a , str > {
547
+ pub fn snippet_block < ' a , T : LintContext > (
548
+ cx : & T ,
549
+ span : Span ,
550
+ default : & ' a str ,
551
+ indent_relative_to : Option < Span > ,
552
+ ) -> Cow < ' a , str > {
547
553
let snip = snippet ( cx, span, default) ;
548
- trim_multiline ( snip, true )
554
+ let indent = indent_relative_to. and_then ( |s| indent_of ( cx, s) ) ;
555
+ trim_multiline ( snip, true , indent)
549
556
}
550
557
551
558
/// Same as `snippet_block`, but adapts the applicability level by the rules of
@@ -554,27 +561,73 @@ pub fn snippet_block_with_applicability<'a, T: LintContext>(
554
561
cx : & T ,
555
562
span : Span ,
556
563
default : & ' a str ,
564
+ indent_relative_to : Option < Span > ,
557
565
applicability : & mut Applicability ,
558
566
) -> Cow < ' a , str > {
559
567
let snip = snippet_with_applicability ( cx, span, default, applicability) ;
560
- trim_multiline ( snip, true )
568
+ let indent = indent_relative_to. and_then ( |s| indent_of ( cx, s) ) ;
569
+ trim_multiline ( snip, true , indent)
561
570
}
562
571
563
- /// Returns a new Span that covers the full last line of the given Span
564
- pub fn last_line_of_span < T : LintContext > ( cx : & T , span : Span ) -> Span {
572
+ /// Returns a new Span that extends the original Span to the first non-whitespace char of the first
573
+ /// line.
574
+ ///
575
+ /// ```rust,ignore
576
+ /// let x = ();
577
+ /// // ^^
578
+ /// // will be converted to
579
+ /// let x = ();
580
+ /// // ^^^^^^^^^^
581
+ /// ```
582
+ pub fn first_line_of_span < T : LintContext > ( cx : & T , span : Span ) -> Span {
583
+ if let Some ( first_char_pos) = first_char_in_first_line ( cx, span) {
584
+ span. with_lo ( first_char_pos)
585
+ } else {
586
+ span
587
+ }
588
+ }
589
+
590
+ fn first_char_in_first_line < T : LintContext > ( cx : & T , span : Span ) -> Option < BytePos > {
591
+ let line_span = line_span ( cx, span) ;
592
+ if let Some ( snip) = snippet_opt ( cx, line_span) {
593
+ snip. find ( |c : char | !c. is_whitespace ( ) )
594
+ . map ( |pos| line_span. lo ( ) + BytePos :: from_usize ( pos) )
595
+ } else {
596
+ None
597
+ }
598
+ }
599
+
600
+ /// Returns the indentation of the line of a span
601
+ ///
602
+ /// ```rust,ignore
603
+ /// let x = ();
604
+ /// // ^^ -- will return 0
605
+ /// let x = ();
606
+ /// // ^^ -- will return 4
607
+ /// ```
608
+ pub fn indent_of < T : LintContext > ( cx : & T , span : Span ) -> Option < usize > {
609
+ if let Some ( snip) = snippet_opt ( cx, line_span ( cx, span) ) {
610
+ snip. find ( |c : char | !c. is_whitespace ( ) )
611
+ } else {
612
+ None
613
+ }
614
+ }
615
+
616
+ /// Extends the span to the beginning of the spans line, incl. whitespaces.
617
+ ///
618
+ /// ```rust,ignore
619
+ /// let x = ();
620
+ /// // ^^
621
+ /// // will be converted to
622
+ /// let x = ();
623
+ /// // ^^^^^^^^^^^^^^
624
+ /// ```
625
+ fn line_span < T : LintContext > ( cx : & T , span : Span ) -> Span {
626
+ let span = original_sp ( span, DUMMY_SP ) ;
565
627
let source_map_and_line = cx. sess ( ) . source_map ( ) . lookup_line ( span. lo ( ) ) . unwrap ( ) ;
566
628
let line_no = source_map_and_line. line ;
567
- let line_start = & source_map_and_line. sf . lines [ line_no] ;
568
- let span = Span :: new ( * line_start, span. hi ( ) , span. ctxt ( ) ) ;
569
- if_chain ! {
570
- if let Some ( snip) = snippet_opt( cx, span) ;
571
- if let Some ( first_ch_pos) = snip. find( |c: char | !c. is_whitespace( ) ) ;
572
- then {
573
- span. with_lo( span. lo( ) + BytePos :: from_usize( first_ch_pos) )
574
- } else {
575
- span
576
- }
577
- }
629
+ let line_start = source_map_and_line. sf . lines [ line_no] ;
630
+ Span :: new ( line_start, span. hi ( ) , span. ctxt ( ) )
578
631
}
579
632
580
633
/// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`.
@@ -584,8 +637,9 @@ pub fn expr_block<'a, T: LintContext>(
584
637
expr : & Expr < ' _ > ,
585
638
option : Option < String > ,
586
639
default : & ' a str ,
640
+ indent_relative_to : Option < Span > ,
587
641
) -> Cow < ' a , str > {
588
- let code = snippet_block ( cx, expr. span , default) ;
642
+ let code = snippet_block ( cx, expr. span , default, indent_relative_to ) ;
589
643
let string = option. unwrap_or_default ( ) ;
590
644
if expr. span . from_expansion ( ) {
591
645
Cow :: Owned ( format ! ( "{{ {} }}" , snippet_with_macro_callsite( cx, expr. span, default ) ) )
@@ -600,14 +654,14 @@ pub fn expr_block<'a, T: LintContext>(
600
654
601
655
/// Trim indentation from a multiline string with possibility of ignoring the
602
656
/// first line.
603
- pub fn trim_multiline ( s : Cow < ' _ , str > , ignore_first : bool ) -> Cow < ' _ , str > {
604
- let s_space = trim_multiline_inner ( s, ignore_first, ' ' ) ;
605
- let s_tab = trim_multiline_inner ( s_space, ignore_first, '\t' ) ;
606
- trim_multiline_inner ( s_tab, ignore_first, ' ' )
657
+ pub fn trim_multiline ( s : Cow < ' _ , str > , ignore_first : bool , indent : Option < usize > ) -> Cow < ' _ , str > {
658
+ let s_space = trim_multiline_inner ( s, ignore_first, indent , ' ' ) ;
659
+ let s_tab = trim_multiline_inner ( s_space, ignore_first, indent , '\t' ) ;
660
+ trim_multiline_inner ( s_tab, ignore_first, indent , ' ' )
607
661
}
608
662
609
- fn trim_multiline_inner ( s : Cow < ' _ , str > , ignore_first : bool , ch : char ) -> Cow < ' _ , str > {
610
- let x = s
663
+ fn trim_multiline_inner ( s : Cow < ' _ , str > , ignore_first : bool , indent : Option < usize > , ch : char ) -> Cow < ' _ , str > {
664
+ let mut x = s
611
665
. lines ( )
612
666
. skip ( ignore_first as usize )
613
667
. filter_map ( |l| {
@@ -620,6 +674,9 @@ fn trim_multiline_inner(s: Cow<'_, str>, ignore_first: bool, ch: char) -> Cow<'_
620
674
} )
621
675
. min ( )
622
676
. unwrap_or ( 0 ) ;
677
+ if let Some ( indent) = indent {
678
+ x = x. saturating_sub ( indent) ;
679
+ }
623
680
if x > 0 {
624
681
Cow :: Owned (
625
682
s. lines ( )
0 commit comments