Module: OffenseToCorrector::AstTools

Included in:
OffenseParser
Defined in:
lib/offense_to_corrector/ast_tools.rb

Instance Method Summary collapse

Instance Method Details

#ast_from(value) ⇒ Object

So why bother with the above then? If we end up into correctors and tree-rewrites we need that original processed source to be the basis of the AST, otherwise we get object ID mismatches.



46
47
48
49
50
51
52
53
# File 'lib/offense_to_corrector/ast_tools.rb', line 46

def ast_from(value)
  case value
  when RuboCop::ProcessedSource
    value.ast
  else
    processed_source_from(value).ast
  end
end

#atom?(value) ⇒ Boolean

Returns:

  • (Boolean)


3
4
5
# File 'lib/offense_to_corrector/ast_tools.rb', line 3

def atom?(value)
  !value.is_a?(RuboCop::AST::Node)
end

#get_children(source_node) ⇒ Object

Descendants leaves out a lot of detail potentially. There has to be a better way to deal with this, but not thinking of one right now.



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/offense_to_corrector/ast_tools.rb', line 63

def get_children(source_node)
  recurse = -> parent do
    collected_children = []
    parent.children.each do |child|
      next if child.nil?

      # If it's a regular parent carry on as you were
      unless atom?(child) # || childless?(child)
        collected_children.concat([child, *recurse[child]])
        next
      end

      # Otherwise we want to find where that parent is in the source code,
      # and the range it exists in, to create an `AtomNode`
      source = child.to_s

      parent_begin = parent.location.expression.to_range.begin
      range_begin  = source_node.source.index(source, parent_begin)
      range_end    = range_begin + source.size
      range        = range_begin..range_end

      collected_children << AtomNode.new(parent:, source:, range:)
    end

    collected_children
  end

  [source_node, *recurse[source_node]]
end

#get_corrector(value) ⇒ Object

Not needed quite yet, but could very potentially be used to verify how accurate generated cops are.



57
58
59
# File 'lib/offense_to_corrector/ast_tools.rb', line 57

def get_corrector(value)
  RuboCop::Cop::Corrector.new(value.buffer)
end

#processed_source_from(string) ⇒ Object

To get an AST we need the processed source of a string



39
40
41
# File 'lib/offense_to_corrector/ast_tools.rb', line 39

def processed_source_from(string)
  RuboCop::ProcessedSource.new(string, RUBY_VERSION.to_f)
end

#range_overlap?(a, b) ⇒ Boolean

See if a range overlaps another one, bidirectional

Returns:

  • (Boolean)


34
35
36
# File 'lib/offense_to_corrector/ast_tools.rb', line 34

def range_overlap?(a, b)
  a.cover?(b.begin) || b.cover?(a.begin)
end

#range_overlap_count(a, b) ⇒ Object

How much do two ranges overlap? Used to see how well a node matches with the associated underline



26
27
28
29
30
31
# File 'lib/offense_to_corrector/ast_tools.rb', line 26

def range_overlap_count(a, b)
  return [a.end, b.end].min - b.begin if a.cover?(b.begin)
  return [a.end, b.end].min - a.begin if b.cover?(a.begin)

  0
end

#string_intersection(a, b) ⇒ Object

I may have to work on getting this one later to try and narrow the band of ‘AtomNode` def childless?(node)

false # TODO

end



13
14
15
16
17
18
19
20
21
22
# File 'lib/offense_to_corrector/ast_tools.rb', line 13

def string_intersection(a, b)
  # Smaller string inside larger string
  target, search_string = a.size < b.size ? [a, b] : [b, a]

  last_char_index = target.size.downto(1).find do |i|
    search_string.include?(target[0..i])
  end or return ""

  target[0..last_char_index]
end