From 9a5bb008e7c780bcadfc8f9727cbf564bc3ff757 Mon Sep 17 00:00:00 2001 From: u9g Date: Sun, 26 Nov 2023 23:22:41 -0500 Subject: [PATCH] feat(query): Add is_getter, is_setter, is_constructor to all Function implementors (#1526) ---
Generated summary (powered by Graphite) > # TL;DR > This pull request adds new properties to the `ArrowFunction`, `FnDeclaration`, and `Function` interfaces in the Trustfall schema. It also adds implementation for resolving these properties in the `adapter.rs` and `properties.rs` files. Additionally, it adds a new method `function_scope_flag` to the `Vertex` struct in the `vertex.rs` file. > > # What changed > - Added new properties (`is_getter`, `is_setter`, `is_constructor`) to the `ArrowFunction`, `FnDeclaration`, and `Function` interfaces in the Trustfall schema. > - Implemented the resolution of these properties in the `resolve_arrow_function_property`, `resolve_fn_declaration_property`, and `resolve_function_property` functions in the `properties.rs` file. > - Added a new method `function_scope_flag` to the `Vertex` struct in the `vertex.rs` file. > > # How to test > 1. Run the test suite to ensure that all existing tests pass. > 2. Add new tests to cover the newly added properties and the `function_scope_flag` method. > 3. Run the test suite again and ensure that all tests pass. > > # Why make this change > - The new properties (`is_getter`, `is_setter`, `is_constructor`) provide additional information about functions in the Trustfall schema, allowing clients to query for these properties. > - The `function_scope_flag` method in the `Vertex` struct provides a convenient way to access the scope flags of a function node, which can be useful for various analysis and processing tasks.
--- crates/oxc_query/src/adapter.rs | 3 +++ crates/oxc_query/src/properties.rs | 31 +++++++++++++++++++++++++++++ crates/oxc_query/src/schema.graphql | 17 ++++++++++++++++ crates/oxc_query/src/vertex.rs | 31 ++++++++++++++++++++++++++++- 4 files changed, 81 insertions(+), 1 deletion(-) diff --git a/crates/oxc_query/src/adapter.rs b/crates/oxc_query/src/adapter.rs index eb43cad18..7b00be6fb 100644 --- a/crates/oxc_query/src/adapter.rs +++ b/crates/oxc_query/src/adapter.rs @@ -86,6 +86,7 @@ impl<'a, 'b: 'a> trustfall::provider::Adapter<'a> for &'a Adapter<'b> { contexts, property_name.as_ref(), resolve_info, + self, ) } "AssignmentType" => super::properties::resolve_assignment_type_property( @@ -128,6 +129,7 @@ impl<'a, 'b: 'a> trustfall::provider::Adapter<'a> for &'a Adapter<'b> { contexts, property_name.as_ref(), resolve_info, + self, ) } "FnCallAST" | "FnCall" => super::properties::resolve_fn_call_property( @@ -139,6 +141,7 @@ impl<'a, 'b: 'a> trustfall::provider::Adapter<'a> for &'a Adapter<'b> { contexts, property_name.as_ref(), resolve_info, + self, ), "ImportAST" | "Import" => super::properties::resolve_import_property( contexts, diff --git a/crates/oxc_query/src/properties.rs b/crates/oxc_query/src/properties.rs index 0b833b96d..20b8f113d 100644 --- a/crates/oxc_query/src/properties.rs +++ b/crates/oxc_query/src/properties.rs @@ -1,6 +1,7 @@ use std::convert::Into; use oxc_ast::ast::{BindingPatternKind, Expression}; +use oxc_semantic::ScopeFlags; use trustfall::{ provider::{ field_property, resolve_property_with, ContextIterator, ContextOutcomeIterator, ResolveInfo, @@ -73,6 +74,7 @@ pub(super) fn resolve_arrow_function_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 { "is_async" => resolve_property_with(contexts, |v| v.function_is_async().into()), @@ -80,6 +82,15 @@ pub(super) fn resolve_arrow_function_property<'a, 'b: 'a>( "as_constant_string" => resolve_property_with(contexts, |v| { v.as_constant_string().map_or(FieldValue::Null, Into::into) }), + "is_getter" => resolve_property_with(contexts, |v| { + v.function_scope_flag(adapter).contains(ScopeFlags::GetAccessor).into() + }), + "is_setter" => resolve_property_with(contexts, |v| { + v.function_scope_flag(adapter).contains(ScopeFlags::SetAccessor).into() + }), + "is_constructor" => resolve_property_with(contexts, |v| { + v.function_scope_flag(adapter).contains(ScopeFlags::Constructor).into() + }), _ => { unreachable!( "attempted to read unexpected property '{property_name}' on type 'ArrowFunction'" @@ -264,6 +275,7 @@ pub(super) fn resolve_fn_declaration_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 { "name" => resolve_property_with(contexts, |v| { @@ -281,6 +293,15 @@ pub(super) fn resolve_fn_declaration_property<'a, 'b: 'a>( "as_constant_string" => resolve_property_with(contexts, |v| { v.as_constant_string().map_or(FieldValue::Null, Into::into) }), + "is_getter" => resolve_property_with(contexts, |v| { + v.function_scope_flag(adapter).contains(ScopeFlags::GetAccessor).into() + }), + "is_setter" => resolve_property_with(contexts, |v| { + v.function_scope_flag(adapter).contains(ScopeFlags::SetAccessor).into() + }), + "is_constructor" => resolve_property_with(contexts, |v| { + v.function_scope_flag(adapter).contains(ScopeFlags::Constructor).into() + }), _ => { unreachable!( "attempted to read unexpected property '{property_name}' on type 'FnDeclaration'" @@ -308,6 +329,7 @@ pub(super) fn resolve_function_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 { "is_async" => resolve_property_with(contexts, |v| v.function_is_async().into()), @@ -315,6 +337,15 @@ pub(super) fn resolve_function_property<'a, 'b: 'a>( "as_constant_string" => resolve_property_with(contexts, |v| { v.as_constant_string().map_or(FieldValue::Null, Into::into) }), + "is_getter" => resolve_property_with(contexts, |v| { + v.function_scope_flag(adapter).contains(ScopeFlags::GetAccessor).into() + }), + "is_setter" => resolve_property_with(contexts, |v| { + v.function_scope_flag(adapter).contains(ScopeFlags::SetAccessor).into() + }), + "is_constructor" => resolve_property_with(contexts, |v| { + v.function_scope_flag(adapter).contains(ScopeFlags::Constructor).into() + }), _ => { unreachable!( "attempted to read unexpected property '{property_name}' on type 'Function'" diff --git a/crates/oxc_query/src/schema.graphql b/crates/oxc_query/src/schema.graphql index ca72b47bb..ec3e0a149 100644 --- a/crates/oxc_query/src/schema.graphql +++ b/crates/oxc_query/src/schema.graphql @@ -661,6 +661,11 @@ type FunctionBodyAST implements HasSpan & ASTNode & FunctionBody { interface Function implements Expression & HasSpan { is_async: Boolean! is_generator: Boolean! + + is_getter: Boolean! + is_setter: Boolean! + is_constructor: Boolean! + """ Does not include rest parameter if it exists. """ @@ -832,6 +837,9 @@ interface ArrowFunction implements Function & HasSpan & Expression { # Function is_async: Boolean! is_generator: Boolean! + is_getter: Boolean! + is_setter: Boolean! + is_constructor: Boolean! """ Does not include rest parameter if it exists. """ @@ -860,6 +868,9 @@ type ArrowFunctionAST implements Function & HasSpan & ArrowFunction & ASTNode & # Function is_async: Boolean! is_generator: Boolean! + is_getter: Boolean! + is_setter: Boolean! + is_constructor: Boolean! """ Does not include rest parameter if it exists. """ @@ -900,6 +911,9 @@ interface FnDeclaration implements Function & HasSpan & Expression { # Function is_async: Boolean! is_generator: Boolean! + is_getter: Boolean! + is_setter: Boolean! + is_constructor: Boolean! """ Does not include rest parameter if it exists. """ @@ -933,6 +947,9 @@ type FnDeclarationAST implements Function & ASTNode & FnDeclaration & HasSpan & # Function is_async: Boolean! is_generator: Boolean! + is_getter: Boolean! + is_setter: Boolean! + is_constructor: Boolean! """ Does not include rest parameter if it exists. """ diff --git a/crates/oxc_query/src/vertex.rs b/crates/oxc_query/src/vertex.rs index 5bbaad092..e819276dc 100644 --- a/crates/oxc_query/src/vertex.rs +++ b/crates/oxc_query/src/vertex.rs @@ -4,7 +4,7 @@ use enum_as_inner::EnumAsInner; use oxc_ast::ast::ArrayExpressionElement; #[allow(clippy::wildcard_imports)] use oxc_ast::{ast::*, AstKind}; -use oxc_semantic::{AstNode, AstNodeId}; +use oxc_semantic::{AstNode, AstNodeId, ScopeFlags}; use oxc_span::{GetSpan, Span}; use trustfall::provider::{Typename, VertexIterator}; use url::Url; @@ -278,6 +278,35 @@ impl<'a> Vertex<'a> { } } + pub fn function_scope_flag(&self, adapter: &Adapter<'_>) -> ScopeFlags { + let (wanted_node_hash, wanted_node_span) = match &self { + Vertex::ArrowFunction(data) => (calculate_hash(data.arrow_expression), data.arrow_expression.span), + Vertex::FnDeclaration(data) => (calculate_hash(data.function), data.function.span), + _ => unreachable!( + "'function_scope_flag' function should only ever be called with an ArrowFunction or FnDeclaration" + ), + }; + + let found = adapter.semantic.nodes().iter().find(|x| { + let span = x.kind().span(); + + if span.start != wanted_node_span.start || span.end != wanted_node_span.end { + return false; + } + + let hash_of_node = match x.kind() { + AstKind::ArrowExpression(ae) => calculate_hash(ae), + AstKind::Function(fn_) => calculate_hash(fn_), + _ => return false, + }; + hash_of_node == wanted_node_hash + }); + + adapter.semantic.scopes().get_flags( + found.expect("an arrowexpression or function should always be in the ast if we can have the struct of it").scope_id(), + ) + } + pub fn function_parameter(&self) -> VertexIterator<'a, Vertex<'a>> { let parameter = match &self { Vertex::ArrowFunction(data) => &data.arrow_expression.params.items,