class JsDuck::Process::InheritMembers

Deals with inheriting member documentation.

Public Class Methods

new(relations) click to toggle source
# File lib/jsduck/process/inherit_members.rb, line 9
def initialize(relations)
  @relations = relations
end

Public Instance Methods

resolve(cls) click to toggle source

Inherits docs for all members in class.

In case of members with explicit @inheritdoc tags we inherit the following fields when they're not empty in current member:

  • :doc

  • :params

  • :return

  • :throws

  • :properties

In case of auto-detected members that inherit from a public member in parent class, we inherit all fields that aren't present in current member, plus the :type field.

Auto-detected members inheriting from other private auto-detected members follow the same rules of inheritance as members with explicit @inheritdoc.

Additionally auto-detected properties get turned into configs when a public configs with same name is detected in parent class.

# File lib/jsduck/process/inherit_members.rb, line 36
def resolve(cls)
  new_cfgs = []

  cls.all_local_members.each do |member|
    if member[:inheritdoc]
      resolve_member(cls, member, new_cfgs)
    end
  end

  move_cfgs(cls, new_cfgs) if new_cfgs.length > 0
end

Private Instance Methods

auto?(m, key) click to toggle source

True when specific field of member has been auto-detected

# File lib/jsduck/process/inherit_members.rb, line 124
def auto?(m, key)
  m[:autodetected] && m[:autodetected][key]
end
auto_inherit(m, parent) click to toggle source
# File lib/jsduck/process/inherit_members.rb, line 111
def auto_inherit(m, parent)
  m[:doc] = parent[:doc] if m[:doc].empty?

  parent.each_pair do |key, value|
    if key == :overrides
      # overrides can't be inherited.
    elsif key == :type || !m[key]
      m[key] = value
    end
  end
end
autodetected?(m) click to toggle source

True when the entire member was auto-detected

# File lib/jsduck/process/inherit_members.rb, line 243
def autodetected?(m)
  m[:autodetected] && m[:autodetected][:tagname]
end
find_parent(m) click to toggle source

Finds parent member of the given member. When @inheritdoc names a member to inherit from, finds that member instead.

If the parent also has @inheritdoc, continues recursively.

# File lib/jsduck/process/inherit_members.rb, line 151
def find_parent(m)

  inherit = m[:inheritdoc] || {}
  if inherit[:cls]
    # @inheritdoc MyClass#member
    parent_cls = @relations[m[:inheritdoc][:cls]]
    return warn("class not found", m) unless parent_cls

    parent = lookup_member(parent_cls, m)
    return warn("member not found", m) unless parent

  elsif inherit[:member]
    # @inheritdoc #member
    parent = lookup_member(@relations[m[:owner]], m)
    return warn("member not found", m) unless parent

  else
    # @inheritdoc
    parent_cls = @relations[m[:owner]].parent
    mixins = @relations[m[:owner]].mixins

    # Warn when no parent or mixins at all
    if !parent_cls && mixins.length == 0
      warn("parent class not found", m) unless autodetected?(m)
      return nil
    end

    # First check for the member in all mixins, because members
    # from mixins override those from parent class.  Looking first
    # from mixins is probably a bit slower, but it's the correct
    # order to do things.
    if mixins.length > 0
      parent = mixins.map {|mix| lookup_member(mix, m) }.compact.first
    end

    # When not found, try looking from parent class
    if !parent && parent_cls
      parent = lookup_member(parent_cls, m)
    end

    # Only when both parent and mixins fail, throw warning
    if !parent
      warn("parent member not found", m) unless autodetected?(m)
      return nil
    end
  end

  return parent
end
inherit(m, parent) click to toggle source
# File lib/jsduck/process/inherit_members.rb, line 80
def inherit(m, parent)
  m[:doc] = parent[:doc] if m[:doc].empty?

  m[:params] = parent[:params] if inherit_params?(m, parent)
  m[:return] = parent[:return] unless m[:return]
  m[:throws] = parent[:throws] unless m[:throws] && m[:throws].length > 0
  m[:properties] = parent[:properties] unless m[:properties] && m[:properties].length > 0

  # Don't inherit type from parent when:
  # - member itself has type and it's not auto-detected
  # - or the type in parent is auto-detected.
  unless m[:type] && m[:type] != "Object" && !auto?(m, :type) || auto?(parent, :type)
    m[:type] = parent[:type]
  end
end
inherit_params?(m, parent) click to toggle source
# File lib/jsduck/process/inherit_members.rb, line 96
def inherit_params?(m, parent)
  # ignore the auto-inserted param of Ext4-style events
  params = (m[:params] || []).reject {|p| p[:ext4_auto_param] }

  if params.length > 0 && !auto?(m, :params)
    # member itself has params and these are not auto-detected
    false
  elsif auto?(parent, :params)
    # Params in parent are auto-detected.
    false
  else
    true
  end
end
lookup_member(cls, m) click to toggle source
# File lib/jsduck/process/inherit_members.rb, line 201
def lookup_member(cls, m)
  inherit = m[:inheritdoc] || {}
  name = inherit[:member] || m[:name]
  tagname = inherit[:type] || m[:tagname]
  # When not explicitly inheriting from static member
  # and the member itself is not static,
  # inherit from instance member.
  static = inherit[:static] || m[:static] || false

  if autodetected?(m)
    # Auto-detected properties can override either a property or a
    # config. So look for both types.
    if tagname == :property
      cfg = cls.find_members(:name => name, :tagname => :cfg, :static => static)[0]
      prop = cls.find_members(:name => name, :tagname => :property, :static => static)[0]

      if cfg && prop
        prop
      elsif cfg
        cfg
      elsif prop
        prop
      else
        nil
      end

    else
      cls.find_members(:name => name, :tagname => tagname, :static => static)[0]
    end
  else
    m = cls.find_members(:name => name, :tagname => tagname, :static => static)[0]
    # When member was not found with explicit staticality and
    # the @inheritdoc tag contained no explicit "static", then
    # look for both static and instance members.
    if !m && !inherit[:static]
      m = cls.find_members(:name => name, :tagname => tagname, :static => nil)[0]
    end
    m
  end
end
move_cfgs(cls, members) click to toggle source

Changes given properties into configs within class

# File lib/jsduck/process/inherit_members.rb, line 129
def move_cfgs(cls, members)
  members.each do |m|
    m[:tagname] = :cfg
  end
end
resolve_member(cls, m, new_cfgs) click to toggle source
# File lib/jsduck/process/inherit_members.rb, line 50
def resolve_member(cls, m, new_cfgs)
  parent = find_parent(m)
  if parent && parent[:inheritdoc]
    resolve_parent(cls, parent)
  end

  if m[:inheritdoc] && parent
    if autodetected?(m) && !parent[:private]
      auto_inherit(m, parent)
    else
      inherit(m, parent)
    end

    # remember properties that have changed to configs
    if autodetected?(m) && m[:tagname] != parent[:tagname]
      new_cfgs << m
    end
  end

  resolve_visibility(m, parent)

  m[:inheritdoc] = nil
end
resolve_parent(cls, parent) click to toggle source
# File lib/jsduck/process/inherit_members.rb, line 74
def resolve_parent(cls, parent)
  new_cfgs = []
  resolve_member(cls, parent, new_cfgs)
  move_cfgs(cls, new_cfgs) if new_cfgs.length > 0
end
resolve_visibility(m, parent) click to toggle source

For auto-detected members/classes (which have @private == :inherit) Use the visibility from parent class (defaulting to private when no parent).

# File lib/jsduck/process/inherit_members.rb, line 137
def resolve_visibility(m, parent)
  if autodetected?(m) && !JsDuck::Class.constructor?(m)
    if !parent || parent[:private]
      m[:private] = true
    end

    m[:protected] = true if parent && parent[:protected]
  end
end
warn(msg, m) click to toggle source
# File lib/jsduck/process/inherit_members.rb, line 247
def warn(msg, m)
  inh_member = m[:inheritdoc][:member]
  inh_target = (m[:inheritdoc][:cls] || "") + (inh_member ? "#" + inh_member : "")

  Logger.warn(:inheritdoc, "@inheritdoc #{inh_target} - #{msg}", m[:files][0])

  return nil
end