about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-01-16 13:30:11 +0000
committerbors <bors@rust-lang.org>2024-01-16 13:30:11 +0000
commite2df3f2ad66d63a58f0ef8400ae5472034d51745 (patch)
treede2a979016e61c0e948a2c4b3ece675e30259f1d
parente7a8d21a5233fe28e05b617ca45f7f2c09ff328d (diff)
parent384488c15702e1207b704113f3f4592e3813c29c (diff)
downloadrust-e2df3f2ad66d63a58f0ef8400ae5472034d51745.tar.gz
rust-e2df3f2ad66d63a58f0ef8400ae5472034d51745.zip
Auto merge of #16370 - Veykril:hover-lit, r=Veykril
feat: Hover for literals showing additional value information
-rw-r--r--crates/hir/src/semantics.rs2
-rw-r--r--crates/ide-assists/src/handlers/convert_integer_literal.rs2
-rw-r--r--crates/ide/src/hover.rs60
-rw-r--r--crates/ide/src/hover/tests.rs309
-rw-r--r--crates/ide/src/markup.rs3
-rw-r--r--crates/syntax/src/ast/token_ext.rs26
6 files changed, 377 insertions, 25 deletions
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index f51fe80931c..4267bd4efae 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -433,7 +433,7 @@ impl<'db> SemanticsImpl<'db> {
                     .find_map(|token| {
                         self.resolve_offset_in_format_args(
                             ast::String::cast(token)?,
-                            offset - quote.end(),
+                            offset.checked_sub(quote.end())?,
                         )
                     })
                     .map(|(range, res)| (range + quote.end(), res));
diff --git a/crates/ide-assists/src/handlers/convert_integer_literal.rs b/crates/ide-assists/src/handlers/convert_integer_literal.rs
index ff2195f7e6c..fd3378e8c26 100644
--- a/crates/ide-assists/src/handlers/convert_integer_literal.rs
+++ b/crates/ide-assists/src/handlers/convert_integer_literal.rs
@@ -20,7 +20,7 @@ pub(crate) fn convert_integer_literal(acc: &mut Assists, ctx: &AssistContext<'_>
         _ => return None,
     };
     let radix = literal.radix();
-    let value = literal.value()?;
+    let value = literal.value().ok()?;
     let suffix = literal.suffix();
 
     let range = literal.syntax().text_range();
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 5ad119ace89..d06577d3db2 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -15,7 +15,7 @@ use ide_db::{
     FxIndexSet, RootDatabase,
 };
 use itertools::Itertools;
-use syntax::{ast, AstNode, SyntaxKind::*, SyntaxNode, T};
+use syntax::{ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxNode, T};
 
 use crate::{
     doc_links::token_as_doc_comment,
@@ -268,6 +268,64 @@ fn hover_simple(
                 let c = token.parent().and_then(|x| x.parent()).and_then(ast::ClosureExpr::cast)?;
                 render::closure_expr(sema, config, c)
             })
+        })
+        // tokens
+        .or_else(|| {
+            let mut res = HoverResult::default();
+            match_ast! {
+                match original_token {
+                    ast::String(string) => {
+                        res.markup = Markup::fenced_block_text(format_args!("{}", string.value()?));
+                    },
+                    ast::ByteString(string) => {
+                        res.markup = Markup::fenced_block_text(format_args!("{:?}", string.value()?));
+                    },
+                    ast::CString(string) => {
+                        let val = string.value()?;
+                        res.markup = Markup::fenced_block_text(format_args!("{}", std::str::from_utf8(val.as_ref()).ok()?));
+                    },
+                    ast::Char(char) => {
+                        let mut res = HoverResult::default();
+                        res.markup = Markup::fenced_block_text(format_args!("{}", char.value()?));
+                    },
+                    ast::Byte(byte) => {
+                        res.markup = Markup::fenced_block_text(format_args!("0x{:X}", byte.value()?));
+                    },
+                    ast::FloatNumber(num) => {
+                        res.markup = if num.suffix() == Some("f32") {
+                            match num.value_f32() {
+                                Ok(num) => {
+                                    Markup::fenced_block_text(format_args!("{num} (bits: 0x{:X})", num.to_bits()))
+                                },
+                                Err(e) => {
+                                    Markup::fenced_block_text(format_args!("{e}"))
+                                },
+                            }
+                        } else {
+                            match num.value() {
+                                Ok(num) => {
+                                    Markup::fenced_block_text(format_args!("{num} (bits: 0x{:X})", num.to_bits()))
+                                },
+                                Err(e) => {
+                                    Markup::fenced_block_text(format_args!("{e}"))
+                                },
+                            }
+                        };
+                    },
+                    ast::IntNumber(num) => {
+                        res.markup = match num.value() {
+                            Ok(num) => {
+                                Markup::fenced_block_text(format_args!("{num} (0x{num:X}|0x{num:b})"))
+                            },
+                            Err(e) => {
+                                Markup::fenced_block_text(format_args!("{e}"))
+                            },
+                        };
+                    },
+                    _ => return None
+                }
+            }
+            Some(res)
         });
 
     result.map(|mut res: HoverResult| {
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index 8ef8f295765..587a6b66c4f 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -1474,20 +1474,6 @@ fn foo(Foo { b$0ar }: &Foo) {}
 }
 
 #[test]
-fn test_hover_through_literal_string_in_builtin_macro() {
-    check_hover_no_result(
-        r#"
-            #[rustc_builtin_macro]
-            macro_rules! format {}
-
-            fn foo() {
-                format!("hel$0lo {}", 0);
-            }
-"#,
-    );
-}
-
-#[test]
 fn test_hover_non_ascii_space_doc() {
     check(
         "
@@ -6778,3 +6764,298 @@ fn main() {
         "#]],
     );
 }
+
+#[test]
+fn string_literal() {
+    check(
+        r#"
+fn main() {
+    $0"🦀\u{1f980}\\\x41";
+}
+"#,
+        expect![[r#"
+            *"🦀\u{1f980}\\\x41"*
+            ```text
+            🦀🦀\A
+            ```
+        "#]],
+    );
+    check(
+        r#"
+fn main() {
+    $0r"🦀\u{1f980}\\\x41";
+}
+"#,
+        expect![[r#"
+            *r"🦀\u{1f980}\\\x41"*
+            ```text
+            🦀\u{1f980}\\\x41
+            ```
+        "#]],
+    );
+}
+
+#[test]
+fn cstring_literal() {
+    check(
+        r#"
+fn main() {
+    $0c"🦀\u{1f980}\\\x41";
+}
+"#,
+        expect![[r#"
+            *c"🦀\u{1f980}\\\x41"*
+            ```text
+            🦀🦀\A
+            ```
+        "#]],
+    );
+}
+
+#[test]
+fn byte_string_literal() {
+    check(
+        r#"
+fn main() {
+    $0b"\xF0\x9F\xA6\x80\\";
+}
+"#,
+        expect![[r#"
+            *b"\xF0\x9F\xA6\x80\\"*
+            ```text
+            [240, 159, 166, 128, 92]
+            ```
+        "#]],
+    );
+    check(
+        r#"
+fn main() {
+    $0br"\xF0\x9F\xA6\x80\\";
+}
+"#,
+        expect![[r#"
+            *br"\xF0\x9F\xA6\x80\\"*
+            ```text
+            [92, 120, 70, 48, 92, 120, 57, 70, 92, 120, 65, 54, 92, 120, 56, 48, 92, 92]
+            ```
+        "#]],
+    );
+}
+
+#[test]
+fn byte_literal() {
+    check(
+        r#"
+fn main() {
+    $0b'\xF0';
+}
+"#,
+        expect![[r#"
+            *b'\xF0'*
+            ```text
+            0xF0
+            ```
+        "#]],
+    );
+    check(
+        r#"
+fn main() {
+    $0b'\\';
+}
+"#,
+        expect![[r#"
+            *b'\\'*
+            ```text
+            0x5C
+            ```
+        "#]],
+    );
+}
+
+#[test]
+fn char_literal() {
+    check(
+        r#"
+fn main() {
+    $0'\x41';
+}
+"#,
+        expect![[r#"
+            *'\x41'*
+
+        "#]],
+    );
+    check(
+        r#"
+fn main() {
+    $0'\\';
+}
+"#,
+        expect![[r#"
+            *'\\'*
+
+        "#]],
+    );
+    check(
+        r#"
+fn main() {
+    $0'\u{1f980}';
+}
+"#,
+        expect![[r#"
+            *'\u{1f980}'*
+
+        "#]],
+    );
+}
+
+#[test]
+fn float_literal() {
+    check(
+        r#"
+fn main() {
+    $01.0;
+}
+"#,
+        expect![[r#"
+            *1.0*
+            ```text
+            1 (bits: 0x3FF0000000000000)
+            ```
+        "#]],
+    );
+    check(
+        r#"
+fn main() {
+    $01.0f32;
+}
+"#,
+        expect![[r#"
+            *1.0f32*
+            ```text
+            1 (bits: 0x3F800000)
+            ```
+        "#]],
+    );
+    check(
+        r#"
+fn main() {
+    $0134e12;
+}
+"#,
+        expect![[r#"
+            *134e12*
+            ```text
+            134000000000000 (bits: 0x42DE77D399980000)
+            ```
+        "#]],
+    );
+    check(
+        r#"
+fn main() {
+    $01523527134274733643531312.0;
+}
+"#,
+        expect![[r#"
+            *1523527134274733643531312.0*
+            ```text
+            1523527134274733600000000 (bits: 0x44F429E9249F629B)
+            ```
+        "#]],
+    );
+    check(
+        r#"
+fn main() {
+    $00.1ea123;
+}
+"#,
+        expect![[r#"
+            *0.1ea123*
+            ```text
+            invalid float literal
+            ```
+        "#]],
+    );
+}
+
+#[test]
+fn int_literal() {
+    check(
+        r#"
+fn main() {
+    $034325236457856836345234;
+}
+"#,
+        expect![[r#"
+            *34325236457856836345234*
+            ```text
+            34325236457856836345234 (0x744C659178614489D92|0x111010001001100011001011001000101111000011000010100010010001001110110010010)
+            ```
+        "#]],
+    );
+    check(
+        r#"
+fn main() {
+    $0134_123424_21;
+}
+"#,
+        expect![[r#"
+            *134_123424_21*
+            ```text
+            13412342421 (0x31F701A95|0x1100011111011100000001101010010101)
+            ```
+        "#]],
+    );
+    check(
+        r#"
+fn main() {
+    $00x12423423;
+}
+"#,
+        expect![[r#"
+            *0x12423423*
+            ```text
+            306328611 (0x12423423|0x10010010000100011010000100011)
+            ```
+        "#]],
+    );
+    check(
+        r#"
+fn main() {
+    $00b1111_1111;
+}
+"#,
+        expect![[r#"
+            *0b1111_1111*
+            ```text
+            255 (0xFF|0x11111111)
+            ```
+        "#]],
+    );
+    check(
+        r#"
+fn main() {
+    $00o12345;
+}
+"#,
+        expect![[r#"
+            *0o12345*
+            ```text
+            5349 (0x14E5|0x1010011100101)
+            ```
+        "#]],
+    );
+    check(
+        r#"
+fn main() {
+    $00xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_F;
+}
+"#,
+        expect![[r#"
+            *0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_F*
+            ```text
+            number too large to fit in target type
+            ```
+        "#]],
+    );
+}
diff --git a/crates/ide/src/markup.rs b/crates/ide/src/markup.rs
index 411eb695fbf..4a4e29fa33b 100644
--- a/crates/ide/src/markup.rs
+++ b/crates/ide/src/markup.rs
@@ -35,4 +35,7 @@ impl Markup {
     pub fn fenced_block(contents: impl fmt::Display) -> Markup {
         format!("```rust\n{contents}\n```").into()
     }
+    pub fn fenced_block_text(contents: impl fmt::Display) -> Markup {
+        format!("```text\n{contents}\n```").into()
+    }
 }
diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs
index ede392fc62a..44e49d6d445 100644
--- a/crates/syntax/src/ast/token_ext.rs
+++ b/crates/syntax/src/ast/token_ext.rs
@@ -1,6 +1,9 @@
 //! There are many AstNodes, but only a few tokens, so we hand-write them here.
 
-use std::borrow::Cow;
+use std::{
+    borrow::Cow,
+    num::{ParseFloatError, ParseIntError},
+};
 
 use rustc_lexer::unescape::{
     unescape_byte, unescape_c_string, unescape_char, unescape_literal, CStrUnit, Mode,
@@ -391,10 +394,9 @@ impl ast::IntNumber {
         (prefix, text, suffix)
     }
 
-    pub fn value(&self) -> Option<u128> {
+    pub fn value(&self) -> Result<u128, ParseIntError> {
         let (_, text, _) = self.split_into_parts();
-        let value = u128::from_str_radix(&text.replace('_', ""), self.radix() as u32).ok()?;
-        Some(value)
+        u128::from_str_radix(&text.replace('_', ""), self.radix() as u32)
     }
 
     pub fn suffix(&self) -> Option<&str> {
@@ -445,9 +447,14 @@ impl ast::FloatNumber {
         }
     }
 
-    pub fn value(&self) -> Option<f64> {
+    pub fn value(&self) -> Result<f64, ParseFloatError> {
         let (text, _) = self.split_into_parts();
-        text.replace('_', "").parse::<f64>().ok()
+        text.replace('_', "").parse::<f64>()
+    }
+
+    pub fn value_f32(&self) -> Result<f32, ParseFloatError> {
+        let (text, _) = self.split_into_parts();
+        text.replace('_', "").parse::<f32>()
     }
 }
 
@@ -484,12 +491,15 @@ mod tests {
     }
 
     fn check_float_value(lit: &str, expected: impl Into<Option<f64>> + Copy) {
-        assert_eq!(FloatNumber { syntax: make::tokens::literal(lit) }.value(), expected.into());
+        assert_eq!(
+            FloatNumber { syntax: make::tokens::literal(lit) }.value().ok(),
+            expected.into()
+        );
         assert_eq!(IntNumber { syntax: make::tokens::literal(lit) }.float_value(), expected.into());
     }
 
     fn check_int_value(lit: &str, expected: impl Into<Option<u128>>) {
-        assert_eq!(IntNumber { syntax: make::tokens::literal(lit) }.value(), expected.into());
+        assert_eq!(IntNumber { syntax: make::tokens::literal(lit) }.value().ok(), expected.into());
     }
 
     #[test]