diff --git a/crates/analysis/src/alpha050/exp.rs b/crates/analysis/src/alpha050/exp.rs index 466a92b..10e386e 100644 --- a/crates/analysis/src/alpha050/exp.rs +++ b/crates/analysis/src/alpha050/exp.rs @@ -73,78 +73,88 @@ pub fn analyze_exp( let mut return_types = vec![]; let mut is_propagating_failure = false; - let ty: DataType = match exp { - Expression::ArrayIndex(exp, index) => { - let ExpAnalysisResult { - exp_ty: array_ty, - return_ty, - is_propagating_failure: prop, - } = analyze_exp( + macro_rules! analyze_expr { + ($exp:expr, $exp_type:expr) => {{ + let result = analyze_exp( file_id, file_version, - exp, - DataType::Array(Box::new(DataType::Any)), + $exp, + $exp_type, files, scoped_generic_types, contexts, ); - is_propagating_failure |= prop; - return_types.extend(return_ty); + is_propagating_failure |= result.is_propagating_failure; + return_types.extend(result.return_ty.iter().cloned()); - let ExpAnalysisResult { - return_ty: index_return_ty, - is_propagating_failure: prop, - exp_ty: index_ty, - } = analyze_exp( - file_id, - file_version, + result + }}; + } + + macro_rules! analyze_binop_codep { + ($exp1:expr, $exp1_type:expr, $exp2:expr) => {{ + let lhs_result = analyze_expr!($exp1, $exp1_type); + let rhs_result = analyze_expr!($exp2, lhs_result.exp_ty.clone()); + + if let DataType::Generic(id) = lhs_result.exp_ty.clone() { + scoped_generic_types.constrain_generic_type(id, rhs_result.exp_ty.clone()); + } + + if !matches_type( + &rhs_result.exp_ty, + &lhs_result.exp_ty, + &scoped_generic_types, + ) { + files.report_error( + &file, + &format!( + "Expected type {}, found type {}", + rhs_result.exp_ty.to_string(&scoped_generic_types), + lhs_result.exp_ty.to_string(&scoped_generic_types), + ), + $exp1.1, + ); + } + + lhs_result.exp_ty + }}; + } + + let ty: DataType = match exp { + Expression::ArrayIndex(exp, index) => { + let array_result = analyze_expr!(exp, DataType::Array(Box::new(DataType::Any))); + + let index_result = analyze_expr!( index, DataType::Union(vec![ DataType::Int, DataType::Array(Box::new(DataType::Int)), - ]), - files, - scoped_generic_types, - contexts, + ]) ); - is_propagating_failure |= prop; - return_types.extend(index_return_ty); - - match array_ty { + match array_result.exp_ty { DataType::Generic(id) => match scoped_generic_types.get_recursive(id) { - DataType::Array(inner) => match scoped_generic_types.deref_type(&index_ty) { - DataType::Array(_) => array_ty, - _ => *inner, - }, + DataType::Array(inner) => { + match scoped_generic_types.deref_type(&index_result.exp_ty) { + DataType::Array(_) => array_result.exp_ty, + _ => *inner, + } + } _ => DataType::Null, }, - DataType::Array(ref inner) => match scoped_generic_types.deref_type(&index_ty) { - DataType::Array(_) => array_ty, - _ => *inner.clone(), - }, + DataType::Array(ref inner) => { + match scoped_generic_types.deref_type(&index_result.exp_ty) { + DataType::Array(_) => array_result.exp_ty, + _ => *inner.clone(), + } + } _ => DataType::Null, } } Expression::Exit(_, exit_code) => { if let Some(exit_code) = exit_code { - let ExpAnalysisResult { - return_ty, - is_propagating_failure: prop, - .. - } = analyze_exp( - file_id, - file_version, - exit_code, - DataType::Int, - files, - scoped_generic_types, - contexts, - ); - - is_propagating_failure |= prop; - return_types.extend(return_ty); + analyze_expr!(exit_code, DataType::Int); } DataType::Null @@ -182,20 +192,6 @@ pub fn analyze_exp( args.iter().enumerate().for_each(|(idx, arg)| { if let Some((ty, _, is_ref, _)) = expected_types.get(idx) { - let ExpAnalysisResult { - is_propagating_failure: propagates_failure, - return_ty, - exp_ty, - } = analyze_exp( - file_id, - file_version, - arg, - ty.clone(), - files, - scoped_generic_types, - contexts, - ); - match (is_ref, arg.0.clone()) { (true, Expression::Var((name, span))) => { if let Some(var) = @@ -222,11 +218,10 @@ pub fn analyze_exp( _ => {} } - return_types.extend(return_ty); - is_propagating_failure |= propagates_failure; + let arg_result = analyze_expr!(arg, ty.clone()); if let DataType::Generic(id) = ty { - scoped_generic_types.constrain_generic_type(*id, exp_ty.clone()); + scoped_generic_types.constrain_generic_type(*id, arg_result.exp_ty.clone()); } } else { files.report_error( @@ -400,13 +395,7 @@ pub fn analyze_exp( } } Expression::Add(exp1, exp2) => { - let ExpAnalysisResult { - exp_ty: ty, - return_ty: return1, - is_propagating_failure: is_prop1, - } = analyze_exp( - file_id, - file_version, + analyze_binop_codep!( exp1, DataType::Union(vec![ DataType::Number, @@ -418,79 +407,11 @@ pub fn analyze_exp( DataType::Text, ]))), ]), - files, - scoped_generic_types, - contexts, - ); - let ExpAnalysisResult { - return_ty: return2, - is_propagating_failure: is_prop2, - exp_ty: right_hand_ty, - } = analyze_exp( - file_id, - file_version, - exp2, - ty.clone(), - files, - scoped_generic_types, - contexts, - ); - - is_propagating_failure |= is_prop1 || is_prop2; - - return_types.extend(return1); - return_types.extend(return2); - - if let DataType::Generic(id) = ty { - scoped_generic_types.constrain_generic_type(id, right_hand_ty.clone()); - } - - if !matches_type(&right_hand_ty, &ty, scoped_generic_types) { - files.report_error( - &file, - &format!( - "Expected type {}, found type {}", - right_hand_ty.to_string(scoped_generic_types), - ty.to_string(scoped_generic_types), - ), - exp1.1, - ); - } - - ty + exp2 + ) } - Expression::And(exp1, _, exp2) => { - let ExpAnalysisResult { - return_ty: return1, - is_propagating_failure: prop1, - .. - } = analyze_exp( - file_id, - file_version, - exp1, - DataType::Boolean, - files, - scoped_generic_types, - contexts, - ); - let ExpAnalysisResult { - return_ty: return2, - is_propagating_failure: prop2, - .. - } = analyze_exp( - file_id, - file_version, - exp2, - DataType::Boolean, - files, - scoped_generic_types, - contexts, - ); - - is_propagating_failure |= prop1 || prop2; - - return_types.extend(return1); - return_types.extend(return2); + Expression::And(exp1, _, exp2) | Expression::Or(exp1, _, exp2) => { + analyze_binop_codep!(exp1, DataType::Boolean, exp2); DataType::Boolean } @@ -498,24 +419,12 @@ pub fn analyze_exp( let types: Vec = elements .iter() .map(|exp| { - let ExpAnalysisResult { - exp_ty: ty, - return_ty, - is_propagating_failure: prop, - } = analyze_exp( - file_id, - file_version, + let result = analyze_expr!( exp, - DataType::Union(vec![DataType::Number, DataType::Int, DataType::Text]), - files, - scoped_generic_types, - contexts, + DataType::Union(vec![DataType::Number, DataType::Int, DataType::Text]) ); - is_propagating_failure |= prop; - return_types.extend(return_ty); - - ty + result.exp_ty }) .collect(); @@ -532,44 +441,14 @@ pub fn analyze_exp( DataType::Array(Box::new(array_type)) } Expression::Cast(exp, _, (ty, _)) => { - let ExpAnalysisResult { - return_ty, - is_propagating_failure: prop, - .. - } = analyze_exp( - file_id, - file_version, - exp, - DataType::Any, - files, - scoped_generic_types, - contexts, - ); - - is_propagating_failure |= prop; - return_types.extend(return_ty); + analyze_expr!(exp, DataType::Any); ty.clone() } Expression::Command(modifiers, inter_cmd, failable_handlers) => { inter_cmd.iter().for_each(|(inter_cmd, _)| { if let InterpolatedCommand::Expression(exp) = inter_cmd { - let ExpAnalysisResult { - return_ty, - is_propagating_failure: is_prop, - .. - } = analyze_exp( - file_id, - file_version, - exp, - DataType::Any, - files, - scoped_generic_types, - contexts, - ); - - is_propagating_failure |= is_prop; - return_types.extend(return_ty); + analyze_expr!(exp, DataType::Any); } }); @@ -612,86 +491,26 @@ pub fn analyze_exp( DataType::Text } - Expression::Divide(exp1, exp2) => { - let ExpAnalysisResult { - return_ty: return1, - is_propagating_failure: prop1, - exp_ty: exp_ty1, - } = analyze_exp( - file_id, - file_version, + Expression::Multiply(exp1, exp2) + | Expression::Divide(exp1, exp2) + | Expression::Modulo(exp1, exp2) + | Expression::Subtract(exp1, exp2) => { + analyze_binop_codep!( exp1, DataType::Union([DataType::Number, DataType::Int].to_vec()), - files, - scoped_generic_types, - contexts, - ); - let ExpAnalysisResult { - return_ty: return2, - is_propagating_failure: prop2, - exp_ty: exp_ty2, - } = analyze_exp( - file_id, - file_version, - exp2, - DataType::Union([DataType::Number, DataType::Int].to_vec()), - files, - scoped_generic_types, - contexts, - ); - - is_propagating_failure |= prop1 || prop2; - return_types.extend(return1); - return_types.extend(return2); - - if exp_ty1 == DataType::Number || exp_ty2 == DataType::Number { - DataType::Number - } else { - DataType::Int - } + exp2 + ) } - Expression::Eq(exp1, exp2) => { - let ExpAnalysisResult { - return_ty: return1, - is_propagating_failure: prop1, - .. - } = analyze_exp( - file_id, - file_version, - exp1, - DataType::Any, - files, - scoped_generic_types, - contexts, - ); - let ExpAnalysisResult { - return_ty: return2, - is_propagating_failure: prop2, - .. - } = analyze_exp( - file_id, - file_version, - exp2, - DataType::Any, - files, - scoped_generic_types, - contexts, - ); - - is_propagating_failure |= prop1 || prop2; - return_types.extend(return1); - return_types.extend(return2); + Expression::Eq(exp1, exp2) | Expression::Neq(exp1, exp2) => { + analyze_binop_codep!(exp1, DataType::Any, exp2); DataType::Boolean } - Expression::Ge(exp1, exp2) => { - let ExpAnalysisResult { - return_ty: return1, - is_propagating_failure: prop1, - exp_ty: left_hand_ty, - } = analyze_exp( - file_id, - file_version, + Expression::Ge(exp1, exp2) + | Expression::Gt(exp1, exp2) + | Expression::Le(exp1, exp2) + | Expression::Lt(exp1, exp2) => { + let lhs = analyze_expr!( exp1, DataType::Union( [ @@ -703,36 +522,19 @@ pub fn analyze_exp( ))), ] .to_vec(), - ), - files, - scoped_generic_types, - contexts, + ) ); let mut left_constrain_ty = - get_constrain_ty_for_compare(left_hand_ty.clone(), scoped_generic_types); + get_constrain_ty_for_compare(lhs.exp_ty.clone(), scoped_generic_types); - let ExpAnalysisResult { - return_ty: return2, - is_propagating_failure: prop2, - exp_ty: right_hand_ty, - } = analyze_exp( - file_id, - file_version, - exp2, - left_constrain_ty.clone(), - files, - scoped_generic_types, - contexts, - ); + let rhs = analyze_expr!(exp2, left_constrain_ty.clone()); - let right_constrain_ty = - get_constrain_ty_for_compare(right_hand_ty, scoped_generic_types); + let right_constrain_ty = get_constrain_ty_for_compare(rhs.exp_ty, scoped_generic_types); - if let DataType::Generic(id) = left_hand_ty { + if let DataType::Generic(id) = lhs.exp_ty.clone() { scoped_generic_types.constrain_generic_type(id, right_constrain_ty.clone()); - left_constrain_ty = - get_constrain_ty_for_compare(left_hand_ty.clone(), scoped_generic_types); + left_constrain_ty = get_constrain_ty_for_compare(lhs.exp_ty, scoped_generic_types); } if !matches_type( @@ -751,614 +553,47 @@ pub fn analyze_exp( ); } - is_propagating_failure |= prop1 || prop2; - return_types.extend(return1); - return_types.extend(return2); - - DataType::Boolean - } - Expression::Gt(exp1, exp2) => { - let ExpAnalysisResult { - return_ty: return1, - is_propagating_failure: prop1, - exp_ty: left_hand_ty, - } = analyze_exp( - file_id, - file_version, - exp1, - DataType::Union( - [ - DataType::Number, - DataType::Int, - DataType::Text, - DataType::Array(Box::new(DataType::Union( - [DataType::Number, DataType::Int, DataType::Text].to_vec(), - ))), - ] - .to_vec(), - ), - files, - scoped_generic_types, - contexts, - ); - - let mut left_constrain_ty = - get_constrain_ty_for_compare(left_hand_ty.clone(), scoped_generic_types); - - let ExpAnalysisResult { - return_ty: return2, - is_propagating_failure: prop2, - exp_ty: right_hand_ty, - } = analyze_exp( - file_id, - file_version, - exp2, - left_constrain_ty.clone(), - files, - scoped_generic_types, - contexts, - ); - - let right_constrain_ty = - get_constrain_ty_for_compare(right_hand_ty, scoped_generic_types); - - if let DataType::Generic(id) = left_hand_ty { - scoped_generic_types.constrain_generic_type(id, right_constrain_ty.clone()); - left_constrain_ty = - get_constrain_ty_for_compare(left_hand_ty.clone(), scoped_generic_types); - } - - if !matches_type( - &right_constrain_ty, - &left_constrain_ty, - scoped_generic_types, - ) { - files.report_error( - &file, - &format!( - "Expected type {}, found type {}", - right_constrain_ty.to_string(scoped_generic_types), - left_constrain_ty.to_string(scoped_generic_types), - ), - exp1.1, - ); - } - - is_propagating_failure |= prop1 || prop2; - return_types.extend(return1); - return_types.extend(return2); - DataType::Boolean } Expression::Is(exp, _, _) => { - let ExpAnalysisResult { - return_ty, - is_propagating_failure: prop, - .. - } = analyze_exp( - file_id, - file_version, - exp, - DataType::Any, - files, - scoped_generic_types, - contexts, - ); - - is_propagating_failure |= prop; - return_types.extend(return_ty); - - DataType::Boolean - } - Expression::Le(exp1, exp2) => { - let ExpAnalysisResult { - return_ty: return1, - is_propagating_failure: prop1, - exp_ty: left_hand_ty, - } = analyze_exp( - file_id, - file_version, - exp1, - DataType::Union( - [ - DataType::Number, - DataType::Int, - DataType::Text, - DataType::Array(Box::new(DataType::Union( - [DataType::Number, DataType::Int, DataType::Text].to_vec(), - ))), - ] - .to_vec(), - ), - files, - scoped_generic_types, - contexts, - ); - - let mut left_constrain_ty = - get_constrain_ty_for_compare(left_hand_ty.clone(), scoped_generic_types); - - let ExpAnalysisResult { - return_ty: return2, - is_propagating_failure: prop2, - exp_ty: right_hand_ty, - } = analyze_exp( - file_id, - file_version, - exp2, - left_constrain_ty.clone(), - files, - scoped_generic_types, - contexts, - ); - - let right_constrain_ty = - get_constrain_ty_for_compare(right_hand_ty, scoped_generic_types); - - if let DataType::Generic(id) = left_hand_ty { - scoped_generic_types.constrain_generic_type(id, right_constrain_ty.clone()); - left_constrain_ty = - get_constrain_ty_for_compare(left_hand_ty.clone(), scoped_generic_types); - } - - if !matches_type( - &right_constrain_ty, - &left_constrain_ty, - scoped_generic_types, - ) { - files.report_error( - &file, - &format!( - "Expected type {}, found type {}", - right_constrain_ty.to_string(scoped_generic_types), - left_constrain_ty.to_string(scoped_generic_types), - ), - exp1.1, - ); - } - - is_propagating_failure |= prop1 || prop2; - return_types.extend(return1); - return_types.extend(return2); - - DataType::Boolean - } - Expression::Lt(exp1, exp2) => { - let ExpAnalysisResult { - return_ty: return1, - is_propagating_failure: prop1, - exp_ty: left_hand_ty, - } = analyze_exp( - file_id, - file_version, - exp1, - DataType::Union( - [ - DataType::Number, - DataType::Int, - DataType::Text, - DataType::Array(Box::new(DataType::Union( - [DataType::Number, DataType::Int, DataType::Text].to_vec(), - ))), - ] - .to_vec(), - ), - files, - scoped_generic_types, - contexts, - ); - - let mut left_constrain_ty = - get_constrain_ty_for_compare(left_hand_ty.clone(), scoped_generic_types); - - let ExpAnalysisResult { - return_ty: return2, - is_propagating_failure: prop2, - exp_ty: right_hand_ty, - } = analyze_exp( - file_id, - file_version, - exp2, - left_constrain_ty.clone(), - files, - scoped_generic_types, - contexts, - ); - - let right_constrain_ty = - get_constrain_ty_for_compare(right_hand_ty, scoped_generic_types); - - if let DataType::Generic(id) = left_hand_ty { - scoped_generic_types.constrain_generic_type(id, right_constrain_ty.clone()); - left_constrain_ty = - get_constrain_ty_for_compare(left_hand_ty.clone(), scoped_generic_types); - } - - if !matches_type( - &right_constrain_ty, - &left_constrain_ty, - scoped_generic_types, - ) { - files.report_error( - &file, - &format!( - "Expected type {}, found type {}", - right_constrain_ty.to_string(scoped_generic_types), - left_constrain_ty.to_string(scoped_generic_types), - ), - exp1.1, - ); - } - - is_propagating_failure |= prop1 || prop2; - return_types.extend(return1); - return_types.extend(return2); + analyze_expr!(exp, DataType::Any); DataType::Boolean } - Expression::Modulo(exp1, exp2) => { - let ExpAnalysisResult { - return_ty: return1, - is_propagating_failure: prop1, - exp_ty: exp_ty1, - } = analyze_exp( - file_id, - file_version, - exp1, - DataType::Union([DataType::Number, DataType::Int].to_vec()), - files, - scoped_generic_types, - contexts, - ); - let ExpAnalysisResult { - return_ty: return2, - is_propagating_failure: prop2, - exp_ty: exp_ty2, - } = analyze_exp( - file_id, - file_version, - exp2, - DataType::Union([DataType::Number, DataType::Int].to_vec()), - files, - scoped_generic_types, - contexts, - ); - - is_propagating_failure |= prop1 || prop2; - return_types.extend(return1); - return_types.extend(return2); - - if exp_ty1 == DataType::Number || exp_ty2 == DataType::Number { - DataType::Number - } else { - DataType::Int - } - } - Expression::Multiply(exp1, exp2) => { - let ExpAnalysisResult { - return_ty: return1, - is_propagating_failure: prop1, - exp_ty: exp_ty1, - } = analyze_exp( - file_id, - file_version, - exp1, - DataType::Union([DataType::Number, DataType::Int].to_vec()), - files, - scoped_generic_types, - contexts, - ); - let ExpAnalysisResult { - return_ty: return2, - is_propagating_failure: prop2, - exp_ty: exp_ty2, - } = analyze_exp( - file_id, - file_version, - exp2, - DataType::Union([DataType::Number, DataType::Int].to_vec()), - files, - scoped_generic_types, - contexts, - ); - - is_propagating_failure |= prop1 || prop2; - return_types.extend(return1); - return_types.extend(return2); - - if exp_ty1 == DataType::Number || exp_ty2 == DataType::Number { - DataType::Number - } else { - DataType::Int - } - } Expression::Nameof(_, exp) => { - let ExpAnalysisResult { - return_ty, - is_propagating_failure: prop, - .. - } = analyze_exp( - file_id, - file_version, - exp, - DataType::Any, - files, - scoped_generic_types, - contexts, - ); - - is_propagating_failure |= prop; - return_types.extend(return_ty); + analyze_expr!(exp, DataType::Any); DataType::Text } Expression::Neg(_, exp) => { - let ExpAnalysisResult { - return_ty, - is_propagating_failure: prop, - exp_ty, - } = analyze_exp( - file_id, - file_version, + analyze_expr!( exp, - DataType::Union([DataType::Number, DataType::Int].to_vec()), - files, - scoped_generic_types, - contexts, - ); - - is_propagating_failure |= prop; - return_types.extend(return_ty); - - exp_ty - } - Expression::Neq(exp1, exp2) => { - let ExpAnalysisResult { - return_ty: return1, - is_propagating_failure: prop1, - .. - } = analyze_exp( - file_id, - file_version, - exp1, - DataType::Any, - files, - scoped_generic_types, - contexts, - ); - let ExpAnalysisResult { - return_ty: return2, - is_propagating_failure: prop2, - .. - } = analyze_exp( - file_id, - file_version, - exp2, - DataType::Any, - files, - scoped_generic_types, - contexts, - ); - - is_propagating_failure |= prop1 || prop2; - return_types.extend(return1); - return_types.extend(return2); - - DataType::Boolean + DataType::Union([DataType::Number, DataType::Int].to_vec()) + ) + .exp_ty } Expression::Not(_, exp) => { - let ExpAnalysisResult { - return_ty, - is_propagating_failure: prop, - .. - } = analyze_exp( - file_id, - file_version, - exp, - DataType::Boolean, - files, - scoped_generic_types, - contexts, - ); - - is_propagating_failure |= prop; - return_types.extend(return_ty); - - DataType::Boolean - } - Expression::Or(exp1, _, exp2) => { - let ExpAnalysisResult { - return_ty: return1, - is_propagating_failure: prop1, - .. - } = analyze_exp( - file_id, - file_version, - exp1, - DataType::Boolean, - files, - scoped_generic_types, - contexts, - ); - let ExpAnalysisResult { - return_ty: return2, - is_propagating_failure: prop2, - .. - } = analyze_exp( - file_id, - file_version, - exp2, - DataType::Boolean, - files, - scoped_generic_types, - contexts, - ); - - is_propagating_failure |= prop1 || prop2; - return_types.extend(return1); - return_types.extend(return2); + analyze_expr!(exp, DataType::Boolean); DataType::Boolean } - Expression::Parentheses(exp) => { - let ExpAnalysisResult { - return_ty, - is_propagating_failure: prop, - exp_ty, - } = analyze_exp( - file_id, - file_version, - exp, - DataType::Any, - files, - scoped_generic_types, - contexts, - ); - - is_propagating_failure |= prop; - return_types.extend(return_ty); - - exp_ty - } + Expression::Parentheses(exp) => analyze_expr!(exp, DataType::Any).exp_ty, Expression::Range(exp1, exp2) => { - let ExpAnalysisResult { - return_ty: return1, - is_propagating_failure: prop1, - .. - } = analyze_exp( - file_id, - file_version, - exp1, - DataType::Int, - files, - scoped_generic_types, - contexts, - ); - let ExpAnalysisResult { - return_ty: return2, - is_propagating_failure: prop2, - .. - } = analyze_exp( - file_id, - file_version, - exp2, - DataType::Int, - files, - scoped_generic_types, - contexts, - ); - - is_propagating_failure |= prop1 || prop2; - return_types.extend(return1); - return_types.extend(return2); + analyze_binop_codep!(exp1, DataType::Int, exp2); DataType::Array(Box::new(DataType::Int)) } - Expression::Subtract(exp1, exp2) => { - let ExpAnalysisResult { - return_ty: return1, - is_propagating_failure: prop1, - exp_ty: exp_ty1, - } = analyze_exp( - file_id, - file_version, - exp1, - DataType::Union([DataType::Number, DataType::Int].to_vec()), - files, - scoped_generic_types, - contexts, - ); - let ExpAnalysisResult { - return_ty: return2, - is_propagating_failure: prop2, - exp_ty: exp_ty2, - } = analyze_exp( - file_id, - file_version, - exp2, - DataType::Union([DataType::Number, DataType::Int].to_vec()), - files, - scoped_generic_types, - contexts, - ); - - is_propagating_failure |= prop1 || prop2; - return_types.extend(return1); - return_types.extend(return2); - - if exp_ty1 == DataType::Number || exp_ty2 == DataType::Number { - DataType::Number - } else { - DataType::Int - } - } Expression::Ternary(exp1, _, exp2, _, exp3) => { - let ExpAnalysisResult { - return_ty: return1, - is_propagating_failure: prop1, - .. - } = analyze_exp( - file_id, - file_version, - exp1, - DataType::Boolean, - files, - scoped_generic_types, - contexts, - ); - let ExpAnalysisResult { - exp_ty: if_true, - return_ty: return2, - is_propagating_failure: prop2, - } = analyze_exp( - file_id, - file_version, - exp2, - expected_type.clone(), - files, - scoped_generic_types, - contexts, - ); - let ExpAnalysisResult { - exp_ty: if_false, - return_ty: return3, - is_propagating_failure: prop3, - } = analyze_exp( - file_id, - file_version, - exp3, - expected_type.clone(), - files, - scoped_generic_types, - contexts, - ); - - is_propagating_failure |= prop1 || prop2 || prop3; - return_types.extend(return1); - return_types.extend(return2); - return_types.extend(return3); + analyze_expr!(exp1, DataType::Boolean); + let if_true = analyze_expr!(exp2, expected_type.clone()).exp_ty; + let if_false = analyze_expr!(exp3, expected_type.clone()).exp_ty; make_union_type(vec![if_true, if_false]) } Expression::Text(int_text) => { int_text.iter().for_each(|(text, _)| { if let InterpolatedText::Expression(exp) = text { - let ExpAnalysisResult { - return_ty, - is_propagating_failure: prop, - .. - } = analyze_exp( - file_id, - file_version, - exp, - DataType::Any, - files, - scoped_generic_types, - contexts, - ); - - is_propagating_failure |= prop; - return_types.extend(return_ty); + analyze_expr!(exp, DataType::Any); } }); @@ -1432,10 +667,6 @@ fn get_constrain_ty_for_compare( } DataType::Int => DataType::Union(vec![DataType::Int, DataType::Number]), DataType::Number => DataType::Union(vec![DataType::Int, DataType::Number]), - DataType::Array(ty) => DataType::Array(Box::new(get_constrain_ty_for_compare( - *ty, - scoped_generic_types, - ))), DataType::Union(types) => DataType::Union( types .iter() diff --git a/crates/grammar/src/alpha050/expressions/atom/command.rs b/crates/grammar/src/alpha050/expressions/atom/command.rs index e922750..4cd8fc0 100644 --- a/crates/grammar/src/alpha050/expressions/atom/command.rs +++ b/crates/grammar/src/alpha050/expressions/atom/command.rs @@ -43,9 +43,7 @@ pub fn command_parser<'a>( .then( choice(( any() - .filter(|c: &Token| { - *c != T!["$"] && *c != T!["{"] && *c != T!["\\"] && *c != T!["-"] - }) + .filter(|c: &Token| *c != T!["$"] && *c != T!["{"]) .map(|text| InterpolatedCommand::Text(text.to_string())) .labelled("command string"), interpolated, diff --git a/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__comparison_with_array_literal-2.snap b/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__comparison_with_array_literal-2.snap index b714e21..e4fa8ab 100644 --- a/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__comparison_with_array_literal-2.snap +++ b/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__comparison_with_array_literal-2.snap @@ -1,9 +1,9 @@ --- -source: tests/analysis/alpha050.rs +source: crates/server/tests/analysis/alpha050.rs expression: "symbol_table.symbols.iter().map(|(_, symbol_info)|\nsymbol_info.to_string(&generic_types)).collect::>()" --- [ - "fun compare_arrays(arr: [Int | Num]): Bool", - "arr: [Int | Num]", - "arr: [Int | Num]", + "fun compare_arrays(arr: [Int]): Bool", + "arr: [Int]", + "arr: [Int]", ] diff --git a/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__complex_function-2.snap b/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__complex_function-2.snap index befd3e2..0be704f 100644 --- a/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__complex_function-2.snap +++ b/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__complex_function-2.snap @@ -1,16 +1,16 @@ --- -source: tests/analysis/alpha050.rs +source: crates/server/tests/analysis/alpha050.rs expression: "symbol_table.symbols.iter().map(|(_, symbol_info)|\nsymbol_info.to_string(&generic_types)).collect::>()" --- [ - "fun foo(a?: Any): Any", - "a: Any", - "a: Any", - "a: Any", + "fun foo(a?: Int): Int", + "a: Int", + "a: Int", + "a: Int", "x: Int", "fun foo(a?: Int): Int", "code: Int", - "y: Text", - "fun foo(a?: Text): Text", + "y: Int", + "fun foo(a?: Int): Int", "code: Int", ] diff --git a/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__complex_function-3.snap b/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__complex_function-3.snap index 2e005cc..43b2660 100644 --- a/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__complex_function-3.snap +++ b/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__complex_function-3.snap @@ -1,5 +1,5 @@ --- -source: tests/analysis/alpha050.rs +source: crates/server/tests/analysis/alpha050.rs expression: backend.files.errors --- { @@ -10,5 +10,10 @@ expression: backend.files.errors FileVersion( 1, ), - ): [], + ): [ + ( + "Expected type `Int`, found type `Text`", + 116..122, + ), + ], } diff --git a/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__complex_function.snap b/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__complex_function.snap index f009b89..8311f62 100644 --- a/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__complex_function.snap +++ b/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__complex_function.snap @@ -1,5 +1,5 @@ --- -source: tests/analysis/alpha050.rs +source: crates/server/tests/analysis/alpha050.rs expression: symbol_table.symbols --- { @@ -145,7 +145,7 @@ expression: symbol_table.symbols is_const: false, }, ), - data_type: Text, + data_type: Int, is_definition: true, undefined: false, span: 108..109, @@ -159,7 +159,7 @@ expression: symbol_table.symbols ( FunctionArgument { name: "a", - data_type: Text, + data_type: Int, is_optional: true, default_value_type: Some( Int, @@ -174,7 +174,7 @@ expression: symbol_table.symbols docs: None, }, ), - data_type: Text, + data_type: Int, is_definition: false, undefined: false, span: 112..115, diff --git a/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__two_generics_compared-2.snap b/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__two_generics_compared-2.snap index 358ae04..8c1cbe4 100644 --- a/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__two_generics_compared-2.snap +++ b/crates/server/tests/analysis/snapshots/r#mod__analysis__alpha050__two_generics_compared-2.snap @@ -1,13 +1,13 @@ --- -source: tests/analysis/alpha050.rs +source: crates/server/tests/analysis/alpha050.rs expression: "symbol_table.symbols.iter().map(|(_, symbol_info)|\nsymbol_info.to_string(&generic_types)).collect::>()" --- [ - "fun max_value(a: Int | Num | Text | [Int | Num | Text], b: Int | Num | Text | [Int | Num | Text]): Int | Num | Text | [Int | Num | Text]", - "a: Int | Num | Text | [Int | Num | Text]", - "b: Int | Num | Text | [Int | Num | Text]", - "a: Int | Num | Text | [Int | Num | Text]", - "b: Int | Num | Text | [Int | Num | Text]", - "a: Int | Num | Text | [Int | Num | Text]", - "b: Int | Num | Text | [Int | Num | Text]", + "fun max_value(a: Int | Num | Text | [Num | Int | Text], b: Int | Num | Text | [Num | Int | Text]): Int | Num | Text | [Num | Int | Text]", + "a: Int | Num | Text | [Num | Int | Text]", + "b: Int | Num | Text | [Num | Int | Text]", + "a: Int | Num | Text | [Num | Int | Text]", + "b: Int | Num | Text | [Num | Int | Text]", + "a: Int | Num | Text | [Num | Int | Text]", + "b: Int | Num | Text | [Num | Int | Text]", ] diff --git a/crates/server/tests/grammar/alpha050.rs b/crates/server/tests/grammar/alpha050.rs index 9955e7a..9d21283 100644 --- a/crates/server/tests/grammar/alpha050.rs +++ b/crates/server/tests/grammar/alpha050.rs @@ -260,6 +260,15 @@ fn test_lexer_command_escapes() { compiler.tokenize(r#"$echo\ with\ spaces$"#) ); assert_debug_snapshot!("command_backslash", compiler.tokenize(r#"$test\n$"#)); + assert_debug_snapshot!( + "command_new_line", + parse(&compiler.tokenize( + r#" + $ MY_VAR=1 \ + my_command $ + "# + )) + ); } #[test] diff --git a/crates/server/tests/grammar/snapshots/r#mod__grammar__alpha050__command_new_line.snap b/crates/server/tests/grammar/snapshots/r#mod__grammar__alpha050__command_new_line.snap new file mode 100644 index 0000000..70372e9 --- /dev/null +++ b/crates/server/tests/grammar/snapshots/r#mod__grammar__alpha050__command_new_line.snap @@ -0,0 +1,60 @@ +--- +source: crates/server/tests/grammar/alpha050.rs +expression: "parse(&compiler.tokenize(r#\"\n $ MY_VAR=1 \\\n my_command $\n \"#))" +--- +( + Some( + [ + ( + Statement( + ( + Expression( + ( + Command( + [], + [ + ( + Text( + "$", + ), + 5..6, + ), + ( + Text( + "MY_VAR=1 ", + ), + 6..16, + ), + ( + Text( + "\\", + ), + 16..17, + ), + ( + Text( + "\n my_command ", + ), + 17..34, + ), + ( + Text( + "$", + ), + 34..35, + ), + ], + [], + ), + 5..35, + ), + ), + 5..35, + ), + ), + 5..35, + ), + ], + ), + [], +)