fn duplicate_top_level_assignment()

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,
    )
}