class PowerAssert::Parser
def extract_idents(sexp)
+--------+
extract_idents(Ripper.sexp('a&.b(c).d')) #=> a--+ +--d
+--c--b--+
Returns idents as graph structure.
def extract_idents(sexp) tag, * = sexp case tag when :arg_paren, :assoc_splat, :fcall, :hash, :method_add_block, :string_literal, :return extract_idents(sexp[1]) when :assign, :massign extract_idents(sexp[2]) when :opassign _, _, (_, op_name, (_, op_column)), s0 = sexp extract_idents(s0) + [Ident[:method, op_name.sub(/=\z/, ''), op_column]] when :dyna_symbol if sexp[1][0].kind_of?(Symbol) # sexp[1] can be [:string_content, [..]] while parsing { "a": 1 } extract_idents(sexp[1]) else sexp[1].flat_map {|s| extract_idents(s) } end when :assoclist_from_args, :bare_assoc_hash, :paren, :string_embexpr, :regexp_literal, :xstring_literal sexp[1].flat_map {|s| extract_idents(s) } when :command [sexp[2], sexp[1]].flat_map {|s| extract_idents(s) } when :assoc_new, :dot2, :dot3, :string_content sexp[1..-1].flat_map {|s| extract_idents(s) } when :unary handle_columnless_ident([], sexp[1], extract_idents(sexp[2])) when :binary op = sexp[2] if AND_OR_OPS.include?(op) extract_idents(sexp[1]) + [Branch[extract_idents(sexp[3]), []]] else handle_columnless_ident(extract_idents(sexp[1]), op, extract_idents(sexp[3])) end when :call _, recv, (op_sym, op_name, _), method = sexp with_safe_op = ((op_sym == :@op and op_name == '&.') or op_sym == :"&.") if method == :call handle_columnless_ident(extract_idents(recv), :call, [], with_safe_op) else extract_idents(recv) + (with_safe_op ? [Branch[extract_idents(method), []]] : extract_idents(method)) end when :array sexp[1] ? sexp[1].flat_map {|s| extract_idents(s) } : [] when :command_call [sexp[1], sexp[4], sexp[3]].flat_map {|s| extract_idents(s) } when :aref handle_columnless_ident(extract_idents(sexp[1]), :[], extract_idents(sexp[2])) when :method_add_arg idents = extract_idents(sexp[1]) if idents.empty? # idents may be empty(e.g. ->{}.()) extract_idents(sexp[2]) else if idents[-1].kind_of?(Branch) and idents[-1][1].empty? # Safe navigation operator is used. See :call clause also. idents[0..-2] + [Branch[extract_idents(sexp[2]) + idents[-1][0], []]] else idents[0..-2] + extract_idents(sexp[2]) + [idents[-1]] end end when :args_add_block _, (tag, ss0, *ss1), _ = sexp if tag == :args_add_star (ss0 + ss1).flat_map {|s| extract_idents(s) } else sexp[1].flat_map {|s| extract_idents(s) } end when :vcall _, (tag, name, (_, column)) = sexp if tag == :@ident [Ident[@proc_local_variables.include?(name) ? :ref : :method, name, column]] else [] end when :program _, ((tag0, (tag1, (tag2, (tag3, mname, _)), _), (tag4, _, ss))) = sexp if tag0 == :method_add_block and tag1 == :method_add_arg and tag2 == :fcall and (tag3 == :@ident or tag3 == :@const) and mname == @assertion_method_name and (tag4 == :brace_block or tag4 == :do_block) ss.flat_map {|s| extract_idents(s) } else _, (s0, *) = sexp extract_idents(s0) end when :ifop _, s0, s1, s2 = sexp [*extract_idents(s0), Branch[extract_idents(s1), extract_idents(s2)]] when :if, :unless _, s0, ss0, (_, ss1) = sexp [*extract_idents(s0), Branch[ss0.flat_map {|s| extract_idents(s) }, ss1 ? ss1.flat_map {|s| extract_idents(s) } : []]] when :if_mod, :unless_mod _, s0, s1 = sexp [*extract_idents(s0), Branch[extract_idents(s1), []]] when :var_ref, :var_field _, (tag, ref_name, (_, column)) = sexp case tag when :@kw if ref_name == 'self' [Ident[:ref, 'self', column]] else [] end when :@ident, :@const, :@cvar, :@ivar, :@gvar [Ident[:ref, ref_name, column]] else [] end when :@ident, :@const, :@op _, method_name, (_, column) = sexp [Ident[:method, method_name, column]] else [] end end