use rustc_hash::FxHashSet as HashSet;
use syntax::ast::{Expression, Span};
use crate::checker::Checker;
impl Checker<'_, '_> {
pub(crate) fn check_failure_propagation_in_subexpression(
&mut self,
inner: &Expression,
span: Span,
) {
let is_failure = match inner {
Expression::Identifier { .. } => {
inner.as_option_constructor() == Some(Err(()))
}
Expression::Call {
expression: callee, ..
} => {
callee.as_result_constructor() == Some(Err(()))
|| callee.as_option_constructor() == Some(Err(()))
}
_ => false,
};
if is_failure {
self.sink
.push(diagnostics::infer::failure_propagation_in_expression(span));
}
}
pub(crate) fn check_reference_sibling_aliasing(&mut self, items: &[Expression]) {
for item in items {
self.walk_check_ref_aliasing(item);
}
}
fn walk_check_ref_aliasing(&mut self, expression: &Expression) {
match expression {
Expression::Call {
args, expression, ..
} => {
self.check_sibling_ref_aliasing_slice(args);
self.walk_check_ref_aliasing(expression);
for arg in args {
self.walk_check_ref_aliasing(arg);
}
}
Expression::Binary { left, right, .. } => {
self.check_sibling_ref_aliasing_refs(&[left, right]);
self.walk_check_ref_aliasing(left);
self.walk_check_ref_aliasing(right);
}
Expression::Tuple { elements, .. } => {
self.check_sibling_ref_aliasing_slice(elements);
for e in elements {
self.walk_check_ref_aliasing(e);
}
}
Expression::StructCall {
field_assignments,
spread,
..
} => {
let mut values: Vec<&Expression> =
field_assignments.iter().map(|fa| &*fa.value).collect();
if let Some(s) = spread.as_ref() {
values.push(s);
}
self.check_sibling_ref_aliasing_refs(&values);
for v in &values {
self.walk_check_ref_aliasing(v);
}
}
Expression::IndexedAccess {
expression, index, ..
} => {
self.check_sibling_ref_aliasing_refs(&[expression.as_ref(), index.as_ref()]);
self.walk_check_ref_aliasing(expression);
self.walk_check_ref_aliasing(index);
}
Expression::Assignment { target, value, .. } => {
self.check_sibling_ref_aliasing_refs(&[target.as_ref(), value.as_ref()]);
self.walk_check_ref_aliasing(target);
self.walk_check_ref_aliasing(value);
}
_ => {
for child in expression.children() {
self.walk_check_ref_aliasing(child);
}
}
}
}
fn check_sibling_ref_aliasing_slice(&mut self, siblings: &[Expression]) {
let refs: Vec<&Expression> = siblings.iter().collect();
self.check_sibling_ref_aliasing_refs(&refs);
}
fn check_sibling_ref_aliasing_refs(&mut self, siblings: &[&Expression]) {
let mut ref_vars: HashSet<String> = HashSet::default();
for sib in siblings {
collect_ref_targets(sib, &mut ref_vars);
}
if ref_vars.is_empty() {
return;
}
for (i, sib) in siblings.iter().enumerate() {
let mut reads: HashSet<String> = HashSet::default();
collect_read_vars(sib, &mut reads, false);
for var in reads.intersection(&ref_vars) {
let mut ref_in_same = HashSet::default();
collect_ref_targets(sib, &mut ref_in_same);
if ref_in_same.contains(var.as_str()) {
continue; }
for (j, other) in siblings.iter().enumerate() {
if i == j {
continue;
}
if let Some(span) = find_ref_span(other, var) {
self.sink
.push(diagnostics::infer::reference_aliases_sibling(span, var));
return; }
}
}
}
}
}
fn collect_ref_targets(expression: &Expression, out: &mut HashSet<String>) {
match expression.unwrap_parens() {
Expression::Reference { expression, .. } => {
if let Expression::Identifier { value, .. } = expression.unwrap_parens() {
out.insert(value.to_string());
}
collect_ref_targets(expression, out);
}
other => {
for child in other.children() {
collect_ref_targets(child, out);
}
}
}
}
fn collect_read_vars(expression: &Expression, out: &mut HashSet<String>, inside_ref: bool) {
match expression.unwrap_parens() {
Expression::Identifier { value, .. } => {
if !inside_ref {
out.insert(value.to_string());
}
}
Expression::Reference { expression, .. } => {
collect_read_vars(expression, out, true);
}
other => {
for child in other.children() {
collect_read_vars(child, out, false);
}
}
}
}
fn find_ref_span(expression: &Expression, var_name: &str) -> Option<Span> {
match expression.unwrap_parens() {
Expression::Reference {
expression, span, ..
} => {
if let Expression::Identifier { value, .. } = expression.unwrap_parens()
&& value.as_str() == var_name
{
return Some(*span);
}
find_ref_span(expression, var_name)
}
other => other
.children()
.into_iter()
.find_map(|child| find_ref_span(child, var_name)),
}
}