fn duplicate_dictionary_key()

in starlark/src/analysis/dubious.rs [48:104]


fn duplicate_dictionary_key(module: &AstModule, res: &mut Vec<LintT<Dubious>>) {
    #[derive(PartialEq, Eq, Hash)]
    enum Key<'a> {
        Int(i32),
        Float(u64),
        String(&'a str),
        Identifier(&'a str),
    }

    fn to_key<'a>(x: &'a AstExpr) -> Option<(Key<'a>, Span)> {
        match &**x {
            Expr::Literal(x) => match &*x {
                AstLiteral::Int(x) => Some((Key::Int(x.node), x.span)),
                AstLiteral::Float(x) => {
                    let n = Num::from(x.node);
                    if let Some(i) = n.as_int() {
                        // make an integer float always collide with other ints
                        Some((Key::Int(i), x.span))
                    } else {
                        // use bits representation of float to be able to always compare them for equality
                        // First normalise -0.0
                        let v = if x.node == 0.0 { 0.0 } else { x.node };
                        Some((Key::Float(v.to_bits()), x.span))
                    }
                }
                AstLiteral::String(x) => Some((Key::String(&x.node), x.span)),
            },
            Expr::Identifier(x, ()) => Some((Key::Identifier(&x.node), x.span)),
            _ => None,
        }
    }

    fn expr<'a>(x: &'a AstExpr, codemap: &CodeMap, res: &mut Vec<LintT<Dubious>>) {
        match &**x {
            Expr::Dict(args) => {
                let mut seen = HashMap::new();
                for (key, _) in args {
                    if let Some((key_id, pos)) = to_key(key) {
                        if let Some(old) = seen.insert(key_id, pos) {
                            res.push(LintT::new(
                                codemap,
                                old,
                                Dubious::DuplicateKey(key.to_string(), codemap.file_span(pos)),
                            ))
                        }
                    }
                }
            }
            _ => {}
        }
        x.visit_expr(|x| expr(x, codemap, res));
    }

    module
        .statement
        .visit_expr(|x| expr(x, &module.codemap, res))
}