Class: Crspec::Transpiler::Rewriter

Inherits:
Prism::Visitor
  • Object
show all
Defined in:
lib/crspec/transpiler/rewriter.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(source_code) ⇒ Rewriter

Returns a new instance of Rewriter.



16
17
18
19
20
21
22
# File 'lib/crspec/transpiler/rewriter.rb', line 16

def initialize(source_code)
  super()
  @source_code = source_code
  @replacements = []
  @lints = []
  @inside_example_depth = 0
end

Instance Attribute Details

#lintsObject (readonly)

Returns the value of attribute lints.



14
15
16
# File 'lib/crspec/transpiler/rewriter.rb', line 14

def lints
  @lints
end

#transformed_codeObject (readonly)

Returns the value of attribute transformed_code.



14
15
16
# File 'lib/crspec/transpiler/rewriter.rb', line 14

def transformed_code
  @transformed_code
end

#warningsObject (readonly)

Backwards-compatible string warnings.



25
26
27
# File 'lib/crspec/transpiler/rewriter.rb', line 25

def warnings
  @warnings
end

Instance Method Details

#safety_scoreObject



35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/crspec/transpiler/rewriter.rb', line 35

def safety_score
  return 100 if @lints.empty?

  deductions = @lints.sum do |l|
    case l.severity
    when :error then 25
    when :warning then 10
    else 2
    end
  end
  [100 - deductions, 0].max
end

#transpileObject



29
30
31
32
33
# File 'lib/crspec/transpiler/rewriter.rb', line 29

def transpile
  result = Prism.parse(@source_code)
  result.value.accept(self)
  apply_replacements
end

#visit_call_node(node) ⇒ Object



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/crspec/transpiler/rewriter.rb', line 48

def visit_call_node(node)
  rewrite_rspec_receiver(node)
  rewrite_matcher_define(node)
  rewrite_before_all(node)
  rewrite_focus_aliases(node)
  lint_any_instance(node)
  lint_env_mutation(node)
  lint_time_mutation(node)

  example_call = %i[it specify example].include?(node.name)
  @inside_example_depth += 1 if example_call
  begin
    super
  ensure
    @inside_example_depth -= 1 if example_call
  end
end

#visit_class_variable_read_node(node) ⇒ Object



79
80
81
82
83
# File 'lib/crspec/transpiler/rewriter.rb', line 79

def visit_class_variable_read_node(node)
  add_lint(node, :warning, :class_variable,
           "class variable read (#{node.name}) implies shared mutable state")
  super
end

#visit_class_variable_write_node(node) ⇒ Object



73
74
75
76
77
# File 'lib/crspec/transpiler/rewriter.rb', line 73

def visit_class_variable_write_node(node)
  add_lint(node, :warning, :class_variable,
           "class variable mutation (#{node.name}) is shared across all threads/fibers")
  super
end

#visit_constant_read_node(node) ⇒ Object



66
67
68
69
70
71
# File 'lib/crspec/transpiler/rewriter.rb', line 66

def visit_constant_read_node(node)
  if node.name == :RSpec
    replace(node.location, "Crspec")
  end
  super
end

#visit_constant_write_node(node) ⇒ Object



85
86
87
88
89
90
91
# File 'lib/crspec/transpiler/rewriter.rb', line 85

def visit_constant_write_node(node)
  if @inside_example_depth.positive?
    add_lint(node, :warning, :constant_mutation,
             "constant assignment (#{node.name}) inside an example mutates global state")
  end
  super
end

#visit_index_and_write_node(node) ⇒ Object



98
99
100
101
# File 'lib/crspec/transpiler/rewriter.rb', line 98

def visit_index_and_write_node(node)
  lint_env_index(node)
  super
end

#visit_index_operator_write_node(node) ⇒ Object



93
94
95
96
# File 'lib/crspec/transpiler/rewriter.rb', line 93

def visit_index_operator_write_node(node)
  lint_env_index(node)
  super
end

#visit_index_or_write_node(node) ⇒ Object



103
104
105
106
# File 'lib/crspec/transpiler/rewriter.rb', line 103

def visit_index_or_write_node(node)
  lint_env_index(node)
  super
end