Remove unused let-binding from type-checking

The typed-AST produced as a result of type-checking the program will
  no longer contain unused let-bindings. They still raise warnings in
  the code so that developers are aware that they are being ignore.

  This is mainly done to prevent mistakes for people coming from an
  imperative background who may think that things like:

  ```
  let _ = foo(...)
  ```

  should have some side-effects. It does not, and it's similar to
  assigned variables that are never used / evaluated. We now properly
  strip those elements from the AST when encountered and raise proper
  warnings, even for discarded values.
This commit is contained in:
KtorZ
2023-03-16 16:51:30 +01:00
committed by Lucas
parent 45ea7acc6a
commit a5e505e6b0
3 changed files with 146 additions and 69 deletions

View File

@@ -1,6 +1,8 @@
use crate::{
ast::{ModuleKind, Tracing, TypedModule, UntypedModule},
builtins, parser,
ast::{Definition, ModuleKind, Tracing, TypedModule, UntypedModule},
builtins,
expr::TypedExpr,
parser,
tipo::error::{Error, Warning},
IdGenerator,
};
@@ -319,3 +321,55 @@ fn utf8_hex_literal_warning() {
Warning::Utf8ByteArrayIsValidHexString { .. }
))
}
#[test]
fn discarded_let_bindings() {
let source_code = r#"
fn foo() {
let result = when 42 is {
1 -> {
let unused = "foo"
Void
}
_ -> {
Void
}
}
let _ = "foo"
result
}
"#;
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 == "_"));
// Controls that unused let-bindings have been erased from the transformed AST.
match ast.definitions.first() {
Some(Definition::Fn(def)) => match &def.body {
TypedExpr::Sequence { expressions, .. } => {
assert_eq!(expressions.len(), 2);
assert!(
matches!(expressions[1], TypedExpr::Var { .. }),
"last expression isn't return variable"
);
match &expressions[0] {
TypedExpr::Assignment { value, .. } => match **value {
TypedExpr::When { ref clauses, .. } => {
assert!(
matches!(clauses[0].then, TypedExpr::Sequence { ref expressions, ..} if expressions.len() == 1)
)
}
_ => unreachable!("first expression isn't when/is"),
},
_ => unreachable!("first expression isn't assignment"),
}
}
_ => unreachable!("body isn't a Sequence"),
},
_ => unreachable!("ast isn't a Fn"),
}
}