diff --git a/crates/oxc_query/src/adapter.rs b/crates/oxc_query/src/adapter.rs index 815958c10..9cf080d1d 100644 --- a/crates/oxc_query/src/adapter.rs +++ b/crates/oxc_query/src/adapter.rs @@ -106,6 +106,11 @@ impl<'a, 'b: 'a> trustfall::provider::Adapter<'a> for &'a Adapter<'b> { property_name.as_ref(), resolve_info, ), + "FnCallAST" | "FnCall" => super::properties::resolve_fn_call_property( + contexts, + property_name.as_ref(), + resolve_info, + ), "ImportAST" | "Import" => super::properties::resolve_import_property( contexts, property_name.as_ref(), @@ -209,6 +214,12 @@ impl<'a, 'b: 'a> trustfall::provider::Adapter<'a> for &'a Adapter<'b> { resolve_info, self, ), + "Argument" => super::edges::resolve_argument_edge( + contexts, + edge_name.as_ref(), + parameters, + resolve_info, + ), "AssignmentType" => super::edges::resolve_assignment_type_edge( contexts, edge_name.as_ref(), @@ -260,6 +271,13 @@ impl<'a, 'b: 'a> trustfall::provider::Adapter<'a> for &'a Adapter<'b> { resolve_info, self, ), + "FnCallAST" | "FnCall" => super::edges::resolve_fn_call_edge( + contexts, + edge_name.as_ref(), + parameters, + resolve_info, + self, + ), "HasSpan" => super::edges::resolve_has_span_edge( contexts, edge_name.as_ref(), diff --git a/crates/oxc_query/src/edges.rs b/crates/oxc_query/src/edges.rs index 38e583cdb..89b8ed9be 100644 --- a/crates/oxc_query/src/edges.rs +++ b/crates/oxc_query/src/edges.rs @@ -5,6 +5,35 @@ use trustfall::provider::{ use super::vertex::Vertex; use crate::Adapter; +pub(super) fn resolve_argument_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" => argument::span(contexts, resolve_info), + _ => { + unreachable!("attempted to resolve unexpected edge '{edge_name}' on type 'Argument'") + } + } +} + +mod argument { + use trustfall::provider::{ + 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 resolve_astnode_edge<'a, 'b: 'a>( contexts: ContextIterator<'a, Vertex<'b>>, edge_name: &str, @@ -640,6 +669,83 @@ mod file { } } +pub(super) fn resolve_fn_call_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" => fn_call::span(contexts, resolve_info), + "ancestor" => ancestors(contexts, adapter), + "parent" => parents(contexts, adapter), + "callee" => fn_call::callee(contexts, resolve_info), + "arguments" => fn_call::arguments(contexts, resolve_info), + _ => { + unreachable!("attempted to resolve unexpected edge '{edge_name}' on type 'FnCall'") + } + } +} + +mod fn_call { + use oxc_ast::ast::Argument; + use oxc_span::GetSpan; + use trustfall::provider::{ + resolve_neighbors_with, ContextIterator, ContextOutcomeIterator, ResolveEdgeInfo, + VertexIterator, + }; + + use super::super::vertex::Vertex; + + pub(super) fn callee<'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_fn_call() + .unwrap_or_else(|| { + panic!("expected to have a fncall vertex, instead have: {v:#?}") + }) + .call_expression + .callee) + .into(), + )) + }) + } + + pub(super) fn arguments<'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( + v.as_fn_call() + .unwrap_or_else(|| { + panic!("expected to have a fncall vertex, instead have: {v:#?}") + }) + .call_expression + .arguments + .iter() + .map(|x| { + Vertex::Argument(match x { + Argument::SpreadElement(spread) => spread.span, + Argument::Expression(expr) => expr.span(), + }) + }), + ) + }) + } + + 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 resolve_has_span_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 adce89007..9b7210ef4 100644 --- a/crates/oxc_query/src/properties.rs +++ b/crates/oxc_query/src/properties.rs @@ -189,6 +189,21 @@ pub(super) fn resolve_expression_property<'a, 'b: 'a>( } } +pub(super) fn resolve_fn_call_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 'FnCall'") + } + } +} + pub(super) fn resolve_import_property<'a, 'b: 'a>( contexts: ContextIterator<'a, Vertex<'b>>, property_name: &str, diff --git a/crates/oxc_query/src/schema.graphql b/crates/oxc_query/src/schema.graphql index 7687f689f..c571b7e9c 100644 --- a/crates/oxc_query/src/schema.graphql +++ b/crates/oxc_query/src/schema.graphql @@ -386,6 +386,33 @@ type ObjectLiteralAST implements ObjectLiteral & Expression & HasSpan & ASTNode span: Span! } +interface Argument { + span: Span! +} + +interface FnCall implements Expression & HasSpan { + callee: Expression! + arguments: [Argument!]! + + # Expression + as_constant_string: String + # HasSpan + span: Span! +} + +type FnCallAST implements FnCall & Expression & HasSpan & ASTNode { + callee: Expression! + arguments: [Argument!]! + + # Expression + as_constant_string: String + # ASTNode + parent: ASTNode + ancestor: [ASTNode!]! + # HasSpan + span: Span! +} + """ Any assignment that isn't a variable declaration, which means no const/let/var. ie: "a = 1;", "a /= 1;", "a += 1;", etc. diff --git a/crates/oxc_query/src/vertex.rs b/crates/oxc_query/src/vertex.rs index d9b67a4f0..b15678f71 100644 --- a/crates/oxc_query/src/vertex.rs +++ b/crates/oxc_query/src/vertex.rs @@ -3,12 +3,12 @@ use std::rc::Rc; use enum_as_inner::EnumAsInner; use oxc_ast::{ ast::{ - AssignmentExpression, 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, + AssignmentExpression, BindingPatternKind, CallExpression, 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, @@ -59,6 +59,8 @@ pub enum Vertex<'a> { ObjectEntry(Rc>), DotProperty(Rc>), Reassignment(Rc>), + FnCall(Rc>), + Argument(Span), } impl<'a> Vertex<'a> { @@ -98,6 +100,8 @@ impl<'a> Vertex<'a> { Self::NumberLiteral(data) => data.number_literal.span, Self::Reassignment(data) => data.assignment_expression.span, Self::Name(data) => data.name.span, + Self::FnCall(data) => data.call_expression.span, + Self::Argument(data) => *data, Self::File | Self::Url(_) | Self::PathPart(_) @@ -127,6 +131,7 @@ impl<'a> Vertex<'a> { Vertex::ObjectEntry(data) => data.ast_node.map(|x| x.id()), Vertex::DotProperty(data) => data.ast_node.map(|x| x.id()), Vertex::Reassignment(data) => data.ast_node.map(|x| x.id()), + Vertex::FnCall(data) => data.ast_node.map(|x| x.id()), Vertex::DefaultImport(_) | Vertex::AssignmentType(_) | Vertex::ClassMethod(_) @@ -145,6 +150,7 @@ impl<'a> Vertex<'a> { | Vertex::SpecificImport(_) | Vertex::Span(_) | Vertex::SearchParameter(_) + | Vertex::Argument(_) | Vertex::ClassProperty(_) => None, } } @@ -225,7 +231,9 @@ impl Typename for Vertex<'_> { Vertex::IfStatementAST(_) => "IfStatementAST", Vertex::SpreadIntoObject(obj) => obj.typename(), Vertex::ObjectEntry(entry) => entry.typename(), + Vertex::FnCall(fncall) => fncall.typename(), Vertex::Reassignment(reassignment) => reassignment.typename(), + Vertex::Argument(_) => "Argument", } } } @@ -293,6 +301,9 @@ impl<'a> From> for Vertex<'a> { AstKind::AssignmentExpression(assignment_expression) => Vertex::Reassignment( ReassignmentVertex { ast_node: Some(ast_node), assignment_expression }.into(), ), + AstKind::CallExpression(call_expression) => { + Vertex::FnCall(FnCallVertex { ast_node: Some(ast_node), call_expression }.into()) + } _ => Vertex::ASTNode(ast_node), } } @@ -321,6 +332,9 @@ impl<'a> From<&'a Expression<'a>> for Vertex<'a> { Expression::AssignmentExpression(assignment_expression) => Vertex::Reassignment( ReassignmentVertex { ast_node: None, assignment_expression }.into(), ), + Expression::CallExpression(call_expression) => { + Vertex::FnCall(FnCallVertex { ast_node: None, call_expression }.into()) + } _ => Vertex::Expression(expr), } } @@ -619,3 +633,20 @@ impl<'a> Typename for ReassignmentVertex<'a> { } } } + +#[non_exhaustive] +#[derive(Debug, Clone)] +pub struct FnCallVertex<'a> { + ast_node: Option>, + pub call_expression: &'a CallExpression<'a>, +} + +impl<'a> Typename for FnCallVertex<'a> { + fn typename(&self) -> &'static str { + if self.ast_node.is_some() { + "FnCallAST" + } else { + "FnCall" + } + } +}