diff --git a/crates/oxc_query/src/adapter.rs b/crates/oxc_query/src/adapter.rs index 7743d2536..815958c10 100644 --- a/crates/oxc_query/src/adapter.rs +++ b/crates/oxc_query/src/adapter.rs @@ -31,7 +31,7 @@ pub const SCHEMA_TEXT: &str = include_str!("./schema.graphql"); /// If the schema parse returns an error, which will not happen unless the schema get's corrupted. pub fn schema() -> &'static Schema { // internal note: this might not parser correctly due to making an incorrect schema during development - SCHEMA.get_or_init(|| Schema::parse(SCHEMA_TEXT).expect("not a valid schema")) + SCHEMA.get_or_init(|| Schema::parse(SCHEMA_TEXT).unwrap_or_else(|e| panic!("{}", e))) } impl<'a> Adapter<'a> { @@ -116,11 +116,6 @@ impl<'a, 'b: 'a> trustfall::provider::Adapter<'a> for &'a Adapter<'b> { property_name.as_ref(), resolve_info, ), - "InterfaceExtend" => super::properties::resolve_interface_extend_property( - contexts, - property_name.as_ref(), - resolve_info, - ), "JSXAttribute" => super::properties::resolve_jsxattribute_property( contexts, property_name.as_ref(), @@ -143,11 +138,6 @@ impl<'a, 'b: 'a> trustfall::provider::Adapter<'a> for &'a Adapter<'b> { property_name.as_ref(), resolve_info, ), - "MemberExtend" => super::properties::resolve_member_extend_property( - contexts, - property_name.as_ref(), - resolve_info, - ), "NameAST" | "Name" => super::properties::resolve_name_property( contexts, property_name.as_ref(), @@ -173,12 +163,12 @@ impl<'a, 'b: 'a> trustfall::provider::Adapter<'a> for &'a Adapter<'b> { resolve_info, self, ), - "SearchParameter" => super::properties::resolve_search_parameter_property( + "ReassignmentAST" | "Reassignment" => super::properties::resolve_reassignment_property( contexts, property_name.as_ref(), resolve_info, ), - "SimpleExtend" => super::properties::resolve_simple_extend_property( + "SearchParameter" => super::properties::resolve_search_parameter_property( contexts, property_name.as_ref(), resolve_info, @@ -187,18 +177,13 @@ impl<'a, 'b: 'a> trustfall::provider::Adapter<'a> for &'a Adapter<'b> { contexts, property_name.as_ref(), resolve_info, + self, ), "SpecificImport" => super::properties::resolve_specific_import_property( contexts, property_name.as_ref(), resolve_info, ), - "Type" => super::properties::resolve_type_property( - contexts, - property_name.as_ref(), - resolve_info, - self, - ), _ => { unreachable!( "attempted to read property '{property_name}' on unexpected type: {type_name}" @@ -407,6 +392,13 @@ impl<'a, 'b: 'a> trustfall::provider::Adapter<'a> for &'a Adapter<'b> { resolve_info, self, ), + "ReassignmentAST" | "Reassignment" => super::edges::resolve_reassignment_edge( + contexts, + edge_name.as_ref(), + parameters, + resolve_info, + self, + ), "ReturnStatementAST" => super::edges::resolve_return_statement_ast_edge( contexts, edge_name.as_ref(), diff --git a/crates/oxc_query/src/edges.rs b/crates/oxc_query/src/edges.rs index 1250dc930..38e583cdb 100644 --- a/crates/oxc_query/src/edges.rs +++ b/crates/oxc_query/src/edges.rs @@ -1745,6 +1745,105 @@ mod path_part { } } +pub(super) fn resolve_reassignment_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 { + "parent" => parents(contexts, adapter), + "ancestor" => ancestors(contexts, adapter), + "span" => reassignment::span(contexts, resolve_info), + "left_as_expression" => reassignment::left_as_expression(contexts, resolve_info), + "right" => reassignment::right(contexts, resolve_info), + _ => { + unreachable!( + "attempted to resolve unexpected edge '{edge_name}' on type 'Reassignment'" + ) + } + } +} + +mod reassignment { + use std::convert::Into; + + use trustfall::provider::{ + resolve_neighbors_with, ContextIterator, ContextOutcomeIterator, ResolveEdgeInfo, + VertexIterator, + }; + + use super::super::vertex::Vertex; + + pub(super) fn left_as_expression<'a, 'b: 'a>( + contexts: ContextIterator<'a, Vertex<'b>>, + _resolve_info: &ResolveEdgeInfo, + ) -> ContextOutcomeIterator<'a, Vertex<'b>, VertexIterator<'a, Vertex<'b>>> { + resolve_neighbors_with(contexts, |v| { + match &v + .as_reassignment() + .unwrap_or_else(|| { + panic!("expected to have a reassignment vertex, instead have: {v:#?}") + }) + .assignment_expression + .left + { + oxc_ast::ast::AssignmentTarget::SimpleAssignmentTarget(target) => { + let expr = match target { + oxc_ast::ast::SimpleAssignmentTarget::AssignmentTargetIdentifier( + assignment_target, + ) => Vertex::try_from_identifier_reference(assignment_target), + oxc_ast::ast::SimpleAssignmentTarget::MemberAssignmentTarget(membexpr) => { + Vertex::try_from_member_expression(membexpr) + } + oxc_ast::ast::SimpleAssignmentTarget::TSAsExpression(it) => { + Some((&it.expression).into()) + } + oxc_ast::ast::SimpleAssignmentTarget::TSSatisfiesExpression(it) => { + Some((&it.expression).into()) + } + oxc_ast::ast::SimpleAssignmentTarget::TSNonNullExpression(it) => { + Some((&it.expression).into()) + } + oxc_ast::ast::SimpleAssignmentTarget::TSTypeAssertion(it) => { + Some((&it.expression).into()) + } + }; + Box::new(expr.into_iter().map(Into::into)) + } + oxc_ast::ast::AssignmentTarget::AssignmentTargetPattern(_) => { + Box::new(std::iter::empty()) + } + } + }) + } + + pub(super) fn right<'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_reassignment() + .unwrap_or_else(|| { + panic!("expected to have a reassignment vertex, instead have: {v:#?}") + }) + .assignment_expression + .right) + .into(), + )) + }) + } + + 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_return_statement_ast_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 58efc1cb8..adce89007 100644 --- a/crates/oxc_query/src/properties.rs +++ b/crates/oxc_query/src/properties.rs @@ -1,7 +1,6 @@ use std::convert::Into; -use oxc_ast::ast::{BindingPatternKind, Expression, MemberExpression}; -use oxc_span::GetSpan; +use oxc_ast::ast::{BindingPatternKind, Expression}; use trustfall::{ provider::{ field_property, resolve_property_with, ContextIterator, ContextOutcomeIterator, ResolveInfo, @@ -15,49 +14,9 @@ use crate::{ accessibility_to_string, jsx_attribute_name_to_string, jsx_attribute_to_constant_string, jsx_element_name_to_string, }, - vertex::InterfaceExtendVertex, Adapter, }; -fn interface_extend_implem<'a, 'b: 'a>( - contexts: ContextIterator<'a, Vertex<'b>>, -) -> ContextOutcomeIterator<'a, Vertex<'b>, FieldValue> { - resolve_property_with(contexts, |v| { - match v - .as_interface_extend() - .unwrap_or_else(|| { - panic!("expected to have an interfaceextend vertex, instead have: {v:#?}") - }) - .as_ref() - { - InterfaceExtendVertex::Identifier(ident) => ident.name.to_string(), - InterfaceExtendVertex::MemberExpression(first_membexpr) => { - let MemberExpression::StaticMemberExpression(static_membexpr) = first_membexpr - else { - unreachable!("TS:2499") - }; - let mut parts = vec![static_membexpr.property.name.to_string()]; - let mut membexpr = first_membexpr.object(); - while let Expression::MemberExpression(expr) = membexpr { - let MemberExpression::StaticMemberExpression(static_membexpr) = &expr.0 else { - unreachable!("TS:2499") - }; - parts.push(static_membexpr.property.name.to_string()); - membexpr = expr.object(); - } - - let Expression::Identifier(ident) = membexpr else { unreachable!("TS:2499") }; - parts.push(ident.name.to_string()); - - parts.reverse(); - - parts.join(".") - } - } - .into() - }) -} - pub(super) fn resolve_assignment_type_property<'a, 'b: 'a>( contexts: ContextIterator<'a, Vertex<'b>>, property_name: &str, @@ -270,21 +229,6 @@ pub(super) fn resolve_interface_property<'a, 'b: 'a>( } } -pub(super) fn resolve_interface_extend_property<'a, 'b: 'a>( - contexts: ContextIterator<'a, Vertex<'b>>, - property_name: &str, - _resolve_info: &ResolveInfo, -) -> ContextOutcomeIterator<'a, Vertex<'b>, FieldValue> { - match property_name { - "str" => interface_extend_implem(contexts), - _ => { - unreachable!( - "attempted to read unexpected property '{property_name}' on type 'InterfaceExtend'" - ) - } - } -} - pub(super) fn resolve_jsxattribute_property<'a, 'b: 'a>( contexts: ContextIterator<'a, Vertex<'b>>, property_name: &str, @@ -386,21 +330,6 @@ pub(super) fn resolve_jsxtext_property<'a, 'b: 'a>( } } -pub(super) fn resolve_member_extend_property<'a, 'b: 'a>( - contexts: ContextIterator<'a, Vertex<'b>>, - property_name: &str, - _resolve_info: &ResolveInfo, -) -> ContextOutcomeIterator<'a, Vertex<'b>, FieldValue> { - match property_name { - "str" => interface_extend_implem(contexts), - _ => { - unreachable!( - "attempted to read unexpected property '{property_name}' on type 'MemberExtend'" - ) - } - } -} - pub(super) fn resolve_name_property<'a, 'b: 'a>( contexts: ContextIterator<'a, Vertex<'b>>, property_name: &str, @@ -507,6 +436,23 @@ pub(super) fn resolve_path_part_property<'a, 'b: 'a>( } } +pub(super) fn resolve_reassignment_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 'Reassignment'" + ) + } + } +} + pub(super) fn resolve_search_parameter_property<'a, 'b: 'a>( contexts: ContextIterator<'a, Vertex<'b>>, property_name: &str, @@ -539,27 +485,19 @@ pub(super) fn resolve_search_parameter_property<'a, 'b: 'a>( } } -pub(super) fn resolve_simple_extend_property<'a, 'b: 'a>( - contexts: ContextIterator<'a, Vertex<'b>>, - property_name: &str, - _resolve_info: &ResolveInfo, -) -> ContextOutcomeIterator<'a, Vertex<'b>, FieldValue> { - match property_name { - "str" => interface_extend_implem(contexts), - _ => { - unreachable!( - "attempted to read unexpected property '{property_name}' on type 'SimpleExtend'" - ) - } - } -} - pub(super) fn resolve_span_property<'a, 'b: 'a>( contexts: ContextIterator<'a, Vertex<'b>>, property_name: &str, _resolve_info: &ResolveInfo, + adapter: &'a Adapter<'b>, ) -> ContextOutcomeIterator<'a, Vertex<'b>, FieldValue> { match property_name { + "str" => resolve_property_with(contexts, |v| { + let span = v + .as_span() + .unwrap_or_else(|| panic!("expected to have a span vertex, instead have: {v:#?}")); + adapter.semantic.source_text()[span.start as usize..span.end as usize].into() + }), "end" => resolve_property_with(contexts, |v| { v.as_span() .unwrap_or_else(|| panic!("expected to have a span vertex, instead have: {v:#?}")) @@ -611,23 +549,3 @@ pub(super) fn resolve_specific_import_property<'a, 'b: 'a>( } } } - -pub(super) fn resolve_type_property<'a, 'b: 'a>( - contexts: ContextIterator<'a, Vertex<'b>>, - property_name: &str, - _resolve_info: &ResolveInfo, - adapter: &'a Adapter<'b>, -) -> ContextOutcomeIterator<'a, Vertex<'b>, FieldValue> { - match property_name { - "str" => resolve_property_with(contexts, |v| { - let span = v - .as_type() - .unwrap_or_else(|| panic!("expected to have a type vertex, instead have: {v:#?}")) - .span(); - adapter.semantic.source_text()[span.start as usize..span.end as usize].into() - }), - _ => { - unreachable!("attempted to read unexpected property '{property_name}' on type 'Type'") - } - } -} diff --git a/crates/oxc_query/src/schema.graphql b/crates/oxc_query/src/schema.graphql index b9c83d5fd..7687f689f 100644 --- a/crates/oxc_query/src/schema.graphql +++ b/crates/oxc_query/src/schema.graphql @@ -179,7 +179,6 @@ interface A extends B.C {} B.C is always more than one identifier. """ type MemberExtend implements InterfaceExtend { - str: String! span: Span! } @@ -188,15 +187,10 @@ interface A extends B {} B is always just one identifier. """ type SimpleExtend implements InterfaceExtend { - str: String! span: Span! } interface InterfaceExtend { - """ - Null if not decidabled such as - """ - str: String span: Span! } @@ -248,7 +242,6 @@ interface Class { } interface Type { - str: String! span: Span! } @@ -260,6 +253,7 @@ interface TypeAnnotation implements HasSpan { type Span { start: Int! end: Int! + str: String! } type JSXSpreadChild { @@ -392,6 +386,43 @@ type ObjectLiteralAST implements ObjectLiteral & Expression & HasSpan & ASTNode span: Span! } +""" +Any assignment that isn't a variable declaration, which means no const/let/var. +ie: "a = 1;", "a /= 1;", "a += 1;", etc. +""" +interface Reassignment implements HasSpan & Expression { + """ + Only nonnull if the left is not an object or array destructuring. + examples that this would be usable: "a = 12;", "a.b = 'apple';" + """ + left_as_expression: Expression + right: Expression! + # Expression + as_constant_string: String + # 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. +""" +type ReassignmentAST implements HasSpan & ASTNode & Reassignment & Expression { + """ + Only nonnull if the left is not an object or array destructuring. + examples that this would be usable: "a = 12;", "a.b = 'apple';" + """ + left_as_expression: Expression + right: Expression! + # Expression + as_constant_string: String + # HasSpan + span: Span! + # ASTNode + parent: ASTNode + ancestor: [ASTNode!]! +} + interface Name implements HasSpan { name: String! # HasSpan diff --git a/crates/oxc_query/src/tests.rs b/crates/oxc_query/src/tests.rs index d2642ecdb..3e689f6c7 100644 --- a/crates/oxc_query/src/tests.rs +++ b/crates/oxc_query/src/tests.rs @@ -164,7 +164,9 @@ fn test_parent_query() { ast_node { ... on TypeAnnotationAST { type { - type_: str @output + span { + type_: str @output + } } parent { tn1: __typename @output diff --git a/crates/oxc_query/src/vertex.rs b/crates/oxc_query/src/vertex.rs index 72599b376..d9b67a4f0 100644 --- a/crates/oxc_query/src/vertex.rs +++ b/crates/oxc_query/src/vertex.rs @@ -3,13 +3,13 @@ use std::rc::Rc; use enum_as_inner::EnumAsInner; use oxc_ast::{ ast::{ - 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, + 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, + TSInterfaceDeclaration, TSType, TSTypeAnnotation, VariableDeclarator, }, AstKind, }; @@ -58,6 +58,7 @@ pub enum Vertex<'a> { SpreadIntoObject(Rc>), ObjectEntry(Rc>), DotProperty(Rc>), + Reassignment(Rc>), } impl<'a> Vertex<'a> { @@ -95,6 +96,7 @@ impl<'a> Vertex<'a> { Self::ReturnStatementAST(data) => data.return_statement.span, Self::IfStatementAST(data) => data.return_statement.span, Self::NumberLiteral(data) => data.number_literal.span, + Self::Reassignment(data) => data.assignment_expression.span, Self::Name(data) => data.name.span, Self::File | Self::Url(_) @@ -124,6 +126,7 @@ impl<'a> Vertex<'a> { 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::Reassignment(data) => data.ast_node.map(|x| x.id()), Vertex::DefaultImport(_) | Vertex::AssignmentType(_) | Vertex::ClassMethod(_) @@ -160,6 +163,25 @@ impl<'a> Vertex<'a> { _ => None, } } + + // todo: remove `Option` when the match covers all the cases + pub fn try_from_member_expression(member_expression: &'a MemberExpression<'a>) -> Option { + match &member_expression { + MemberExpression::StaticMemberExpression(static_member_expr) => { + Some(Vertex::DotProperty( + DotPropertyVertex { ast_node: None, static_member_expr }.into(), + )) + } + _ => None, + } + } + + // todo: remove `Option` when this doesn't return none + pub fn try_from_identifier_reference( + _identifier_reference: &'a IdentifierReference, + ) -> Option { + None + } } impl Typename for Vertex<'_> { @@ -203,6 +225,7 @@ impl Typename for Vertex<'_> { Vertex::IfStatementAST(_) => "IfStatementAST", Vertex::SpreadIntoObject(obj) => obj.typename(), Vertex::ObjectEntry(entry) => entry.typename(), + Vertex::Reassignment(reassignment) => reassignment.typename(), } } } @@ -267,6 +290,9 @@ impl<'a> From> for Vertex<'a> { _ => unreachable!("we should only ever have StaticMemberExpression"), } } + AstKind::AssignmentExpression(assignment_expression) => Vertex::Reassignment( + ReassignmentVertex { ast_node: Some(ast_node), assignment_expression }.into(), + ), _ => Vertex::ASTNode(ast_node), } } @@ -287,18 +313,14 @@ 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"), - } + Expression::MemberExpression(me) => { + let vertex = Vertex::try_from_member_expression(me); + + vertex.unwrap_or(Vertex::Expression(expr)) } + Expression::AssignmentExpression(assignment_expression) => Vertex::Reassignment( + ReassignmentVertex { ast_node: None, assignment_expression }.into(), + ), _ => Vertex::Expression(expr), } } @@ -580,3 +602,20 @@ impl<'a> Typename for DotPropertyVertex<'a> { } } } + +#[non_exhaustive] +#[derive(Debug, Clone)] +pub struct ReassignmentVertex<'a> { + ast_node: Option>, + pub assignment_expression: &'a AssignmentExpression<'a>, +} + +impl<'a> Typename for ReassignmentVertex<'a> { + fn typename(&self) -> &'static str { + if self.ast_node.is_some() { + "ReassignmentAST" + } else { + "Reassignment" + } + } +}