From 228cab92699bf2433fc2202ff308ed352592f976 Mon Sep 17 00:00:00 2001 From: u9g Date: Sun, 30 Jul 2023 18:15:57 -0400 Subject: [PATCH] feat(query): add ast counterpart to ObjectLiteral (#666) --- crates/oxc_query/src/adapter.rs | 15 ++++--- crates/oxc_query/src/edges.rs | 5 ++- crates/oxc_query/src/schema.graphql | 12 ++++++ crates/oxc_query/src/tests.rs | 61 +++++++++++++++++++++++++++++ crates/oxc_query/src/vertex.rs | 32 ++++++++++++--- 5 files changed, 113 insertions(+), 12 deletions(-) diff --git a/crates/oxc_query/src/adapter.rs b/crates/oxc_query/src/adapter.rs index e42f71590..1dfd23764 100644 --- a/crates/oxc_query/src/adapter.rs +++ b/crates/oxc_query/src/adapter.rs @@ -136,11 +136,13 @@ impl<'a, 'b: 'a> trustfall::provider::Adapter<'a> for &'a Adapter<'b> { property_name.as_ref(), resolve_info, ), - "ObjectLiteral" => super::properties::resolve_object_literal_property( - contexts, - property_name.as_ref(), - resolve_info, - ), + "ObjectLiteralAST" | "ObjectLiteral" => { + super::properties::resolve_object_literal_property( + contexts, + property_name.as_ref(), + resolve_info, + ) + } "PathPart" => super::properties::resolve_path_part_property( contexts, property_name.as_ref(), @@ -323,11 +325,12 @@ impl<'a, 'b: 'a> trustfall::provider::Adapter<'a> for &'a Adapter<'b> { parameters, resolve_info, ), - "ObjectLiteral" => super::edges::resolve_object_literal_edge( + "ObjectLiteral" | "ObjectLiteralAST" => super::edges::resolve_object_literal_edge( contexts, edge_name.as_ref(), parameters, resolve_info, + self, ), "PathPart" => super::edges::resolve_path_part_edge( contexts, diff --git a/crates/oxc_query/src/edges.rs b/crates/oxc_query/src/edges.rs index ccaa360d5..11665e90f 100644 --- a/crates/oxc_query/src/edges.rs +++ b/crates/oxc_query/src/edges.rs @@ -1272,6 +1272,7 @@ pub(super) fn resolve_object_literal_edge<'a, 'b: 'a>( edge_name: &str, parameters: &EdgeParameters, resolve_info: &ResolveEdgeInfo, + adapter: &'a Adapter<'b>, ) -> ContextOutcomeIterator<'a, Vertex<'b>, VertexIterator<'a, Vertex<'b>>> { match edge_name { "span" => object_literal::span(contexts, resolve_info), @@ -1283,6 +1284,8 @@ pub(super) fn resolve_object_literal_edge<'a, 'b: 'a>( .expect("unexpected null or other incorrect datatype for Trustfall type 'String!'"); object_literal::value(contexts, key, resolve_info) } + "ancestor" => ancestors(contexts, adapter), + "parent" => parents(contexts, adapter), _ => { unreachable!( "attempted to resolve unexpected edge '{edge_name}' on type 'ObjectLiteral'" @@ -1322,7 +1325,7 @@ mod object_literal { panic!("expected to have an objectliteral vertex, instead have: {v:#?}") }); - Box::new(obj.properties.iter().filter_map(move |property| { + Box::new(obj.object_expression.properties.iter().filter_map(move |property| { let ObjectPropertyKind::ObjectProperty(prop) = property else { return None }; let has_right_key_name = match &prop.key { diff --git a/crates/oxc_query/src/schema.graphql b/crates/oxc_query/src/schema.graphql index dab2fe8c5..6131cc2ef 100644 --- a/crates/oxc_query/src/schema.graphql +++ b/crates/oxc_query/src/schema.graphql @@ -306,6 +306,18 @@ type ObjectLiteral implements Expression & HasSpan { span: Span! } +type ObjectLiteralAST implements Expression & HasSpan & ASTNode { + value(key: String!): [Expression!]! + + # Expression + as_constant_string: String + # ASTNode + parent: ASTNode + ancestor: [ASTNode!]! + # HasSpan + span: Span! +} + interface Expression implements HasSpan { """ Only non-null if the string can be trivially coerced to a constant string diff --git a/crates/oxc_query/src/tests.rs b/crates/oxc_query/src/tests.rs index fde5ca742..133af6eb9 100644 --- a/crates/oxc_query/src/tests.rs +++ b/crates/oxc_query/src/tests.rs @@ -138,6 +138,67 @@ query { ); } +#[test] +fn test_object_literal_ast() { + let code = "const colors = {blue: 1, green: 2, red: {a: 1}};"; + + let allocator = Allocator::default(); + let source_type = SourceType::default().with_module(true).with_jsx(true).with_typescript(true); + let ret = Parser::new(&allocator, code, source_type).parse(); + let program = allocator.alloc(ret.program); + let semantic_ret = + SemanticBuilder::new(code, source_type).with_trivias(&ret.trivias).build(program); + + let adapter = Adapter { + path_components: vec![Some("index".to_string())], + semantic: Rc::new(semantic_ret.semantic), + }; + + let args: BTreeMap, FieldValue> = BTreeMap::new(); + + let adapter = Arc::from(&adapter); + + #[allow(clippy::items_after_statements)] + #[derive(Debug, PartialOrd, Ord, PartialEq, Eq, serde::Deserialize)] + struct Output { + __typename: String, + value_typename: String, + } + + let mut results: Vec = execute_query( + schema(), + adapter, + r#" +query { + File { + ast_node { + ... on ObjectLiteralAST { + value(key: "red") { + value_typename: __typename @output + } + __typename @output + } + } + } +} + "#, + args, + ) + .expect("to successfully execute the query") + .map(|row| row.try_into_struct().expect("shape mismatch")) + .collect::>(); + + results.sort_unstable(); + + assert_eq!( + vec![Output { + __typename: "ObjectLiteralAST".to_owned(), + value_typename: "ObjectLiteral".to_owned() + }], + results + ); +} + #[test] fn test_parent_query() { let code = "interface MyGreatInterface { myGreatProperty: number }"; diff --git a/crates/oxc_query/src/vertex.rs b/crates/oxc_query/src/vertex.rs index e074193c0..eb44ad0ec 100644 --- a/crates/oxc_query/src/vertex.rs +++ b/crates/oxc_query/src/vertex.rs @@ -41,7 +41,7 @@ pub enum Vertex<'a> { JSXSpreadAttribute(&'a JSXSpreadAttribute<'a>), JSXSpreadChild(&'a JSXSpreadChild<'a>), JSXText(&'a JSXText), - ObjectLiteral(&'a ObjectExpression<'a>), + ObjectLiteral(Rc>), PathPart(usize), SearchParameter(Rc), Span(Span), @@ -77,7 +77,7 @@ impl<'a> Vertex<'a> { Self::JSXSpreadAttribute(data) => data.span, Self::JSXSpreadChild(data) => data.span, Self::JSXText(data) => data.span, - Self::ObjectLiteral(data) => data.span, + Self::ObjectLiteral(data) => data.object_expression.span, Self::SpecificImport(data) => data.span, Self::TypeAnnotation(data) => data.type_annotation.span, Self::Type(data) => data.span(), @@ -102,6 +102,7 @@ impl<'a> Vertex<'a> { Vertex::JSXElement(data) => data.ast_node.map(|x| x.id()), Vertex::TypeAnnotation(data) => data.ast_node.map(|x| x.id()), Vertex::VariableDeclaration(data) => data.ast_node.map(|x| x.id()), + Vertex::ObjectLiteral(data) => data.ast_node.map(|x| x.id()), Vertex::ReturnStatementAST(data) => data.ast_node.map(|x| x.id()), Vertex::DefaultImport(_) | Vertex::AssignmentType(_) @@ -112,7 +113,6 @@ impl<'a> Vertex<'a> { | Vertex::JSXAttribute(_) | Vertex::JSXExpressionContainer(_) | Vertex::JSXFragment(_) - | Vertex::ObjectLiteral(_) | Vertex::JSXText(_) | Vertex::JSXSpreadChild(_) | Vertex::JSXSpreadAttribute(_) @@ -168,7 +168,7 @@ impl Typename for Vertex<'_> { Vertex::JSXSpreadAttribute(_) => "JSXSpreadAttribute", Vertex::JSXSpreadChild(_) => "JSXSpreadChild", Vertex::JSXText(_) => "JSXText", - Vertex::ObjectLiteral(_) => "ObjectLiteral", + Vertex::ObjectLiteral(objlit) => objlit.typename(), Vertex::PathPart(_) => "PathPart", Vertex::SearchParameter(_) => "SearchParameter", Vertex::Span(_) => "Span", @@ -206,6 +206,9 @@ impl<'a> From> for Vertex<'a> { AstKind::Class(class) => { Self::Class(ClassVertex { ast_node: Some(ast_node), class }.into()) } + AstKind::ObjectExpression(objexpr) => Self::ObjectLiteral( + ObjectLiteralVertex { ast_node: Some(ast_node), object_expression: objexpr }.into(), + ), _ => Vertex::ASTNode(ast_node), } } @@ -217,7 +220,9 @@ impl<'a> From<&'a Expression<'a>> for Vertex<'a> { // NOTE: When string literal / template literal is added, add to as_constant_string match &expr.get_inner_expression() { - Expression::ObjectExpression(objexpr) => Vertex::ObjectLiteral(objexpr), + Expression::ObjectExpression(object_expression) => Vertex::ObjectLiteral( + ObjectLiteralVertex { ast_node: None, object_expression }.into(), + ), Expression::JSXElement(element) => { Vertex::JSXElement(JSXElementVertex { ast_node: None, element }.into()) } @@ -376,3 +381,20 @@ impl<'a> Typename for VariableDeclarationVertex<'a> { } } } + +#[non_exhaustive] +#[derive(Debug, Clone)] +pub struct ObjectLiteralVertex<'a> { + ast_node: Option>, + pub object_expression: &'a ObjectExpression<'a>, +} + +impl<'a> Typename for ObjectLiteralVertex<'a> { + fn typename(&self) -> &'static str { + if self.ast_node.is_some() { + "ObjectLiteralAST" + } else { + "ObjectLiteral" + } + } +}