Skip to content

Commit

Permalink
fix: Detect infinite paths and break early to avoid stalls when gener…
Browse files Browse the repository at this point in the history
…ating server SDK crates
  • Loading branch information
LukeMathWalker committed Oct 20, 2024
1 parent 3048137 commit ddc36ed
Showing 1 changed file with 36 additions and 11 deletions.
47 changes: 36 additions & 11 deletions libs/pavexc/src/rustdoc/queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -720,6 +720,7 @@ impl Crate {
index_local_types(
&krate,
&package_id,
IndexSet::new(),
vec![],
&mut id2public_import_paths,
&mut id2private_import_paths,
Expand Down Expand Up @@ -899,6 +900,9 @@ impl Crate {
fn index_local_types<'a>(
krate: &'a rustdoc_types::Crate,
package_id: &'a PackageId,
// The ordered set of modules we navigated to reach this item.
// It used to detect infinite loops.
mut navigation_history: IndexSet<rustdoc_types::Id>,
mut current_path: Vec<&'a str>,
public_path_index: &mut HashMap<rustdoc_types::Id, BTreeSet<Vec<String>>>,
private_path_index: &mut HashMap<rustdoc_types::Id, BTreeSet<Vec<String>>>,
Expand Down Expand Up @@ -934,10 +938,12 @@ fn index_local_types<'a>(
.as_deref()
.expect("All 'module' items have a 'name' property");
current_path.push(current_path_segment);
navigation_history.insert(*current_item_id);
for item_id in &m.items {
index_local_types(
krate,
package_id,
navigation_history.clone(),
current_path.clone(),
public_path_index,
private_path_index,
Expand Down Expand Up @@ -978,22 +984,41 @@ fn index_local_types<'a>(
if !i.is_glob {
current_path.push(&i.name);
}
for re_exported_item_id in &re_exported_module.items {
index_local_types(
krate,
package_id,
current_path.clone(),
public_path_index,
private_path_index,
re_exports,
re_exported_item_id,
is_public,
)?;
// In Rust it is possible to create infinite loops with local modules!
// Minimal example:
// ```rust
// pub struct A;
// mod inner {
// pub use crate as b;
// }
// ```
// We use this check to detect if we're about to get stuck in an infinite
// loop, so that we can break early.
// It does mean that some paths that _would_ be valid won't be recognised,
// but this pattern is rarely used and for the time being we don't want to
// take the complexity hit of making visible paths lazily evaluated.
let infinite_loop = !navigation_history.insert(*imported_id);
if !infinite_loop {
for re_exported_item_id in &re_exported_module.items {
index_local_types(
krate,
package_id,
navigation_history.clone(),
current_path.clone(),
public_path_index,
private_path_index,
re_exports,
re_exported_item_id,
is_public,
)?;
}
}
} else {
navigation_history.insert(*imported_id);
index_local_types(
krate,
package_id,
navigation_history,
current_path.clone(),
public_path_index,
private_path_index,
Expand Down

0 comments on commit ddc36ed

Please sign in to comment.