• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Qiskit / qiskit / 26659970905

29 May 2026 08:15PM UTC coverage: 87.545% (-0.02%) from 87.564%
26659970905

push

github

web-flow
Use a single type alias for indexmap and indexset to enforce hasher (#16130)

Building off of #15920 which moved our hasher for IndexMap usage to be
foldhash this moves to having a common type alias for IndexMap and
IndexSet that has the hasher set. All the internal usage of IndexMap and
IndexSet are updated to use this new alias instead which removes the
boiler plate around dealing with initializing the hasher for the most
part, the only exception is when using `with_capacity_and_hasher()`
there is no method to create an empty object with a capacity
preallocated and the hasher is initialized from the generic type. To
enforce that we're using the correct hasher for all indexmap usage the
clippy rules are updated to disallow the indexmap::IndexMap and
indexmap::IndexSet types directly. This means that after this commit all
indexmap usage with be forced by clippy to leverage our standard pattern
using foldhash. The advantage is if we ever need to change the default
hasher again in the future we only need to update one central place
(and the places using with_capacity_and_hasher).

152 of 161 new or added lines in 29 files covered. (94.41%)

26 existing lines in 5 files now uncovered.

108486 of 123920 relevant lines covered (87.55%)

963878.9 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/crates/bindgen-cli/src/lint.rs
1
// This code is part of Qiskit.
2
//
3
// (C) Copyright IBM 2026
4
//
5
// This code is licensed under the Apache License, Version 2.0. You may
6
// obtain a copy of this license in the LICENSE.txt file in the root directory
7
// of this source tree or at https://www.apache.org/licenses/LICENSE-2.0.
8
//
9
// Any modifications or derivative works of this code must retain this
10
// copyright notice, and modified files need to carry a notice indicating
11
// that they have been altered from the originals.
12

13
use crate::SlotsLists;
14
use qiskit_bindgen::fn_attrs;
15
use qiskit_util::IndexMap;
16
use std::fmt::Write;
17

18
/// Tracked failures from the linting of a resolved vtable listing against a set of extracted
19
/// `cbindgen` bindings.
20
#[derive(Clone, Debug)]
21
pub struct Failures {
22
    /// Functions that are in the `cbindgen` bindings but not in any of the listed slots (or
23
    /// explicitly skipped).
24
    pub missing: Vec<String>,
25
    /// Functions that appear more than once in the slots listing.
26
    pub duplicates: IndexMap<String, Vec<(String, usize)>>,
27
    /// Functions that are marked as "skipped" in the bindings but aren't actually skipped.
28
    pub skipped_but_exported: IndexMap<String, (String, usize)>,
29
}
30
impl Failures {
31
    pub fn is_empty(&self) -> bool {
×
32
        self.missing.is_empty()
×
33
            && self.duplicates.is_empty()
×
34
            && self.skipped_but_exported.is_empty()
×
35
    }
×
36

37
    pub fn explain(&self) -> String {
×
38
        let mut explanation = String::from("Found discrepancies between bindings and slots.");
×
39
        if !self.missing.is_empty() {
×
40
            explanation.push_str("\n\nExported functions without a slot assignment or skip:");
×
41
            for missing in self.missing.iter() {
×
42
                write!(explanation, "\n* {missing}").unwrap();
×
43
            }
×
44
        }
×
45
        if !self.duplicates.is_empty() {
×
46
            explanation.push_str("\n\nFunctions exported more than once:");
×
47
            for (name, locations) in self.duplicates.iter() {
×
48
                write!(explanation, "\n* {name}").unwrap();
×
49
                for (api, slot) in locations.iter() {
×
50
                    write!(explanation, "\n - ({api}, {slot})").unwrap()
×
51
                }
52
            }
53
        }
×
54
        if !self.skipped_but_exported.is_empty() {
×
55
            explanation.push_str("\n\nFunctions marked as vtable skipped, but still present:");
×
56
            for (fname, (api, slot)) in self.skipped_but_exported.iter() {
×
57
                write!(explanation, "\n* {fname} at ({api}, {slot})").unwrap();
×
58
            }
×
59
        }
×
60
        explanation
×
61
    }
×
62
}
63
/// Lint for simple errors between an extract set of exported functions (`bindings`) and a static
64
/// listing of slots (`slots`).
65
///
66
/// See [`Failures`] for the types of lints performed here.
67
pub fn lint(
×
68
    bindings: &cbindgen::Bindings,
×
69
    slots: &SlotsLists,
×
70
) -> anyhow::Result<Result<(), Failures>> {
×
NEW
71
    let mut seen = IndexMap::<&str, (&str, usize)>::default();
×
NEW
72
    let mut duplicates = IndexMap::<String, Vec<(String, usize)>>::default();
×
73
    for (api_name, list) in slots.slots.iter() {
×
74
        for (slot, export) in list.iter_names() {
×
75
            if let Some((prev_api, prev_slot)) = seen.insert(export, (api_name, slot)) {
×
76
                // We put everything into `duplicates` here, then take it out again later if we
77
                // discover post cbindgen-parsing that that's allowed.
78
                duplicates
×
79
                    .entry(export.to_owned())
×
80
                    .or_insert_with(|| vec![(String::from(prev_api), prev_slot)])
×
81
                    .push((String::from(api_name), slot));
×
82
            }
×
83
        }
84
    }
NEW
85
    let mut skipped_but_exported = IndexMap::default();
×
86
    let mut missing = Vec::new();
×
87
    for func in bindings.functions.iter() {
×
88
        let fname = func.path.name();
×
89
        let attrs = fn_attrs(func)?;
×
90
        match (seen.get(fname), attrs.skipped) {
×
91
            (Some((api, slot)), true) => {
×
92
                skipped_but_exported.insert(String::from(fname), (String::from(*api), *slot));
×
93
            }
×
94
            (None, false) => missing.push(String::from(fname)),
×
95
            (Some(_), false) | (None, true) => (),
×
96
        }
97
        if attrs.allow_duplicate {
×
98
            duplicates.swap_remove(fname);
×
99
        }
×
100
    }
101
    let failures = Failures {
×
102
        missing,
×
103
        duplicates,
×
104
        skipped_but_exported,
×
105
    };
×
106
    if failures.is_empty() {
×
107
        Ok(Ok(()))
×
108
    } else {
109
        Ok(Err(failures))
×
110
    }
111
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc