module T::Props::GeneratedCodeValidation

def self.assert_equal(expected, actual)

def self.assert_equal(expected, actual)
al
rror.new("Expected #{expected}, got #{actual}")

def self.self_class_decorator

def self.self_class_decorator
or ||= s(:send, s(:send, s(:self), :class), :decorator).freeze

def self.validate_deserialize(source)

def self.validate_deserialize(source)
  parsed = parse(source)
  # def %<name>(hash)
  #   ...
  # end
  assert_equal(:def, parsed.type)
  name, args, body = parsed.children
  assert_equal(:__t_props_generated_deserialize, name)
  assert_equal(s(:args, s(:arg, :hash)), args)
  assert_equal(:begin, body.type)
  init, *prop_clauses, ret = body.children
  # found = %<prop_count>
  # ...
  # found
  assert_equal(:lvasgn, init.type)
  init_name, init_val = init.children
  assert_equal(:found, init_name)
  assert_equal(:int, init_val.type)
  assert_equal(s(:lvar, :found), ret)
  prop_clauses.each_with_index do |clause, i|
    if i.even?
      validate_deserialize_hash_read(clause)
    else
      validate_deserialize_ivar_set(clause)
    end
  end
end

def self.validate_deserialize_handle_nil(node)

def self.validate_deserialize_handle_nil(node)
 :str, :sym, :int, :float, :true, :false, :nil, :const # rubocop:disable Lint/BooleanSymbol
constants are safe
 arg = node.children
_missing_from_deserialize(%<prop>)
equired_prop_missing_from_deserialize, method)
ym, arg.type)
 self_class_decorator
corator.raise_nil_deserialize_error(%<serialized_form>)
aise_nil_deserialize_error, method)
tr, arg.type)
default
corator.props_with_defaults.fetch(%<prop>).default
end, receiver.type)
 inner_method, inner_arg = receiver.children
_class_decorator, :props_with_defaults),
r,
etch, inner_method)
ym, inner_arg.type)
nError.new("Unexpected receiver in nil handler: #{node.inspect}")
rror.new("Unexpected nil handler: #{node.inspect}")

def self.validate_deserialize_hash_read(clause)

def self.validate_deserialize_hash_read(clause)
alized_form>s]
n, clause.type)
children
name)
 val.type)
rg = val.children
r, :hash), receiver)
ethod)
arg.type)

def self.validate_deserialize_ivar_set(clause)

def self.validate_deserialize_ivar_set(clause)
= if val.nil?
ss hash.key?(%<serialized_form>s)

l>s
n, clause.type)
l = clause.children
_a?(Symbol)
rror.new("Unexpected ivar: #{ivar_name}")
eser_val.type)
 else_body = deser_val.children
d, s(:lvar, :val), :nil?), condition)
, if_body.type)
e_nil = if_body.children
pdate_found.type)
und_if_body, found_else_body = update_found.children
 found_condition.type)
rg = found_condition.children
r, :hash), receiver)
 method)
arg.type)
ound_if_body)
asgn, s(:lvasgn, :found), :-, s(:int, 1)), found_else_body)
e_handle_nil(handle_nil)
= :kwbegin
, = else_body.children
cue, rescue_expression.type)
= rescue_expression.children
side_effects(try, whitelisted_methods_for_deserialize)
body, rescue_body.type)
nment, handler =  rescue_body.children
ay, exceptions.type)
en.each {|c| assert_equal(:const, c.type)}
sgn, assignment.type)
, assignment.children)
rror, val_return = handler.children
d, deserialization_error.type)
 *args = deserialization_error.children
 receiver)
se_deserialization_error, method)
lidate_lack_of_side_effects(a, whitelisted_methods_for_deserialize)}
side_effects(val_return, whitelisted_methods_for_deserialize)
side_effects(else_body, whitelisted_methods_for_deserialize)

def self.validate_lack_of_side_effects(node, whitelisted_methods_by_receiver_type)

def self.validate_lack_of_side_effects(node, whitelisted_methods_by_receiver_type)
ause we'll have validated what method has been called
 :str, :sym, :int, :float, :true, :false, :nil, :self # rubocop:disable Lint/BooleanSymbol
lf are ok
ivar
 instance variables & arguments is ok
ren.all? {|c| c.is_a?(Symbol)}
nError.new("Unexpected child for #{node.type}: #{node.inspect}")
:block, :begin, :if
read-only if their contents are read-only
h {|c| validate_lack_of_side_effects(c, whitelisted_methods_by_receiver_type) if c}
er so check a whitelist
 *args = node.children
e == :send
r
r.type
_of_side_effects(receiver, whitelisted_methods_by_receiver_type)
_methods_by_receiver_type[key]&.include?(method)
ionError.new("Unexpected method #{method} called on #{receiver.inspect}")
|
f_side_effects(arg, whitelisted_methods_by_receiver_type)
rror.new("Unexpected node type #{node.type}: #{node.inspect}")

def self.validate_serialize(source)

def self.validate_serialize(source)
  parsed = parse(source)
  # def %<name>(strict)
  # ...
  # end
  assert_equal(:def, parsed.type)
  name, args, body = parsed.children
  assert_equal(:__t_props_generated_serialize, name)
  assert_equal(s(:args, s(:arg, :strict)), args)
  assert_equal(:begin, body.type)
  init, *prop_clauses, ret = body.children
  # h = {}
  # ...
  # h
  assert_equal(s(:lvasgn, :h, s(:hash)), init)
  assert_equal(s(:lvar, :h), ret)
  prop_clauses.each do |clause|
    validate_serialize_clause(clause)
  end
end

def self.validate_serialize_clause(clause)

def self.validate_serialize_clause(clause)
lause.type)
 else_body = clause.children
y>.nil?
 condition.type)
condition.children
 receiver.type)
 method)

issing_from_serialize(%<prop>) if strict
 if_body.type)
on, if_strict_body, if_strict_else = if_body.children
var, :strict), if_strict_condition)
d, if_strict_body.type)
r, on_strict_method, on_strict_arg = if_strict_body.children
 on_strict_receiver)
uired_prop_missing_from_serialize, on_strict_method)
, on_strict_arg.type)
 if_strict_else)
rm>] = ...
 else_body.type)
_key, h_val = else_body.children
r, :h), receiver)
method)
h_key.type)
de_effects(h_val, whitelisted_methods_for_serialize)

def self.whitelisted_methods_for_deserialize

Method calls generated by SerdeTransform
def self.whitelisted_methods_for_deserialize
s_for_deserialize ||= {
transform_values transform_keys each_with_object nil? []= to_f},
lize from_hash deep_clone_object],

def self.whitelisted_methods_for_serialize

Method calls generated by SerdeTransform
def self.whitelisted_methods_for_serialize
s_for_serialize ||= {
transform_values transform_keys each_with_object nil? []= serialize},
transform_values transform_keys each_with_object serialize],
_serialize deep_clone_object],