@@ -939,6 +939,7 @@ pub fn expand_preparsed_format_args(
939
939
940
940
let msg = "format argument must be a string literal" ;
941
941
let fmt_sp = efmt. span ;
942
+ let efmt_kind_is_lit: bool = matches ! ( efmt. kind, ast:: ExprKind :: Lit ( _) ) ;
942
943
let ( fmt_str, fmt_style, fmt_span) = match expr_to_spanned_string ( ecx, efmt, msg) {
943
944
Ok ( mut fmt) if append_newline => {
944
945
fmt. 0 = Symbol :: intern ( & format ! ( "{}\n " , fmt. 0 ) ) ;
@@ -989,7 +990,19 @@ pub fn expand_preparsed_format_args(
989
990
990
991
if !parser. errors . is_empty ( ) {
991
992
let err = parser. errors . remove ( 0 ) ;
992
- let sp = fmt_span. from_inner ( err. span ) ;
993
+ let sp = if efmt_kind_is_lit {
994
+ fmt_span. from_inner ( err. span )
995
+ } else {
996
+ // The format string could be another macro invocation, e.g.:
997
+ // format!(concat!("abc", "{}"), 4);
998
+ // However, `err.span` is an inner span relative to the *result* of
999
+ // the macro invocation, which is why we would get a nonsensical
1000
+ // result calling `fmt_span.from_inner(err.span)` as above, and
1001
+ // might even end up inside a multibyte character (issue #86085).
1002
+ // Therefore, we conservatively report the error for the entire
1003
+ // argument span here.
1004
+ fmt_span
1005
+ } ;
993
1006
let mut e = ecx. struct_span_err ( sp, & format ! ( "invalid format string: {}" , err. description) ) ;
994
1007
e. span_label ( sp, err. label + " in format string" ) ;
995
1008
if let Some ( note) = err. note {
0 commit comments