14
14
// We want to be able to build this crate with a stable compiler, so no
15
15
// `#![feature]` attributes should be added.
16
16
17
+ use rustc_lexer:: unescape;
17
18
pub use Alignment :: * ;
18
19
pub use Count :: * ;
19
20
pub use Piece :: * ;
@@ -324,7 +325,7 @@ impl<'a> Parser<'a> {
324
325
append_newline : bool ,
325
326
mode : ParseMode ,
326
327
) -> 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) ;
328
329
let ( width_map, is_source_literal) = match input_string_kind {
329
330
InputStringKind :: Literal { width_mappings } => ( width_mappings, true ) ,
330
331
InputStringKind :: NotALiteral => ( Vec :: new ( ) , false ) ,
@@ -892,6 +893,7 @@ impl<'a> Parser<'a> {
892
893
/// written code (code snippet) and the `InternedString` that gets processed in the `Parser`
893
894
/// in order to properly synthesise the intra-string `Span`s for error diagnostics.
894
895
fn find_width_map_from_snippet (
896
+ input : & str ,
895
897
snippet : Option < string:: String > ,
896
898
str_style : Option < usize > ,
897
899
) -> InputStringKind {
@@ -904,8 +906,27 @@ fn find_width_map_from_snippet(
904
906
return InputStringKind :: Literal { width_mappings : Vec :: new ( ) } ;
905
907
}
906
908
909
+ // Strip quotes.
907
910
let snippet = & snippet[ 1 ..snippet. len ( ) - 1 ] ;
908
911
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
+
909
930
let mut s = snippet. char_indices ( ) ;
910
931
let mut width_mappings = vec ! [ ] ;
911
932
while let Some ( ( pos, c) ) = s. next ( ) {
@@ -988,6 +1009,19 @@ fn find_width_map_from_snippet(
988
1009
InputStringKind :: Literal { width_mappings }
989
1010
}
990
1011
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
+
991
1025
// Assert a reasonable size for `Piece`
992
1026
#[ cfg( all( target_arch = "x86_64" , target_pointer_width = "64" ) ) ]
993
1027
rustc_data_structures:: static_assert_size!( Piece <' _>, 16 ) ;
0 commit comments