From 95663781194ae75f29ced815bfa2612a6069e4cc Mon Sep 17 00:00:00 2001 From: u9g Date: Sat, 12 Aug 2023 04:43:58 -0400 Subject: [PATCH] feat(query): add many types to oxc_query (#724) Adds `Name(AST)?`, `IfStatementAST`, `SpreadIntoObject(AST)?`, `ObjectEntry(AST)?`, `DotProperty(AST)?` types Fixes `ObjectLiteral`'s value field's output type Added `entry` field to `ObjectLiteral(AST)?` --- crates/oxc_query/src/adapter.rs | 53 +++++ crates/oxc_query/src/edges.rs | 356 +++++++++++++++++++++++++++- crates/oxc_query/src/properties.rs | 39 ++- crates/oxc_query/src/schema.graphql | 108 ++++++++- crates/oxc_query/src/vertex.rs | 145 ++++++++++- 5 files changed, 689 insertions(+), 12 deletions(-) diff --git a/crates/oxc_query/src/adapter.rs b/crates/oxc_query/src/adapter.rs index 58522dd33..7743d2536 100644 --- a/crates/oxc_query/src/adapter.rs +++ b/crates/oxc_query/src/adapter.rs @@ -96,6 +96,11 @@ impl<'a, 'b: 'a> trustfall::provider::Adapter<'a> for &'a Adapter<'b> { property_name.as_ref(), resolve_info, ), + "DotPropertyAST" | "DotProperty" => super::properties::resolve_dot_property_property( + contexts, + property_name.as_ref(), + resolve_info, + ), "Expression" => super::properties::resolve_expression_property( contexts, property_name.as_ref(), @@ -143,6 +148,11 @@ impl<'a, 'b: 'a> trustfall::provider::Adapter<'a> for &'a Adapter<'b> { property_name.as_ref(), resolve_info, ), + "NameAST" | "Name" => super::properties::resolve_name_property( + contexts, + property_name.as_ref(), + resolve_info, + ), "NumberLiteralAST" | "NumberLiteral" => { super::properties::resolve_number_literal_property( contexts, @@ -239,6 +249,13 @@ impl<'a, 'b: 'a> trustfall::provider::Adapter<'a> for &'a Adapter<'b> { parameters, resolve_info, ), + "DotPropertyAST" | "DotProperty" => super::edges::resolve_dot_property_edge( + contexts, + edge_name.as_ref(), + parameters, + resolve_info, + self, + ), "DefaultImport" => super::edges::resolve_default_import_edge( contexts, edge_name.as_ref(), @@ -264,6 +281,13 @@ impl<'a, 'b: 'a> trustfall::provider::Adapter<'a> for &'a Adapter<'b> { parameters, resolve_info, ), + "IfStatementAST" => super::edges::resolve_if_statement_edge( + contexts, + edge_name.as_ref(), + parameters, + resolve_info, + self, + ), "Import" | "ImportAST" => super::edges::resolve_import_edge( contexts, edge_name.as_ref(), @@ -342,6 +366,13 @@ impl<'a, 'b: 'a> trustfall::provider::Adapter<'a> for &'a Adapter<'b> { parameters, resolve_info, ), + "NameAST" | "Name" => super::edges::resolve_name_edge( + contexts, + edge_name.as_ref(), + parameters, + resolve_info, + self, + ), "NumberLiteralAST" | "NumberLiteral" => super::edges::resolve_number_literal_edge( contexts, edge_name.as_ref(), @@ -349,6 +380,13 @@ impl<'a, 'b: 'a> trustfall::provider::Adapter<'a> for &'a Adapter<'b> { resolve_info, self, ), + "ObjectEntryAST" | "ObjectEntry" => super::edges::resolve_object_entry_edge( + contexts, + edge_name.as_ref(), + parameters, + resolve_info, + self, + ), "ObjectLiteral" | "ObjectLiteralAST" => super::edges::resolve_object_literal_edge( contexts, edge_name.as_ref(), @@ -356,6 +394,12 @@ impl<'a, 'b: 'a> trustfall::provider::Adapter<'a> for &'a Adapter<'b> { resolve_info, self, ), + "ObjectProperty" => super::edges::resolve_object_property_edge( + contexts, + edge_name.as_ref(), + parameters, + resolve_info, + ), "PathPart" => super::edges::resolve_path_part_edge( contexts, edge_name.as_ref(), @@ -382,6 +426,15 @@ impl<'a, 'b: 'a> trustfall::provider::Adapter<'a> for &'a Adapter<'b> { parameters, resolve_info, ), + "SpreadIntoObjectAST" | "SpreadIntoObject" => { + super::edges::resolve_spread_into_object_edge( + contexts, + edge_name.as_ref(), + parameters, + resolve_info, + self, + ) + } "TypeAnnotation" | "TypeAnnotationAST" => super::edges::resolve_type_annotation_edge( contexts, edge_name.as_ref(), diff --git a/crates/oxc_query/src/edges.rs b/crates/oxc_query/src/edges.rs index 915c70d5e..1250dc930 100644 --- a/crates/oxc_query/src/edges.rs +++ b/crates/oxc_query/src/edges.rs @@ -391,6 +391,83 @@ mod default_import { } } +pub(super) fn resolve_dot_property_edge<'a, 'b: 'a>( + contexts: ContextIterator<'a, Vertex<'b>>, + edge_name: &str, + _parameters: &EdgeParameters, + resolve_info: &ResolveEdgeInfo, + adapter: &'a Adapter<'b>, +) -> ContextOutcomeIterator<'a, Vertex<'b>, VertexIterator<'a, Vertex<'b>>> { + match edge_name { + "span" => dot_property::span(contexts, resolve_info), + "ancestor" => ancestors(contexts, adapter), + "parent" => parents(contexts, adapter), + "called_on" => dot_property::called_on(contexts, resolve_info), + "accessed_property" => dot_property::accessed_property(contexts, resolve_info), + _ => { + unreachable!( + "attempted to resolve unexpected edge '{edge_name}' on type 'ClassProperty'" + ) + } + } +} + +mod dot_property { + use trustfall::provider::{ + resolve_neighbors_with, ContextIterator, ContextOutcomeIterator, ResolveEdgeInfo, + VertexIterator, + }; + + use crate::vertex::NameVertex; + + use super::super::vertex::Vertex; + + pub(super) fn span<'a, 'b: 'a>( + contexts: ContextIterator<'a, Vertex<'b>>, + _resolve_info: &ResolveEdgeInfo, + ) -> ContextOutcomeIterator<'a, Vertex<'b>, VertexIterator<'a, Vertex<'b>>> { + super::get_span(contexts) + } + + pub(super) fn called_on<'a, 'b: 'a>( + contexts: ContextIterator<'a, Vertex<'b>>, + _resolve_info: &ResolveEdgeInfo, + ) -> ContextOutcomeIterator<'a, Vertex<'b>, VertexIterator<'a, Vertex<'b>>> { + resolve_neighbors_with(contexts, |v| { + Box::new(std::iter::once( + (&v.as_dot_property() + .unwrap_or_else(|| { + panic!("expected to have a dotproperty vertex, instead have: {v:#?}") + }) + .static_member_expr + .object) + .into(), + )) + }) + } + + pub(super) fn accessed_property<'a, 'b: 'a>( + contexts: ContextIterator<'a, Vertex<'b>>, + _resolve_info: &ResolveEdgeInfo, + ) -> ContextOutcomeIterator<'a, Vertex<'b>, VertexIterator<'a, Vertex<'b>>> { + resolve_neighbors_with(contexts, |v| { + Box::new(std::iter::once(Vertex::Name( + NameVertex { + ast_node: None, + name: &v + .as_dot_property() + .unwrap_or_else(|| { + panic!("expected to have a dotproperty vertex, instead have: {v:#?}") + }) + .static_member_expr + .property, + } + .into(), + ))) + }) + } +} + pub(super) fn resolve_expression_edge<'a, 'b: 'a>( contexts: ContextIterator<'a, Vertex<'b>>, edge_name: &str, @@ -592,6 +669,59 @@ mod has_span { } } +pub(super) fn resolve_if_statement_edge<'a, 'b: 'a>( + contexts: ContextIterator<'a, Vertex<'b>>, + edge_name: &str, + _parameters: &EdgeParameters, + resolve_info: &ResolveEdgeInfo, + adapter: &'a Adapter<'b>, +) -> ContextOutcomeIterator<'a, Vertex<'b>, VertexIterator<'a, Vertex<'b>>> { + match edge_name { + "span" => if_statement::span(contexts, resolve_info), + "ancestor" => ancestors(contexts, adapter), + "parent" => parents(contexts, adapter), + "condition" => if_statement::condition(contexts, resolve_info), + _ => { + unreachable!( + "attempted to resolve unexpected edge '{edge_name}' on type 'IfStatementAST'" + ) + } + } +} + +mod if_statement { + use trustfall::provider::{ + resolve_neighbors_with, ContextIterator, ContextOutcomeIterator, ResolveEdgeInfo, + VertexIterator, + }; + + use super::super::vertex::Vertex; + + pub(super) fn span<'a, 'b: 'a>( + contexts: ContextIterator<'a, Vertex<'b>>, + _resolve_info: &ResolveEdgeInfo, + ) -> ContextOutcomeIterator<'a, Vertex<'b>, VertexIterator<'a, Vertex<'b>>> { + super::get_span(contexts) + } + + pub(super) fn condition<'a, 'b: 'a>( + contexts: ContextIterator<'a, Vertex<'b>>, + _resolve_info: &ResolveEdgeInfo, + ) -> ContextOutcomeIterator<'a, Vertex<'b>, VertexIterator<'a, Vertex<'b>>> { + resolve_neighbors_with(contexts, |v| { + Box::new(std::iter::once( + (&v.as_if_statement_ast() + .unwrap_or_else(|| { + panic!("expected to have an if_statement_ast vertex, instead have: {v:#?}") + }) + .return_statement + .test) + .into(), + )) + }) + } +} + pub(super) fn resolve_import_edge<'a, 'b: 'a>( contexts: ContextIterator<'a, Vertex<'b>>, edge_name: &str, @@ -1277,6 +1407,38 @@ mod member_extend { } } +pub(super) fn resolve_name_edge<'a, 'b: 'a>( + contexts: ContextIterator<'a, Vertex<'b>>, + edge_name: &str, + _parameters: &EdgeParameters, + resolve_info: &ResolveEdgeInfo, + adapter: &'a Adapter<'b>, +) -> ContextOutcomeIterator<'a, Vertex<'b>, VertexIterator<'a, Vertex<'b>>> { + match edge_name { + "span" => name::span(contexts, resolve_info), + "ancestor" => ancestors(contexts, adapter), + "parent" => parents(contexts, adapter), + _ => { + unreachable!("attempted to resolve unexpected edge '{edge_name}' on type 'Name'") + } + } +} + +mod name { + use trustfall::provider::{ + ContextIterator, ContextOutcomeIterator, ResolveEdgeInfo, VertexIterator, + }; + + use super::{super::vertex::Vertex, get_span}; + + pub(super) fn span<'a, 'b: 'a>( + contexts: ContextIterator<'a, Vertex<'b>>, + _resolve_info: &ResolveEdgeInfo, + ) -> ContextOutcomeIterator<'a, Vertex<'b>, VertexIterator<'a, Vertex<'b>>> { + get_span(contexts) + } +} + pub(super) fn resolve_number_literal_edge<'a, 'b: 'a>( contexts: ContextIterator<'a, Vertex<'b>>, edge_name: &str, @@ -1290,7 +1452,7 @@ pub(super) fn resolve_number_literal_edge<'a, 'b: 'a>( "parent" => parents(contexts, adapter), _ => { unreachable!( - "attempted to resolve unexpected edge '{edge_name}' on type 'MemberExtend'" + "attempted to resolve unexpected edge '{edge_name}' on type 'NumberLiteral'" ) } } @@ -1311,6 +1473,83 @@ mod number_literal { } } +pub(super) fn resolve_object_entry_edge<'a, 'b: 'a>( + contexts: ContextIterator<'a, Vertex<'b>>, + 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_entry::span(contexts, resolve_info), + "key" => object_entry::key(contexts, resolve_info), + "value" => object_entry::value(contexts, resolve_info), + "ancestor" => ancestors(contexts, adapter), + "parent" => parents(contexts, adapter), + _ => { + unreachable!("attempted to resolve unexpected edge '{edge_name}' on type 'ObjectEntry'") + } + } +} + +mod object_entry { + use trustfall::provider::{ + resolve_neighbors_with, ContextIterator, ContextOutcomeIterator, ResolveEdgeInfo, + VertexIterator, + }; + + use super::{super::vertex::Vertex, get_span}; + + pub(super) fn span<'a, 'b: 'a>( + contexts: ContextIterator<'a, Vertex<'b>>, + _resolve_info: &ResolveEdgeInfo, + ) -> ContextOutcomeIterator<'a, Vertex<'b>, VertexIterator<'a, Vertex<'b>>> { + get_span(contexts) + } + + pub(super) fn key<'a, 'b: 'a>( + contexts: ContextIterator<'a, Vertex<'b>>, + _resolve_info: &ResolveEdgeInfo, + ) -> ContextOutcomeIterator<'a, Vertex<'b>, VertexIterator<'a, Vertex<'b>>> { + resolve_neighbors_with(contexts, |v| { + let key = &v + .as_object_entry() + .map_or_else( + || panic!("expected to have a objectentry vertex, instead have: {v:#?}"), + |x| &x.property, + ) + .key; + + let vertex: Vertex<'_> = match &key { + oxc_ast::ast::PropertyKey::Identifier(_) => return Box::new(std::iter::empty()), // TODO: FINISH + oxc_ast::ast::PropertyKey::PrivateIdentifier(_) => unreachable!( + "private identifiers don't exist in objects, so this should never be called" + ), + oxc_ast::ast::PropertyKey::Expression(expr) => expr.into(), + }; + + Box::new(std::iter::once(vertex)) + }) + } + + pub(super) fn value<'a, 'b: 'a>( + contexts: ContextIterator<'a, Vertex<'b>>, + _resolve_info: &ResolveEdgeInfo, + ) -> ContextOutcomeIterator<'a, Vertex<'b>, VertexIterator<'a, Vertex<'b>>> { + resolve_neighbors_with(contexts, |v| { + let value = &v + .as_object_entry() + .map_or_else( + || panic!("expected to have a objectentry vertex, instead have: {v:#?}"), + |x| &x.property, + ) + .value; + + Box::new(std::iter::once(value.into())) + }) + } +} + pub(super) fn resolve_object_literal_edge<'a, 'b: 'a>( contexts: ContextIterator<'a, Vertex<'b>>, edge_name: &str, @@ -1328,6 +1567,7 @@ 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) } + "entry" => object_literal::entry(contexts, parameters, resolve_info), "ancestor" => ancestors(contexts, adapter), "parent" => parents(contexts, adapter), _ => { @@ -1343,12 +1583,15 @@ mod object_literal { use oxc_ast::ast::ObjectPropertyKind; use trustfall::provider::{ - resolve_neighbors_with, ContextIterator, ContextOutcomeIterator, ResolveEdgeInfo, - VertexIterator, + resolve_neighbors_with, ContextIterator, ContextOutcomeIterator, EdgeParameters, + ResolveEdgeInfo, VertexIterator, }; use super::{super::vertex::Vertex, get_span}; - use crate::util::expr_to_maybe_const_string; + use crate::{ + util::expr_to_maybe_const_string, + vertex::{ObjectEntryVertex, SpreadIntoObjectVertex}, + }; pub(super) fn span<'a, 'b: 'a>( contexts: ContextIterator<'a, Vertex<'b>>, @@ -1389,6 +1632,58 @@ mod object_literal { })) }) } + + pub(super) fn entry<'a, 'b: 'a>( + contexts: ContextIterator<'a, Vertex<'b>>, + _parameters: &EdgeParameters, + _resolve_info: &ResolveEdgeInfo, + ) -> ContextOutcomeIterator<'a, Vertex<'b>, VertexIterator<'a, Vertex<'b>>> { + resolve_neighbors_with(contexts, |v| { + let obj = v.as_object_literal().unwrap_or_else(|| { + panic!("expected to have an objectliteral vertex, instead have: {v:#?}") + }); + + Box::new(obj.object_expression.properties.iter().map(|property| match property { + oxc_ast::ast::ObjectPropertyKind::ObjectProperty(property) => { + Vertex::ObjectEntry(ObjectEntryVertex { property, ast_node: None }.into()) + } + oxc_ast::ast::ObjectPropertyKind::SpreadProperty(property) => { + Vertex::SpreadIntoObject( + SpreadIntoObjectVertex { property, ast_node: None }.into(), + ) + } + })) + }) + } +} + +pub(super) fn resolve_object_property_edge<'a, 'b: 'a>( + contexts: ContextIterator<'a, Vertex<'b>>, + edge_name: &str, + _parameters: &EdgeParameters, + resolve_info: &ResolveEdgeInfo, +) -> ContextOutcomeIterator<'a, Vertex<'b>, VertexIterator<'a, Vertex<'b>>> { + match edge_name { + "span" => object_property::span(contexts, resolve_info), + _ => { + unreachable!("attempted to resolve unexpected edge '{edge_name}' on type 'Name'") + } + } +} + +mod object_property { + use trustfall::provider::{ + ContextIterator, ContextOutcomeIterator, ResolveEdgeInfo, VertexIterator, + }; + + use super::{super::vertex::Vertex, get_span}; + + pub(super) fn span<'a, 'b: 'a>( + contexts: ContextIterator<'a, Vertex<'b>>, + _resolve_info: &ResolveEdgeInfo, + ) -> ContextOutcomeIterator<'a, Vertex<'b>, VertexIterator<'a, Vertex<'b>>> { + get_span(contexts) + } } pub(super) fn resolve_path_part_edge<'a, 'b: 'a>( @@ -1584,6 +1879,59 @@ mod specific_import { } } +pub(super) fn resolve_spread_into_object_edge<'a, 'b: 'a>( + contexts: ContextIterator<'a, Vertex<'b>>, + edge_name: &str, + _parameters: &EdgeParameters, + resolve_info: &ResolveEdgeInfo, + adapter: &'a Adapter<'b>, +) -> ContextOutcomeIterator<'a, Vertex<'b>, VertexIterator<'a, Vertex<'b>>> { + match edge_name { + "span" => resolve_spread_into_object_edge::span(contexts, resolve_info), + "value" => resolve_spread_into_object_edge::value(contexts, resolve_info), + "ancestor" => ancestors(contexts, adapter), + "parent" => parents(contexts, adapter), + _ => { + unreachable!( + "attempted to resolve unexpected edge '{edge_name}' on type 'SpreadIntoObject'" + ) + } + } +} + +mod resolve_spread_into_object_edge { + use trustfall::provider::{ + resolve_neighbors_with, ContextIterator, ContextOutcomeIterator, ResolveEdgeInfo, + VertexIterator, + }; + + use super::{super::vertex::Vertex, get_span}; + + pub(super) fn value<'a, 'b: 'a>( + contexts: ContextIterator<'a, Vertex<'b>>, + _resolve_info: &ResolveEdgeInfo, + ) -> ContextOutcomeIterator<'a, Vertex<'b>, VertexIterator<'a, Vertex<'b>>> { + resolve_neighbors_with(contexts, |v| { + let argument = &v + .as_spread_into_object() + .map_or_else( + || panic!("expected to have a spreadintoobject vertex, instead have: {v:#?}"), + |x| &x.property, + ) + .argument; + + Box::new(std::iter::once(argument.into())) + }) + } + + pub(super) fn span<'a, 'b: 'a>( + contexts: ContextIterator<'a, Vertex<'b>>, + _resolve_info: &ResolveEdgeInfo, + ) -> ContextOutcomeIterator<'a, Vertex<'b>, VertexIterator<'a, Vertex<'b>>> { + get_span(contexts) + } +} + pub(super) fn resolve_type_annotation_edge<'a, 'b: 'a>( contexts: ContextIterator<'a, Vertex<'b>>, edge_name: &str, diff --git a/crates/oxc_query/src/properties.rs b/crates/oxc_query/src/properties.rs index afcd125f1..58efc1cb8 100644 --- a/crates/oxc_query/src/properties.rs +++ b/crates/oxc_query/src/properties.rs @@ -196,6 +196,23 @@ pub(super) fn resolve_default_import_property<'a, 'b: 'a>( } } +pub(super) fn resolve_dot_property_property<'a, 'b: 'a>( + contexts: ContextIterator<'a, Vertex<'b>>, + property_name: &str, + _resolve_info: &ResolveInfo, +) -> ContextOutcomeIterator<'a, Vertex<'b>, FieldValue> { + match property_name { + "as_constant_string" => resolve_property_with(contexts, |v| { + v.as_constant_string().map_or(FieldValue::Null, Into::into) + }), + _ => { + unreachable!( + "attempted to read unexpected property '{property_name}' on type 'DotProperty'" + ) + } + } +} + pub(super) fn resolve_expression_property<'a, 'b: 'a>( contexts: ContextIterator<'a, Vertex<'b>>, property_name: &str, @@ -384,6 +401,26 @@ pub(super) fn resolve_member_extend_property<'a, 'b: 'a>( } } +pub(super) fn resolve_name_property<'a, 'b: 'a>( + contexts: ContextIterator<'a, Vertex<'b>>, + property_name: &str, + _resolve_info: &ResolveInfo, +) -> ContextOutcomeIterator<'a, Vertex<'b>, FieldValue> { + match property_name { + "name" => resolve_property_with(contexts, |v| { + v.as_name() + .unwrap_or_else(|| panic!("expected to have a name vertex, instead have: {v:#?}")) + .name + .name + .to_string() + .into() + }), + _ => { + unreachable!("attempted to read unexpected property '{property_name}' on type 'Name'") + } + } +} + pub(super) fn resolve_number_literal_property<'a, 'b: 'a>( contexts: ContextIterator<'a, Vertex<'b>>, property_name: &str, @@ -410,7 +447,7 @@ pub(super) fn resolve_number_literal_property<'a, 'b: 'a>( }), _ => { unreachable!( - "attempted to read unexpected property '{property_name}' on type 'MemberExtend'" + "attempted to read unexpected property '{property_name}' on type 'NumberLiteral'" ) } } diff --git a/crates/oxc_query/src/schema.graphql b/crates/oxc_query/src/schema.graphql index 81b02b999..b9c83d5fd 100644 --- a/crates/oxc_query/src/schema.graphql +++ b/crates/oxc_query/src/schema.graphql @@ -326,8 +326,52 @@ interface JSXElement implements Expression & HasSpan { span: Span! } +interface ObjectProperty implements HasSpan { + # HasSpan + span: Span! +} + +interface SpreadIntoObject implements ObjectProperty & HasSpan { + value: Expression! + + # HasSpan + span: Span! +} + +type SpreadIntoObjectAST implements ASTNode & SpreadIntoObject & ObjectProperty & HasSpan { + value: Expression! + + # ASTNode + parent: ASTNode + ancestor: [ASTNode!]! + + # HasSpan + span: Span! +} + +interface ObjectEntry implements ObjectProperty & HasSpan { + key: Expression! + value: Expression! + + # HasSpan + span: Span! +} + +type ObjectEntryAST implements ASTNode & ObjectEntry & ObjectProperty & HasSpan { + key: Expression! + value: Expression! + + # ASTNode + parent: ASTNode + ancestor: [ASTNode!]! + + # HasSpan + span: Span! +} + interface ObjectLiteral implements Expression & HasSpan { - value(key: String!): [Expression!]! + value(key: String!): Expression + entry: [ObjectProperty!]! # Expression as_constant_string: String @@ -336,7 +380,8 @@ interface ObjectLiteral implements Expression & HasSpan { } type ObjectLiteralAST implements ObjectLiteral & Expression & HasSpan & ASTNode { - value(key: String!): [Expression!]! + value(key: String!): Expression + entry: [ObjectProperty!]! # Expression as_constant_string: String @@ -347,6 +392,54 @@ type ObjectLiteralAST implements ObjectLiteral & Expression & HasSpan & ASTNode span: Span! } +interface Name implements HasSpan { + name: String! + # HasSpan + span: Span! +} + +type NameAST implements ASTNode & Name & HasSpan { + name: String! + # ASTNode + parent: ASTNode + ancestor: [ASTNode!]! + # HasSpan + span: Span! +} + +interface DotProperty implements HasSpan & Expression { + """ + In "data.user.name", this would be "data.user" + """ + called_on: Expression! + """ + In "data.user.name", this would be "name" + """ + accessed_property: Name! + # Expression + as_constant_string: String + # HasSpan + span: Span! +} + +type DotPropertyAST implements DotProperty & ASTNode & HasSpan & Expression { + """ + In "data.user.name", this would be "data.user" + """ + called_on: Expression! + """ + In "data.user.name", this would be "name" + """ + accessed_property: Name! + # ASTNode + parent: ASTNode + ancestor: [ASTNode!]! + # Expression + as_constant_string: String + # HasSpan + span: Span! +} + interface Expression implements HasSpan { """ Only non-null if the string can be trivially coerced to a constant string @@ -464,6 +557,17 @@ type ClassAST implements ASTNode & Class & HasSpan { span: Span! } +type IfStatementAST implements ASTNode & HasSpan { + condition: Expression! + + # ASTNode + parent: ASTNode + ancestor: [ASTNode!]! + + # HasSpan + span: Span! +} + type ReturnStatementAST implements ASTNode & HasSpan { expression: Expression diff --git a/crates/oxc_query/src/vertex.rs b/crates/oxc_query/src/vertex.rs index db7c5a9bf..72599b376 100644 --- a/crates/oxc_query/src/vertex.rs +++ b/crates/oxc_query/src/vertex.rs @@ -3,11 +3,12 @@ use std::rc::Rc; use enum_as_inner::EnumAsInner; use oxc_ast::{ ast::{ - BindingPatternKind, Class, Expression, IdentifierReference, ImportDeclaration, - ImportDefaultSpecifier, ImportSpecifier, JSXAttribute, JSXElement, JSXExpressionContainer, - JSXFragment, JSXOpeningElement, JSXSpreadAttribute, JSXSpreadChild, JSXText, - MemberExpression, MethodDefinition, ModuleDeclaration, NumberLiteral, ObjectExpression, - PropertyDefinition, ReturnStatement, TSInterfaceDeclaration, TSType, TSTypeAnnotation, + BindingPatternKind, Class, Expression, IdentifierName, IdentifierReference, IfStatement, + ImportDeclaration, ImportDefaultSpecifier, ImportSpecifier, JSXAttribute, JSXElement, + JSXExpressionContainer, JSXFragment, JSXOpeningElement, JSXSpreadAttribute, JSXSpreadChild, + JSXText, MemberExpression, MethodDefinition, ModuleDeclaration, NumberLiteral, + ObjectExpression, ObjectProperty, PropertyDefinition, ReturnStatement, SpreadElement, + StaticMemberExpression, TSInterfaceDeclaration, TSType, TSTypeAnnotation, VariableDeclarator, }, AstKind, @@ -43,6 +44,7 @@ pub enum Vertex<'a> { JSXText(&'a JSXText), ObjectLiteral(Rc>), NumberLiteral(Rc>), + Name(Rc>), PathPart(usize), SearchParameter(Rc), Span(Span), @@ -52,6 +54,10 @@ pub enum Vertex<'a> { Url(Rc), VariableDeclaration(Rc>), ReturnStatementAST(Rc>), + IfStatementAST(Rc>), + SpreadIntoObject(Rc>), + ObjectEntry(Rc>), + DotProperty(Rc>), } impl<'a> Vertex<'a> { @@ -75,16 +81,21 @@ impl<'a> Vertex<'a> { Self::JSXExpressionContainer(data) => data.span, Self::JSXFragment(data) => data.span, Self::JSXOpeningElement(data) => data.opening_element.span, + Self::DotProperty(data) => data.static_member_expr.span, Self::JSXSpreadAttribute(data) => data.span, Self::JSXSpreadChild(data) => data.span, Self::JSXText(data) => data.span, Self::ObjectLiteral(data) => data.object_expression.span, + Self::SpreadIntoObject(data) => data.property.span, + Self::ObjectEntry(data) => data.property.span, Self::SpecificImport(data) => data.span, Self::TypeAnnotation(data) => data.type_annotation.span, Self::Type(data) => data.span(), Self::VariableDeclaration(data) => data.variable_declaration.span, Self::ReturnStatementAST(data) => data.return_statement.span, + Self::IfStatementAST(data) => data.return_statement.span, Self::NumberLiteral(data) => data.number_literal.span, + Self::Name(data) => data.name.span, Self::File | Self::Url(_) | Self::PathPart(_) @@ -106,8 +117,13 @@ impl<'a> Vertex<'a> { 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::IfStatementAST(data) => data.ast_node.map(|x| x.id()), Vertex::JSXOpeningElement(data) => data.ast_node.map(|x| x.id()), Vertex::NumberLiteral(data) => data.ast_node.map(|x| x.id()), + Vertex::Name(data) => data.ast_node.map(|x| x.id()), + Vertex::SpreadIntoObject(data) => data.ast_node.map(|x| x.id()), + Vertex::ObjectEntry(data) => data.ast_node.map(|x| x.id()), + Vertex::DotProperty(data) => data.ast_node.map(|x| x.id()), Vertex::DefaultImport(_) | Vertex::AssignmentType(_) | Vertex::ClassMethod(_) @@ -160,6 +176,7 @@ impl Typename for Vertex<'_> { Vertex::Import(import) => import.typename(), Vertex::Interface(iface) => iface.typename(), Vertex::NumberLiteral(nlit) => nlit.typename(), + Vertex::DotProperty(dot_property) => dot_property.typename(), Vertex::InterfaceExtend(iex) => match **iex { InterfaceExtendVertex::Identifier(_) => "SimpleExtend", InterfaceExtendVertex::MemberExpression(_) => "MemberExtend", @@ -181,7 +198,11 @@ impl Typename for Vertex<'_> { Vertex::Type(_) => "Type", Vertex::Url(_) => "URL", Vertex::VariableDeclaration(vd) => vd.typename(), + Vertex::Name(name) => name.typename(), Vertex::ReturnStatementAST(_) => "ReturnStatementAST", + Vertex::IfStatementAST(_) => "IfStatementAST", + Vertex::SpreadIntoObject(obj) => obj.typename(), + Vertex::ObjectEntry(entry) => entry.typename(), } } } @@ -192,6 +213,10 @@ impl<'a> From> for Vertex<'a> { AstKind::ReturnStatement(return_statement) => Self::ReturnStatementAST( ReturnStatementVertex { ast_node: Some(ast_node), return_statement }.into(), ), + AstKind::IfStatement(if_statement) => Self::IfStatementAST( + IfStatementVertex { ast_node: Some(ast_node), return_statement: if_statement } + .into(), + ), AstKind::JSXElement(element) => { Self::JSXElement(JSXElementVertex { ast_node: Some(ast_node), element }.into()) } @@ -219,6 +244,29 @@ impl<'a> From> for Vertex<'a> { AstKind::NumberLiteral(number_literal) => Self::NumberLiteral( NumberLiteralVertex { ast_node: Some(ast_node), number_literal }.into(), ), + AstKind::IdentifierName(identifier_name) => { + Self::Name(NameVertex { ast_node: Some(ast_node), name: identifier_name }.into()) + } + AstKind::ObjectProperty(property) => { + Self::ObjectEntry(ObjectEntryVertex { ast_node: Some(ast_node), property }.into()) + } + AstKind::SpreadElement(property) => Self::SpreadIntoObject( + SpreadIntoObjectVertex { ast_node: Some(ast_node), property }.into(), + ), + AstKind::MemberExpression(member_expr) + if matches!(member_expr, MemberExpression::StaticMemberExpression(_)) => + { + match member_expr { + MemberExpression::StaticMemberExpression(member_expr) => Self::DotProperty( + DotPropertyVertex { + ast_node: Some(ast_node), + static_member_expr: member_expr, + } + .into(), + ), + _ => unreachable!("we should only ever have StaticMemberExpression"), + } + } _ => Vertex::ASTNode(ast_node), } } @@ -239,6 +287,18 @@ impl<'a> From<&'a Expression<'a>> for Vertex<'a> { Expression::NumberLiteral(number_literal) => { Vertex::NumberLiteral(NumberLiteralVertex { ast_node: None, number_literal }.into()) } + Expression::MemberExpression(member_expr) + if matches!(**member_expr, MemberExpression::StaticMemberExpression(_)) => + { + match &**member_expr { + MemberExpression::StaticMemberExpression(static_member_expr) => { + Vertex::DotProperty( + DotPropertyVertex { ast_node: None, static_member_expr }.into(), + ) + } + _ => unreachable!("we should only ever have StaticMemberExpression"), + } + } _ => Vertex::Expression(expr), } } @@ -347,6 +407,13 @@ impl<'a> Typename for JSXElementVertex<'a> { } } +#[non_exhaustive] +#[derive(Debug, Clone)] +pub struct IfStatementVertex<'a> { + ast_node: Option>, + pub return_statement: &'a IfStatement<'a>, +} + #[non_exhaustive] #[derive(Debug, Clone)] pub struct ReturnStatementVertex<'a> { @@ -445,3 +512,71 @@ impl<'a> Typename for NumberLiteralVertex<'a> { } } } + +#[non_exhaustive] +#[derive(Debug, Clone)] +pub struct NameVertex<'a> { + pub ast_node: Option>, + pub name: &'a IdentifierName, +} + +impl<'a> Typename for NameVertex<'a> { + fn typename(&self) -> &'static str { + if self.ast_node.is_some() { + "NameAST" + } else { + "Name" + } + } +} + +#[non_exhaustive] +#[derive(Debug, Clone)] +pub struct ObjectEntryVertex<'a> { + pub ast_node: Option>, + pub property: &'a ObjectProperty<'a>, +} + +impl<'a> Typename for ObjectEntryVertex<'a> { + fn typename(&self) -> &'static str { + if self.ast_node.is_some() { + "ObjectEntryAST" + } else { + "ObjectEntry" + } + } +} + +#[non_exhaustive] +#[derive(Debug, Clone)] +pub struct SpreadIntoObjectVertex<'a> { + pub ast_node: Option>, + pub property: &'a SpreadElement<'a>, +} + +impl<'a> Typename for SpreadIntoObjectVertex<'a> { + fn typename(&self) -> &'static str { + if self.ast_node.is_some() { + "SpreadIntoObjectAST" + } else { + "SpreadIntoObject" + } + } +} + +#[non_exhaustive] +#[derive(Debug, Clone)] +pub struct DotPropertyVertex<'a> { + ast_node: Option>, + pub static_member_expr: &'a StaticMemberExpression<'a>, +} + +impl<'a> Typename for DotPropertyVertex<'a> { + fn typename(&self) -> &'static str { + if self.ast_node.is_some() { + "DotPropertyAST" + } else { + "DotProperty" + } + } +}