use diagnostics::LocalSink;
use syntax::ast::{Expression, FormatStringPart, Literal};
use syntax::program::ReceiverCoercion;
pub(crate) fn run(typed_ast: &[Expression], sink: &LocalSink) {
for item in typed_ast {
visit_expression(item, sink);
}
}
fn visit_expression(expression: &Expression, sink: &LocalSink) {
match expression {
Expression::Call {
expression: callee,
args,
spread,
..
} => {
for arg in args {
check(arg, sink);
}
if let Some(s) = spread.as_ref() {
check(s, sink);
}
visit_expression(callee, sink);
for arg in args {
visit_expression(arg, sink);
}
if let Some(s) = spread.as_ref() {
visit_expression(s, sink);
}
return;
}
Expression::StructCall {
field_assignments, ..
} => {
for f in field_assignments {
check(&f.value, sink);
}
}
Expression::Binary { left, right, .. } => {
check(left, sink);
check(right, sink);
}
Expression::Unary { expression, .. } | Expression::Reference { expression, .. } => {
check(expression, sink);
}
Expression::Cast { expression, .. } => {
check(expression, sink);
}
Expression::If { condition, .. } | Expression::While { condition, .. } => {
check(condition, sink);
}
Expression::IndexedAccess { index, .. } => {
check(index, sink);
}
Expression::Range { start, end, .. } => {
if let Some(s) = start {
check(s, sink);
}
if let Some(e) = end {
check(e, sink);
}
}
Expression::Literal {
literal: Literal::Slice(elements),
..
} => {
for e in elements {
check(e, sink);
}
}
Expression::Literal {
literal: Literal::FormatString(parts),
..
} => {
for p in parts {
if let FormatStringPart::Expression(e) = p {
check(e, sink);
}
}
}
_ => {}
}
for child in expression.children() {
visit_expression(child, sink);
}
}
fn check(expression: &Expression, sink: &LocalSink) {
if is_temp_producing(expression) || has_auto_address_on_call(expression) {
sink.push(diagnostics::infer::complex_sub_expression(
expression.get_span(),
));
}
}
pub(crate) fn is_temp_producing(expression: &Expression) -> bool {
matches!(
expression.unwrap_parens(),
Expression::If { .. }
| Expression::IfLet { .. }
| Expression::Match { .. }
| Expression::Block { .. }
| Expression::Loop { .. }
| Expression::Select { .. }
| Expression::TryBlock { .. }
| Expression::RecoverBlock { .. }
)
}
fn has_auto_address_on_call(expression: &Expression) -> bool {
let expression = expression.unwrap_parens();
if let Expression::Call { expression, .. } = expression
&& let Expression::DotAccess {
expression: receiver,
receiver_coercion,
..
} = expression.unwrap_parens()
{
if matches!(receiver.unwrap_parens(), Expression::Call { .. })
&& *receiver_coercion == Some(ReceiverCoercion::AutoAddress)
{
return true;
}
return has_auto_address_on_call(receiver);
}
false
}