module YARD::Handlers::C::HandlerMethods

def find_constant_docstring(object)

def find_constant_docstring(object)
  comment = nil
  # look inside overrides for declaration value
  override_comments.each do |name, override_comment|
    next unless override_comment.file == statement.file
    just_const_name = name.gsub(/\A.+::/, '')
    if object.path == name || object.name.to_s == just_const_name
      comment = override_comment.source
      break
    end
  end
  # use any comments on this statement as a last resort
  if comment.nil? && statement.comments && statement.comments.source =~ /\S/
    comment = statement.comments.source
    stmt = statement.comments
  end
  # In the case of rb_define_const, the definition and comment are in
  # "/* definition: comment */" form.  The literal ':' and '\' characters
  # can be escaped with a backslash.
  if comment
    comment.scan(/\A\s*(.*?[^\s\\]):\s*(.+)/m) do |new_value, new_comment|
      object.value = new_value.gsub(/\\:/, ':')
      comment = new_comment
    end
    register_docstring(object, comment, stmt)
  end
end

def find_method_body(object, symbol)

def find_method_body(object, symbol)
  file = statement.file
  in_file = false
  if statement.comments && statement.comments.source =~ /\A\s*in (\S+)\Z/
    file = $1
    in_file = true
    process_file(file, object)
  end
  src_stmt = symbols[symbol]
  if src_stmt
    register_file_info(object, src_stmt.file, src_stmt.line, true)
    register_source(object, src_stmt)
    record_parameters(object, symbol, src_stmt)
    unless src_stmt.comments.nil? || src_stmt.comments.source.empty?
      register_docstring(object, src_stmt.comments.source, src_stmt)
      return # found docstring
    end
  end
  # found source (possibly) but no docstring
  # so look in overrides
  return if override_comments.any? do |name, override_comment|
    next unless override_comment.file == file
    name = name.gsub(/::([^:\.#]+?)\Z/, '.\1')
    # explicit namespace in override comment
    path = (name =~ /\.|#/ ? object.path : object.name.to_s)
    if path == name || path == name.sub(/new$/, 'initialize') || path == name.sub('.', '#')
      register_docstring(object, override_comment.source, override_comment)
      true
    else
      false
    end
  end
  # use any comments on this statement as a last resort
  if !in_file && statement.comments && statement.comments.source =~ /\S/
    register_docstring(object, statement.comments.source, statement)
  end
end

def handle_alias(var_name, new_name, old_name)

def handle_alias(var_name, new_name, old_name)
  namespace = namespace_for_variable(var_name)
  return if namespace.nil?
  new_meth = new_name.to_sym
  old_meth = old_name.to_sym
  old_obj = namespace.child(:name => old_meth, :scope => :instance)
  new_obj = register MethodObject.new(namespace, new_meth, :instance) do |o|
    register_visibility(o, visibility)
    register_file_info(o, statement.file, statement.line)
  end
  if old_obj
    new_obj.signature = old_obj.signature
    new_obj.source = old_obj.source
    new_obj.docstring = old_obj.docstring
    new_obj.docstring.object = new_obj
  else
    new_obj.signature = "def #{new_meth}" # this is all we know.
  end
  namespace.aliases[new_obj] = old_meth
end

def handle_attribute(var_name, name, read, write)

def handle_attribute(var_name, name, read, write)
  values = {:read => read.to_i, :write => write.to_i}
  {:read => name, :write => "#{name}="}.each do |type, meth_name|
    next unless values[type] > 0
    obj = handle_method(:instance, var_name, meth_name, nil)
    register_file_info(obj, statement.file, statement.line)
    obj.namespace.attributes[:instance][name] ||= SymbolHash[:read => nil, :write => nil]
    obj.namespace.attributes[:instance][name][type] = obj
  end
end

def handle_class(var_name, class_name, parent, in_module = nil)

def handle_class(var_name, class_name, parent, in_module = nil)
  parent = nil if parent == "0"
  namespace = in_module ? ensure_variable_defined!(in_module) : Registry.root
  if namespace.nil?
    raise Parser::UndocumentableError,
      "class #{class_name}. Cannot find definition for parent namespace."
  end
  register ClassObject.new(namespace, class_name) do |obj|
    if parent
      parent_class = namespace_for_variable(parent)
      if parent_class.is_a?(Proxy)
        obj.superclass = "::#{parent_class.path}"
        obj.superclass.type = :class
      else
        obj.superclass = parent_class
      end
    end
    namespaces[var_name] = obj
    register_file_info(obj, statement.file, statement.line)
  end
end

def handle_constants(type, var_name, const_name, value)

def handle_constants(type, var_name, const_name, value)
  return unless type =~ /^const$|^global_const$/
  namespace = type == 'global_const' ?
    :root : namespace_for_variable(var_name)
  register ConstantObject.new(namespace, const_name) do |obj|
    obj.source_type = :c
    obj.value = value
    register_file_info(obj, statement.file, statement.line)
    find_constant_docstring(obj)
  end
end

def handle_method(scope, var_name, name, func_name, _source_file = nil)

def handle_method(scope, var_name, name, func_name, _source_file = nil)
  visibility = :public
  case scope
  when "singleton_method"; scope = :class
  when "module_function"; scope = :module
  when "private_method"; scope = :instance; visibility = :private
  else; scope = :instance
  end
  namespace = namespace_for_variable(var_name)
  # Is this method being defined on a core Ruby class or module?
  if namespace.is_a?(Proxy)
    if var_name =~ /^rb_c(\w+)/ && YARD::CodeObjects::BUILTIN_CLASSES.include?($1)
      namespace = namespaces[var_name] = YARD::CodeObjects::ClassObject.new(:root, $1)
    elsif var_name =~ /^rb_m(\w+)/ && YARD::CodeObjects::BUILTIN_MODULES.include?($1)
      namespace = namespaces[var_name] = YARD::CodeObjects::ModuleObject.new(:root, $1)
    end
  end
  return if namespace.nil? # XXX: raise UndocumentableError might be too noisy.
  register MethodObject.new(namespace, name, scope) do |obj|
    register_visibility(obj, visibility)
    find_method_body(obj, func_name)
    obj.explicit = true
    add_predicate_return_tag(obj) if name =~ /\?$/
  end
end

def handle_module(var_name, module_name, in_module = nil)

def handle_module(var_name, module_name, in_module = nil)
  namespace = in_module ? ensure_variable_defined!(in_module) : Registry.root
  if namespace.nil?
    raise Parser::UndocumentableError,
      "module #{module_name}. Cannot find definition for parent namespace."
  end
  register ModuleObject.new(namespace, module_name) do |obj|
    namespaces[var_name] = obj
    register_file_info(obj, statement.file, statement.line)
  end
end

def record_parameters(object, symbol, src)

def record_parameters(object, symbol, src)
  # use regex to extract comma-delimited list of parameters from cfunc definition
  if src.source =~ /VALUE\s+#{symbol}\(([^)]*)\)\s*\{/m
    params = $~[1].split(/\s*,\s*/) # rubocop:disable Style/SpecialGlobalVars
    # cfunc for a "varargs" method has params "int argc, VALUE *argv"
    if params[0] =~ /int\s+argc/ && params[1] =~ /VALUE\s*\*\s*argv/
      object.parameters = [['*args', nil]]
    else
      # the first cfunc argument is the 'self' argument, we don't need that
      object.parameters = params.drop(1).map {|s| [s[/VALUE\s+(\S+)/, 1], nil] }
    end
  end
end