Browse Source

Do not put huge numbers in diagnostics (#1326)

Displaying huge number will costs lots of memory, cpu and is of no benefit.

Signed-off-by: Sean Young <sean@mess.org>
Sean Young 2 years ago
parent
commit
359c2ed786
3 changed files with 82 additions and 28 deletions
  1. 68 27
      src/sema/eval.rs
  2. 14 0
      tests/contract_testcases/solana/large_ints.sol
  3. 0 1
      tests/evm.rs

+ 68 - 27
src/sema/eval.rs

@@ -719,39 +719,80 @@ fn eval_constants_in_expression(
 
 /// Function that takes a BigInt and an expected type. If the number of bits in the type required to represent the BigInt is not suffiecient, it will return a diagnostic.
 pub(super) fn overflow_check(result: &BigInt, ty: &Type, loc: &Loc) -> Option<Diagnostic> {
-    if let Type::Uint(bits) = ty {
-        // If the result sign is minus, throw an error.
-        if let Sign::Minus = result.sign() {
-            return Some(Diagnostic::error(
-                *loc,
-            format!( "negative value {} does not fit into type uint{}. Cannot implicitly convert signed literal to unsigned type.",result,ty.get_type_size()),
-            ));
+    if result.bits() > 1024 {
+        // Do not try to print large values. For example:
+        // uint x = 80 ** 0x100000;
+        // is an enormous value, and having all those decimals in the diagnostic does not help. Also,
+        // printing all those decimals will take a long time
+        if let Type::Uint(bits) = ty {
+            // If the result sign is minus, throw an error.
+            if let Sign::Minus = result.sign() {
+                return Some(Diagnostic::error(
+                    *loc,
+                    format!( "large negative value does not fit into type uint{}. Cannot implicitly convert signed literal to unsigned type.",
+                    ty.get_type_size()),
+                ));
+            }
+
+            // If bits of the result is more than bits of the type, throw and error.
+            if result.bits() > *bits as u64 {
+                return Some(Diagnostic::error(
+                    *loc,
+                    format!(
+                        "value is too large to fit into type uint{}",
+                        ty.get_type_size(),
+                    ),
+                ));
+            }
         }
 
-        // If bits of the result is more than bits of the type, throw and error.
-        if result.bits() > *bits as u64 {
-            return Some(Diagnostic::error(
+        if let Type::Int(bits) = ty {
+            // If number of bits is more than what the type can hold. BigInt.bits() is not used here since it disregards the sign.
+            if result.to_signed_bytes_be().len() * 8 > (*bits as usize) {
+                return Some(Diagnostic::error(
+                    *loc,
+                    format!(
+                        "value is too large to fit into type int{}",
+                        ty.get_type_size(),
+                    ),
+                ));
+            }
+        }
+    } else {
+        if let Type::Uint(bits) = ty {
+            // If the result sign is minus, throw an error.
+            if let Sign::Minus = result.sign() {
+                return Some(Diagnostic::error(
                 *loc,
-                format!(
-                    "value {} does not fit into type uint{}.",
-                    result,
-                    ty.get_type_size(),
-                ),
+            format!( "negative value {} does not fit into type uint{}. Cannot implicitly convert signed literal to unsigned type.",result,ty.get_type_size()),
             ));
+            }
+
+            // If bits of the result is more than bits of the type, throw and error.
+            if result.bits() > *bits as u64 {
+                return Some(Diagnostic::error(
+                    *loc,
+                    format!(
+                        "value {} does not fit into type uint{}.",
+                        result,
+                        ty.get_type_size(),
+                    ),
+                ));
+            }
         }
-    }
 
-    if let Type::Int(bits) = ty {
-        // If number of bits is more than what the type can hold. BigInt.bits() is not used here since it disregards the sign.
-        if result.to_signed_bytes_be().len() * 8 > (*bits as usize) {
-            return Some(Diagnostic::error(
-                *loc,
-                format!(
-                    "value {} does not fit into type int{}.",
-                    result,
-                    ty.get_type_size(),
-                ),
-            ));
+        if let Type::Int(bits) = ty {
+            // If number of bits is more than what the type can hold. BigInt.bits() is not used here since it disregards the sign.
+            if result.to_signed_bytes_be().len() * 8 > (*bits as usize) {
+                return Some(Diagnostic::error(
+                    *loc,
+                    format!(
+                        "value {} does not fit into type int{}.",
+                        result,
+                        ty.get_type_size(),
+                    ),
+                ));
+            }
         }
     }
     None

+ 14 - 0
tests/contract_testcases/solana/large_ints.sol

@@ -0,0 +1,14 @@
+contract c {
+	function test() public {
+		uint x = -80 ** 512;
+		int y = 80 << 100000;
+		int z = -80 << 100000;
+	}
+}
+
+// ---- Expect: diagnostics ----
+// error: 3:12-22: value is too large to fit into type uint256
+// warning: 4:11-23: left shift by 100000 may overflow the final result
+// error: 4:11-23: value is too large to fit into type int256
+// warning: 5:11-24: left shift by 100000 may overflow the final result
+// error: 5:11-24: value is too large to fit into type int256

+ 0 - 1
tests/evm.rs

@@ -197,7 +197,6 @@ fn ethereum_solidity_tests() {
             // FIXME: others listed explicitly cause panics and need fixing
             if !file_name.ends_with("max_depth_reached_4.sol")
                 && !file_name.ends_with("invalid_utf8_sequence.sol")
-                && !file_name.ends_with("370_shift_constant_left_excessive_rvalue.sol")
                 && file_name.ends_with(".sol")
             {
                 Some(entry)