1731 lines · 63.0 KB · Python
RawDownload
            r = KernRe(r'^(\w+)\s+')            if r.search(prototype):                return_type = ''                declaration_name = r.group(1)                func_macro = True                found = True        else:            #            # Apply the initial transformations.            #            prototype = self.xforms.apply("func", prototype)         # 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,                                source=source,                                typedef=('typedef' in return_type),                                functiontype=return_type,                                purpose=self.entry.declaration_purpose,                                func_macro=func_macro)      def dump_typedef(self, ln, proto, source):        """        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*\((.*)\);'         source = source         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,                                    source=source,                                    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,                                    source=source,                                    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, source):        """        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, source):        """        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, source):        """        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, source):        """        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, source):        """        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, source):        """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             #            # Don't let it add partial comments at the code, as breaks the            # logic meant to remove comments from prototypes.            #            self.process_proto_type(ln, "/**\n" + line, source)        # else ... ??     def process_inline_text(self, ln, line, source):        """STATE_INLINE_TEXT: docbook comments within a prototype."""         if doc_inline_end.search(line):            self.dump_section()            self.state = state.PROTO        elif doc_content.search(line):            self.entry.add_text(doc_content.group(1))        # else ... ??     def syscall_munge(self, ln, proto):         # pylint: disable=W0613        """        Handle syscall definitions.        """         is_void = False         # Strip newlines/CR's        proto = re.sub(r'[\r\n]+', ' ', proto)         # Check if it's a SYSCALL_DEFINE0        if 'SYSCALL_DEFINE0' in proto:            is_void = True         # Replace SYSCALL_DEFINE with correct return type & function name        proto = KernRe(r'SYSCALL_DEFINE.*\(').sub('long sys_', proto)         r = KernRe(r'long\s+(sys_.*?),')        if r.search(proto):            proto = KernRe(',').sub('(', proto, count=1)        elif is_void:            proto = KernRe(r'\)').sub('(void)', proto, count=1)         # Now delete all of the odd-numbered commas in the proto        # so that argument types & names don't have a comma between them        count = 0        length = len(proto)         if is_void:            length = 0  # skip the loop if is_void         for ix in range(length):            if proto[ix] == ',':                count += 1                if count % 2 == 1:                    proto = proto[:ix] + ' ' + proto[ix + 1:]         return proto     def tracepoint_munge(self, ln, proto):        """        Handle tracepoint definitions.        """         tracepointname = None        tracepointargs = None         # Match tracepoint name based on different patterns        r = KernRe(r'TRACE_EVENT\((.*?),')        if r.search(proto):            tracepointname = r.group(1)         r = KernRe(r'DEFINE_SINGLE_EVENT\((.*?),')        if r.search(proto):            tracepointname = r.group(1) 

Lines 1001–1500 of 1731. Previous lines