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