'Return a hit for a getattr on the given element.
Returns (<hit>, <num-tokens-consumed>) or raises an EvalError.
Typically this just does a getattr of tokens[0], but handling
some multi-level imports can result in multiple tokens being
consumed.'

def _hit_from_getattr(self, tokens, elem, scoperef):

    first_token = tokens[0]
    self.log("_hit_from_getattr::    resolve    '%s'    on    %r    in    %r:", first_token, elem, scoperef)
    if (elem.tag == 'variable'):
        (elem, scoperef) = self._hit_from_variable_type_inference(elem, scoperef)
    assert (elem.tag == 'scope')
    ilk = elem.get('ilk')
    if (ilk == 'function'):
        pass
    elif ((ilk == 'class') or (ilk == 'trait')):
        static_member = False
        if first_token.startswith('$'):
            static_member = True
            first_token = first_token[1:]
        attr = elem.names.get(first_token)
        if (attr is not None):
            self.log('_hit_from_getattr::    attr    is    %r    in    %r', attr, elem)
            if (static_member and ('static' not in attr.get('attributes', '').split())):
                self.warn('_hit_from_getattr::    %r    in    class    %r    is    not    marked    static', first_token, elem)
            classname = elem.get('name')
            if ((len(scoperef[1]) == 0) or (scoperef[1][(-1)] != classname)):
                class_scoperef = (scoperef[0], (scoperef[1] + [classname]))
            else:
                class_scoperef = scoperef
            return ((attr, class_scoperef), 1)
        for classref in (elem.get('traitrefs', '').split() + elem.get('classrefs', '').split()):
            self.log("_hit_from_getattr::    is    '%s'    available    on    parent    class:    %r?", first_token, classref)
            (base_elem, base_scoperef) = self._hit_from_citdl(classref, scoperef)
            if ((base_elem is not None) and (base_elem.get('ilk') in ('class', 'trait'))):
                self.log("_hit_from_getattr::    is    '%s'    from    %s    base    %s?", first_token, base_elem, base_elem.get('ilk'))
                try:
                    (hit, nconsumed) = self._hit_from_getattr(tokens, base_elem, base_scoperef)
                    if ((hit is not None) and self._return_with_hit(hit, nconsumed)):
                        self.log("_hit_from_getattr::    is    '%s'    accessible    on    %s?    yes:    %s", '.'.join(tokens[:nconsumed]), scoperef, hit[0])
                        return (hit, nconsumed)
                except CodeIntelError as e:
                    pass
    elif (ilk == 'blob'):
        attr = elem.names.get(first_token)
        if (attr is not None):
            self.log('_hit_from_getattr::    attr    is    %r    in    %r', attr, elem)
            return ((attr, scoperef), 1)
        (hit, nconsumed) = self._hit_from_elem_imports(tokens, elem)
        if (hit is not None):
            return (hit, nconsumed)
    else:
        raise NotImplementedError(('unexpected    scope    ilk:    %r' % ilk))
    raise CodeIntelError(("could    not    resolve    '%s'    getattr    on    %r    in    %r" % (first_token, elem, scoperef)))
