@@ -7,13 +7,14 @@ use std::sync::Arc;
77use rustc_ast:: { LitKind , StrStyle } ;
88use rustc_errors:: Applicability ;
99use rustc_hir:: { BlockCheckMode , Expr , ExprKind , UnsafeSource } ;
10+ use rustc_lexer:: { LiteralKind , TokenKind , tokenize} ;
1011use rustc_lint:: { EarlyContext , LateContext } ;
1112use rustc_middle:: ty:: TyCtxt ;
1213use rustc_session:: Session ;
1314use rustc_span:: source_map:: { SourceMap , original_sp} ;
1415use rustc_span:: {
15- BytePos , DUMMY_SP , FileNameDisplayPreference , Pos , SourceFile , SourceFileAndLine , Span , SpanData , SyntaxContext ,
16- hygiene,
16+ BytePos , DUMMY_SP , FileNameDisplayPreference , Pos , RelativeBytePos , SourceFile , SourceFileAndLine , Span , SpanData ,
17+ SyntaxContext , hygiene,
1718} ;
1819use std:: borrow:: Cow ;
1920use std:: fmt;
@@ -137,24 +138,24 @@ pub trait SpanRangeExt: SpanRange {
137138 fn map_range (
138139 self ,
139140 cx : & impl HasSession ,
140- f : impl for < ' a > FnOnce ( & ' a str , Range < usize > ) -> Option < Range < usize > > ,
141+ f : impl for < ' a > FnOnce ( & ' a SourceFile , & ' a str , Range < usize > ) -> Option < Range < usize > > ,
141142 ) -> Option < Range < BytePos > > {
142143 map_range ( cx. sess ( ) . source_map ( ) , self . into_range ( ) , f)
143144 }
144145
145- /// Extends the range to include all preceding whitespace characters, unless there
146- /// are non-whitespace characters left on the same line after `self`.
146+ /// Extends the range to include all preceding whitespace characters.
147+ ///
148+ /// The range will not be expanded if it would cross a line boundary, the line the range would
149+ /// be extended to ends with a line comment and the text after the range contains a
150+ /// non-whitespace character on the same line. e.g.
147151 ///
148- /// This extra condition prevents a problem when removing the '}' in:
149152 /// ```ignore
150- /// ( // There was an opening bracket after the parenthesis, which has been removed
151- /// // This is a comment
152- /// })
153+ /// ( // Some comment
154+ /// foo)
153155 /// ```
154- /// Removing the whitespaces, including the linefeed, before the '}', would put the
155- /// closing parenthesis at the end of the `// This is a comment` line, which would
156- /// make it part of the comment as well. In this case, it is best to keep the span
157- /// on the '}' alone.
156+ ///
157+ /// When the range points to `foo`, suggesting to remove the range after it's been extended will
158+ /// cause the `)` to be placed inside the line comment as `( // Some comment)`.
158159 fn with_leading_whitespace ( self , cx : & impl HasSession ) -> Range < BytePos > {
159160 with_leading_whitespace ( cx. sess ( ) . source_map ( ) , self . into_range ( ) )
160161 }
@@ -253,11 +254,11 @@ fn with_source_text_and_range<T>(
253254fn map_range (
254255 sm : & SourceMap ,
255256 sp : Range < BytePos > ,
256- f : impl for < ' a > FnOnce ( & ' a str , Range < usize > ) -> Option < Range < usize > > ,
257+ f : impl for < ' a > FnOnce ( & ' a SourceFile , & ' a str , Range < usize > ) -> Option < Range < usize > > ,
257258) -> Option < Range < BytePos > > {
258259 if let Some ( src) = get_source_range ( sm, sp. clone ( ) )
259260 && let Some ( text) = & src. sf . src
260- && let Some ( range) = f ( text, src. range . clone ( ) )
261+ && let Some ( range) = f ( & * src . sf , text, src. range . clone ( ) )
261262 {
262263 debug_assert ! (
263264 range. start <= text. len( ) && range. end <= text. len( ) ,
@@ -274,20 +275,57 @@ fn map_range(
274275 }
275276}
276277
278+ fn ends_with_line_comment_or_broken ( text : & str ) -> bool {
279+ let Some ( last) = tokenize ( text) . last ( ) else {
280+ return false ;
281+ } ;
282+ match last. kind {
283+ // Will give the wrong result on text like `" // "` where the first quote ends a string
284+ // started earlier. The only workaround is to lex the whole file which we don't really want
285+ // to do.
286+ TokenKind :: LineComment { .. } | TokenKind :: BlockComment { terminated : false , .. } => true ,
287+ TokenKind :: Literal { kind, .. } => matches ! (
288+ kind,
289+ LiteralKind :: Byte { terminated: false }
290+ | LiteralKind :: ByteStr { terminated: false }
291+ | LiteralKind :: CStr { terminated: false }
292+ | LiteralKind :: Char { terminated: false }
293+ | LiteralKind :: RawByteStr { n_hashes: None }
294+ | LiteralKind :: RawCStr { n_hashes: None }
295+ | LiteralKind :: RawStr { n_hashes: None }
296+ ) ,
297+ _ => false ,
298+ }
299+ }
300+
301+ fn with_leading_whitespace_inner ( lines : & [ RelativeBytePos ] , src : & str , range : Range < usize > ) -> Option < usize > {
302+ debug_assert ! ( lines. is_empty( ) || lines[ 0 ] . to_u32( ) == 0 ) ;
303+
304+ let start = src. get ( ..range. start ) ?. trim_end ( ) ;
305+ let next_line = lines. partition_point ( |& pos| pos. to_usize ( ) <= start. len ( ) ) ;
306+ if let Some ( line_end) = lines. get ( next_line)
307+ && line_end. to_usize ( ) <= range. start
308+ && let prev_start = lines. get ( next_line - 1 ) . map_or ( 0 , |& x| x. to_usize ( ) )
309+ && ends_with_line_comment_or_broken ( & start[ prev_start..] )
310+ && let next_line = lines. partition_point ( |& pos| pos. to_usize ( ) < range. end )
311+ && let next_start = lines. get ( next_line) . map_or ( src. len ( ) , |& x| x. to_usize ( ) )
312+ && tokenize ( src. get ( range. end ..next_start) ?) . any ( |t| !matches ! ( t. kind, TokenKind :: Whitespace ) )
313+ {
314+ Some ( range. start )
315+ } else {
316+ Some ( start. len ( ) )
317+ }
318+ }
319+
277320fn with_leading_whitespace ( sm : & SourceMap , sp : Range < BytePos > ) -> Range < BytePos > {
278- map_range ( sm, sp, |src, range| {
279- let non_blank_after = src. len ( ) - src. get ( range. end ..) ?. trim_start ( ) . len ( ) ;
280- if src. get ( range. end ..non_blank_after) ?. contains ( [ '\r' , '\n' ] ) {
281- Some ( src. get ( ..range. start ) ?. trim_end ( ) . len ( ) ..range. end )
282- } else {
283- Some ( range)
284- }
321+ map_range ( sm, sp. clone ( ) , |sf, src, range| {
322+ Some ( with_leading_whitespace_inner ( sf. lines ( ) , src, range. clone ( ) ) ?..range. end )
285323 } )
286- . unwrap ( )
324+ . unwrap_or ( sp )
287325}
288326
289327fn trim_start ( sm : & SourceMap , sp : Range < BytePos > ) -> Range < BytePos > {
290- map_range ( sm, sp. clone ( ) , |src, range| {
328+ map_range ( sm, sp. clone ( ) , |_ , src, range| {
291329 let src = src. get ( range. clone ( ) ) ?;
292330 Some ( range. start + ( src. len ( ) - src. trim_start ( ) . len ( ) ) ..range. end )
293331 } )
0 commit comments