about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-07-08 16:10:58 +0000
committerbors <bors@rust-lang.org>2024-07-08 16:10:58 +0000
commit5445aef843f1a921ba146ace01a2cbbfc33ee38f (patch)
treed91ec197c2db970a26831c865f100461902740fd
parentfa29aa3a4f17def436da5c980550c55bce8eec49 (diff)
parentda41ae7b3acd899654a532fd56f17d79fbfec321 (diff)
downloadrust-5445aef843f1a921ba146ace01a2cbbfc33ee38f.tar.gz
rust-5445aef843f1a921ba146ace01a2cbbfc33ee38f.zip
Auto merge of #17558 - beetrees:fix-double-rounding, r=Veykril
fix: Fix double rounding of `f32` literals

Fixes #17556 by delaying parsing until the type is known. Also adds a test to check the issue is fixed.
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/hir.rs29
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover/tests.rs22
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs25
4 files changed, 47 insertions, 33 deletions
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs
index fd6f4a3d089..378ce1df672 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs
@@ -56,29 +56,29 @@ pub struct Label {
 }
 pub type LabelId = Idx<Label>;
 
-// We convert float values into bits and that's how we don't need to deal with f32 and f64.
-// For PartialEq, bits comparison should work, as ordering is not important
+// We leave float values as a string to avoid double rounding.
+// For PartialEq, string comparison should work, as ordering is not important
 // https://github.com/rust-lang/rust-analyzer/issues/12380#issuecomment-1137284360
-#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
-pub struct FloatTypeWrapper(u64);
+#[derive(Default, Debug, Clone, Eq, PartialEq)]
+pub struct FloatTypeWrapper(Box<str>);
 
 impl FloatTypeWrapper {
-    pub fn new(value: f64) -> Self {
-        Self(value.to_bits())
+    pub fn new(value: String) -> Self {
+        Self(value.into())
     }
 
-    pub fn into_f64(self) -> f64 {
-        f64::from_bits(self.0)
+    pub fn to_f64(&self) -> f64 {
+        self.0.parse().unwrap_or_default()
     }
 
-    pub fn into_f32(self) -> f32 {
-        f64::from_bits(self.0) as f32
+    pub fn to_f32(&self) -> f32 {
+        self.0.parse().unwrap_or_default()
     }
 }
 
 impl fmt::Display for FloatTypeWrapper {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "{:?}", f64::from_bits(self.0))
+        f.write_str(&self.0)
     }
 }
 
@@ -120,10 +120,7 @@ impl From<ast::LiteralKind> for Literal {
         match ast_lit_kind {
             LiteralKind::IntNumber(lit) => {
                 if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) {
-                    Literal::Float(
-                        FloatTypeWrapper::new(lit.float_value().unwrap_or(Default::default())),
-                        builtin,
-                    )
+                    Literal::Float(FloatTypeWrapper::new(lit.value_string()), builtin)
                 } else if let builtin @ Some(_) = lit.suffix().and_then(BuiltinUint::from_suffix) {
                     Literal::Uint(lit.value().unwrap_or(0), builtin)
                 } else {
@@ -133,7 +130,7 @@ impl From<ast::LiteralKind> for Literal {
             }
             LiteralKind::FloatNumber(lit) => {
                 let ty = lit.suffix().and_then(BuiltinFloat::from_suffix);
-                Literal::Float(FloatTypeWrapper::new(lit.value().unwrap_or(Default::default())), ty)
+                Literal::Float(FloatTypeWrapper::new(lit.value_string()), ty)
             }
             LiteralKind::ByteString(bs) => {
                 let text = bs.value().map_or_else(|_| Default::default(), Box::from);
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
index 3402b761d32..3ceb94ad7af 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
@@ -1432,8 +1432,8 @@ impl<'ctx> MirLowerCtx<'ctx> {
             hir_def::hir::Literal::Int(it, _) => Box::from(&it.to_le_bytes()[0..size()?]),
             hir_def::hir::Literal::Uint(it, _) => Box::from(&it.to_le_bytes()[0..size()?]),
             hir_def::hir::Literal::Float(f, _) => match size()? {
-                8 => Box::new(f.into_f64().to_le_bytes()),
-                4 => Box::new(f.into_f32().to_le_bytes()),
+                8 => Box::new(f.to_f64().to_le_bytes()),
+                4 => Box::new(f.to_f32().to_le_bytes()),
                 _ => {
                     return Err(MirLowerError::TypeError("float with size other than 4 or 8 bytes"))
                 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
index 84793746475..51b60df7cc0 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
@@ -5310,6 +5310,28 @@ const FOO$0: f64 = expf64(1.2);
             ```
         "#]],
     );
+    // check `f32` isn't double rounded via `f64`
+    check(
+        r#"
+/// This is a doc
+const FOO$0: f32 = 1.9999999403953552_f32;
+"#,
+        expect![[r#"
+            *FOO*
+
+            ```rust
+            test
+            ```
+
+            ```rust
+            const FOO: f32 = 1.9999999
+            ```
+
+            ---
+
+            This is a doc
+        "#]],
+    );
 }
 
 #[test]
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs
index 1ce548f8fc7..f00e5c6d0e1 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs
@@ -1,9 +1,6 @@
 //! There are many AstNodes, but only a few tokens, so we hand-write them here.
 
-use std::{
-    borrow::Cow,
-    num::{ParseFloatError, ParseIntError},
-};
+use std::{borrow::Cow, num::ParseIntError};
 
 use rustc_lexer::unescape::{
     unescape_byte, unescape_char, unescape_mixed, unescape_unicode, EscapeError, MixedUnit, Mode,
@@ -393,9 +390,9 @@ impl ast::IntNumber {
         }
     }
 
-    pub fn float_value(&self) -> Option<f64> {
+    pub fn value_string(&self) -> String {
         let (_, text, _) = self.split_into_parts();
-        text.replace('_', "").parse::<f64>().ok()
+        text.replace('_', "")
     }
 }
 
@@ -432,14 +429,9 @@ impl ast::FloatNumber {
         }
     }
 
-    pub fn value(&self) -> Result<f64, ParseFloatError> {
-        let (text, _) = self.split_into_parts();
-        text.replace('_', "").parse::<f64>()
-    }
-
-    pub fn value_f32(&self) -> Result<f32, ParseFloatError> {
+    pub fn value_string(&self) -> String {
         let (text, _) = self.split_into_parts();
-        text.replace('_', "").parse::<f32>()
+        text.replace('_', "")
     }
 }
 
@@ -509,10 +501,13 @@ mod tests {
 
     fn check_float_value(lit: &str, expected: impl Into<Option<f64>> + Copy) {
         assert_eq!(
-            FloatNumber { syntax: make::tokens::literal(lit) }.value().ok(),
+            FloatNumber { syntax: make::tokens::literal(lit) }.value_string().parse::<f64>().ok(),
+            expected.into()
+        );
+        assert_eq!(
+            IntNumber { syntax: make::tokens::literal(lit) }.value_string().parse::<f64>().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>>) {