about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--clippy_lints/src/write.rs14
-rw-r--r--tests/ui/auxiliary/proc_macros.rs2
-rw-r--r--tests/ui/print_literal.rs4
3 files changed, 15 insertions, 5 deletions
diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs
index 8114a8463fa..d7c94b909bd 100644
--- a/clippy_lints/src/write.rs
+++ b/clippy_lints/src/write.rs
@@ -463,12 +463,18 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) {
             && let Some(value_string) = snippet_opt(cx, arg.expr.span)
     {
             let (replacement, replace_raw) = match lit.kind {
-                LitKind::Str | LitKind::StrRaw(_)  => extract_str_literal(&value_string),
+                LitKind::Str | LitKind::StrRaw(_)  => match extract_str_literal(&value_string) {
+                    Some(extracted) => extracted,
+                    None => return,
+                },
                 LitKind::Char => (
                     match lit.symbol.as_str() {
                         "\"" => "\\\"",
                         "\\'" => "'",
-                        _ => &value_string[1..value_string.len() - 1],
+                        _ => match value_string.strip_prefix('\'').and_then(|s| s.strip_suffix('\'')) {
+                            Some(stripped) => stripped,
+                            None => return,
+                        },
                     }
                     .to_string(),
                     false,
@@ -533,13 +539,13 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) {
 /// `r#"a"#` -> (`a`, true)
 ///
 /// `"b"` -> (`b`, false)
-fn extract_str_literal(literal: &str) -> (String, bool) {
+fn extract_str_literal(literal: &str) -> Option<(String, bool)> {
     let (literal, raw) = match literal.strip_prefix('r') {
         Some(stripped) => (stripped.trim_matches('#'), true),
         None => (literal, false),
     };
 
-    (literal[1..literal.len() - 1].to_string(), raw)
+    Some((literal.strip_prefix('"')?.strip_suffix('"')?.to_string(), raw))
 }
 
 enum UnescapeErr {
diff --git a/tests/ui/auxiliary/proc_macros.rs b/tests/ui/auxiliary/proc_macros.rs
index 325be83a0d7..3d5beab1eff 100644
--- a/tests/ui/auxiliary/proc_macros.rs
+++ b/tests/ui/auxiliary/proc_macros.rs
@@ -63,7 +63,7 @@ fn group_with_span(delimiter: Delimiter, stream: TokenStream, span: Span) -> Gro
 /// Token used to escape the following token from the macro's span rules.
 const ESCAPE_CHAR: char = '$';
 
-/// Takes a single token followed by a sequence tokens. Returns the sequence of tokens with their
+/// Takes a single token followed by a sequence of tokens. Returns the sequence of tokens with their
 /// span set to that of the first token. Tokens may be escaped with either `#ident` or `#(tokens)`.
 #[proc_macro]
 pub fn with_span(input: TokenStream) -> TokenStream {
diff --git a/tests/ui/print_literal.rs b/tests/ui/print_literal.rs
index 86f908f66b8..538513e9156 100644
--- a/tests/ui/print_literal.rs
+++ b/tests/ui/print_literal.rs
@@ -38,4 +38,8 @@ fn main() {
     // named args shouldn't change anything either
     println!("{foo} {bar}", foo = "hello", bar = "world");
     println!("{bar} {foo}", foo = "hello", bar = "world");
+
+    // The string literal from `file!()` has a callsite span that isn't marked as coming from an
+    // expansion
+    println!("file: {}", file!());
 }