1731 lines · 63.0 KB · Python
RawDownload
            #            # The array-of-pointers case.  Dig the parameter name out from the middle            # of the declaration.            #            elif KernRe(r'\(.+\)\s*\[').search(arg):                r = KernRe(r'[^\(]+\(\s*\*\s*'		# Up to "(" and maybe "*"                           r'([\w.]*?)'			# The actual pointer name                           r'\s*(\[\s*\w+\s*\]\s*)*\)') # The [array portion]                if r.match(arg):                    param = r.group(1)                else:                    self.emit_msg(ln, f"Invalid param: {arg}")                    param = arg                dtype = arg.replace(param, '')                self.push_parameter(ln, decl_type, param, dtype, arg, declaration_name)            elif arg:                #                # Clean up extraneous spaces and split the string at commas; the first                # element of the resulting list will also include the type information.                #                arg = KernRe(r'\s*:\s*').sub(":", arg)                arg = KernRe(r'\s*\[').sub('[', arg)                args = KernRe(r'\s*,\s*').split(arg)                args[0] = re.sub(r'(\*+)\s*', r' \1', args[0])                #                # args[0] has a string of "type a".  If "a" includes an [array]                # declaration, we want to not be fooled by any white space inside                # the brackets, so detect and handle that case specially.                #                r = KernRe(r'^([^[\]]*\s+)(.*)$')                if r.match(args[0]):                    args[0] = r.group(2)                    dtype = r.group(1)                else:                    # No space in args[0]; this seems wrong but preserves previous behavior                    dtype = ''                 bitfield_re = KernRe(r'(.*?):(\w+)')                for param in args:                    #                    # For pointers, shift the star(s) from the variable name to the                    # type declaration.                    #                    r = KernRe(r'^(\*+)\s*(.*)')                    if r.match(param):                        self.push_parameter(ln, decl_type, r.group(2),                                            f"{dtype} {r.group(1)}",                                            arg, declaration_name)                    #                    # Perform a similar shift for bitfields.                    #                    elif bitfield_re.search(param):                        if dtype != "":  # Skip unnamed bit-fields                            self.push_parameter(ln, decl_type, bitfield_re.group(1),                                                f"{dtype}:{bitfield_re.group(2)}",                                                arg, declaration_name)                    else:                        self.push_parameter(ln, decl_type, param, dtype,                                            arg, declaration_name)     def check_sections(self, ln, decl_name, decl_type):        """        Check for errors inside sections, emitting warnings if not found        parameters are described.        """        for section in self.entry.sections:            if section not in self.entry.parameterlist and \               not known_sections.search(section):                if decl_type == 'function':                    dname = f"{decl_type} parameter"                else:                    dname = f"{decl_type} member"                self.emit_msg(ln,                              f"Excess {dname} '{section}' description in '{decl_name}'")         #        # Check that documented parameter names (from doc comments, including        # inline ``/** @member: */`` tags) actually match real members in        # the declaration.  This catches mismatched or stale kernel-doc        # member tags that don't correspond to any actual struct/union        # member or function parameter.        #        for param_name, desc in self.entry.parameterdescs.items():            # Skip auto-generated entries from push_parameter()            if desc == self.undescribed:                continue            if desc in ("no arguments", "anonymous\n", "variable arguments"):                continue            if param_name.startswith("{unnamed_"):                continue            if param_name in self.entry.parameterlist:                continue             if decl_type == 'function':                dname = f"{decl_type} parameter"            else:                dname = f"{decl_type} member"            self.emit_msg(ln,                          f"Excess {dname} '{param_name}' description in '{decl_name}'")     def check_return_section(self, ln, declaration_name, return_type):        """        If the function doesn't return void, warns about the lack of a        return description.        """         if not self.config.wreturn:            return         # Ignore an empty return type (It's a macro)        # Ignore functions with a "void" return type (but not "void *")        if not return_type or KernRe(r'void\s*\w*\s*$').search(return_type):            return         if not self.entry.sections.get("Return", None):            self.emit_msg(ln,                          f"No description found for return value of '{declaration_name}'")     def split_struct_proto(self, proto):        """        Split apart a structure prototype; returns (struct|union, name,        members) or ``None``.        """         type_pattern = r'(struct|union)'        qualifiers = [            "__attribute__",            "__packed",            "__aligned",            "____cacheline_aligned_in_smp",            "____cacheline_aligned",        ]        definition_body = r'\{(.*)\}\s*' + "(?:" + '|'.join(qualifiers) + ")?"         r = KernRe(type_pattern + r'\s+(\w+)\s*' + definition_body)        if r.search(proto):            return (r.group(1), r.group(2), r.group(3))        else:            r = KernRe(r'typedef\s+' + type_pattern + r'\s*' + definition_body + r'\s*(\w+)\s*;')            if r.search(proto):                return (r.group(1), r.group(3), r.group(2))        return None     def rewrite_struct_members(self, members):        """        Process ``struct``/``union`` members from the most deeply nested        outward.         Rewrite the members of a ``struct`` or ``union`` for easier formatting        later on. Among other things, this function will turn a member like::           struct { inner_members; } foo;         into::           struct foo; inner_members;        """         #        # The trick is in the ``^{`` below - it prevents a match of an outer        # ``struct``/``union`` until the inner one has been munged        # (removing the ``{`` in the process).        #        struct_members = KernRe(r'(struct|union)'   # 0: declaration type                                r'([^\{\};]+)' 	    # 1: possible name                                r'(\{)'                                r'([^\{\}]*)'       # 3: Contents of declaration                                r'(\})'                                r'([^\{\};]*)(;)')  # 5: Remaining stuff after declaration        tuples = struct_members.findall(members)        while tuples:            for t in tuples:                newmember = ""                oldmember = "".join(t) # Reconstruct the original formatting                dtype, name, lbr, content, rbr, rest, semi = t                #                # Pass through each field name, normalizing the form and formatting.                #                for s_id in rest.split(','):                    s_id = s_id.strip()                    newmember += f"{dtype} {s_id}; "                    #                    # Remove bitfield/array/pointer info, getting the bare name.                    #                    s_id = KernRe(r'[:\[].*').sub('', s_id)                    s_id = KernRe(r'^\s*\**(\S+)\s*').sub(r'\1', s_id)                    #                    # Pass through the members of this inner structure/union.                    #                    for arg in content.split(';'):                        arg = arg.strip()                        #                        # Look for (type)(*name)(args) - pointer to function                        #                        r = KernRe(r'^([^\(]+\(\*?\s*)([\w.]*)(\s*\).*)')                        if r.match(arg):                            dtype, name, extra = r.group(1), r.group(2), r.group(3)                            # Pointer-to-function                            if not s_id:                                # Anonymous struct/union                                newmember += f"{dtype}{name}{extra}; "                            else:                                newmember += f"{dtype}{s_id}.{name}{extra}; "                        #                        # Otherwise a non-function member.                        #                        else:                            #                            # Remove bitmap and array portions and spaces around commas                            #                            arg = KernRe(r':\s*\d+\s*').sub('', arg)                            arg = KernRe(r'\[.*\]').sub('', arg)                            arg = KernRe(r'\s*,\s*').sub(',', arg)                            #                            # Look for a normal decl - "type name[,name...]"                            #                            r = KernRe(r'(.*)\s+([\S+,]+)')                            if r.search(arg):                                for name in r.group(2).split(','):                                    name = KernRe(r'^\s*\**(\S+)\s*').sub(r'\1', name)                                    if not s_id:                                        # Anonymous struct/union                                        newmember += f"{r.group(1)} {name}; "                                    else:                                        newmember += f"{r.group(1)} {s_id}.{name}; "                            else:                                newmember += f"{arg}; "                #                # At the end of the s_id loop, replace the original declaration with                # the munged version.                #                members = members.replace(oldmember, newmember)            #            # End of the tuple loop - search again and see if there are outer members            # that now turn up.            #            tuples = struct_members.findall(members)        return members     def format_struct_decl(self, declaration):        """        Format the ``struct`` declaration into a standard form for inclusion        in the resulting docs.        """         #        # Insert newlines, get rid of extra spaces.        #        declaration = KernRe(r'([\{;])').sub(r'\1\n', declaration)        declaration = KernRe(r'\}\s+;').sub('};', declaration)        #        # Format inline enums with each member on its own line.        #        r = KernRe(r'(enum\s+\{[^\}]+),([^\n])')        while r.search(declaration):            declaration = r.sub(r'\1,\n\2', declaration)        #        # Now go through and supply the right number of tabs        # for each line.        #        def_args = declaration.split('\n')        level = 1        declaration = ""        for clause in def_args:            clause = KernRe(r'\s+').sub(' ', clause.strip(), count=1)            if clause:                if '}' in clause and level > 1:                    level -= 1                if not clause.startswith('#'):                    declaration += "\t" * level                declaration += "\t" + clause + "\n"                if "{" in clause and "}" not in clause:                    level += 1        return declaration      def dump_struct(self, ln, proto, source):        """        Store an entry for a ``struct`` or ``union``        """        #        # Do the basic parse to get the pieces of the declaration.        #        source = source        proto = trim_private_members(proto)        struct_parts = self.split_struct_proto(proto)        if not struct_parts:            self.emit_msg(ln, f"{proto} error: Cannot parse struct or union!")            return        decl_type, declaration_name, members = struct_parts         if self.entry.identifier != declaration_name:            self.emit_msg(ln, f"expecting prototype for {decl_type} {self.entry.identifier}. "                          f"Prototype was for {decl_type} {declaration_name} instead\n")            return        #        # Go through the list of members applying all of our transformations.        #        members = self.xforms.apply("struct", members)         #        # Deal with embedded struct and union members, and drop enums entirely.        #        declaration = members        members = self.rewrite_struct_members(members)        members = re.sub(r'(\{[^\{\}]*\})', '', members)        #        # Output the result and we are done.        #        self.create_parameter_list(ln, decl_type, members, ';',                                   declaration_name)        self.check_sections(ln, declaration_name, decl_type)        self.output_declaration(decl_type, declaration_name,                                source=source,                                definition=self.format_struct_decl(declaration),                                purpose=self.entry.declaration_purpose)     def dump_enum(self, ln, proto, source):        """        Store an ``enum`` inside self.entries array.        """        #        # Strip preprocessor directives.  Note that this depends on the        # trailing semicolon we added in process_proto_type().        #        source = source        proto = trim_private_members(proto)        proto = KernRe(r'#\s*((define|ifdef|if)\s+|endif)[^;]*;', flags=re.S).sub('', proto)        #        # Parse out the name and members of the enum.  Typedef form first.        #        r = KernRe(r'typedef\s+enum\s*\{(.*)\}\s*(\w*)\s*;')        if r.search(proto):            declaration_name = r.group(2)            members = r.group(1)        #        # Failing that, look for a straight enum        #        else:            r = KernRe(r'enum\s+(\w*)\s*\{(.*)\}')            if r.match(proto):                declaration_name = r.group(1)                members = r.group(2)        #        # OK, this isn't going to work.        #            else:                self.emit_msg(ln, f"{proto}: error: Cannot parse enum!")                return        #        # Make sure we found what we were expecting.        #        if self.entry.identifier != declaration_name:            if self.entry.identifier == "":                self.emit_msg(ln,                              f"{proto}: wrong kernel-doc identifier on prototype")            else:                self.emit_msg(ln,                              f"expecting prototype for enum {self.entry.identifier}. "                              f"Prototype was for enum {declaration_name} instead")            return         if not declaration_name:            declaration_name = "(anonymous)"        #        # Parse out the name of each enum member, and verify that we        # have a description for it.        #        member_set = set()        members = KernRe(r'\([^;)]*\)').sub('', members)        for arg in members.split(','):            arg = KernRe(r'^\s*(\w+).*').sub(r'\1', arg)            if not arg.strip():                continue             self.entry.parameterlist.append(arg)            if arg not in self.entry.parameterdescs:                self.entry.parameterdescs[arg] = self.undescribed                self.emit_msg(ln,                              f"Enum value '{arg}' not described in enum '{declaration_name}'")            member_set.add(arg)        #        # Ensure that every described member actually exists in the enum.        #        for k in self.entry.parameterdescs:            if k not in member_set:                self.emit_msg(ln,                              f"Excess enum value '@{k}' description in '{declaration_name}'")         self.output_declaration('enum', declaration_name,                                source=source,                                purpose=self.entry.declaration_purpose)     def dump_var(self, ln, proto, source):        """        Store variables that are part of kAPI.        """        VAR_ATTRIBS = [            "extern",            "const",        ]        OPTIONAL_VAR_ATTR = r"^(?:\b(?:" +"|".join(VAR_ATTRIBS) +r")\b\s*)*"         #        # Store the full prototype before modifying it        #        source = source        full_proto = proto        declaration_name = None         #        # Handle macro definitions        #        macro_prefixes = [            KernRe(r"DEFINE_[\w_]+\s*\(([\w_]+)\)"),        ]         for r in macro_prefixes:            match = r.search(proto)            if match:                declaration_name = match.group(1)                break         #        # Drop comments and macros to have a pure C prototype        #        if not declaration_name:            proto = self.xforms.apply("var", proto)         proto = proto.rstrip()         #        # Variable name is at the end of the declaration        #         default_val = None         r= KernRe(OPTIONAL_VAR_ATTR + r"\s*[\w_\s]*\s+(?:\*+)?([\w_]+)\s*[\d\]\[]*\s*(=.*)?")        if r.match(proto):            if not declaration_name:                declaration_name = r.group(1)             default_val = r.group(2)        else:            r= KernRe(OPTIONAL_VAR_ATTR + r"(?:[\w_\s]*)?\s+(?:\*+)?(?:[\w_]+)\s*[\d\]\[]*\s*(=.*)?")             if r.match(proto):                default_val = r.group(1)        if not declaration_name:           self.emit_msg(ln,f"{proto}: can't parse variable")           return         if default_val:            default_val = default_val.lstrip("=").strip()         self.output_declaration("var", declaration_name,                                source=source,                                full_proto=full_proto,                                default_val=default_val,                                purpose=self.entry.declaration_purpose)     def dump_declaration(self, ln, prototype, source):        """        Store a data declaration inside self.entries array.        """         if self.entry.decl_type == "enum":            self.dump_enum(ln, prototype, source)        elif self.entry.decl_type == "typedef":            self.dump_typedef(ln, prototype, source)        elif self.entry.decl_type in ["union", "struct"]:            self.dump_struct(ln, prototype, source)        elif self.entry.decl_type == "var":            self.dump_var(ln, prototype, source)        else:            # This would be a bug            self.emit_message(ln, f'Unknown declaration type: {self.entry.decl_type}')     def dump_function(self, ln, prototype, source):        """        Store a function or function macro inside self.entries array.        """         source = source        found = func_macro = False        return_type = ''        decl_type = 'function'         #        # If we have a macro, remove the "#define" at the front.        #        new_proto = KernRe(r"^#\s*define\s+").sub("", prototype)        if new_proto != prototype:            prototype = new_proto            #            # Dispense with the simple "#define A B" case here; the key            # is the space after the name of the symbol being defined.            # NOTE that the seemingly misnamed "func_macro" indicates a            # macro *without* arguments.            #

Lines 501–1000 of 1731. Previous lines