about summary refs log tree commit diff
diff options
context:
space:
mode:
authorGuillaume Gomez <guillaume1.gomez@gmail.com>2025-01-12 23:08:57 +0100
committerGitHub <noreply@github.com>2025-01-12 23:08:57 +0100
commit9d3ae11c54646a9c7377eee5e9db9ea90cabbab1 (patch)
tree0f62321fed5585828c0fee05b941f54a18703d7b
parent627513a764a696b59bb2679cc1731a197a088a1d (diff)
parent2a2b090d12afd9fbc4b04138a228122a8896cead (diff)
downloadrust-9d3ae11c54646a9c7377eee5e9db9ea90cabbab1.tar.gz
rust-9d3ae11c54646a9c7377eee5e9db9ea90cabbab1.zip
Rollup merge of #135348 - aDotInTheVoid:pathspathspaths, r=GuillaumeGomez
rustdoc-json: Include items in stripped modules in `Crate::paths`.

Closes #135309

When we're running rustdoc-json, we should err on the side of adding more items to `Cache::paths`, as that directly becomes `Crate::paths` in the output.

r? ``@GuillaumeGomez.`` Best reviewed commit-by-commit.
-rw-r--r--src/librustdoc/formats/cache.rs23
-rw-r--r--src/tools/jsondocck/src/main.rs27
-rw-r--r--src/tools/jsondoclint/src/validator.rs6
-rw-r--r--src/tools/jsondoclint/src/validator/tests.rs97
-rw-r--r--tests/rustdoc-json/reexport/simple_private.rs6
-rw-r--r--tests/rustdoc-json/reexport/simple_public.rs5
6 files changed, 151 insertions, 13 deletions
diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs
index 7a95d33723e..cbb3ce6abe6 100644
--- a/src/librustdoc/formats/cache.rs
+++ b/src/librustdoc/formats/cache.rs
@@ -140,6 +140,7 @@ struct CacheBuilder<'a, 'tcx> {
     /// This field is used to prevent duplicated impl blocks.
     impl_ids: DefIdMap<DefIdSet>,
     tcx: TyCtxt<'tcx>,
+    is_json_output: bool,
 }
 
 impl Cache {
@@ -184,8 +185,13 @@ impl Cache {
         }
 
         let (krate, mut impl_ids) = {
-            let mut cache_builder =
-                CacheBuilder { tcx, cache: &mut cx.cache, impl_ids: Default::default() };
+            let is_json_output = cx.is_json_output();
+            let mut cache_builder = CacheBuilder {
+                tcx,
+                cache: &mut cx.cache,
+                impl_ids: Default::default(),
+                is_json_output,
+            };
             krate = cache_builder.fold_crate(krate);
             (krate, cache_builder.impl_ids)
         };
@@ -307,12 +313,13 @@ impl DocFolder for CacheBuilder<'_, '_> {
             | clean::ProcMacroItem(..)
             | clean::VariantItem(..) => {
                 use rustc_data_structures::fx::IndexEntry as Entry;
-                if !self.cache.stripped_mod
-                    && !matches!(
-                        item.stability.map(|stab| stab.level),
-                        Some(StabilityLevel::Stable { allowed_through_unstable_modules: true, .. })
-                    )
-                {
+
+                let skip_because_unstable = matches!(
+                    item.stability.map(|stab| stab.level),
+                    Some(StabilityLevel::Stable { allowed_through_unstable_modules: true, .. })
+                );
+
+                if (!self.cache.stripped_mod && !skip_because_unstable) || self.is_json_output {
                     // Re-exported items mean that the same id can show up twice
                     // in the rustdoc ast that we're looking at. We know,
                     // however, that a re-exported item doesn't show up in the
diff --git a/src/tools/jsondocck/src/main.rs b/src/tools/jsondocck/src/main.rs
index b6a1d7dfa7a..7bfa7e3355d 100644
--- a/src/tools/jsondocck/src/main.rs
+++ b/src/tools/jsondocck/src/main.rs
@@ -65,6 +65,11 @@ enum CommandKind {
     /// Checks the path doesn't exist.
     HasNotPath,
 
+    /// `//@ !has <path> <value>`
+    ///
+    /// Checks the path exists, but doesn't have the given value.
+    HasNotValue { value: String },
+
     /// `//@ is <path> <value>`
     ///
     /// Check the path is the given value.
@@ -128,10 +133,11 @@ impl CommandKind {
                 [_path, value] => Self::HasValue { value: value.clone() },
                 _ => panic!("`//@ has` must have 2 or 3 arguments, but got {args:?}"),
             },
-            ("has", true) => {
-                assert_eq!(args.len(), 1, "args={args:?}");
-                Self::HasNotPath
-            }
+            ("has", true) => match args {
+                [_path] => Self::HasNotPath,
+                [_path, value] => Self::HasNotValue { value: value.clone() },
+                _ => panic!("`//@ !has` must have 2 or 3 arguments, but got {args:?}"),
+            },
 
             (_, false) if KNOWN_DIRECTIVE_NAMES.contains(&command_name) => {
                 return None;
@@ -223,6 +229,19 @@ fn check_command(command: &Command, cache: &mut Cache) -> Result<(), String> {
                 return Err(format!("matched to {matches:?}, which didn't contain {want_value:?}"));
             }
         }
+        CommandKind::HasNotValue { value } => {
+            let wantnt_value = string_to_value(value, cache);
+            if matches.contains(&wantnt_value.as_ref()) {
+                return Err(format!(
+                    "matched to {matches:?}, which contains unwanted {wantnt_value:?}"
+                ));
+            } else if matches.is_empty() {
+                return Err(format!(
+                    "got no matches, but expected some matched (not containing {wantnt_value:?}"
+                ));
+            }
+        }
+
         CommandKind::Is { value } => {
             let want_value = string_to_value(value, cache);
             let matched = get_one(&matches)?;
diff --git a/src/tools/jsondoclint/src/validator.rs b/src/tools/jsondoclint/src/validator.rs
index f7c752033c5..791b231c27a 100644
--- a/src/tools/jsondoclint/src/validator.rs
+++ b/src/tools/jsondoclint/src/validator.rs
@@ -303,6 +303,12 @@ impl<'a> Validator<'a> {
             PathKind::Trait => self.add_trait_or_alias_id(&x.id),
             PathKind::Type => self.add_type_id(&x.id),
         }
+
+        // FIXME: More robust support for checking things in $.index also exist in $.paths
+        if !self.krate.paths.contains_key(&x.id) {
+            self.fail(&x.id, ErrorKind::Custom(format!("No entry in '$.paths' for {x:?}")));
+        }
+
         if let Some(args) = &x.args {
             self.check_generic_args(&**args);
         }
diff --git a/src/tools/jsondoclint/src/validator/tests.rs b/src/tools/jsondoclint/src/validator/tests.rs
index e842e1318db..7fcb8ffd1f9 100644
--- a/src/tools/jsondoclint/src/validator/tests.rs
+++ b/src/tools/jsondoclint/src/validator/tests.rs
@@ -1,5 +1,5 @@
 use rustc_hash::FxHashMap;
-use rustdoc_json_types::{FORMAT_VERSION, Item, ItemKind, Visibility};
+use rustdoc_json_types::{Abi, FORMAT_VERSION, FunctionHeader, Item, ItemKind, Visibility};
 
 use super::*;
 use crate::json_find::SelectorPart;
@@ -103,6 +103,101 @@ fn errors_on_local_in_paths_and_not_index() {
 }
 
 #[test]
+fn errors_on_missing_path() {
+    // crate-name=foo
+    // ```
+    // pub struct Bar;
+    // pub fn mk_bar() -> Bar { ... }
+    // ```
+
+    let generics = Generics { params: vec![], where_predicates: vec![] };
+
+    let krate = Crate {
+        root: Id(0),
+        crate_version: None,
+        includes_private: false,
+        index: FxHashMap::from_iter([
+            (Id(0), Item {
+                id: Id(0),
+                crate_id: 0,
+                name: Some("foo".to_owned()),
+                span: None,
+                visibility: Visibility::Public,
+                docs: None,
+                links: FxHashMap::default(),
+                attrs: Vec::new(),
+                deprecation: None,
+                inner: ItemEnum::Module(Module {
+                    is_crate: true,
+                    items: vec![Id(1), Id(2)],
+                    is_stripped: false,
+                }),
+            }),
+            (Id(1), Item {
+                id: Id(0),
+                crate_id: 0,
+                name: Some("Bar".to_owned()),
+                span: None,
+                visibility: Visibility::Public,
+                docs: None,
+                links: FxHashMap::default(),
+                attrs: Vec::new(),
+                deprecation: None,
+                inner: ItemEnum::Struct(Struct {
+                    kind: StructKind::Unit,
+                    generics: generics.clone(),
+                    impls: vec![],
+                }),
+            }),
+            (Id(2), Item {
+                id: Id(0),
+                crate_id: 0,
+                name: Some("mk_bar".to_owned()),
+                span: None,
+                visibility: Visibility::Public,
+                docs: None,
+                links: FxHashMap::default(),
+                attrs: Vec::new(),
+                deprecation: None,
+                inner: ItemEnum::Function(Function {
+                    sig: FunctionSignature {
+                        inputs: vec![],
+                        output: Some(Type::ResolvedPath(Path {
+                            name: "Bar".to_owned(),
+                            id: Id(1),
+                            args: None,
+                        })),
+                        is_c_variadic: false,
+                    },
+                    generics,
+                    header: FunctionHeader {
+                        is_const: false,
+                        is_unsafe: false,
+                        is_async: false,
+                        abi: Abi::Rust,
+                    },
+                    has_body: true,
+                }),
+            }),
+        ]),
+        paths: FxHashMap::from_iter([(Id(0), ItemSummary {
+            crate_id: 0,
+            path: vec!["foo".to_owned()],
+            kind: ItemKind::Module,
+        })]),
+        external_crates: FxHashMap::default(),
+        format_version: rustdoc_json_types::FORMAT_VERSION,
+    };
+
+    check(&krate, &[Error {
+        kind: ErrorKind::Custom(
+            r#"No entry in '$.paths' for Path { name: "Bar", id: Id(1), args: None }"#.to_owned(),
+        ),
+        id: Id(1),
+    }]);
+}
+
+#[test]
 #[should_panic = "LOCAL_CRATE_ID is wrong"]
 fn checks_local_crate_id_is_correct() {
     let krate = Crate {
diff --git a/tests/rustdoc-json/reexport/simple_private.rs b/tests/rustdoc-json/reexport/simple_private.rs
index 8a936f5da1b..405d57d342e 100644
--- a/tests/rustdoc-json/reexport/simple_private.rs
+++ b/tests/rustdoc-json/reexport/simple_private.rs
@@ -12,3 +12,9 @@ mod inner {
 pub use inner::Public;
 
 //@ ismany "$.index[*][?(@.name=='simple_private')].inner.module.items[*]" $use_id
+
+// Test for https://github.com/rust-lang/rust/issues/135309
+//@ has  "$.paths[*][?(@.kind=='module')].path" '["simple_private"]'
+//@ !has "$.paths[*].path"                      '["simple_private", "inner"]'
+//@ has  "$.paths[*][?(@.kind=='struct')].path" '["simple_private", "inner", "Public"]'
+//@ !has "$.paths[*].path"                      '["simple_private", "Public"]'
diff --git a/tests/rustdoc-json/reexport/simple_public.rs b/tests/rustdoc-json/reexport/simple_public.rs
index e5a8dc7d2ad..f1335828314 100644
--- a/tests/rustdoc-json/reexport/simple_public.rs
+++ b/tests/rustdoc-json/reexport/simple_public.rs
@@ -14,3 +14,8 @@ pub mod inner {
 pub use inner::Public;
 
 //@ ismany "$.index[*][?(@.name=='simple_public')].inner.module.items[*]" $import_id $inner_id
+
+//@ has  "$.paths[*][?(@.kind=='module')].path" '["simple_public"]'
+//@ has  "$.paths[*][?(@.kind=='module')].path" '["simple_public", "inner"]'
+//@ has  "$.paths[*][?(@.kind=='struct')].path" '["simple_public", "inner", "Public"]'
+//@ !has "$.paths[*].path"                      '["simple_public", "Public"]'