Merge pull request #353 from aiken-lang/rvcas/assert_expect

Rename assert to expect
This commit is contained in:
Matthias Benkort
2023-02-09 15:17:14 +01:00
committed by GitHub
25 changed files with 227 additions and 77 deletions

View File

@@ -801,7 +801,7 @@ impl<A, B> Pattern<A, B> {
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
pub enum AssignmentKind {
Let,
Assert,
Expect,
}
impl AssignmentKind {
@@ -809,8 +809,15 @@ impl AssignmentKind {
matches!(self, AssignmentKind::Let)
}
pub fn is_assert(&self) -> bool {
matches!(self, AssignmentKind::Assert)
pub fn is_expect(&self) -> bool {
matches!(self, AssignmentKind::Expect)
}
pub fn location_offset(&self) -> usize {
match self {
AssignmentKind::Let => 3,
AssignmentKind::Expect => 6,
}
}
}

View File

@@ -606,7 +606,7 @@ impl<'comments> Formatter<'comments> {
let keyword = match kind {
Some(AssignmentKind::Let) => "let ",
Some(AssignmentKind::Assert) => "assert ",
Some(AssignmentKind::Expect) => "expect ",
None => "try ",
};

View File

@@ -1028,7 +1028,7 @@ pub fn expr_parser(
},
);
let assert_parser = just(Token::Assert)
let expect_parser = just(Token::Expect)
.ignore_then(pattern_parser())
.then(just(Token::Colon).ignore_then(type_parser()).or_not())
.then_ignore(just(Token::Equal))
@@ -1038,7 +1038,7 @@ pub fn expr_parser(
location: span,
value: Box::new(value),
pattern,
kind: ast::AssignmentKind::Assert,
kind: ast::AssignmentKind::Expect,
annotation,
},
);
@@ -1093,7 +1093,7 @@ pub fn expr_parser(
block_parser,
when_parser,
let_parser,
assert_parser,
expect_parser,
if_parser,
));

View File

@@ -87,7 +87,8 @@ pub fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = ParseError> {
"trace" => Token::Trace,
"error" => Token::ErrorTerm,
"as" => Token::As,
"assert" => Token::Assert,
"assert" => Token::Expect,
"expect" => Token::Expect,
"const" => Token::Const,
"fn" => Token::Fn,
"test" => Token::Test,

View File

@@ -60,11 +60,12 @@ pub enum Token {
NewLine,
// Keywords (alphabetically):
As,
Assert,
Const,
Fn,
If,
Else,
ErrorTerm,
Expect,
Is,
Let,
Opaque,
@@ -75,7 +76,6 @@ pub enum Token {
Type,
When,
Trace,
ErrorTerm,
}
impl fmt::Display for Token {
@@ -140,7 +140,7 @@ impl fmt::Display for Token {
Token::EmptyLine => "EMPTYLINE",
Token::NewLine => "NEWLINE",
Token::As => "as",
Token::Assert => "assert",
Token::Expect => "expect",
Token::When => "when",
Token::Is => "is",
Token::Const => "const",

View File

@@ -324,6 +324,77 @@ fn empty_function() {
)
}
#[test]
fn expect() {
let code = indoc! {r#"
pub fn run() {
expect Some(x) = something.field
x.other_field
}
"#};
assert_definitions(
code,
vec![ast::UntypedDefinition::Fn(Function {
arguments: vec![],
body: expr::UntypedExpr::Sequence {
location: Span::new((), 19..69),
expressions: vec![
expr::UntypedExpr::Assignment {
location: Span::new((), 19..51),
value: expr::UntypedExpr::FieldAccess {
location: Span::new((), 36..51),
label: "field".to_string(),
container: expr::UntypedExpr::Var {
location: Span::new((), 36..45),
name: "something".to_string(),
}
.into(),
}
.into(),
pattern: ast::Pattern::Constructor {
is_record: false,
location: Span::new((), 26..33),
name: "Some".to_string(),
arguments: vec![ast::CallArg {
label: None,
location: Span::new((), 31..32),
value: ast::Pattern::Var {
location: Span::new((), 31..32),
name: "x".to_string(),
},
}],
module: None,
constructor: (),
with_spread: false,
tipo: (),
},
kind: ast::AssignmentKind::Expect,
annotation: None,
},
expr::UntypedExpr::FieldAccess {
location: Span::new((), 56..69),
label: "other_field".to_string(),
container: expr::UntypedExpr::Var {
location: Span::new((), 56..57),
name: "x".to_string(),
}
.into(),
},
],
},
doc: None,
location: Span::new((), 0..12),
name: "run".to_string(),
public: true,
return_annotation: None,
return_type: (),
end_position: 70,
})],
)
}
#[test]
fn plus_binop() {
let code = indoc! {r#"

View File

@@ -333,7 +333,7 @@ Perhaps, try the following:
#[diagnostic(code("illegal::module_name"))]
#[diagnostic(help(r#"You cannot use keywords as part of a module path name. As a quick reminder, here's a list of all the keywords (and thus, of invalid module path names):
as, assert, check, const, else, fn, if, is, let, opaque, pub, test, todo, trace, type, use, when"#))]
as, expect, check, const, else, fn, if, is, let, opaque, pub, test, todo, trace, type, use, when"#))]
KeywordInModuleName { name: String, keyword: String },
#[error("I discovered a function which is ending with an assignment.\n")]
@@ -1076,6 +1076,21 @@ pub enum Warning {
sample: UntypedExpr,
},
#[error("I found an {} trying to match a type with one constructor", "expect".purple())]
#[diagnostic(
code("single_constructor_expect"),
help("If your type has one constructor, unless you are casting {} {}, you can\nprefer using a {} binding like so...\n\n{}", "FROM".bold(), "Data".purple(), "let".purple(), format_suggestion(sample))
)]
SingleConstructorExpect {
#[label("use let")]
location: Span,
#[label("only one constructor")]
pattern_location: Span,
#[label("is not Data")]
value_location: Span,
sample: UntypedExpr,
},
#[error("I found a todo left in the code.\n")]
#[diagnostic(help("You probably want to replace that one with real code... eventually."))]
#[diagnostic(code("todo"))]

View File

@@ -817,15 +817,18 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
fn infer_assignment(
&mut self,
pattern: UntypedPattern,
value: UntypedExpr,
untyped_pattern: UntypedPattern,
untyped_value: UntypedExpr,
kind: AssignmentKind,
annotation: &Option<Annotation>,
location: Span,
) -> Result<TypedExpr, Error> {
let typed_value = self.in_new_scope(|value_typer| value_typer.infer(value.clone()))?;
let typed_value =
self.in_new_scope(|value_typer| value_typer.infer(untyped_value.clone()))?;
let mut value_typ = typed_value.tipo();
let value_is_data = value_typ.is_data();
// Check that any type annotation is accurate.
let pattern = if let Some(ann) = annotation {
let ann_typ = self
@@ -836,25 +839,25 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
ann_typ.clone(),
value_typ.clone(),
typed_value.type_defining_location(),
(kind.is_let() && ann_typ.is_data()) || (kind.is_assert() && value_typ.is_data()),
(kind.is_let() && ann_typ.is_data()) || (kind.is_expect() && value_is_data),
)?;
value_typ = ann_typ.clone();
// Ensure the pattern matches the type of the value
PatternTyper::new(self.environment, &self.hydrator).unify(
pattern,
untyped_pattern.clone(),
value_typ.clone(),
Some(ann_typ),
)?
} else {
if value_typ.is_data() && !pattern.is_var() && !pattern.is_discard() {
if value_is_data && !untyped_pattern.is_var() && !untyped_pattern.is_discard() {
return Err(Error::CastDataNoAnn {
location,
value: UntypedExpr::Assignment {
location,
value: value.into(),
pattern,
value: untyped_value.into(),
pattern: untyped_pattern,
kind,
annotation: Some(Annotation::Constructor {
location: Span::empty(),
@@ -868,7 +871,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
// Ensure the pattern matches the type of the value
PatternTyper::new(self.environment, &self.hydrator).unify(
pattern,
untyped_pattern.clone(),
value_typ.clone(),
None,
)?
@@ -877,7 +880,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
// We currently only do limited exhaustiveness checking of custom types
// at the top level of patterns.
// Do not perform exhaustiveness checking if user explicitly used `assert`.
if kind != AssignmentKind::Assert {
if kind != AssignmentKind::Expect {
if let Err(unmatched) = self.environment.check_exhaustiveness(
vec![pattern.clone()],
collapse_links(value_typ.clone()),
@@ -888,6 +891,33 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
unmatched,
});
}
} else if !value_is_data
&& self
.environment
.check_exhaustiveness(
vec![pattern.clone()],
collapse_links(value_typ.clone()),
location,
)
.is_ok()
{
self.environment
.warnings
.push(Warning::SingleConstructorExpect {
location: Span {
start: location.start,
end: kind.location_offset(),
},
pattern_location: dbg!(untyped_pattern.location()),
value_location: dbg!(untyped_value.location()),
sample: UntypedExpr::Assignment {
location: Span::empty(),
value: Box::new(untyped_value),
pattern: untyped_pattern,
kind: AssignmentKind::Let,
annotation: None,
},
})
}
Ok(TypedExpr::Assignment {

View File

@@ -20,6 +20,8 @@ use super::{
TypeInfo, ValueConstructor, ValueConstructorVariant,
};
const PUB_OFFSET: usize = 3;
impl UntypedModule {
pub fn infer(
mut self,
@@ -162,7 +164,7 @@ fn infer_definition(
environment.warnings.push(Warning::PubInValidatorModule {
location: Span {
start: location.start,
end: location.start + 3,
end: location.start + PUB_OFFSET,
},
})
}
@@ -270,7 +272,7 @@ fn infer_definition(
environment.warnings.push(Warning::PubInValidatorModule {
location: Span {
start: location.start,
end: location.start + 3,
end: location.start + PUB_OFFSET,
},
})
}
@@ -306,7 +308,7 @@ fn infer_definition(
environment.warnings.push(Warning::PubInValidatorModule {
location: Span {
start: location.start,
end: location.start + 3,
end: location.start + PUB_OFFSET,
},
})
}
@@ -449,7 +451,7 @@ fn infer_definition(
environment.warnings.push(Warning::PubInValidatorModule {
location: Span {
start: location.start,
end: location.start + 3,
end: location.start + PUB_OFFSET,
},
})
}
@@ -511,8 +513,11 @@ fn validate_module_name(name: &str) -> Result<(), Error> {
fn str_to_keyword(word: &str) -> Option<Token> {
// Alphabetical keywords:
match word {
"assert" => Some(Token::Expect),
"expect" => Some(Token::Expect),
"else" => Some(Token::Else),
"is" => Some(Token::Is),
"as" => Some(Token::As),
"assert" => Some(Token::Assert),
"when" => Some(Token::When),
"const" => Some(Token::Const),
"fn" => Some(Token::Fn),

View File

@@ -1578,7 +1578,7 @@ impl<'a> CodeGenerator<'a> {
pattern_vec.append(value_vec);
if matches!(assignment_properties.kind, AssignmentKind::Assert)
if matches!(assignment_properties.kind, AssignmentKind::Expect)
&& assignment_properties.value_type.is_data()
&& !tipo.is_data()
{
@@ -1604,7 +1604,7 @@ impl<'a> CodeGenerator<'a> {
pattern_vec.append(value_vec);
}
list @ Pattern::List { .. } => {
if matches!(assignment_properties.kind, AssignmentKind::Assert)
if matches!(assignment_properties.kind, AssignmentKind::Expect)
&& assignment_properties.value_type.is_data()
&& !tipo.is_data()
{
@@ -1629,7 +1629,7 @@ impl<'a> CodeGenerator<'a> {
}
// TODO: Check constr for assert on all cases
constr @ Pattern::Constructor { .. } => {
if matches!(assignment_properties.kind, AssignmentKind::Assert)
if matches!(assignment_properties.kind, AssignmentKind::Expect)
&& assignment_properties.value_type.is_data()
&& !tipo.is_data()
{
@@ -1653,7 +1653,7 @@ impl<'a> CodeGenerator<'a> {
}
}
tuple @ Pattern::Tuple { .. } => {
if matches!(assignment_properties.kind, AssignmentKind::Assert)
if matches!(assignment_properties.kind, AssignmentKind::Expect)
&& assignment_properties.value_type.is_data()
&& !tipo.is_data()
{
@@ -2503,7 +2503,7 @@ impl<'a> CodeGenerator<'a> {
let id = self.id_gen.next();
let list_name = format!("__list_{id}");
if matches!(assignment_properties.kind, AssignmentKind::Assert)
if matches!(assignment_properties.kind, AssignmentKind::Expect)
&& assignment_properties.value_type.is_data()
&& !tipo.is_data()
{
@@ -2556,7 +2556,7 @@ impl<'a> CodeGenerator<'a> {
let id = self.id_gen.next();
let constr_name = format!("{constr_name}_{id}");
if matches!(assignment_properties.kind, AssignmentKind::Assert)
if matches!(assignment_properties.kind, AssignmentKind::Expect)
&& assignment_properties.value_type.is_data()
&& !tipo.is_data()
{
@@ -2605,7 +2605,7 @@ impl<'a> CodeGenerator<'a> {
let id = self.id_gen.next();
let tuple_name = format!("__tuple_name_{id}");
if matches!(assignment_properties.kind, AssignmentKind::Assert)
if matches!(assignment_properties.kind, AssignmentKind::Expect)
&& assignment_properties.value_type.is_data()
&& !tipo.is_data()
{