in starlark/src/analysis/incompatible.rs [120:183]
fn duplicate_top_level_assignment(module: &AstModule, res: &mut Vec<LintT<Incompatibility>>) {
let mut defined = HashMap::new(); //(name, (location, is_load))
let mut exported = HashSet::new(); // name's already exported by is_load
fn ident<'a>(
x: &'a AstAssignIdent,
is_load: bool,
codemap: &CodeMap,
defined: &mut HashMap<&'a str, (Span, bool)>,
res: &mut Vec<LintT<Incompatibility>>,
) {
if let Some((old, _)) = defined.get(x.0.as_str()) {
res.push(LintT::new(
codemap,
x.span,
Incompatibility::DuplicateTopLevelAssign(x.0.clone(), codemap.file_span(*old)),
));
} else {
defined.insert(&x.0, (x.span, is_load));
}
}
fn stmt<'a>(
x: &'a AstStmt,
codemap: &CodeMap,
defined: &mut HashMap<&'a str, (Span, bool)>,
exported: &mut HashSet<&'a str>,
res: &mut Vec<LintT<Incompatibility>>,
) {
match &**x {
Stmt::Assign(lhs, rhs) => match (&**lhs, &***rhs) {
(Assign::Identifier(x), Expr::Identifier(y, _))
if x.node.0 == y.node
&& defined.get(x.node.0.as_str()).map_or(false, |x| x.1)
&& !exported.contains(x.node.0.as_str()) =>
{
// Normally this would be an error, but if we load()'d it, this is how we'd reexport through Starlark.
// But only allow one export
exported.insert(x.node.0.as_str());
}
_ => lhs.visit_lvalue(|x| ident(x, false, codemap, defined, res)),
},
Stmt::AssignModify(lhs, _, _) => {
lhs.visit_lvalue(|x| ident(x, false, codemap, defined, res))
}
Stmt::Def(name, ..) => ident(name, false, codemap, defined, res),
Stmt::Load(load) => {
for (name, _) in &load.args {
ident(name, true, codemap, defined, res)
}
}
// Visit statements, but don't descend under def - only top-level statements are interesting
_ => x.visit_stmt(|x| stmt(x, codemap, defined, exported, res)),
}
}
stmt(
&module.statement,
&module.codemap,
&mut defined,
&mut exported,
res,
)
}