diff --git a/compiler-core/src/ast.rs b/compiler-core/src/ast.rs index 3770c997699..ba38666f947 100644 --- a/compiler-core/src/ast.rs +++ b/compiler-core/src/ast.rs @@ -15,7 +15,7 @@ use crate::analyse::Inferred; use crate::bit_array; use crate::build::{ExpressionPosition, Located, Target, module_erlang_name}; use crate::exhaustiveness::CompiledCase; -use crate::parse::SpannedString; +use crate::parse::{NotNan, SpannedString}; use crate::type_::error::VariableOrigin; use crate::type_::expression::{Implementations, Purity}; use crate::type_::printer::Names; @@ -1702,11 +1702,11 @@ fn pattern_and_expression_are_the_same(pattern: &TypedPattern, expression: &Type // `"wibble" -> "wibble"` ( TypedPattern::Float { - value: pattern_value, + float_value: pattern_value, .. }, - TypedExpr::Float { value, .. }, - ) => pattern_value == value, + TypedExpr::Float { float_value, .. }, + ) => pattern_value == float_value, (TypedPattern::Float { .. }, _) => false, ( @@ -2319,6 +2319,7 @@ pub enum Pattern { Float { location: SrcSpan, value: EcoString, + float_value: NotNan, }, String { diff --git a/compiler-core/src/ast/constant.rs b/compiler-core/src/ast/constant.rs index e66d00e1f55..55b2ec468e3 100644 --- a/compiler-core/src/ast/constant.rs +++ b/compiler-core/src/ast/constant.rs @@ -16,6 +16,7 @@ pub enum Constant { Float { location: SrcSpan, value: EcoString, + float_value: NotNan, }, String { diff --git a/compiler-core/src/ast/typed.rs b/compiler-core/src/ast/typed.rs index 4e8cb77193c..70cac62697f 100644 --- a/compiler-core/src/ast/typed.rs +++ b/compiler-core/src/ast/typed.rs @@ -6,6 +6,7 @@ use super::*; use crate::{ build::ExpressionPosition, exhaustiveness::CompiledCase, + parse::NotNan, type_::{HasType, Type, ValueConstructorVariant, bool}, }; @@ -22,6 +23,7 @@ pub enum TypedExpr { location: SrcSpan, type_: Arc, value: EcoString, + float_value: NotNan, }, String { diff --git a/compiler-core/src/ast/untyped.rs b/compiler-core/src/ast/untyped.rs index c0f716e59c6..fd3c05169a6 100644 --- a/compiler-core/src/ast/untyped.rs +++ b/compiler-core/src/ast/untyped.rs @@ -1,5 +1,7 @@ use vec1::Vec1; +use crate::parse::NotNan; + use super::*; #[derive(Debug, Clone, PartialEq, Eq)] @@ -13,6 +15,7 @@ pub enum UntypedExpr { Float { location: SrcSpan, value: EcoString, + float_value: NotNan, }, String { diff --git a/compiler-core/src/ast/visit.rs b/compiler-core/src/ast/visit.rs index 3fc34e65f07..8af9cf2e63e 100644 --- a/compiler-core/src/ast/visit.rs +++ b/compiler-core/src/ast/visit.rs @@ -774,6 +774,7 @@ where location, type_, value, + float_value: _, } => v.visit_typed_expr_float(location, type_, value), TypedExpr::String { location, @@ -1568,7 +1569,11 @@ where value, int_value: _, } => v.visit_typed_pattern_int(location, value), - Pattern::Float { location, value } => v.visit_typed_pattern_float(location, value), + Pattern::Float { + location, + value, + float_value: _, + } => v.visit_typed_pattern_float(location, value), Pattern::String { location, value } => v.visit_typed_pattern_string(location, value), Pattern::Variable { location, diff --git a/compiler-core/src/ast_folder.rs b/compiler-core/src/ast_folder.rs index 65bcb1dee3a..8ec05c643b7 100644 --- a/compiler-core/src/ast_folder.rs +++ b/compiler-core/src/ast_folder.rs @@ -17,6 +17,7 @@ use crate::{ UntypedUseAssignment, Use, UseAssignment, }, build::Target, + parse::NotNan, type_::error::VariableOrigin, }; @@ -258,7 +259,11 @@ pub trait UntypedExprFolder: TypeAstFolder + UntypedConstantFolder + PatternFold value, int_value, } => self.fold_int(location, value, int_value), - UntypedExpr::Float { location, value } => self.fold_float(location, value), + UntypedExpr::Float { + location, + value, + float_value, + } => self.fold_float(location, value, float_value), UntypedExpr::String { location, value } => self.fold_string(location, value), UntypedExpr::Block { @@ -714,8 +719,17 @@ pub trait UntypedExprFolder: TypeAstFolder + UntypedConstantFolder + PatternFold } } - fn fold_float(&mut self, location: SrcSpan, value: EcoString) -> UntypedExpr { - UntypedExpr::Float { location, value } + fn fold_float( + &mut self, + location: SrcSpan, + value: EcoString, + float_value: NotNan, + ) -> UntypedExpr { + UntypedExpr::Float { + location, + value, + float_value, + } } fn fold_string(&mut self, location: SrcSpan, value: EcoString) -> UntypedExpr { @@ -937,7 +951,11 @@ pub trait UntypedConstantFolder { int_value, } => self.fold_constant_int(location, value, int_value), - Constant::Float { location, value } => self.fold_constant_float(location, value), + Constant::Float { + location, + value, + float_value, + } => self.fold_constant_float(location, value, float_value), Constant::String { location, value } => self.fold_constant_string(location, value), @@ -998,8 +1016,17 @@ pub trait UntypedConstantFolder { } } - fn fold_constant_float(&mut self, location: SrcSpan, value: EcoString) -> UntypedConstant { - Constant::Float { location, value } + fn fold_constant_float( + &mut self, + location: SrcSpan, + value: EcoString, + float_value: NotNan, + ) -> UntypedConstant { + Constant::Float { + location, + value, + float_value, + } } fn fold_constant_string(&mut self, location: SrcSpan, value: EcoString) -> UntypedConstant { @@ -1188,7 +1215,11 @@ pub trait PatternFolder { int_value, } => self.fold_pattern_int(location, value, int_value), - Pattern::Float { location, value } => self.fold_pattern_float(location, value), + Pattern::Float { + location, + value, + float_value, + } => self.fold_pattern_float(location, value, float_value), Pattern::String { location, value } => self.fold_pattern_string(location, value), @@ -1277,8 +1308,17 @@ pub trait PatternFolder { } } - fn fold_pattern_float(&mut self, location: SrcSpan, value: EcoString) -> UntypedPattern { - Pattern::Float { location, value } + fn fold_pattern_float( + &mut self, + location: SrcSpan, + value: EcoString, + float_value: NotNan, + ) -> UntypedPattern { + Pattern::Float { + location, + value, + float_value, + } } fn fold_pattern_string(&mut self, location: SrcSpan, value: EcoString) -> UntypedPattern { diff --git a/compiler-core/src/metadata/module_decoder.rs b/compiler-core/src/metadata/module_decoder.rs index e23e5dc1f86..ffbf87ba992 100755 --- a/compiler-core/src/metadata/module_decoder.rs +++ b/compiler-core/src/metadata/module_decoder.rs @@ -12,6 +12,7 @@ use crate::{ }, build::Origin, line_numbers::{Character, LineNumbers}, + parse::NotNan, reference::{Reference, ReferenceKind, ReferenceMap}, schema_capnp::{self as schema, *}, type_::{ @@ -396,6 +397,7 @@ impl ModuleDecoder { Constant::Float { location: Default::default(), value: value.into(), + float_value: NotNan::parse(value).expect("float value to parse as non-NaN f64"), } } diff --git a/compiler-core/src/metadata/tests.rs b/compiler-core/src/metadata/tests.rs index f45052370eb..95f90dbb08c 100644 --- a/compiler-core/src/metadata/tests.rs +++ b/compiler-core/src/metadata/tests.rs @@ -10,6 +10,7 @@ use crate::{ }, build::Origin, line_numbers::LineNumbers, + parse::NotNan, reference::{Reference, ReferenceKind}, type_::{ self, Deprecation, ModuleInterface, Opaque, References, Type, TypeAliasConstructor, @@ -1056,6 +1057,7 @@ fn constant_float() { let module = constant_module(Constant::Float { location: Default::default(), value: "1.0".into(), + float_value: NotNan::ONE, }); assert_eq!(roundtrip(&module), module); @@ -1084,6 +1086,7 @@ fn constant_tuple() { Constant::Float { location: Default::default(), value: "1.0".into(), + float_value: NotNan::ONE, }, Constant::Tuple { location: Default::default(), @@ -1096,6 +1099,7 @@ fn constant_tuple() { Constant::Float { location: Default::default(), value: "1.0".into(), + float_value: NotNan::ONE, }, ], }, @@ -1146,6 +1150,7 @@ fn constant_record() { value: Constant::Float { location: Default::default(), value: "0.0".into(), + float_value: NotNan::ZERO, }, }, CallArg { diff --git a/compiler-core/src/parse.rs b/compiler-core/src/parse.rs index a67c334a6bc..9b58d72ebe9 100644 --- a/compiler-core/src/parse.rs +++ b/compiler-core/src/parse.rs @@ -515,11 +515,12 @@ where } } - Some((start, Token::Float { value }, end)) => { + Some((start, Token::Float { value, float_value }, end)) => { self.advance(); UntypedExpr::Float { location: SrcSpan { start, end }, value, + float_value, } } @@ -1399,11 +1400,12 @@ where int_value, } } - Some((start, Token::Float { value }, end)) => { + Some((start, Token::Float { value, float_value }, end)) => { self.advance(); Pattern::Float { location: SrcSpan { start, end }, value, + float_value, } } Some((start, Token::Hash, _)) => { @@ -3096,11 +3098,12 @@ where })) } - Some((start, Token::Float { value }, end)) => { + Some((start, Token::Float { value, float_value }, end)) => { self.advance(); Ok(Some(Constant::Float { value, location: SrcSpan { start, end }, + float_value, })) } @@ -4696,3 +4699,46 @@ impl PatternPosition { } } } + +/// A thin f64 wrapper that does not permit NaN. +/// This allows us to implement `Eq`, which require reflexivity. +/// +/// Used for gleam float literals, which cannot be NaN. +/// While there is no syntax for "infinity", float literals +/// may overflow into (possibly negative) infinity on the JS target. +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct NotNan(f64); + +impl NotNan { + pub const ONE: Self = NotNan(1.0); + pub const ZERO: Self = NotNan(0.0); + + /// Parse from a string, returning `None` if the string + /// is not a valid f64 or the float is `NaN`` + pub fn parse(value: &str) -> Option { + value + .replace("_", "") + .parse::() + .ok() + .filter(|f| !f.is_nan()) + .map(NotNan) + } + + pub fn value(&self) -> f64 { + self.0 + } +} + +impl Eq for NotNan {} +impl Ord for NotNan { + fn cmp(&self, other: &Self) -> Ordering { + self.0 + .partial_cmp(&other.0) + .expect("Only NaN comparisons should fail") + } +} +impl PartialOrd for NotNan { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} diff --git a/compiler-core/src/parse/lexer.rs b/compiler-core/src/parse/lexer.rs index b58fc42c8de..253c460f3eb 100644 --- a/compiler-core/src/parse/lexer.rs +++ b/compiler-core/src/parse/lexer.rs @@ -1,6 +1,7 @@ use ecow::EcoString; use crate::ast::SrcSpan; +use crate::parse::NotNan; use crate::parse::error::{LexicalError, LexicalErrorType}; use crate::parse::token::Token; use std::char; @@ -621,10 +622,12 @@ where value.push_str(&exponent_run); } let end_pos = self.get_pos(); + let float_value = NotNan::parse(&value).expect("float value to parse as non-NaN f64"); Ok(( start_pos, Token::Float { value: value.into(), + float_value, }, end_pos, )) diff --git a/compiler-core/src/parse/snapshots/gleam_core__parse__tests__nested_block.snap b/compiler-core/src/parse/snapshots/gleam_core__parse__tests__nested_block.snap index a4cf8760125..05aa256aa5b 100644 --- a/compiler-core/src/parse/snapshots/gleam_core__parse__tests__nested_block.snap +++ b/compiler-core/src/parse/snapshots/gleam_core__parse__tests__nested_block.snap @@ -34,6 +34,9 @@ expression: "{ 1 { 1.0 2.0 } 3 }" end: 9, }, value: "1.0", + float_value: NotNan( + 1.0, + ), }, ), Expression( @@ -43,6 +46,9 @@ expression: "{ 1 { 1.0 2.0 } 3 }" end: 13, }, value: "2.0", + float_value: NotNan( + 2.0, + ), }, ), ], diff --git a/compiler-core/src/parse/token.rs b/compiler-core/src/parse/token.rs index 5e3bafe9f38..9a78ef347b4 100644 --- a/compiler-core/src/parse/token.rs +++ b/compiler-core/src/parse/token.rs @@ -3,15 +3,33 @@ use std::fmt; use ecow::EcoString; +use crate::parse::NotNan; + #[derive(Clone, Debug, PartialEq, Eq)] pub enum Token { - Name { name: EcoString }, - UpName { name: EcoString }, - DiscardName { name: EcoString }, - Int { value: EcoString, int_value: BigInt }, - Float { value: EcoString }, - String { value: EcoString }, - CommentDoc { content: EcoString }, + Name { + name: EcoString, + }, + UpName { + name: EcoString, + }, + DiscardName { + name: EcoString, + }, + Int { + value: EcoString, + int_value: BigInt, + }, + Float { + value: EcoString, + float_value: NotNan, + }, + String { + value: EcoString, + }, + CommentDoc { + content: EcoString, + }, // Groupings LeftParen, // ( RightParen, // ) @@ -207,7 +225,10 @@ impl fmt::Display for Token { value, int_value: _, } - | Token::Float { value } + | Token::Float { + value, + float_value: _, + } | Token::String { value } => value.as_str(), Token::AmperAmper => "&&", Token::As => "as", diff --git a/compiler-core/src/type_/error.rs b/compiler-core/src/type_/error.rs index 749f77329ae..e27f194ef5e 100644 --- a/compiler-core/src/type_/error.rs +++ b/compiler-core/src/type_/error.rs @@ -6,6 +6,7 @@ use crate::{ ast::{BinOp, BitArraySegmentTruncation, Layer, SrcSpan, TodoKind}, build::Target, exhaustiveness::ImpossibleBitArraySegmentPattern, + parse::NotNan, type_::{Type, expression::ComparisonOutcome}, }; @@ -1966,18 +1967,11 @@ pub fn check_javascript_int_safety(int_value: &BigInt, location: SrcSpan, proble /// -1.7976931348623157e308 to 1.7976931348623157e308 which is the allowed range for /// Erlang's floating point numbers /// -pub fn check_erlang_float_safety( - string_value: &EcoString, - location: SrcSpan, - problems: &mut Problems, -) { +pub fn check_erlang_float_safety(value: NotNan, location: SrcSpan, problems: &mut Problems) { let erl_min_float = -1.7976931348623157e308f64; let erl_max_float = 1.7976931348623157e308f64; - let float_value: f64 = string_value - .replace("_", "") - .parse() - .expect("Unable to parse string to floating point value"); + let float_value = value.value(); if float_value < erl_min_float || float_value > erl_max_float { problems.error(Error::ErlangFloatUnsafe { location }); diff --git a/compiler-core/src/type_/expression.rs b/compiler-core/src/type_/expression.rs index 059281adaf4..7259f40353c 100644 --- a/compiler-core/src/type_/expression.rs +++ b/compiler-core/src/type_/expression.rs @@ -16,7 +16,7 @@ use crate::{ }, build::Target, exhaustiveness::{self, CompileCaseResult, CompiledCase, Reachability}, - parse::PatternPosition, + parse::{NotNan, PatternPosition}, reference::ReferenceKind, }; use ecow::eco_format; @@ -454,15 +454,17 @@ impl<'a, 'b> ExprTyper<'a, 'b> { } => Ok(self.infer_tuple(elements, location)), UntypedExpr::Float { - location, value, .. + location, + value, + float_value, } => { if self.environment.target == Target::Erlang && !self.current_function_definition.has_erlang_external { - check_erlang_float_safety(&value, location, self.problems) + check_erlang_float_safety(float_value, location, self.problems) } - Ok(self.infer_float(value, location)) + Ok(self.infer_float(value, float_value, location)) } UntypedExpr::String { @@ -665,10 +667,16 @@ impl<'a, 'b> ExprTyper<'a, 'b> { } } - fn infer_float(&mut self, value: EcoString, location: SrcSpan) -> TypedExpr { + fn infer_float( + &mut self, + value: EcoString, + float_value: NotNan, + location: SrcSpan, + ) -> TypedExpr { TypedExpr::Float { location, value, + float_value, type_: float(), } } @@ -1937,6 +1945,22 @@ impl<'a, 'b> ExprTyper<'a, 'b> { } } + ( + TypedExpr::Float { float_value: n, .. }, + op, + TypedExpr::Float { float_value: m, .. }, + ) => match op { + BinOp::LtFloat if n < m => ComparisonOutcome::AlwaysSucceeds, + BinOp::LtFloat => ComparisonOutcome::AlwaysFails, + BinOp::LtEqFloat if n <= m => ComparisonOutcome::AlwaysSucceeds, + BinOp::LtEqFloat => ComparisonOutcome::AlwaysFails, + BinOp::GtFloat if n > m => ComparisonOutcome::AlwaysSucceeds, + BinOp::GtFloat => ComparisonOutcome::AlwaysFails, + BinOp::GtEqFloat if n >= m => ComparisonOutcome::AlwaysSucceeds, + BinOp::GtEqFloat => ComparisonOutcome::AlwaysFails, + _ => return, + }, + _ => return, }; @@ -3754,13 +3778,19 @@ impl<'a, 'b> ExprTyper<'a, 'b> { } Constant::Float { - location, value, .. + location, + value, + float_value, } => { if self.environment.target == Target::Erlang { - check_erlang_float_safety(&value, location, self.problems) + check_erlang_float_safety(float_value, location, self.problems) } - Ok(Constant::Float { location, value }) + Ok(Constant::Float { + location, + value, + float_value, + }) } Constant::String { @@ -5184,7 +5214,7 @@ fn static_compare(one: &TypedExpr, other: &TypedExpr) -> StaticComparison { } } - (TypedExpr::Float { value: n, .. }, TypedExpr::Float { value: m, .. }) => { + (TypedExpr::Float { float_value: n, .. }, TypedExpr::Float { float_value: m, .. }) => { if n == m { StaticComparison::CertainlyEqual } else { diff --git a/compiler-core/src/type_/pattern.rs b/compiler-core/src/type_/pattern.rs index 1b879bd3532..85eed8c52c1 100644 --- a/compiler-core/src/type_/pattern.rs +++ b/compiler-core/src/type_/pattern.rs @@ -735,16 +735,24 @@ impl<'a, 'b> PatternTyper<'a, 'b> { } } - Pattern::Float { location, value } => { + Pattern::Float { + location, + value, + float_value, + } => { self.unify_types(type_, float(), location); if self.environment.target == Target::Erlang && !self.implementations.uses_erlang_externals { - check_erlang_float_safety(&value, location, self.problems) + check_erlang_float_safety(float_value, location, self.problems) } - Pattern::Float { location, value } + Pattern::Float { + location, + value, + float_value, + } } Pattern::String { location, value } => { diff --git a/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__warnings__float_literals_redundant_comparison.snap b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__warnings__float_literals_redundant_comparison.snap new file mode 100644 index 00000000000..b701a8eeac8 --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__warnings__float_literals_redundant_comparison.snap @@ -0,0 +1,15 @@ +--- +source: compiler-core/src/type_/tests/warnings.rs +expression: "pub fn main() { 1.0 == 1.0 }" +--- +----- SOURCE CODE +pub fn main() { 1.0 == 1.0 } + +----- WARNING +warning: Redundant comparison + ┌─ /src/warning/wrn.gleam:1:17 + │ +1 │ pub fn main() { 1.0 == 1.0 } + │ ^^^^^^^^^^ This is always `True` + +This comparison is redundant since it always succeeds. diff --git a/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__warnings__float_literals_redundant_comparison_2.snap b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__warnings__float_literals_redundant_comparison_2.snap new file mode 100644 index 00000000000..61ac3bd943f --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__warnings__float_literals_redundant_comparison_2.snap @@ -0,0 +1,15 @@ +--- +source: compiler-core/src/type_/tests/warnings.rs +expression: "pub fn main() { 1.0 == 2.0 }" +--- +----- SOURCE CODE +pub fn main() { 1.0 == 2.0 } + +----- WARNING +warning: Redundant comparison + ┌─ /src/warning/wrn.gleam:1:17 + │ +1 │ pub fn main() { 1.0 == 2.0 } + │ ^^^^^^^^^^ This is always `False` + +This comparison is redundant since it always fails. diff --git a/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__warnings__float_literals_redundant_comparison_3.snap b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__warnings__float_literals_redundant_comparison_3.snap new file mode 100644 index 00000000000..8e534be77f5 --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__warnings__float_literals_redundant_comparison_3.snap @@ -0,0 +1,15 @@ +--- +source: compiler-core/src/type_/tests/warnings.rs +expression: "pub fn main() { 1.0 != 1.0 }" +--- +----- SOURCE CODE +pub fn main() { 1.0 != 1.0 } + +----- WARNING +warning: Redundant comparison + ┌─ /src/warning/wrn.gleam:1:17 + │ +1 │ pub fn main() { 1.0 != 1.0 } + │ ^^^^^^^^^^ This is always `False` + +This comparison is redundant since it always fails. diff --git a/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__warnings__float_literals_redundant_comparison_4.snap b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__warnings__float_literals_redundant_comparison_4.snap new file mode 100644 index 00000000000..529d6b518bf --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__warnings__float_literals_redundant_comparison_4.snap @@ -0,0 +1,15 @@ +--- +source: compiler-core/src/type_/tests/warnings.rs +expression: "pub fn main() { 1.0 != 2.0 }" +--- +----- SOURCE CODE +pub fn main() { 1.0 != 2.0 } + +----- WARNING +warning: Redundant comparison + ┌─ /src/warning/wrn.gleam:1:17 + │ +1 │ pub fn main() { 1.0 != 2.0 } + │ ^^^^^^^^^^ This is always `True` + +This comparison is redundant since it always succeeds. diff --git a/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__warnings__float_literals_redundant_comparison_5.snap b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__warnings__float_literals_redundant_comparison_5.snap new file mode 100644 index 00000000000..3df40076c1a --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__warnings__float_literals_redundant_comparison_5.snap @@ -0,0 +1,15 @@ +--- +source: compiler-core/src/type_/tests/warnings.rs +expression: "pub fn main() { 1.0 >. 2.0 }" +--- +----- SOURCE CODE +pub fn main() { 1.0 >. 2.0 } + +----- WARNING +warning: Redundant comparison + ┌─ /src/warning/wrn.gleam:1:17 + │ +1 │ pub fn main() { 1.0 >. 2.0 } + │ ^^^^^^^^^^ This is always `False` + +This comparison is redundant since it always fails. diff --git a/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__warnings__float_literals_redundant_comparison_6.snap b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__warnings__float_literals_redundant_comparison_6.snap new file mode 100644 index 00000000000..cba86a9347a --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__warnings__float_literals_redundant_comparison_6.snap @@ -0,0 +1,15 @@ +--- +source: compiler-core/src/type_/tests/warnings.rs +expression: "pub fn main() { 1.0 <=. 2.0 }" +--- +----- SOURCE CODE +pub fn main() { 1.0 <=. 2.0 } + +----- WARNING +warning: Redundant comparison + ┌─ /src/warning/wrn.gleam:1:17 + │ +1 │ pub fn main() { 1.0 <=. 2.0 } + │ ^^^^^^^^^^^ This is always `True` + +This comparison is redundant since it always succeeds. diff --git a/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__warnings__float_literals_redundant_comparison_7.snap b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__warnings__float_literals_redundant_comparison_7.snap new file mode 100644 index 00000000000..7565c2300bb --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__warnings__float_literals_redundant_comparison_7.snap @@ -0,0 +1,15 @@ +--- +source: compiler-core/src/type_/tests/warnings.rs +expression: "pub fn main() { 1.0 <. 2.0 }" +--- +----- SOURCE CODE +pub fn main() { 1.0 <. 2.0 } + +----- WARNING +warning: Redundant comparison + ┌─ /src/warning/wrn.gleam:1:17 + │ +1 │ pub fn main() { 1.0 <. 2.0 } + │ ^^^^^^^^^^ This is always `True` + +This comparison is redundant since it always succeeds. diff --git a/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__warnings__float_literals_redundant_comparison_8.snap b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__warnings__float_literals_redundant_comparison_8.snap new file mode 100644 index 00000000000..ada10821837 --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__warnings__float_literals_redundant_comparison_8.snap @@ -0,0 +1,15 @@ +--- +source: compiler-core/src/type_/tests/warnings.rs +expression: "pub fn main() { 1.0 >=. 2.0 }" +--- +----- SOURCE CODE +pub fn main() { 1.0 >=. 2.0 } + +----- WARNING +warning: Redundant comparison + ┌─ /src/warning/wrn.gleam:1:17 + │ +1 │ pub fn main() { 1.0 >=. 2.0 } + │ ^^^^^^^^^^^ This is always `False` + +This comparison is redundant since it always fails. diff --git a/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__warnings__float_literals_redundant_comparison_different_repr.snap b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__warnings__float_literals_redundant_comparison_different_repr.snap new file mode 100644 index 00000000000..a1c56923140 --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__warnings__float_literals_redundant_comparison_different_repr.snap @@ -0,0 +1,15 @@ +--- +source: compiler-core/src/type_/tests/warnings.rs +expression: "pub fn main() { 1_0.0 == 10.0 }" +--- +----- SOURCE CODE +pub fn main() { 1_0.0 == 10.0 } + +----- WARNING +warning: Redundant comparison + ┌─ /src/warning/wrn.gleam:1:17 + │ +1 │ pub fn main() { 1_0.0 == 10.0 } + │ ^^^^^^^^^^^^^ This is always `True` + +This comparison is redundant since it always succeeds. diff --git a/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__warnings__float_literals_redundant_comparison_different_repr_2.snap b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__warnings__float_literals_redundant_comparison_different_repr_2.snap new file mode 100644 index 00000000000..02ba79e8641 --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__warnings__float_literals_redundant_comparison_different_repr_2.snap @@ -0,0 +1,15 @@ +--- +source: compiler-core/src/type_/tests/warnings.rs +expression: "pub fn main() { 10.0 == 1.0e1 }" +--- +----- SOURCE CODE +pub fn main() { 10.0 == 1.0e1 } + +----- WARNING +warning: Redundant comparison + ┌─ /src/warning/wrn.gleam:1:17 + │ +1 │ pub fn main() { 10.0 == 1.0e1 } + │ ^^^^^^^^^^^^^ This is always `True` + +This comparison is redundant since it always succeeds. diff --git a/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__warnings__float_literals_redundant_comparison_infinity.snap b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__warnings__float_literals_redundant_comparison_infinity.snap new file mode 100644 index 00000000000..9bd26fb17b1 --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__warnings__float_literals_redundant_comparison_infinity.snap @@ -0,0 +1,15 @@ +--- +source: compiler-core/src/type_/tests/warnings.rs +expression: "pub fn main() { 1.0e500 == 1.0e600 }" +--- +----- SOURCE CODE +pub fn main() { 1.0e500 == 1.0e600 } + +----- WARNING +warning: Redundant comparison + ┌─ /src/warning/wrn.gleam:1:17 + │ +1 │ pub fn main() { 1.0e500 == 1.0e600 } + │ ^^^^^^^^^^^^^^^^^^ This is always `True` + +This comparison is redundant since it always succeeds. diff --git a/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__warnings__float_literals_redundant_comparison_precision_loss.snap b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__warnings__float_literals_redundant_comparison_precision_loss.snap new file mode 100644 index 00000000000..7219d60d324 --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__warnings__float_literals_redundant_comparison_precision_loss.snap @@ -0,0 +1,15 @@ +--- +source: compiler-core/src/type_/tests/warnings.rs +expression: "pub fn main() { 1.0e-500 == 1.0e-600 }" +--- +----- SOURCE CODE +pub fn main() { 1.0e-500 == 1.0e-600 } + +----- WARNING +warning: Redundant comparison + ┌─ /src/warning/wrn.gleam:1:17 + │ +1 │ pub fn main() { 1.0e-500 == 1.0e-600 } + │ ^^^^^^^^^^^^^^^^^^^^ This is always `True` + +This comparison is redundant since it always succeeds. diff --git a/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__warnings__float_literals_redundant_comparison_signed_zero.snap b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__warnings__float_literals_redundant_comparison_signed_zero.snap new file mode 100644 index 00000000000..42a79bc194a --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__warnings__float_literals_redundant_comparison_signed_zero.snap @@ -0,0 +1,15 @@ +--- +source: compiler-core/src/type_/tests/warnings.rs +expression: "pub fn main() { 0.0 == -0.0 }" +--- +----- SOURCE CODE +pub fn main() { 0.0 == -0.0 } + +----- WARNING +warning: Redundant comparison + ┌─ /src/warning/wrn.gleam:1:17 + │ +1 │ pub fn main() { 0.0 == -0.0 } + │ ^^^^^^^^^^^ This is always `True` + +This comparison is redundant since it always succeeds. diff --git a/compiler-core/src/type_/tests/warnings.rs b/compiler-core/src/type_/tests/warnings.rs index e8d87449b73..a7c32d3c7ee 100644 --- a/compiler-core/src/type_/tests/warnings.rs +++ b/compiler-core/src/type_/tests/warnings.rs @@ -4129,6 +4129,71 @@ fn int_literals_redundant_comparison_8() { assert_warning!("pub fn main() { 1 >= 2 }"); } +#[test] +fn float_literals_redundant_comparison() { + assert_warning!("pub fn main() { 1.0 == 1.0 }"); +} + +#[test] +fn float_literals_redundant_comparison_2() { + assert_warning!("pub fn main() { 1.0 == 2.0 }"); +} + +#[test] +fn float_literals_redundant_comparison_3() { + assert_warning!("pub fn main() { 1.0 != 1.0 }"); +} + +#[test] +fn float_literals_redundant_comparison_4() { + assert_warning!("pub fn main() { 1.0 != 2.0 }"); +} + +#[test] +fn float_literals_redundant_comparison_5() { + assert_warning!("pub fn main() { 1.0 >. 2.0 }"); +} + +#[test] +fn float_literals_redundant_comparison_6() { + assert_warning!("pub fn main() { 1.0 <=. 2.0 }"); +} + +#[test] +fn float_literals_redundant_comparison_7() { + assert_warning!("pub fn main() { 1.0 <. 2.0 }"); +} + +#[test] +fn float_literals_redundant_comparison_8() { + assert_warning!("pub fn main() { 1.0 >=. 2.0 }"); +} + +#[test] +fn float_literals_redundant_comparison_different_repr() { + assert_warning!("pub fn main() { 1_0.0 == 10.0 }"); +} + +#[test] +fn float_literals_redundant_comparison_different_repr_2() { + assert_warning!("pub fn main() { 10.0 == 1.0e1 }"); +} + +#[test] +fn float_literals_redundant_comparison_precision_loss() { + assert_warning!("pub fn main() { 1.0e-500 == 1.0e-600 }"); +} + +#[test] +fn float_literals_redundant_comparison_infinity() { + assert_warning!("pub fn main() { 1.0e500 == 1.0e600 }"); +} + +#[test] +fn float_literals_redundant_comparison_signed_zero() { + assert_warning!("pub fn main() { 0.0 == -0.0 }"); +} + #[test] fn bool_literals_redundant_comparison() { assert_warning!("pub fn main() { True == False }");