1784 lines · 64.6 KB · Python
RawDownload
                break         #        # Drop comments and macros to have a pure C prototype        #        if not declaration_name:            for r, sub in sub_prefixes:                proto = r.sub(sub, proto)         proto = proto.rstrip()         #        # Variable name is at the end of the declaration        #         default_val = None         r= KernRe(OPTIONAL_VAR_ATTR + r"\w.*\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+(?:\*+)?(?:[\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,                                full_proto=full_proto,                                default_val=default_val,                                purpose=self.entry.declaration_purpose)     def dump_declaration(self, ln, prototype):        """        Store a data declaration inside self.entries array.        """         if self.entry.decl_type == "enum":            self.dump_enum(ln, prototype)        elif self.entry.decl_type == "typedef":            self.dump_typedef(ln, prototype)        elif self.entry.decl_type in ["union", "struct"]:            self.dump_struct(ln, prototype)        elif self.entry.decl_type == "var":            self.dump_var(ln, prototype)        else:            # This would be a bug            self.emit_message(ln, f'Unknown declaration type: {self.entry.decl_type}')     def dump_function(self, ln, prototype):        """        Store a function or function macro inside self.entries array.        """         found = func_macro = False        return_type = ''        decl_type = 'function'        #        # Apply the initial transformations.        #        prototype = apply_transforms(function_xforms, prototype)        #        # 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.            #            r = KernRe(r'^(\w+)\s+')            if r.search(prototype):                return_type = ''                declaration_name = r.group(1)                func_macro = True                found = True         # Yes, this truly is vile.  We are looking for:        # 1. Return type (may be nothing if we're looking at a macro)        # 2. Function name        # 3. Function parameters.        #        # All the while we have to watch out for function pointer parameters        # (which IIRC is what the two sections are for), C types (these        # regexps don't even start to express all the possibilities), and        # so on.        #        # If you mess with these regexps, it's a good idea to check that        # the following functions' documentation still comes out right:        # - parport_register_device (function pointer parameters)        # - atomic_set (macro)        # - pci_match_device, __copy_to_user (long return type)         name = r'\w+'        type1 = r'(?:[\w\s]+)?'        type2 = r'(?:[\w\s]+\*+)+'        #        # Attempt to match first on (args) with no internal parentheses; this        # lets us easily filter out __acquires() and other post-args stuff.  If        # that fails, just grab the rest of the line to the last closing        # parenthesis.        #        proto_args = r'\(([^\(]*|.*)\)'        #        # (Except for the simple macro case) attempt to split up the prototype        # in the various ways we understand.        #        if not found:            patterns = [                rf'^()({name})\s*{proto_args}',                rf'^({type1})\s+({name})\s*{proto_args}',                rf'^({type2})\s*({name})\s*{proto_args}',            ]             for p in patterns:                r = KernRe(p)                if r.match(prototype):                    return_type = r.group(1)                    declaration_name = r.group(2)                    args = r.group(3)                    self.create_parameter_list(ln, decl_type, args, ',',                                               declaration_name)                    found = True                    break        #        # Parsing done; make sure that things are as we expect.        #        if not found:            self.emit_msg(ln,                          f"cannot understand function prototype: '{prototype}'")            return        if self.entry.identifier != declaration_name:            self.emit_msg(ln, f"expecting prototype for {self.entry.identifier}(). "                          f"Prototype was for {declaration_name}() instead")            return        self.check_sections(ln, declaration_name, "function")        self.check_return_section(ln, declaration_name, return_type)        #        # Store the result.        #        self.output_declaration(decl_type, declaration_name,                                typedef=('typedef' in return_type),                                functiontype=return_type,                                purpose=self.entry.declaration_purpose,                                func_macro=func_macro)      def dump_typedef(self, ln, proto):        """        Store a ``typedef`` inside self.entries array.        """        #        # We start by looking for function typedefs.        #        typedef_type = r'typedef((?:\s+[\w*]+\b){0,7}\s+(?:\w+\b|\*+))\s*'        typedef_ident = r'\*?\s*(\w\S+)\s*'        typedef_args = r'\s*\((.*)\);'         typedef1 = KernRe(typedef_type + r'\(' + typedef_ident + r'\)' + typedef_args)        typedef2 = KernRe(typedef_type + typedef_ident + typedef_args)         # Parse function typedef prototypes        for r in [typedef1, typedef2]:            if not r.match(proto):                continue             return_type = r.group(1).strip()            declaration_name = r.group(2)            args = r.group(3)             if self.entry.identifier != declaration_name:                self.emit_msg(ln,                              f"expecting prototype for typedef {self.entry.identifier}. Prototype was for typedef {declaration_name} instead\n")                return             self.create_parameter_list(ln, 'function', args, ',', declaration_name)             self.output_declaration('function', declaration_name,                                    typedef=True,                                    functiontype=return_type,                                    purpose=self.entry.declaration_purpose)            return        #        # Not a function, try to parse a simple typedef.        #        r = KernRe(r'typedef.*\s+(\w+)\s*;')        if r.match(proto):            declaration_name = r.group(1)             if self.entry.identifier != declaration_name:                self.emit_msg(ln,                              f"expecting prototype for typedef {self.entry.identifier}. Prototype was for typedef {declaration_name} instead\n")                return             self.output_declaration('typedef', declaration_name,                                    purpose=self.entry.declaration_purpose)            return         self.emit_msg(ln, "error: Cannot parse typedef!")     @staticmethod    def process_export(function_set, line):        """        process ``EXPORT_SYMBOL*`` tags         This method doesn't use any variable from the class, so declare it        with a staticmethod decorator.        """         # We support documenting some exported symbols with different        # names.  A horrible hack.        suffixes = [ '_noprof' ]         # Note: it accepts only one EXPORT_SYMBOL* per line, as having        # multiple export lines would violate Kernel coding style.         if export_symbol.search(line):            symbol = export_symbol.group(2)        elif export_symbol_ns.search(line):            symbol = export_symbol_ns.group(2)        else:            return False        #        # Found an export, trim out any special suffixes        #        for suffix in suffixes:            # Be backward compatible with Python < 3.9            if symbol.endswith(suffix):                symbol = symbol[:-len(suffix)]        function_set.add(symbol)        return True     def process_normal(self, ln, line):        """        STATE_NORMAL: looking for the ``/**`` to begin everything.        """         if not doc_start.match(line):            return         # start a new entry        self.reset_state(ln)         # next line is always the function name        self.state = state.NAME     def process_name(self, ln, line):        """        STATE_NAME: Looking for the "name - description" line        """        #        # Check for a DOC: block and handle them specially.        #        if doc_block.search(line):             if not doc_block.group(1):                self.entry.begin_section(ln, "Introduction")            else:                self.entry.begin_section(ln, doc_block.group(1))             self.entry.identifier = self.entry.section            self.state = state.DOCBLOCK        #        # Otherwise we're looking for a normal kerneldoc declaration line.        #        elif doc_decl.search(line):            self.entry.identifier = doc_decl.group(1)             # Test for data declaration            if doc_begin_data.search(line):                self.entry.decl_type = doc_begin_data.group(1)                self.entry.identifier = doc_begin_data.group(2)            #            # Look for a function description            #            elif doc_begin_func.search(line):                self.entry.identifier = doc_begin_func.group(1)                self.entry.decl_type = "function"            #            # We struck out.            #            else:                self.emit_msg(ln,                              f"This comment starts with '/**', but isn't a kernel-doc comment. Refer to Documentation/doc-guide/kernel-doc.rst\n{line}")                self.state = state.NORMAL                return            #            # OK, set up for a new kerneldoc entry.            #            self.state = state.BODY            self.entry.identifier = self.entry.identifier.strip(" ")            # if there's no @param blocks need to set up default section here            self.entry.begin_section(ln + 1)            #            # Find the description portion, which *should* be there but            # isn't always.            # (We should be able to capture this from the previous parsing - someday)            #            r = KernRe("[-:](.*)")            if r.search(line):                self.entry.declaration_purpose = trim_whitespace(r.group(1))                self.state = state.DECLARATION            else:                self.entry.declaration_purpose = ""             if not self.entry.declaration_purpose and self.config.wshort_desc:                self.emit_msg(ln,                              f"missing initial short description on line:\n{line}")             if not self.entry.identifier and self.entry.decl_type != "enum":                self.emit_msg(ln,                              f"wrong kernel-doc identifier on line:\n{line}")                self.state = state.NORMAL             if self.config.verbose:                self.emit_msg(ln,                              f"Scanning doc for {self.entry.decl_type} {self.entry.identifier}",                                  warning=False)        #        # Failed to find an identifier. Emit a warning        #        else:            self.emit_msg(ln, f"Cannot find identifier on line:\n{line}")     def is_new_section(self, ln, line):        """        Helper function to determine if a new section is being started.        """        if doc_sect.search(line):            self.state = state.BODY            #            # Pick out the name of our new section, tweaking it if need be.            #            newsection = doc_sect.group(1)            if newsection.lower() == 'description':                newsection = 'Description'            elif newsection.lower() == 'context':                newsection = 'Context'                self.state = state.SPECIAL_SECTION            elif newsection.lower() in ["@return", "@returns",                                        "return", "returns"]:                newsection = "Return"                self.state = state.SPECIAL_SECTION            elif newsection[0] == '@':                self.state = state.SPECIAL_SECTION            #            # Initialize the contents, and get the new section going.            #            newcontents = doc_sect.group(2)            if not newcontents:                newcontents = ""            self.dump_section()            self.entry.begin_section(ln, newsection)            self.entry.leading_space = None             self.entry.add_text(newcontents.lstrip())            return True        return False     def is_comment_end(self, ln, line):        """        Helper function to detect (and effect) the end of a kerneldoc comment.        """        if doc_end.search(line):            self.dump_section()             # Look for doc_com + <text> + doc_end:            r = KernRe(r'\s*\*\s*[a-zA-Z_0-9:.]+\*/')            if r.match(line):                self.emit_msg(ln, f"suspicious ending line: {line}")             self.entry.prototype = ""            self.entry.new_start_line = ln + 1             self.state = state.PROTO            return True        return False      def process_decl(self, ln, line):        """        STATE_DECLARATION: We've seen the beginning of a declaration.        """        if self.is_new_section(ln, line) or self.is_comment_end(ln, line):            return        #        # Look for anything with the " * " line beginning.        #        if doc_content.search(line):            cont = doc_content.group(1)            #            # A blank line means that we have moved out of the declaration            # part of the comment (without any "special section" parameter            # descriptions).            #            if cont == "":                self.state = state.BODY            #            # Otherwise we have more of the declaration section to soak up.            #            else:                self.entry.declaration_purpose = \                    trim_whitespace(self.entry.declaration_purpose + ' ' + cont)        else:            # Unknown line, ignore            self.emit_msg(ln, f"bad line: {line}")      def process_special(self, ln, line):        """        STATE_SPECIAL_SECTION: a section ending with a blank line.        """        #        # If we have hit a blank line (only the " * " marker), then this        # section is done.        #        if KernRe(r"\s*\*\s*$").match(line):            self.entry.begin_section(ln, dump = True)            self.state = state.BODY            return        #        # Not a blank line, look for the other ways to end the section.        #        if self.is_new_section(ln, line) or self.is_comment_end(ln, line):            return        #        # OK, we should have a continuation of the text for this section.        #        if doc_content.search(line):            cont = doc_content.group(1)            #            # If the lines of text after the first in a special section have            # leading white space, we need to trim it out or Sphinx will get            # confused.  For the second line (the None case), see what we            # find there and remember it.            #            if self.entry.leading_space is None:                r = KernRe(r'^(\s+)')                if r.match(cont):                    self.entry.leading_space = len(r.group(1))                else:                    self.entry.leading_space = 0            #            # Otherwise, before trimming any leading chars, be *sure*            # that they are white space.  We should maybe warn if this            # isn't the case.            #            for i in range(0, self.entry.leading_space):                if cont[i] != " ":                    self.entry.leading_space = i                    break            #            # Add the trimmed result to the section and we're done.            #            self.entry.add_text(cont[self.entry.leading_space:])        else:            # Unknown line, ignore            self.emit_msg(ln, f"bad line: {line}")     def process_body(self, ln, line):        """        STATE_BODY: the bulk of a kerneldoc comment.        """        if self.is_new_section(ln, line) or self.is_comment_end(ln, line):            return         if doc_content.search(line):            cont = doc_content.group(1)            self.entry.add_text(cont)        else:            # Unknown line, ignore            self.emit_msg(ln, f"bad line: {line}")     def process_inline_name(self, ln, line):        """STATE_INLINE_NAME: beginning of docbook comments within a prototype."""         if doc_inline_sect.search(line):            self.entry.begin_section(ln, doc_inline_sect.group(1))            self.entry.add_text(doc_inline_sect.group(2).lstrip())            self.state = state.INLINE_TEXT        elif doc_inline_end.search(line):            self.dump_section()            self.state = state.PROTO        elif doc_content.search(line):            self.emit_msg(ln, f"Incorrect use of kernel-doc format: {line}")            self.state = state.PROTO        # else ... ??     def process_inline_text(self, ln, line):

Lines 1001–1500 of 1784. Previous lines