Fork of daniellemaywood.uk/gleam — Wasm codegen work
2

Configure Feed

Select the types of activity you want to include in your feed.

Warn when asserting on variants which have been inferred

+105 -14
+10
CHANGELOG.md
··· 65 65 66 66 ([Surya Rose](https://github.com/GearsDatapacks)) 67 67 68 + - The compiler now emits a warning when using `let assert` to assert a value 69 + whose variant has already been inferred. For example: 70 + 71 + ```gleam 72 + // warning: This will always crash 73 + let assert Ok(_) = Error("Some error") 74 + ``` 75 + 76 + ([Surya Rose](https://github.com/GearsDatapacks)) 77 + 68 78 ### Build tool 69 79 70 80 - Include a type annotation for the `main` function generated by `gleam new`.
+5
compiler-core/src/type_/error.rs
··· 864 864 location: SrcSpan, 865 865 }, 866 866 867 + AssertAssignmentOnInferredVariant { 868 + location: SrcSpan, 869 + }, 870 + 867 871 /// When a `todo` or `panic` is used as a function instead of providing the 868 872 /// error message with the `as` syntax. 869 873 /// ··· 1125 1129 | Warning::OpaqueExternalType { location, .. } 1126 1130 | Warning::InternalTypeLeak { location, .. } 1127 1131 | Warning::RedundantAssertAssignment { location, .. } 1132 + | Warning::AssertAssignmentOnInferredVariant { location, .. } 1128 1133 | Warning::TodoOrPanicUsedAsFunction { location, .. } 1129 1134 | Warning::UnreachableCodeAfterPanic { location, .. } 1130 1135 | Warning::RedundantPipeFunctionCapture { location, .. }
+23 -8
compiler-core/src/type_/expression.rs
··· 12 12 UntypedUseAssignment, Use, UseAssignment, 13 13 }, 14 14 build::Target, 15 - exhaustiveness::{self, Reachability}, 15 + exhaustiveness::{self, Match, Reachability}, 16 16 reference::ReferenceKind, 17 17 }; 18 18 use hexpm::version::Version; ··· 1524 1524 } 1525 1525 1526 1526 // Do not perform exhaustiveness checking if user explicitly used `let assert ... = ...`. 1527 - let exhaustiveness_check = self.check_let_exhaustiveness(location, value.type_(), &pattern); 1527 + let (output, exhaustiveness_check) = 1528 + self.check_let_exhaustiveness(location, value.type_(), &pattern); 1528 1529 match (&kind, exhaustiveness_check) { 1529 1530 // The pattern is exhaustive in a let assignment, there's no problem here. 1530 1531 (AssignmentKind::Let | AssignmentKind::Generated, Ok(_)) => {} ··· 1543 1544 }) 1544 1545 } 1545 1546 1546 - // Otherwise we just don't care, asserting will ignore the any missing 1547 - // pattern error. 1548 - (AssignmentKind::Assert { .. }, Err(_)) => {} 1547 + // Otherwise, if the pattern is never reachable (through variant inference), 1548 + // we can warn the user about this. 1549 + (AssignmentKind::Assert { .. }, Err(_)) => { 1550 + // There is only one pattern to match, so it is index 0 1551 + match output.is_reachable(0) { 1552 + Reachability::Unreachable(UnreachableCaseClauseReason::ImpossibleVariant) => { 1553 + self.problems 1554 + .warning(Warning::AssertAssignmentOnInferredVariant { 1555 + location: pattern.location(), 1556 + }) 1557 + } 1558 + // A duplicate pattern warning should not happen, since there is only on pattern. 1559 + Reachability::Reachable 1560 + | Reachability::Unreachable(UnreachableCaseClauseReason::DuplicatePattern) => {} 1561 + } 1562 + } 1549 1563 } 1550 1564 1551 1565 Assignment { ··· 4009 4023 location: SrcSpan, 4010 4024 subject: Arc<Type>, 4011 4025 pattern: &TypedPattern, 4012 - ) -> Result<(), Error> { 4026 + ) -> (Match, Result<(), Error>) { 4013 4027 let mut case = exhaustiveness::CaseToCompile::new(&[subject]); 4014 4028 case.add_pattern(pattern); 4015 4029 let output = case.compile(self.environment); 4016 4030 4017 4031 // Error for missing clauses that would cause a crash 4018 - if output.diagnostics.missing { 4032 + let result = if output.diagnostics.missing { 4019 4033 Err(Error::InexhaustiveLetAssignment { 4020 4034 location, 4021 4035 missing: output.missing_patterns(self.environment), 4022 4036 }) 4023 4037 } else { 4024 4038 Ok(()) 4025 - } 4039 + }; 4040 + (output, result) 4026 4041 } 4027 4042 4028 4043 fn check_case_exhaustiveness(
+26
compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__warnings__assert_on_inferred_variant.snap
··· 1 + --- 2 + source: compiler-core/src/type_/tests/warnings.rs 3 + expression: "\ntype Wibble {\n Wibble(w: Int)\n Wobble(w: String)\n}\n\npub fn main() {\n let assert Wobble(w) = Wibble(10)\n w\n}\n" 4 + --- 5 + ----- SOURCE CODE 6 + 7 + type Wibble { 8 + Wibble(w: Int) 9 + Wobble(w: String) 10 + } 11 + 12 + pub fn main() { 13 + let assert Wobble(w) = Wibble(10) 14 + w 15 + } 16 + 17 + 18 + ----- WARNING 19 + warning: Assertion on inferred variant 20 + ┌─ /src/warning/wrn.gleam:8:14 21 + 22 + 8 │ let assert Wobble(w) = Wibble(10) 23 + │ ^^^^^^^^^ 24 + 25 + This assertion is will always crash since it matches on a variant which is 26 + never present.
+5 -5
compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__warnings__let_assert_with_message_requires_v1_7.snap
··· 1 1 --- 2 2 source: compiler-core/src/type_/tests/warnings.rs 3 - expression: "\npub fn main() {\n let assert Ok(10) = Error(20) as \"This will crash...\"\n}\n" 3 + expression: "\npub fn main() {\n let assert Ok(10) = Ok(20) as \"This will crash...\"\n}\n" 4 4 --- 5 5 ----- SOURCE CODE 6 6 7 7 pub fn main() { 8 - let assert Ok(10) = Error(20) as "This will crash..." 8 + let assert Ok(10) = Ok(20) as "This will crash..." 9 9 } 10 10 11 11 12 12 ----- WARNING 13 13 warning: Incompatible gleam version range 14 - ┌─ /src/warning/wrn.gleam:3:36 14 + ┌─ /src/warning/wrn.gleam:3:33 15 15 16 - 3 │ let assert Ok(10) = Error(20) as "This will crash..." 17 - │ ^^^^^^^^^^^^^^^^^^^^ This requires a Gleam version >= 1.7.0 16 + 3 │ let assert Ok(10) = Ok(20) as "This will crash..." 17 + │ ^^^^^^^^^^^^^^^^^^^^ This requires a Gleam version >= 1.7.0 18 18 19 19 Specifying a custom panic message when using let assert was introduced in 20 20 version v1.7.0. But the Gleam version range specified in your `gleam.toml`
+18 -1
compiler-core/src/type_/tests/warnings.rs
··· 2525 2525 Range::higher_than(Version::new(1, 0, 0)), 2526 2526 r#" 2527 2527 pub fn main() { 2528 - let assert Ok(10) = Error(20) as "This will crash..." 2528 + let assert Ok(10) = Ok(20) as "This will crash..." 2529 2529 } 2530 2530 "#, 2531 2531 ); ··· 3014 3014 " 3015 3015 ); 3016 3016 } 3017 + 3018 + #[test] 3019 + fn assert_on_inferred_variant() { 3020 + assert_warning!( 3021 + " 3022 + type Wibble { 3023 + Wibble(w: Int) 3024 + Wobble(w: String) 3025 + } 3026 + 3027 + pub fn main() { 3028 + let assert Wobble(w) = Wibble(10) 3029 + w 3030 + } 3031 + " 3032 + ); 3033 + }
+18
compiler-core/src/warning.rs
··· 940 940 }), 941 941 }, 942 942 943 + type_::Warning::AssertAssignmentOnInferredVariant { location } => Diagnostic { 944 + title: "Assertion on inferred variant".into(), 945 + text: wrap( 946 + "This assertion is will always crash since it matches on a variant which is never present.", 947 + ), 948 + hint: None, 949 + level: diagnostic::Level::Warning, 950 + location: Some(Location { 951 + label: diagnostic::Label { 952 + text: None, 953 + span: *location, 954 + }, 955 + path: path.clone(), 956 + src: src.clone(), 957 + extra_labels: vec![], 958 + }), 959 + }, 960 + 943 961 type_::Warning::TodoOrPanicUsedAsFunction { 944 962 kind, 945 963 location,