Fix nondeterministic macro output#330
Fix nondeterministic macro output#330dtolnay wants to merge 2 commits intoasomers:masterfrom dtolnay-contrib:unordered
Conversation
error[E0277]: the trait bound `K: std::cmp::Eq` is not satisfied
--> mockall_derive/src/collections.rs:27:22
|
25 | impl<K, V> UnorderedMap<K, V> {
| - help: consider restricting this bound: `K: std::cmp::Eq`
26 | pub fn new() -> Self {
27 | UnorderedMap(HashMap::new())
| ^^^^^^^^^^^^^^ the trait `std::cmp::Eq` is not implemented for `K`
|
= note: required by `std::collections::HashMap::<K, V>::new`
error[E0277]: the trait bound `K: std::hash::Hash` is not satisfied
--> mockall_derive/src/collections.rs:27:22
|
25 | impl<K, V> UnorderedMap<K, V> {
| - help: consider restricting this bound: `K: std::hash::Hash`
26 | pub fn new() -> Self {
27 | UnorderedMap(HashMap::new())
| ^^^^^^^^^^^^^^ the trait `std::hash::Hash` is not implemented for `K`
|
= note: required by `std::collections::HashMap::<K, V>::new`
|
Interesting find! I promise to review this by the weekend, but I may not have time until then. |
asomers
left a comment
There was a problem hiding this comment.
While the problem is real, your solution seems excessively complicated. What's wrong with just using a deterministic HashMap/HashSet? They're only random because they use a random hasher by default. But they can be made deterministic by constructing one like this: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=420f28eeccedee9cd077fff59f831635 .
Also, this bug needs a regression test. I'll work on that.
|
If you check the "allow edits by maintainers" button, I'll push the test to the PR. |
|
@asomers, I believe the complexity over your proposed alternative is justified because that's what makes this mistake machine catchable. Replacing all current places that create a randomized hashmap with |
|
That's what the test is for. Also, would your |
A test can (probabilistically) catch nondeterminism in a handful of automock invocations -- the ones covered by the test. Whereas banning HashMap rules out the entire category of HashMap-related nondeterminism across all possible automock invocations, including ones not well covered by the test suite. I do agree with you that another test would be good, because a test could catch new sources of nondeterminism outside of HashMap. In practice that's less of a concern to me because I've debugged many of these nondeterministic compilation issues and it's been HashMap iteration every time, with the one exception of https://crates.io/crates/const-random which is blatant and intentional.
It does not catch this as far as I can tell. The mechanism here is the same as what is used for deprecations -- it's catching whether your code has named the thing. If the only place the word |
|
Well, |
I think this isn't how the linter works. (But you could propose an enhancement to clippy. I don't know enough about the linter infrastructure to know how reasonable this would be to implement.)
use std::marker::PhantomData;
#[deprecated]
pub struct Thing;
#[allow(deprecated)]
pub struct Generic<T = Thing>(PhantomData<T>);
fn main() {
// does not mention "Thing", no warning
let _g: Generic = Generic(PhantomData);
}
There is a
I think the reality is there isn't an appropriate collection type in std, so for a solution that involves banning iteration in collection order, you would either need it in this crate or you would need to add a dependency that provides it (which is an option if you are open to it). FWIW it's adding <1% to the Rust code already in this crate. |
|
@dtolnay I ended up going the "HashMap with BuildHasherDefault" route. But I did make one new change: instead of a single test case for nondeterministic output, I added a Superseded by #333 . |
Summary: The mentioned PR asomers/mockall#330 was superceded by asomers/mockall#333 and is part of 0.11 release Reviewed By: dtolnay Differential Revision: D33881560 fbshipit-source-id: 1676f725739939695e4b7b2b8eb14b08ad27e6c1
Summary: The mentioned PR asomers/mockall#330 was superceded by asomers/mockall#333 and is part of 0.11 release Reviewed By: dtolnay Differential Revision: D33881560 fbshipit-source-id: 1676f725739939695e4b7b2b8eb14b08ad27e6c1
Summary: The mentioned PR asomers/mockall#330 was superceded by asomers/mockall#333 and is part of 0.11 release Reviewed By: dtolnay Differential Revision: D33881560 fbshipit-source-id: 1676f725739939695e4b7b2b8eb14b08ad27e6c1
The automock attribute currently creates nondeterministic output, which breaks in environments that cache crate compilations, and causes unnecessary rebuilds of downstream code when the code shouldn't have changed.
Repro:
Running
cargo expand | rg "for<'., '.>"this shows mockall sometimes expanding to:and sometimes to:
The source of the nondeterminism is iterating over a randomized HashSet.
This PR fixes the nondeterminism and uses https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_type to disallow iteration of hash based collections in the future.