Skip to content

Commit

Permalink
Change to efficient ast search
Browse files Browse the repository at this point in the history
  • Loading branch information
mbj committed Jul 31, 2022
1 parent 0ea4ae0 commit 6ce6098
Show file tree
Hide file tree
Showing 37 changed files with 120 additions and 123 deletions.
8 changes: 7 additions & 1 deletion Changelog.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
# v0.11.14 2022-07-25
# v0.11.14 2022-07-31

* [#1348](https://github.com/mbj/mutant/pull/1348)

Change to improved AST matching performance. Mutant boot performance
is positively affected mostly on larger projects with larger AST node
count per file.

* [#1347](https://github.com/mbj/mutant/pull/1347/files)

Expand Down
60 changes: 31 additions & 29 deletions lib/mutant/ast.rb
Original file line number Diff line number Diff line change
@@ -1,41 +1,43 @@
# frozen_string_literal: true

module Mutant
# AST helpers
module AST

# Find last node satisfying predicate (as block)
#
# @return [Parser::AST::Node]
# if satisfying node is found
#
# @yield [Parser::AST::Node]
#
# @yieldreturn [Boolean]
# true in case node satisfies predicate
#
# @return [nil]
# otherwise
def self.find_last_path(node, &predicate)
fail ArgumentError, 'block expected' unless block_given?
path = []
walk(node, [node]) do |candidate, stack|
if predicate.call(candidate)
path = stack.dup
end
class AST
include Adamantium, Anima.new(
:node,
:comment_associations
)

class View
include Adamantium, Anima.new(:node, :path)
end

def view(symbol)
type_map.fetch(symbol, EMPTY_HASH).map do |node, path|
View.new(node: node, path: path)
end
end

private

def type_map
type_map = {}

walk_path(node) do |node, path|
path_map = type_map[node.type] ||= {}.tap(&:compare_by_identity)
path_map[node] = path
end
path

type_map
end
memoize :type_map

def self.walk(node, stack, &block)
block.call(node, stack)
def walk_path(node, stack = [node.type], &block)
block.call(node, stack.dup)
node.children.grep(::Parser::AST::Node) do |child|
stack.push(child)
walk(child, stack, &block)
stack.push(child.type)
walk_path(child, stack, &block)
stack.pop
end
end
private_class_method :walk

end # AST
end # Mutant
6 changes: 3 additions & 3 deletions lib/mutant/ast/find_metaclass_containing.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

module Mutant
module AST
class AST
# Given an AST, finds the sclass that directly(-ish) contains the provided
# node.
# This won't match arbitrarily complex structures - it only searches the
Expand All @@ -10,7 +10,7 @@ module AST
# Descending into 'begin' nodes is supported because these are generated for
# the one-line syntax class << self; def foo; end
class FindMetaclassContaining
include NodePredicates, Concord.new(:root, :target), Procto
include NodePredicates, Concord.new(:ast, :target), Procto

SCLASS_BODY_INDEX = 1

Expand All @@ -22,7 +22,7 @@ class FindMetaclassContaining
#
# @api private
def call
Structure.for(root.type).each_node(root) do |current|
Structure.for(ast.node.type).each_node(ast.node) do |current|
return current if n_sclass?(current) && metaclass_of?(current)
end

Expand Down
2 changes: 1 addition & 1 deletion lib/mutant/ast/meta.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

module Mutant
module AST
class AST
# Node meta information mixin
module Meta
end # Meta
Expand Down
2 changes: 1 addition & 1 deletion lib/mutant/ast/meta/const.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

module Mutant
module AST
class AST
# Node meta information mixin
module Meta

Expand Down
2 changes: 1 addition & 1 deletion lib/mutant/ast/meta/optarg.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

module Mutant
module AST
class AST
# Node meta information mixin
module Meta

Expand Down
2 changes: 1 addition & 1 deletion lib/mutant/ast/meta/resbody.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

module Mutant
module AST
class AST
# Node meta information mixin
module Meta

Expand Down
2 changes: 1 addition & 1 deletion lib/mutant/ast/meta/send.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

module Mutant
module AST
class AST
# Node meta information mixin
module Meta

Expand Down
2 changes: 1 addition & 1 deletion lib/mutant/ast/meta/symbol.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

module Mutant
module AST
class AST
# Node meta information mixin
module Meta

Expand Down
2 changes: 1 addition & 1 deletion lib/mutant/ast/named_children.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

module Mutant
module AST
class AST

# Helper methods to define named children
module NamedChildren
Expand Down
2 changes: 1 addition & 1 deletion lib/mutant/ast/node_predicates.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

module Mutant
module AST
class AST
# Module for node predicates
module NodePredicates

Expand Down
2 changes: 1 addition & 1 deletion lib/mutant/ast/nodes.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

module Mutant
module AST
class AST
# Singleton nodes
module Nodes
extend Sexp
Expand Down
2 changes: 1 addition & 1 deletion lib/mutant/ast/pattern.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

module Mutant
module AST
class AST
class Pattern
include Adamantium

Expand Down
2 changes: 1 addition & 1 deletion lib/mutant/ast/pattern/lexer.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

module Mutant
module AST
class AST
class Pattern
# rubocop:disable Metrics/ClassLength
class Lexer
Expand Down
2 changes: 1 addition & 1 deletion lib/mutant/ast/pattern/parser.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

module Mutant
module AST
class AST
# rubocop:disable Metrics/ClassLength
# rubocop:disable Metrics/MethodLength
class Pattern
Expand Down
2 changes: 1 addition & 1 deletion lib/mutant/ast/pattern/source.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

module Mutant
module AST
class AST
class Pattern
class Source
include Anima.new(:string)
Expand Down
2 changes: 1 addition & 1 deletion lib/mutant/ast/pattern/token.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

module Mutant
module AST
class AST
class Pattern
class Token
include Anima.new(:type, :value, :location)
Expand Down
2 changes: 1 addition & 1 deletion lib/mutant/ast/regexp.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

module Mutant
module AST
class AST
# Regexp source mapper
module Regexp
# Parse regex string into expression
Expand Down
2 changes: 1 addition & 1 deletion lib/mutant/ast/regexp/transformer.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

module Mutant
module AST
class AST
module Regexp
# Regexp bijective mapper
#
Expand Down
2 changes: 1 addition & 1 deletion lib/mutant/ast/regexp/transformer/direct.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

module Mutant
module AST
class AST
module Regexp
class Transformer
# Transformer for nodes which map directly to other domain
Expand Down
2 changes: 1 addition & 1 deletion lib/mutant/ast/regexp/transformer/named_group.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

module Mutant
module AST
class AST
module Regexp
class Transformer
# Transformer for named groups
Expand Down
2 changes: 1 addition & 1 deletion lib/mutant/ast/regexp/transformer/options_group.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

module Mutant
module AST
class AST
module Regexp
class Transformer
# Transformer for option groups
Expand Down
2 changes: 1 addition & 1 deletion lib/mutant/ast/regexp/transformer/quantifier.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

module Mutant
module AST
class AST
module Regexp
class Transformer
# Transformer for regexp quantifiers
Expand Down
2 changes: 1 addition & 1 deletion lib/mutant/ast/regexp/transformer/recursive.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

module Mutant
module AST
class AST
module Regexp
class Transformer
# Transformer for nodes with children
Expand Down
2 changes: 1 addition & 1 deletion lib/mutant/ast/regexp/transformer/root.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

module Mutant
module AST
class AST
module Regexp
class Transformer
# Transformer for root nodes
Expand Down
2 changes: 1 addition & 1 deletion lib/mutant/ast/regexp/transformer/text.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

module Mutant
module AST
class AST
module Regexp
class Transformer
# Regexp AST transformer for nodes that encode a text value
Expand Down
2 changes: 1 addition & 1 deletion lib/mutant/ast/sexp.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

module Mutant
module AST
class AST
# Mixin for node sexp syntax
module Sexp

Expand Down
2 changes: 1 addition & 1 deletion lib/mutant/ast/structure.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

module Mutant
module AST
class AST
# AST Structure metadata
# rubocop:disable Metrics/ModuleLength
module Structure
Expand Down
2 changes: 1 addition & 1 deletion lib/mutant/ast/types.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

module Mutant
module AST
class AST
# Groups of node types
module Types # rubocop:disable Metrics/ModuleLength
ASSIGNABLE_VARIABLES = Set.new(%i[ivasgn lvasgn cvasgn gvasgn]).freeze
Expand Down
17 changes: 9 additions & 8 deletions lib/mutant/matcher/method.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,9 @@ def call
def skip?
location = source_location

file = location&.first

if location.nil? || !file.end_with?('.rb')
if location.nil? || !location.first.end_with?('.rb')
env.warn(SOURCE_LOCATION_WARNING_FORMAT % target_method)
elsif matched_node_path.any?(&method(:n_block?))
elsif matched_view&.path&.any?(&:block.public_method(:equal?))
env.warn(CLOSURE_WARNING_FORMAT % target_method)
end
end
Expand Down Expand Up @@ -96,7 +94,7 @@ def sorbet_signature
end

def subject
node = matched_node_path.last || return
node = matched_view&.node || return

self.class::SUBJECT_CLASS.new(
config: subject_config(node),
Expand All @@ -113,10 +111,13 @@ def subject_config(node)
)
end

def matched_node_path
AST.find_last_path(ast.node, &method(:match?))
def matched_view
ast
.view(self.class::MATCH_NODE_TYPE)
.select { |view| match?(view.node) }
.last
end
memoize :matched_node_path
memoize :matched_view

def visibility
# This can be cleaned up once we are on >ruby-3.0
Expand Down
5 changes: 3 additions & 2 deletions lib/mutant/matcher/method/instance.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@ def self.memoized_method?(scope, method_name)

# Instance method specific evaluator
class Evaluator < Evaluator
SUBJECT_CLASS = Subject::Method::Instance
NAME_INDEX = 0
MATCH_NODE_TYPE = :def
NAME_INDEX = 0
SUBJECT_CLASS = Subject::Method::Instance

private

Expand Down
Loading

0 comments on commit 6ce6098

Please sign in to comment.