diff --git a/crates/aiken-lang/src/tests/check.rs b/crates/aiken-lang/src/tests/check.rs index e6815aa7..21ec7311 100644 --- a/crates/aiken-lang/src/tests/check.rs +++ b/crates/aiken-lang/src/tests/check.rs @@ -440,7 +440,7 @@ fn exhaustiveness_missing_constr_with_args() { } fn foo() { - let thing = Bar + let thing = Bar when thing is { Bar -> True } @@ -1191,7 +1191,7 @@ fn discarded_let_bindings() { let (warnings, ast) = check(parse(source_code)).unwrap(); assert!(matches!(warnings[0], Warning::UnusedVariable { ref name, .. } if name == "unused")); - assert!(matches!(warnings[1], Warning::UnusedVariable { ref name, .. } if name == "_")); + assert!(matches!(warnings[1], Warning::DiscardedLetAssignment { ref name, .. } if name == "_")); // Controls that unused let-bindings have been erased from the transformed AST. match ast.definitions.first() { diff --git a/crates/aiken-lang/src/tipo/error.rs b/crates/aiken-lang/src/tipo/error.rs index 493d0e7f..5cbddb4f 100644 --- a/crates/aiken-lang/src/tipo/error.rs +++ b/crates/aiken-lang/src/tipo/error.rs @@ -1535,6 +1535,28 @@ pub enum Warning { name: String, }, + #[error( + "I came across a discarded variable in a let assignment: {}.\n", + name.if_supports_color(Stderr, |s| s.purple()) + )] + #[diagnostic(help("{}", formatdoc! { + r#"If you do want to enforce some side-effects, use {keyword_expect} with {name} instead of {keyword_let}. + + You should also know that, unlike in typical imperative languages, unused let-bindings are {fully_ignored} in Aiken. + They will not produce any side-effect (such as error calls). Programs with or without unused variables are semantically equivalent. + "#, + fully_ignored = "fully_ignored".if_supports_color(Stderr, |s| s.bold()), + keyword_expect = "expect".if_supports_color(Stderr, |s| s.yellow()), + keyword_let = "let".if_supports_color(Stderr, |s| s.yellow()), + name = name.if_supports_color(Stderr, |s| s.yellow()) + }))] + #[diagnostic(code("unused::discarded_let_assignment"))] + DiscardedLetAssignment { + #[label("discarded")] + location: Span, + name: String, + }, + #[error( "I came across a validator in a {} module which means\nI'm going to ignore it.\n", "lib/".if_supports_color(Stderr, |s| s.purple()) @@ -1595,6 +1617,7 @@ impl ExtraData for Warning { | Warning::UnusedPrivateModuleConstant { .. } | Warning::UnusedType { .. } | Warning::UnusedVariable { .. } + | Warning::DiscardedLetAssignment { .. } | Warning::Utf8ByteArrayIsValidHexString { .. } | Warning::ValidatorInLibraryModule { .. } => None, Warning::UnusedImportedModule { location, .. } => { diff --git a/crates/aiken-lang/src/tipo/expr.rs b/crates/aiken-lang/src/tipo/expr.rs index 681d262d..c542fb23 100644 --- a/crates/aiken-lang/src/tipo/expr.rs +++ b/crates/aiken-lang/src/tipo/expr.rs @@ -1707,7 +1707,8 @@ impl<'a, 'b> ExprTyper<'a, 'b> { .warnings .iter() .filter_map(|w| match w { - Warning::UnusedVariable { location, .. } => Some(*location), + Warning::UnusedVariable { location, .. } + | Warning::DiscardedLetAssignment { location, .. } => Some(*location), _ => None, }) .collect::>(); diff --git a/crates/aiken-lang/src/tipo/pattern.rs b/crates/aiken-lang/src/tipo/pattern.rs index afe90ec6..e8da2cff 100644 --- a/crates/aiken-lang/src/tipo/pattern.rs +++ b/crates/aiken-lang/src/tipo/pattern.rs @@ -10,7 +10,7 @@ use itertools::Itertools; use super::{ environment::{assert_no_labeled_arguments, collapse_links, EntityKind, Environment}, - error::Error, + error::{Error, Warning}, hydrator::Hydrator, PatternConstructor, Type, ValueConstructorVariant, }; @@ -150,7 +150,11 @@ impl<'a, 'b> PatternTyper<'a, 'b> { if is_assignment { // Register declaration for the unused variable detection self.environment - .init_usage(name.to_string(), EntityKind::Variable, location); + .warnings + .push(Warning::DiscardedLetAssignment { + name: name.clone(), + location, + }); }; Ok(Pattern::Discard { name, location }) }