1414// We want to be able to build this crate with a stable compiler, so no
1515// `#![feature]` attributes should be added.
1616
17+ use rustc_lexer:: unescape;
1718pub use Alignment :: * ;
1819pub use Count :: * ;
1920pub use Piece :: * ;
@@ -324,7 +325,7 @@ impl<'a> Parser<'a> {
324325 append_newline : bool ,
325326 mode : ParseMode ,
326327 ) -> Parser < ' a > {
327- let input_string_kind = find_width_map_from_snippet ( snippet, style) ;
328+ let input_string_kind = find_width_map_from_snippet ( s , snippet, style) ;
328329 let ( width_map, is_source_literal) = match input_string_kind {
329330 InputStringKind :: Literal { width_mappings } => ( width_mappings, true ) ,
330331 InputStringKind :: NotALiteral => ( Vec :: new ( ) , false ) ,
@@ -892,6 +893,7 @@ impl<'a> Parser<'a> {
892893/// written code (code snippet) and the `InternedString` that gets processed in the `Parser`
893894/// in order to properly synthesise the intra-string `Span`s for error diagnostics.
894895fn find_width_map_from_snippet (
896+ input : & str ,
895897 snippet : Option < string:: String > ,
896898 str_style : Option < usize > ,
897899) -> InputStringKind {
@@ -904,8 +906,27 @@ fn find_width_map_from_snippet(
904906 return InputStringKind :: Literal { width_mappings : Vec :: new ( ) } ;
905907 }
906908
909+ // Strip quotes.
907910 let snippet = & snippet[ 1 ..snippet. len ( ) - 1 ] ;
908911
912+ // Macros like `println` add a newline at the end. That technically doens't make them "literals" anymore, but it's fine
913+ // since we will never need to point our spans there, so we lie about it here by ignoring it.
914+ // Since there might actually be newlines in the source code, we need to normalize away all trailing newlines.
915+ // If we only trimmed it off the input, `format!("\n")` would cause a mismatch as here we they actually match up.
916+ // Alternatively, we could just count the trailing newlines and only trim one from the input if they don't match up.
917+ let input_no_nl = input. trim_end_matches ( '\n' ) ;
918+ let Some ( unescaped) = unescape_string ( snippet) else {
919+ return InputStringKind :: NotALiteral ;
920+ } ;
921+
922+ let unescaped_no_nl = unescaped. trim_end_matches ( '\n' ) ;
923+
924+ if unescaped_no_nl != input_no_nl {
925+ // The source string that we're pointing at isn't our input, so spans pointing at it will be incorrect.
926+ // This can for example happen with proc macros that respan generated literals.
927+ return InputStringKind :: NotALiteral ;
928+ }
929+
909930 let mut s = snippet. char_indices ( ) ;
910931 let mut width_mappings = vec ! [ ] ;
911932 while let Some ( ( pos, c) ) = s. next ( ) {
@@ -988,6 +1009,19 @@ fn find_width_map_from_snippet(
9881009 InputStringKind :: Literal { width_mappings }
9891010}
9901011
1012+ fn unescape_string ( string : & str ) -> Option < string:: String > {
1013+ let mut buf = string:: String :: new ( ) ;
1014+ let mut ok = true ;
1015+ unescape:: unescape_literal ( string, unescape:: Mode :: Str , & mut |_, unescaped_char| {
1016+ match unescaped_char {
1017+ Ok ( c) => buf. push ( c) ,
1018+ Err ( _) => ok = false ,
1019+ }
1020+ } ) ;
1021+
1022+ ok. then_some ( buf)
1023+ }
1024+
9911025// Assert a reasonable size for `Piece`
9921026#[ cfg( all( target_arch = "x86_64" , target_pointer_width = "64" ) ) ]
9931027rustc_data_structures:: static_assert_size!( Piece <' _>, 16 ) ;
0 commit comments