about summary refs log tree commit diff
path: root/clippy_utils/src/source.rs
diff options
context:
space:
mode:
authorPhilipp Krones <hello@philkrones.com>2024-06-13 12:18:48 +0200
committerPhilipp Krones <hello@philkrones.com>2024-06-13 12:24:08 +0200
commitcc63143bbf5ae28daae127902090492560313ca1 (patch)
tree3cf8901da2767e0e330b32ef8a1f8a3bf75adc0c /clippy_utils/src/source.rs
parent35f54fd439432f56c749fbb34f1326e34ed1fc42 (diff)
parentaaade2d12d3cf78fd9eb24bf7973e86433d6373d (diff)
Merge remote-tracking branch 'upstream/master' into rustup
Diffstat (limited to 'clippy_utils/src/source.rs')
-rw-r--r--clippy_utils/src/source.rs47
1 files changed, 46 insertions, 1 deletions
diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs
index fd67e039c29..b7ff7ebe910 100644
--- a/clippy_utils/src/source.rs
+++ b/clippy_utils/src/source.rs
@@ -2,13 +2,14 @@
 
 #![allow(clippy::module_name_repetitions)]
 
+use rustc_ast::{LitKind, StrStyle};
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::Applicability;
 use rustc_hir::{BlockCheckMode, Expr, ExprKind, UnsafeSource};
 use rustc_lint::{LateContext, LintContext};
 use rustc_session::Session;
 use rustc_span::source_map::{original_sp, SourceMap};
-use rustc_span::{hygiene, BytePos, SourceFileAndLine, Pos, SourceFile, Span, SpanData, SyntaxContext, DUMMY_SP};
+use rustc_span::{hygiene, BytePos, Pos, SourceFile, SourceFileAndLine, Span, SpanData, SyntaxContext, DUMMY_SP};
 use std::borrow::Cow;
 use std::ops::Range;
 
@@ -500,6 +501,50 @@ pub fn expand_past_previous_comma(cx: &LateContext<'_>, span: Span) -> Span {
     extended.with_lo(extended.lo() - BytePos(1))
 }
 
+/// Converts `expr` to a `char` literal if it's a `str` literal containing a single
+/// character (or a single byte with `ascii_only`)
+pub fn str_literal_to_char_literal(
+    cx: &LateContext<'_>,
+    expr: &Expr<'_>,
+    applicability: &mut Applicability,
+    ascii_only: bool,
+) -> Option<String> {
+    if let ExprKind::Lit(lit) = &expr.kind
+        && let LitKind::Str(r, style) = lit.node
+        && let string = r.as_str()
+        && let len = if ascii_only {
+            string.len()
+        } else {
+            string.chars().count()
+        }
+        && len == 1
+    {
+        let snip = snippet_with_applicability(cx, expr.span, string, applicability);
+        let ch = if let StrStyle::Raw(nhash) = style {
+            let nhash = nhash as usize;
+            // for raw string: r##"a"##
+            &snip[(nhash + 2)..(snip.len() - 1 - nhash)]
+        } else {
+            // for regular string: "a"
+            &snip[1..(snip.len() - 1)]
+        };
+
+        let hint = format!(
+            "'{}'",
+            match ch {
+                "'" => "\\'",
+                r"\" => "\\\\",
+                "\\\"" => "\"", // no need to escape `"` in `'"'`
+                _ => ch,
+            }
+        );
+
+        Some(hint)
+    } else {
+        None
+    }
+}
+
 #[cfg(test)]
 mod test {
     use super::{reindent_multiline, without_block_comments};