Change:
/cc
So the explosion is that somehow the crate index in the is broken, or at least not entirely right. Interestingly if you add to the then it no longer ICEs (but has the same ) Edit: So this looks like it's somehow a bug when trying to resolve it as a global name (both and prevent the iCE)
Maybe it's linked to ? If so, it's not in rustdoc but below.
I believe I've worked out approximately what's going on: Rustdoc freezes the compiler state (resolver, etc) as part of the setup to ready the instance. Since was made optional, and crate names are automatically part of the global scope, people stopped necessarily telling the compiler about the crates in their code. If a comment references a path in this manner, but the code itself doesn't, then the resolver frozen into the doesn't have that crate's metadata loaded into it. When we then ask the resolver to resolve a new path (e.g. ) it goes ahead and loads the crate into a different instance of the than will be used when we then try and use the later. It feels like the moment a is d it should also be locked so that it can never be modified again. It's also possible that rustdoc could be forced to enter the right context so that we never do this wrongly, but that's beyond me right now. Given that, , I feel this is probably either a bug in how is handling the resolver, or else a change in how the resolver behaves which either needs fixing, or changing in to cope.
My gut feeling is that we probably need to stop capturing/cloning the resolver as we do here: and instead try and find a resolver as part of the closure here: Of course, it looks like a has an immutable copy of the resolver's outputs, and no access to the resolver itself, urgh, and we have no easy way to inject changes into the context at this point.
I wonder why that type can be cloned in the first place...

Discussion:
/// Extracts `--extern CRATE=PATH` arguments from `matches` and /// returns a map mapping crate names to their paths or else an /// error message. <ins> /// Also handles `--extern-private` which for the purposes of rustdoc /// we can treat as `--extern` </ins> // FIXME(eddyb) This shouldn't be duplicated with `rustc::session`. fn parse_externs(matches: &getopts::Matches) -> Result<Externs, String> { let mut externs: BTreeMap<_, ExternEntry> = BTreeMap::new(); <del> for arg in &matches.opt_strs("extern") { </del> <ins> for arg in matches.opt_strs("extern").iter().chain(matches.opt_strs("extern-private").iter()) { </ins> let mut parts = arg.splitn(2, '='); let name = parts.next().ok_or("--extern value must not be empty".to_string())?; let location = parts.next().map(|s| s.to_string());

use rustc_lint; use rustc::session::{self, config}; <ins> use rustc::hir::def::Namespace::TypeNS; </ins> use rustc::hir::def_id::{DefId, DefIndex, CrateNum, LOCAL_CRATE}; use rustc::hir::HirId; use rustc::middle::cstore::CrateStore;

use rustc_driver::abort_on_err; use rustc_resolve as resolve; <ins> use syntax::ast::CRATE_NODE_ID; </ins> use syntax::source_map; use syntax::attr; use syntax::feature_gate::UnstableFeatures; use syntax::json::JsonEmitter; use syntax::symbol::sym; <ins> use syntax_pos::DUMMY_SP; </ins> use errors; use errors::emitter::{Emitter, EmitterWriter};

.. } = options; <ins> let extern_names: Vec<String> = externs.iter().map(|(s,_)| s).cloned().collect(); </ins> // Add the rustdoc cfg into the doc build. cfgs.push("rustdoc".to_string());

// We need to hold on to the complete resolver, so we cause everything to be // cloned for the analysis passes to use. Suboptimal, but necessary in the // current architecture. <del> let resolver = abort_on_err(compiler.expansion(), sess).peek().1.borrow().clone(); </del> <ins> let resolver = { let parts = abort_on_err(compiler.expansion(), sess).peek(); let resolver = parts.1.borrow(); // Before we actually clone it, let's force all the extern'd crates to // actually be loaded, just in case they're only referred to inside // intra-doc-links resolver.borrow_mut().access(|resolver| { for extern_name in &extern_names { resolver.resolve_str_path_error(DUMMY_SP, extern_name, TypeNS, CRATE_NODE_ID) .unwrap_or_else( |()| panic!("Unable to resolve external crate {}", extern_name) ); } }); // Now we're good to clone the resolver because everything should be loaded resolver.clone() }; </ins> if sess.has_errors() { sess.fatal("Compilation failed, aborting rustdoc");

stable("extern", |o| { o.optmulti("", "extern", "pass an --extern to rustc", "NAME=PATH") }), <ins> stable("extern-private", |o| { o.optmulti("", "extern-private", "pass an --extern to rustc (compatibility only)", "NAME=PATH") }), </ins> unstable("extern-html-root-url", |o| { o.optmulti("", "extern-html-root-url", "base URL to use for dependencies", "NAME=URL")

<ins> /// This will be referred to by the test docstring pub struct Something; </ins>

<ins> // aux-build:issue-66159-1.rs // extern-private:issue_66159_1 // The issue was an ICE which meant that we never actually generated the docs // so if we have generated the docs, we're okay. // Since we don't generate the docs for the auxilliary files, we can't actually // verify that the struct is linked correctly. // @has issue_66159/index.html //! [issue_66159_1::Something] </ins>