Repository: rust
Issue: concurrent rustdoc usage can lead to bad times
Labels: T-rustdoc,C-bug,E-needs-mcve

I noticed that the documentation generated by `cargo doc` was not exactly the same depending on if I ran `cargo test` or `cargo build` first.

Running:

``` shell
$ cargo clean
$ cargo doc
$ mv target/doc tdoc
$ cargo clean
$ cargo test # or cargo build
$ cargo doc
$ diff -r tdoc target/doc
```
- in `search-index.js`: the indexes for my two targets (a [lib] and a [[bin]]) are swapped. The two lines are identical but since they're swapped git detects a file modification when my script copies the doc over to the gh-pages branch, and the file is commited for nothing.

``` diff
diff -r tdoc/search-index.js target/doc/search-index.js
2d1
< searchIndex['takuzu'] = {"items":[[0,"","takuzu","A Takuzu (a.k.a. Binairo) solving library.",null,null],[3,"Grid","","A container for takuzu grid manipulation.",null,null],[4,"GridError","","An error returned when checking if the grid is well-sized and legal.",null,null],[13,"BadSize","","The grid does not have the right size.",0,null],[13,"Illegal","","The grid is illegal, that is it infringes at least one of the rules.",0,null],[4,"GridParseError","","An error returned when parsing a string to create a grid failed.",null,null],[13,"CreationError","","A `Grid` cannot be created from this `Array`.",1,null],[13,"UnexpectedCharacter","","At least one character other than `0`, `1`, `.` or '\\n'\nwas found in the string.",1,null],[4,"GridSizeError","","An error returned when the grid is not properly sized.",null,null],[13,"Empty","","The grid is empty.",2,null],[13,"NotASquare","","The grid is not a square.\nThe field contains the line number that triggered the error.",2,null],[13,"OddRowNumber","","The grid has an odd number of rows.",2,null],[4,"SourceError","","An error returned by the `source` method when either reading or parsing failed.",null,null],[13,"IO","","Reading from the source failed.",3,null],[13,"Parsing","","Parsing failed.",3,null],[11,"eq","","",0,{"inputs":[{"name":"griderror"},{"name":"griderror"}],"output":{"name":"bool"}}],[11,"ne","","",0,{"inputs":[{"name":"griderror"},{"name":"griderror"}],"output":{"name":"bool"}}],[11,"hash","","",0,null],[11,"fmt","","",0,{"inputs":[{"name":"griderror"},{"name":"formatter"}],"output":{"name":"result"}}],[11,"clone","","",0,{"inputs":[{"name":"griderror"}],"output":{"name":"griderror"}}],[11,"fmt","","",0,{"inputs":[{"name":"griderror"},{"name":"formatter"}],"output":{"name":"result"}}],[11,"description","","",0,{"inputs":[{"name":"griderror"}],"output":{"name":"str"}}],[11,"cause","","",0,{"inputs":[{"name":"griderror"}],"output":{"name":"option"}}],[11,"from","","",0,{"inputs":[{"name":"griderror"},{"name":"gridsizeerror"}],"output":{"name":"self"}}],[11,"eq","","",1,{"inputs":[{"name":"gridparseerror"},{"name":"gridparseerror"}],"output":{"name":"bool"}}],[11,"ne","","",1,{"inputs":[{"name":"gridparseerror"},{"name":"gridparseerror"}],"output":{"name":"bool"}}],[11,"hash","","",1,null],[11,"fmt","","",1,{"inputs":[{"name":"gridparseerror"},{"name":"formatter"}],"output":{"name":"result"}}],[11,"clone","","",1,{"inputs":[{"name":"gridparseerror"}],"output":{"name":"gridparseerror"}}],[11,"fmt","","",1,{"inputs":[{"name":"gridparseerror"},{"name":"formatter"}],"output":{"name":"result"}}],[11,"description","","",1,{"inputs":[{"name":"gridparseerror"}],"output":{"name":"str"}}],[11,"cause","","",1,{"inputs":[{"name":"gridparseerror"}],"output":{"name":"option"}}],[11,"eq","","",2,{"inputs":[{"name":"gridsizeerror"},{"name":"gridsizeerror"}],"output":{"name":"bool"}}],[11,"ne","","",2,{"inputs":[{"name":"gridsizeerror"},{"name":"gridsizeerror"}],"output":{"name":"bool"}}],[11,"hash","","",2,null],[11,"fmt","","",2,{"inputs":[{"name":"gridsizeerror"},{"name":"formatter"}],"output":{"name":"result"}}],[11,"clone","","",2,{"inputs":[{"name":"gridsizeerror"}],"output":{"name":"gridsizeerror"}}],[11,"fmt","","",2,{"inputs":[{"name":"gridsizeerror"},{"name":"formatter"}],"output":{"name":"result"}}],[11,"description","","",2,{"inputs":[{"name":"gridsizeerror"}],"output":{"name":"str"}}],[11,"eq","","",4,{"inputs":[{"name":"grid"},{"name":"grid"}],"output":{"name":"bool"}}],[11,"ne","","",4,{"inputs":[{"name":"grid"},{"name":"grid"}],"output":{"name":"bool"}}],[11,"hash","","",4,null],[11,"fmt","","",4,{"inputs":[{"name":"grid"},{"name":"formatter"}],"output":{"name":"result"}}],[11,"clone","","",4,{"inputs":[{"name":"grid"}],"output":{"name":"grid"}}],[11,"fmt","","",4,{"inputs":[{"name":"grid"},{"name":"formatter"}],"output":{"name":"result"}}],[11,"from_str","","",4,{"inputs":[{"name":"grid"},{"name":"str"}],"output":{"name":"result"}}],[11,"index","","",4,null],[11,"index_mut","","",4,null],[11,"new","","Creates a `Grid` from a preexisting array.",4,{"inputs":[{"name":"grid"},{"name":"array"}],"output":{"name":"result"}}],[11,"size","","Returns the size of the grid.",4,{"inputs":[{"name":"grid"}],"output":{"name":"usize"}}],[11,"into_inner","","Consumes a `Grid` and returns the underlying array.",4,{"inputs":[{"name":"grid"}],"output":{"name":"array"}}],[11,"is_filled","","Returns `true` if the grid contains no empty cell.",4,{"inputs":[{"name":"grid"}],"output":{"name":"bool"}}],[11,"is_legal","","Verifies that the grid does not currently violate any of the rules.\nReturns `true` if the grid is legal.",4,{"inputs":[{"name":"grid"}],"output":{"name":"bool"}}],[11,"is_cell_legal","","Verifies that a certain cell does not violate any of the rules.\nReturns `true` if the value is legal.",4,{"inputs":[{"name":"grid"},{"name":"usize"},{"name":"usize"}],"output":{"name":"bool"}}],[11,"next_empty","","Returns the index of the first empty cell or None if the grid is filled.",4,{"inputs":[{"name":"grid"}],"output":{"name":"option"}}],[11,"apply_rules","","Skims through the grid once for each rule and fills in the blanks\nwhere the value is unambiguous.\nReturns `true` if the grid was modified.",4,{"inputs":[{"name":"grid"}],"output":{"name":"bool"}}],[11,"solve","","Solves the grid using both rules logic and a backtracking algorithm,\nand returns an array containing the solution(s).\nIf no solution exists, an empty array is returned.",4,{"inputs":[{"name":"grid"}],"output":{"name":"vec"}}],[11,"to_string_diff","","Suitable for terminals.",4,{"inputs":[{"name":"grid"},{"name":"grid"}],"output":{"name":"string"}}],[11,"fmt","","",3,{"inputs":[{"name":"sourceerror"},{"name":"formatter"}],"output":{"name":"result"}}],[11,"fmt","","",3,{"inputs":[{"name":"sourceerror"},{"name":"formatter"}],"output":{"name":"result"}}],[11,"description","","",3,{"inputs":[{"name":"sourceerror"}],"output":{"name":"str"}}],[11,"cause","","",3,{"inputs":[{"name":"sourceerror"}],"output":{"name":"option"}}],[11,"from","","",3,{"inputs":[{"name":"sourceerror"},{"name":"ioerror"}],"output":{"name":"self"}}],[11,"from","","",3,{"inputs":[{"name":"sourceerror"},{"name":"gridparseerror"}],"output":{"name":"self"}}],[6,"Array","","A raw takuzu grid representation.",null,null],[8,"Source","","The `Source` trait allows to use any implementor of the `Read` trait\nas an input source for the grid string format with no additional effort.",null,null],[11,"source","","Creates a `Grid` from a readable source.\nReads from the source until EOF, parses the data as a string,\nthen checks the array for size and legality and converts it to a `Grid`",5,{"inputs":[{"name":"source"}],"output":{"name":"result"}}],[11,"source","","Creates a `Grid` from a readable source.\nReads from the source until EOF, parses the data as a string,\nthen checks the array for size and legality and converts it to a `Grid`",5,{"inputs":[{"name":"source"}],"output":{"name":"result"}}]],"paths":[[4,"GridError"],[4,"GridParseError"],[4,"GridSizeError"],[4,"SourceError"],[3,"Grid"],[8,"Source"]]};
3a3
> searchIndex['takuzu'] = {"items":[[0,"","takuzu","A Takuzu (a.k.a. Binairo) solving library.",null,null],[3,"Grid","","A container for takuzu grid manipulation.",null,null],[4,"GridError","","An error returned when checking if the grid is well-sized and legal.",null,null],[13,"BadSize","","The grid does not have the right size.",0,null],[13,"Illegal","","The grid is illegal, that is it infringes at least one of the rules.",0,null],[4,"GridParseError","","An error returned when parsing a string to create a grid failed.",null,null],[13,"CreationError","","A `Grid` cannot be created from this `Array`.",1,null],[13,"UnexpectedCharacter","","At least one character other than `0`, `1`, `.` or '\\n'\nwas found in the string.",1,null],[4,"GridSizeError","","An error returned when the grid is not properly sized.",null,null],[13,"Empty","","The grid is empty.",2,null],[13,"NotASquare","","The grid is not a square.\nThe field contains the line number that triggered the error.",2,null],[13,"OddRowNumber","","The grid has an odd number of rows.",2,null],[4,"SourceError","","An error returned by the `source` method when either reading or parsing failed.",null,null],[13,"IO","","Reading from the source failed.",3,null],[13,"Parsing","","Parsing failed.",3,null],[11,"eq","","",0,{"inputs":[{"name":"griderror"},{"name":"griderror"}],"output":{"name":"bool"}}],[11,"ne","","",0,{"inputs":[{"name":"griderror"},{"name":"griderror"}],"output":{"name":"bool"}}],[11,"hash","","",0,null],[11,"fmt","","",0,{"inputs":[{"name":"griderror"},{"name":"formatter"}],"output":{"name":"result"}}],[11,"clone","","",0,{"inputs":[{"name":"griderror"}],"output":{"name":"griderror"}}],[11,"fmt","","",0,{"inputs":[{"name":"griderror"},{"name":"formatter"}],"output":{"name":"result"}}],[11,"description","","",0,{"inputs":[{"name":"griderror"}],"output":{"name":"str"}}],[11,"cause","","",0,{"inputs":[{"name":"griderror"}],"output":{"name":"option"}}],[11,"from","","",0,{"inputs":[{"name":"griderror"},{"name":"gridsizeerror"}],"output":{"name":"self"}}],[11,"eq","","",1,{"inputs":[{"name":"gridparseerror"},{"name":"gridparseerror"}],"output":{"name":"bool"}}],[11,"ne","","",1,{"inputs":[{"name":"gridparseerror"},{"name":"gridparseerror"}],"output":{"name":"bool"}}],[11,"hash","","",1,null],[11,"fmt","","",1,{"inputs":[{"name":"gridparseerror"},{"name":"formatter"}],"output":{"name":"result"}}],[11,"clone","","",1,{"inputs":[{"name":"gridparseerror"}],"output":{"name":"gridparseerror"}}],[11,"fmt","","",1,{"inputs":[{"name":"gridparseerror"},{"name":"formatter"}],"output":{"name":"result"}}],[11,"description","","",1,{"inputs":[{"name":"gridparseerror"}],"output":{"name":"str"}}],[11,"cause","","",1,{"inputs":[{"name":"gridparseerror"}],"output":{"name":"option"}}],[11,"eq","","",2,{"inputs":[{"name":"gridsizeerror"},{"name":"gridsizeerror"}],"output":{"name":"bool"}}],[11,"ne","","",2,{"inputs":[{"name":"gridsizeerror"},{"name":"gridsizeerror"}],"output":{"name":"bool"}}],[11,"hash","","",2,null],[11,"fmt","","",2,{"inputs":[{"name":"gridsizeerror"},{"name":"formatter"}],"output":{"name":"result"}}],[11,"clone","","",2,{"inputs":[{"name":"gridsizeerror"}],"output":{"name":"gridsizeerror"}}],[11,"fmt","","",2,{"inputs":[{"name":"gridsizeerror"},{"name":"formatter"}],"output":{"name":"result"}}],[11,"description","","",2,{"inputs":[{"name":"gridsizeerror"}],"output":{"name":"str"}}],[11,"eq","","",4,{"inputs":[{"name":"grid"},{"name":"grid"}],"output":{"name":"bool"}}],[11,"ne","","",4,{"inputs":[{"name":"grid"},{"name":"grid"}],"output":{"name":"bool"}}],[11,"hash","","",4,null],[11,"fmt","","",4,{"inputs":[{"name":"grid"},{"name":"formatter"}],"output":{"name":"result"}}],[11,"clone","","",4,{"inputs":[{"name":"grid"}],"output":{"name":"grid"}}],[11,"fmt","","",4,{"inputs":[{"name":"grid"},{"name":"formatter"}],"output":{"name":"result"}}],[11,"from_str","","",4,{"inputs":[{"name":"grid"},{"name":"str"}],"output":{"name":"result"}}],[11,"index","","",4,null],[11,"index_mut","","",4,null],[11,"new","","Creates a `Grid` from a preexisting array.",4,{"inputs":[{"name":"grid"},{"name":"array"}],"output":{"name":"result"}}],[11,"size","","Returns the size of the grid.",4,{"inputs":[{"name":"grid"}],"output":{"name":"usize"}}],[11,"into_inner","","Consumes a `Grid` and returns the underlying array.",4,{"inputs":[{"name":"grid"}],"output":{"name":"array"}}],[11,"is_filled","","Returns `true` if the grid contains no empty cell.",4,{"inputs":[{"name":"grid"}],"output":{"name":"bool"}}],[11,"is_legal","","Verifies that the grid does not currently violate any of the rules.\nReturns `true` if the grid is legal.",4,{"inputs":[{"name":"grid"}],"output":{"name":"bool"}}],[11,"is_cell_legal","","Verifies that a certain cell does not violate any of the rules.\nReturns `true` if the value is legal.",4,{"inputs":[{"name":"grid"},{"name":"usize"},{"name":"usize"}],"output":{"name":"bool"}}],[11,"next_empty","","Returns the index of the first empty cell or None if the grid is filled.",4,{"inputs":[{"name":"grid"}],"output":{"name":"option"}}],[11,"apply_rules","","Skims through the grid once for each rule and fills in the blanks\nwhere the value is unambiguous.\nReturns `true` if the grid was modified.",4,{"inputs":[{"name":"grid"}],"output":{"name":"bool"}}],[11,"solve","","Solves the grid using both rules logic and a backtracking algorithm,\nand returns an array containing the solution(s).\nIf no solution exists, an empty array is returned.",4,{"inputs":[{"name":"grid"}],"output":{"name":"vec"}}],[11,"to_string_diff","","Suitable for terminals.",4,{"inputs":[{"name":"grid"},{"name":"grid"}],"output":{"name":"string"}}],[11,"fmt","","",3,{"inputs":[{"name":"sourceerror"},{"name":"formatter"}],"output":{"name":"result"}}],[11,"fmt","","",3,{"inputs":[{"name":"sourceerror"},{"name":"formatter"}],"output":{"name":"result"}}],[11,"description","","",3,{"inputs":[{"name":"sourceerror"}],"output":{"name":"str"}}],[11,"cause","","",3,{"inputs":[{"name":"sourceerror"}],"output":{"name":"option"}}],[11,"from","","",3,{"inputs":[{"name":"sourceerror"},{"name":"ioerror"}],"output":{"name":"self"}}],[11,"from","","",3,{"inputs":[{"name":"sourceerror"},{"name":"gridparseerror"}],"output":{"name":"self"}}],[6,"Array","","A raw takuzu grid representation.",null,null],[8,"Source","","The `Source` trait allows to use any implementor of the `Read` trait\nas an input source for the grid string format with no additional effort.",null,null],[11,"source","","Creates a `Grid` from a readable source.\nReads from the source until EOF, parses the data as a string,\nthen checks the array for size and legality and converts it to a `Grid`",5,{"inputs":[{"name":"source"}],"output":{"name":"result"}}],[11,"source","","Creates a `Grid` from a readable source.\nReads from the source until EOF, parses the data as a string,\nthen checks the array for size and legality and converts it to a `Grid`",5,{"inputs":[{"name":"source"}],"output":{"name":"result"}}]],"paths":[[4,"GridError"],[4,"GridParseError"],[4,"GridSizeError"],[4,"SourceError"],[3,"Grid"],[8,"Source"]]};
```
- in `fn.solve_from.html` (which is a public function in the [[bin]] crate): the trait bound on the parametric type does not link over to its own documentation in the [lib] crate any more:

``` diff
diff -r tdoc/tackle/fn.solve_from.html target/doc/tackle/fn.solve_from.html
49c49
< <pre class='rust fn'>pub fn solve_from&lt;T: Source + ?<a class='trait' href='https://doc.rust-lang.org/nightly/core/marker/trait.Sized.html' title='core::marker::Sized'>Sized</a>&gt;(source: &amp;mut T)</pre><div class='docblock'><p>Reads a grid from a source, triggers the solving algorithm

---
> <pre class='rust fn'>pub fn solve_from&lt;T: <a class='trait' href='../takuzu/source/trait.Source.html' title='takuzu::source::Source'>Source</a> + ?<a class='trait' href='https://doc.rust-lang.org/nightly/core/marker/trait.Sized.html' title='core::marker::Sized'>Sized</a>&gt;(source: &amp;mut T)</pre><div class='docblock'><p>Reads a grid from a source, triggers the solving algorithm
```

The first point is not that big of a problem but the second one is clearly a bug.

As suggested by @alexcrichton, using the `--jobs 1` flag on both `cargo doc` and `cargo build/test` solves the problem by making the process deterministic, at the expense of compile/docgen time obviously.