From ba992f571ec1768dee44540cee3545a7864e11bd Mon Sep 17 00:00:00 2001 From: Predrag Gruevski <2348618+obi1kenobi@users.noreply.github.com> Date: Fri, 20 Dec 2024 13:17:00 -0500 Subject: [PATCH] Ensure our `impl` lookup optimization accounts for optional edges. (#682) (#683) --- src/adapter/optimizations/impl_lookup.rs | 4 +- src/adapter/tests.rs | 59 ++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/src/adapter/optimizations/impl_lookup.rs b/src/adapter/optimizations/impl_lookup.rs index 542d20e..3e4156b 100644 --- a/src/adapter/optimizations/impl_lookup.rs +++ b/src/adapter/optimizations/impl_lookup.rs @@ -28,10 +28,10 @@ pub(crate) fn resolve_owner_impl<'a, V: AsVertex> + 'a>( _ => unreachable!("unexpected edge name: {edge_name}"), }; - // Check if the `method` edge is used next at the destination. + // Check if the `method` edge is used in a mandatory fashion at the destination. if let Some(method_vertex_info) = resolve_info .destination() - .first_edge("method") + .first_mandatory_edge("method") .as_ref() .map(|e| e.destination()) { diff --git a/src/adapter/tests.rs b/src/adapter/tests.rs index a0836b5..9cdbe78 100644 --- a/src/adapter/tests.rs +++ b/src/adapter/tests.rs @@ -4001,3 +4001,62 @@ fn item_lookup_by_path_optimization() { expected_results.sort_unstable(); similar_asserts::assert_eq!(expected_results, results); } + +#[test] +fn impl_lookup_by_method_name_optimization() { + // Any test crate that has `impl` blocks without methods would work for this test. + get_test_data!(data, associated_consts); + let adapter = RustdocAdapter::new(&data, None); + let adapter = Arc::new(&adapter); + + let query = r#" +{ + Crate { + item { + ... on ImplOwner { + name @output + + # Since this edge is optional, this query matches: + # - types with inherent impls containing the named method + # - types that *do not have* any methods in their inherent impls. + # + # Failure to account for either of these cases means we have a bug in + # the "item lookup by importable path" optimization code path. + inherent_impl { + method @optional { + method: name @output @filter(op: "=", value: ["$name"]) + } + } + } + } + } +} + "#; + + let variables = btreemap! { + "name" => "non_existent", + }; + + let schema = + Schema::parse(include_str!("../rustdoc_schema.graphql")).expect("schema failed to parse"); + + #[derive(Debug, PartialOrd, Ord, PartialEq, Eq, serde::Deserialize)] + struct Output { + name: String, + method: Option, + } + + let mut results: Vec<_> = + trustfall::execute_query(&schema, adapter.clone(), query, variables.clone()) + .expect("failed to run query") + .map(|row| row.try_into_struct().expect("shape mismatch")) + .collect(); + results.sort_unstable(); + + let mut expected_results = vec![Output { + name: "Counter".into(), + method: None, + }]; + expected_results.sort_unstable(); + similar_asserts::assert_eq!(expected_results, results); +}