From 9856144b0bdc1519c04a387ac4a9805722710c2d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= <lnicola@dend.ro>
Date: Thu, 5 May 2022 08:10:07 +0300
Subject: [PATCH] Lower values of char and byte literals

---
 crates/hir-def/src/body/lower.rs          |  6 +-
 crates/ide/src/hover/tests.rs             | 88 +++++++++++++++++++++++
 crates/rust-analyzer/src/lib.rs           |  1 -
 crates/syntax/src/ast/expr_ext.rs         | 12 ++--
 crates/syntax/src/ast/generated/tokens.rs | 42 +++++++++++
 crates/syntax/src/ast/token_ext.rs        | 34 ++++++++-
 crates/syntax/src/tests/sourcegen_ast.rs  |  2 +-
 crates/syntax/src/validation.rs           |  4 +-
 8 files changed, 178 insertions(+), 11 deletions(-)

diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs
index f0cb39ec362..e8303ec40f7 100644
--- a/crates/hir-def/src/body/lower.rs
+++ b/crates/hir-def/src/body/lower.rs
@@ -983,9 +983,11 @@ impl From<ast::LiteralKind> for Literal {
                 let text = s.value().map(Box::from).unwrap_or_else(Default::default);
                 Literal::String(text)
             }
-            LiteralKind::Byte => Literal::Uint(Default::default(), Some(BuiltinUint::U8)),
+            LiteralKind::Byte(b) => {
+                Literal::Uint(b.value().unwrap_or_default() as u128, Some(BuiltinUint::U8))
+            }
+            LiteralKind::Char(c) => Literal::Char(c.value().unwrap_or_default()),
             LiteralKind::Bool(val) => Literal::Bool(val),
-            LiteralKind::Char => Literal::Char(Default::default()),
         }
     }
 }
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index 95420f2ffe1..74c5c98689b 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -3507,6 +3507,94 @@ const FOO$0: &str = "bar";
 
             ---
 
+            This is a doc
+        "#]],
+    );
+    // show char literal
+    check(
+        r#"
+/// This is a doc
+const FOO$0: char = 'a';
+"#,
+        expect![[r#"
+            *FOO*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            const FOO: char = 'a'
+            ```
+
+            ---
+
+            This is a doc
+        "#]],
+    );
+    // show escaped char literal
+    check(
+        r#"
+/// This is a doc
+const FOO$0: char = '\x61';
+"#,
+        expect![[r#"
+            *FOO*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            const FOO: char = 'a'
+            ```
+
+            ---
+
+            This is a doc
+        "#]],
+    );
+    // show byte literal
+    check(
+        r#"
+/// This is a doc
+const FOO$0: u8 = b'a';
+"#,
+        expect![[r#"
+            *FOO*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            const FOO: u8 = 97 (0x61)
+            ```
+
+            ---
+
+            This is a doc
+        "#]],
+    );
+    // show escaped byte literal
+    check(
+        r#"
+/// This is a doc
+const FOO$0: u8 = b'\x61';
+"#,
+        expect![[r#"
+            *FOO*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            const FOO: u8 = 97 (0x61)
+            ```
+
+            ---
+
             This is a doc
         "#]],
     );
diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs
index d29ec512d61..3b9e201f573 100644
--- a/crates/rust-analyzer/src/lib.rs
+++ b/crates/rust-analyzer/src/lib.rs
@@ -8,7 +8,6 @@
 //!
 //! The `cli` submodule implements some batch-processing analysis, primarily as
 //! a debugging aid.
-#![recursion_limit = "512"]
 
 pub mod cli;
 
diff --git a/crates/syntax/src/ast/expr_ext.rs b/crates/syntax/src/ast/expr_ext.rs
index 1d0f393ec17..17785152bc5 100644
--- a/crates/syntax/src/ast/expr_ext.rs
+++ b/crates/syntax/src/ast/expr_ext.rs
@@ -283,8 +283,8 @@ pub enum LiteralKind {
     ByteString(ast::ByteString),
     IntNumber(ast::IntNumber),
     FloatNumber(ast::FloatNumber),
-    Char,
-    Byte,
+    Char(ast::Char),
+    Byte(ast::Byte),
     Bool(bool),
 }
 
@@ -312,12 +312,16 @@ impl ast::Literal {
         if let Some(t) = ast::ByteString::cast(token.clone()) {
             return LiteralKind::ByteString(t);
         }
+        if let Some(t) = ast::Char::cast(token.clone()) {
+            return LiteralKind::Char(t);
+        }
+        if let Some(t) = ast::Byte::cast(token.clone()) {
+            return LiteralKind::Byte(t);
+        }
 
         match token.kind() {
             T![true] => LiteralKind::Bool(true),
             T![false] => LiteralKind::Bool(false),
-            CHAR => LiteralKind::Char,
-            BYTE => LiteralKind::Byte,
             _ => unreachable!(),
         }
     }
diff --git a/crates/syntax/src/ast/generated/tokens.rs b/crates/syntax/src/ast/generated/tokens.rs
index 30f23b9d969..a3209c5abd2 100644
--- a/crates/syntax/src/ast/generated/tokens.rs
+++ b/crates/syntax/src/ast/generated/tokens.rs
@@ -132,6 +132,48 @@ impl AstToken for FloatNumber {
     fn syntax(&self) -> &SyntaxToken { &self.syntax }
 }
 
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct Char {
+    pub(crate) syntax: SyntaxToken,
+}
+impl std::fmt::Display for Char {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        std::fmt::Display::fmt(&self.syntax, f)
+    }
+}
+impl AstToken for Char {
+    fn can_cast(kind: SyntaxKind) -> bool { kind == CHAR }
+    fn cast(syntax: SyntaxToken) -> Option<Self> {
+        if Self::can_cast(syntax.kind()) {
+            Some(Self { syntax })
+        } else {
+            None
+        }
+    }
+    fn syntax(&self) -> &SyntaxToken { &self.syntax }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct Byte {
+    pub(crate) syntax: SyntaxToken,
+}
+impl std::fmt::Display for Byte {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        std::fmt::Display::fmt(&self.syntax, f)
+    }
+}
+impl AstToken for Byte {
+    fn can_cast(kind: SyntaxKind) -> bool { kind == BYTE }
+    fn cast(syntax: SyntaxToken) -> Option<Self> {
+        if Self::can_cast(syntax.kind()) {
+            Some(Self { syntax })
+        } else {
+            None
+        }
+    }
+    fn syntax(&self) -> &SyntaxToken { &self.syntax }
+}
+
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct Ident {
     pub(crate) syntax: SyntaxToken,
diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs
index f1e5c2136f3..4b6dc236b53 100644
--- a/crates/syntax/src/ast/token_ext.rs
+++ b/crates/syntax/src/ast/token_ext.rs
@@ -2,7 +2,7 @@
 
 use std::borrow::Cow;
 
-use rustc_lexer::unescape::{unescape_literal, Mode};
+use rustc_lexer::unescape::{unescape_byte, unescape_char, unescape_literal, Mode};
 
 use crate::{
     ast::{self, AstToken},
@@ -406,3 +406,35 @@ mod tests {
         check_string_value(r"C:\\Windows\\System32\\", "C:\\Windows\\System32\\");
     }
 }
+
+impl ast::Char {
+    pub fn value(&self) -> Option<char> {
+        let mut text = self.text();
+        if text.starts_with('\'') {
+            text = &text[1..];
+        } else {
+            return None;
+        }
+        if text.ends_with('\'') {
+            text = &text[0..text.len() - 1];
+        }
+
+        unescape_char(text).ok()
+    }
+}
+
+impl ast::Byte {
+    pub fn value(&self) -> Option<u8> {
+        let mut text = self.text();
+        if text.starts_with("b\'") {
+            text = &text[2..];
+        } else {
+            return None;
+        }
+        if text.ends_with('\'') {
+            text = &text[0..text.len() - 1];
+        }
+
+        unescape_byte(text).ok()
+    }
+}
diff --git a/crates/syntax/src/tests/sourcegen_ast.rs b/crates/syntax/src/tests/sourcegen_ast.rs
index c2ed3eab0a5..4cfb8075cb1 100644
--- a/crates/syntax/src/tests/sourcegen_ast.rs
+++ b/crates/syntax/src/tests/sourcegen_ast.rs
@@ -585,7 +585,7 @@ impl Field {
 
 fn lower(grammar: &Grammar) -> AstSrc {
     let mut res = AstSrc {
-        tokens: "Whitespace Comment String ByteString IntNumber FloatNumber Ident"
+        tokens: "Whitespace Comment String ByteString IntNumber FloatNumber Char Byte Ident"
             .split_ascii_whitespace()
             .map(|it| it.to_string())
             .collect::<Vec<_>>(),
diff --git a/crates/syntax/src/validation.rs b/crates/syntax/src/validation.rs
index 286affbba92..c2c2c82e11f 100644
--- a/crates/syntax/src/validation.rs
+++ b/crates/syntax/src/validation.rs
@@ -151,12 +151,12 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) {
                 }
             }
         }
-        ast::LiteralKind::Char => {
+        ast::LiteralKind::Char(_) => {
             if let Some(Err(e)) = unquote(text, 1, '\'').map(unescape_char) {
                 push_err(1, e);
             }
         }
-        ast::LiteralKind::Byte => {
+        ast::LiteralKind::Byte(_) => {
             if let Some(Err(e)) = unquote(text, 2, '\'').map(unescape_byte) {
                 push_err(2, e);
             }