Esempio n. 1
0
    def handle_contents(self, file_, project_info):
        finder = self.finder_factory.create_finder(project_info)
        finder_filter = self.filter_factory.create_file_finder_filter(file_)

        matches = []
        finder.filter_(finder_filter, matches)

        if len(matches) > 1:
            warning = self.create_warning(None, file=file_, directivename=self.directive_name)
            return warning.warn('{directivename}: Found multiple matches for file "{file} {tail}')
        elif not matches:
            warning = self.create_warning(None, file=file_, directivename=self.directive_name)
            return warning.warn('{directivename}: Cannot find file "{file} {tail}')

        target_handler = create_target_handler(self.options, project_info, self.state.document)
        filter_ = self.filter_factory.create_file_filter(file_, self.options)

        node_list = []
        for node_stack in matches:
            object_renderer = SphinxRenderer(
                self.parser_factory.app,
                project_info,
                node_stack,
                self.state,
                self.state.document,
                target_handler,
                self.parser_factory.create_compound_parser(project_info),
                filter_,
            )

            mask_factory = NullMaskFactory()
            context = RenderContext(node_stack, mask_factory, self.directive_args)
            node_list.extend(object_renderer.render(node_stack[0], context))

        return node_list
Esempio n. 2
0
    def run(self) -> List[Node]:
        name = self.arguments[0]

        try:
            project_info = self.project_info_factory.create_project_info(self.options)
        except ProjectError as e:
            warning = create_warning(None, self.state, self.lineno, kind=self.kind)
            return warning.warn('doxygen{kind}: %s' % e)

        try:
            finder = self.finder_factory.create_finder(project_info)
        except MTimeError as e:
            warning = create_warning(None, self.state, self.lineno, kind=self.kind)
            return warning.warn('doxygen{kind}: %s' % e)

        finder_filter = self.filter_factory.create_compound_finder_filter(name, self.kind)

        # TODO: find a more specific type for the Doxygen nodes
        matches = []  # type: List[Any]
        finder.filter_(finder_filter, matches)

        if len(matches) == 0:
            warning = create_warning(project_info, self.state, self.lineno, name=name,
                                     kind=self.kind)
            return warning.warn('doxygen{kind}: Cannot find class "{name}" {tail}')

        target_handler = create_target_handler(self.options, project_info, self.state.document)
        filter_ = self.filter_factory.create_class_filter(name, self.options)

        mask_factory = NullMaskFactory()
        return self.render(matches[0], project_info, filter_, target_handler, mask_factory,
                           self.directive_args)
Esempio n. 3
0
    def run(self) -> List[Node]:
        try:
            namespace, name = self.arguments[0].rsplit("::", 1)
        except ValueError:
            namespace, name = "", self.arguments[0]

        try:
            project_info = self.project_info_factory.create_project_info(
                self.options)
        except ProjectError as e:
            warning = create_warning(None,
                                     self.state,
                                     self.lineno,
                                     kind=self.kind)
            return warning.warn('doxygen{kind}: %s' % e)

        try:
            finder = self.finder_factory.create_finder(project_info)
        except MTimeError as e:
            warning = create_warning(None,
                                     self.state,
                                     self.lineno,
                                     kind=self.kind)
            return warning.warn('doxygen{kind}: %s' % e)

        finder_filter = self.create_finder_filter(namespace, name)

        # TODO: find a more specific type for the Doxygen nodes
        matches = []  # type: List[Any]
        finder.filter_(finder_filter, matches)

        if len(matches) == 0:
            display_name = "%s::%s" % (namespace, name) if namespace else name
            warning = create_warning(project_info,
                                     self.state,
                                     self.lineno,
                                     kind=self.kind,
                                     display_name=display_name)
            return warning.warn(
                'doxygen{kind}: Cannot find {kind} "{display_name}" {tail}')

        target_handler = create_target_handler(self.options, project_info,
                                               self.state.document)
        filter_ = self.filter_factory.create_outline_filter(self.options)

        node_stack = matches[0]
        mask_factory = NullMaskFactory()
        return self.render(node_stack, project_info, filter_, target_handler,
                           mask_factory, self.directive_args)
Esempio n. 4
0
    def _resolve_function(self, matches,
                          args: Optional[cpp.ASTParametersQualifiers],
                          project_info):
        if not matches:
            raise _NoMatchingFunctionError()

        res = []
        candSignatures = []
        for entry in matches:
            text_options = {"no-link": "", "outline": ""}

            # Render the matches to docutils nodes
            target_handler = create_target_handler({"no-link": ""},
                                                   project_info,
                                                   self.state.document)
            filter_ = self.filter_factory.create_outline_filter(text_options)
            mask_factory = MaskFactory({"param": NoParameterNamesMask})

            # Override the directive args for this render
            directive_args = self.directive_args[:]
            directive_args[2] = text_options

            signature = self._create_function_signature(
                entry, project_info, filter_, target_handler, mask_factory,
                directive_args)
            candSignatures.append(signature)

            if args is not None:
                match = re.match(r"([^(]*)(.*)", signature)
                assert match
                _match_args = match.group(2)

                # Parse the text to find the arguments
                # This one should succeed as it came from _create_function_signature
                match_args = self._parse_args(_match_args)

                # Match them against the arg spec
                if args != match_args:
                    continue

            res.append((entry, signature))

        if len(res) == 1:
            return res[0][0]
        else:
            raise _UnableToResolveFunctionError(candSignatures)
Esempio n. 5
0
    def handle_contents(self, project_info):
        try:
            finder = self.finder_factory.create_finder(project_info)
        except ParserError as e:
            return format_parser_error(self.name, e.error, e.filename,
                                       self.state, self.lineno, True)
        except FileIOError as e:
            return format_parser_error(self.name, e.error, e.filename,
                                       self.state, self.lineno)

        data_object = finder.root()

        target_handler = create_target_handler(self.options, project_info,
                                               self.state.document)
        filter_ = self.filter_factory.create_index_filter(self.options)

        object_renderer = SphinxRenderer(
            self.parser_factory.app,
            project_info,
            [data_object],
            self.state,
            self.state.document,
            target_handler,
            self.parser_factory.create_compound_parser(project_info),
            filter_,
        )

        mask_factory = NullMaskFactory()
        context = RenderContext([data_object, RootDataObject()], mask_factory,
                                self.directive_args)

        try:
            node_list = object_renderer.render(context.node_stack[0], context)
        except ParserError as e:
            return format_parser_error(self.name, e.error, e.filename,
                                       self.state, self.lineno, True)
        except FileIOError as e:
            return format_parser_error(self.name, e.error, e.filename,
                                       self.state, self.lineno)

        return node_list
Esempio n. 6
0
    def resolve_function(self, matches, args, project_info):
        if not matches:
            raise NoMatchingFunctionError()

        if len(matches) == 1:
            return matches[0]

        signatures = []

        # Iterate over the potential matches
        for entry in matches:
            text_options = {'no-link': u'', 'outline': u''}

            # Render the matches to docutils nodes
            target_handler = create_target_handler({'no-link': u''},
                                                   project_info,
                                                   self.state.document)
            filter_ = self.filter_factory.create_outline_filter(text_options)
            mask_factory = MaskFactory({'param': NoParameterNamesMask})

            # Override the directive args for this render
            directive_args = self.directive_args[:]
            directive_args[2] = text_options

            signature = self.create_function_signature(entry, project_info,
                                                       filter_, target_handler,
                                                       mask_factory,
                                                       directive_args)
            signatures.append(signature)

            match = re.match(r"([^(]*)(.*)", signature)
            _match_args = match.group(2)

            # Parse the text to find the arguments
            match_args = self.parse_args(_match_args)

            # Match them against the arg spec
            if args == match_args:
                return entry

        raise UnableToResolveFunctionError(signatures)
Esempio n. 7
0
    def run(self) -> List[Node]:
        # Separate possible arguments (delimited by a "(") from the namespace::name
        match = re.match(r"([^(]*)(.*)", self.arguments[0])
        assert match is not None  # TODO: this is probably not appropriate, for now it fixes typing
        namespaced_function, args = match.group(1), match.group(2)

        # Split the namespace and the function name
        try:
            (namespace, function_name) = namespaced_function.rsplit("::", 1)
        except ValueError:
            (namespace, function_name) = "", namespaced_function

        try:
            project_info = self.project_info_factory.create_project_info(
                self.options)
        except ProjectError as e:
            warning = create_warning(None, self.state, self.lineno)
            return warning.warn('doxygenfunction: %s' % e)

        try:
            finder = self.finder_factory.create_finder(project_info)
        except MTimeError as e:
            warning = create_warning(None, self.state, self.lineno)
            return warning.warn('doxygenfunction: %s' % e)

        # Extract arguments from the function name.
        args = self.parse_args(args)

        finder_filter = self.filter_factory.create_function_finder_filter(
            namespace, function_name)

        # TODO: find a more specific type for the Doxygen nodes
        matches = []  # type: List[Any]
        finder.filter_(finder_filter, matches)

        # Create it ahead of time as it is cheap and it is ugly to declare it for both exception
        # clauses below
        warning = create_warning(project_info,
                                 self.state,
                                 self.lineno,
                                 namespace='%s::' %
                                 namespace if namespace else '',
                                 function=function_name,
                                 args=', '.join(args))

        try:
            node_stack = self.resolve_function(matches, args, project_info)
        except NoMatchingFunctionError:
            return warning.warn(
                'doxygenfunction: Cannot find function "{namespace}{function}" '
                '{tail}')
        except UnableToResolveFunctionError as error:
            message = 'doxygenfunction: Unable to resolve multiple matches for function ' \
                '"{namespace}{function}" with arguments ({args}) {tail}.\n' \
                'Potential matches:\n'

            # We want to create a string for the console output and a set of docutils nodes
            # for rendering into the final output. We handle the final output as a literal string
            # with a txt based list of the options.
            literal_text = ''

            # TODO: We're cheating here with the set() as signatures has repeating entries for some
            # reason (failures in the matcher_stack code) so we consolidate them by shoving them in
            # a set to remove duplicates. Should be fixed!
            signatures = ''
            for i, entry in enumerate(sorted(set(error.signatures))):
                if i:
                    literal_text += '\n'
                # Replace new lines with a new line & enough spacing to reach the appropriate
                # alignment for our simple plain text list
                literal_text += '- %s' % entry.replace('\n', '\n  ')
                signatures += '    - %s\n' % entry.replace('\n', '\n      ')
            block = nodes.literal_block('', '', nodes.Text(literal_text))
            formatted_message = warning.format(message)
            warning_nodes = [
                nodes.paragraph("", "", nodes.Text(formatted_message)), block
            ]
            result = warning.warn(message,
                                  rendered_nodes=warning_nodes,
                                  unformatted_suffix=signatures)
            return result

        target_handler = create_target_handler(self.options, project_info,
                                               self.state.document)
        filter_ = self.filter_factory.create_outline_filter(self.options)

        return self.render(node_stack, project_info, filter_, target_handler,
                           NullMaskFactory(), self.directive_args)
Esempio n. 8
0
    def run(self) -> List[Node]:
        name = self.arguments[0]

        try:
            project_info = self.project_info_factory.create_project_info(
                self.options)
        except ProjectError as e:
            warning = create_warning(None,
                                     self.state,
                                     self.lineno,
                                     kind=self.kind)
            return warning.warn('doxygen{kind}: %s' % e)

        try:
            finder = self.finder_factory.create_finder(project_info)
        except MTimeError as e:
            warning = create_warning(None,
                                     self.state,
                                     self.lineno,
                                     kind=self.kind)
            return warning.warn('doxygen{kind}: %s' % e)

        finder_filter = self.filter_factory.create_finder_filter(
            self.kind, name)

        # TODO: find a more specific type for the Doxygen nodes
        matches = []  # type: List[Any]
        finder.filter_(finder_filter, matches)

        # It shouldn't be possible to have too many matches as namespaces & groups in their nature
        # are merged together if there are multiple declarations, so we only check for no matches
        if not matches:
            warning = create_warning(project_info,
                                     self.state,
                                     self.lineno,
                                     name=name,
                                     kind=self.kind)
            return warning.warn(
                'doxygen{kind}: Cannot find namespace "{name}" {tail}')

        if 'content-only' in self.options:
            # Unpack the single entry in the matches list
            (node_stack, ) = matches

            filter_ = self.filter_factory.create_content_filter(
                self.kind, self.options)

            # Having found the compound node for the namespace or group in the index we want to grab
            # the contents of it which match the filter
            contents_finder = self.finder_factory.create_finder_from_root(
                node_stack[0], project_info)
            # TODO: find a more specific type for the Doxygen nodes
            contents = []  # type: List[Any]
            contents_finder.filter_(filter_, contents)

            # Replaces matches with our new starting points
            matches = contents

        target_handler = create_target_handler(self.options, project_info,
                                               self.state.document)
        filter_ = self.filter_factory.create_render_filter(
            self.kind, self.options)

        node_list = []
        for node_stack in matches:
            object_renderer = SphinxRenderer(
                self.parser_factory.app, project_info, node_stack, self.state,
                self.state.document, target_handler,
                self.parser_factory.create_compound_parser(project_info),
                filter_)

            mask_factory = NullMaskFactory()
            context = RenderContext(node_stack, mask_factory,
                                    self.directive_args)
            node_list.extend(
                object_renderer.render(context.node_stack[0], context))

        return node_list
Esempio n. 9
0
    def run(self) -> List[Node]:
        # Separate possible arguments (delimited by a "(") from the namespace::name
        match = re.match(r"([^(]*)(.*)", self.arguments[0])
        assert match is not None  # TODO: this is probably not appropriate, for now it fixes typing
        namespaced_function, args = match.group(1), match.group(2)

        # Split the namespace and the function name
        try:
            (namespace, function_name) = namespaced_function.rsplit("::", 1)
        except ValueError:
            (namespace, function_name) = "", namespaced_function

        try:
            project_info = self.project_info_factory.create_project_info(self.options)
        except ProjectError as e:
            warning = create_warning(None, self.state, self.lineno)
            return warning.warn('doxygenfunction: %s' % e)

        try:
            finder = self.finder_factory.create_finder(project_info)
        except MTimeError as e:
            warning = create_warning(None, self.state, self.lineno)
            return warning.warn('doxygenfunction: %s' % e)

        # Extract arguments from the function name.
        args = self._parse_args(args)

        finder_filter = self.filter_factory.create_function_and_all_friend_finder_filter(
            namespace, function_name)

        # TODO: find a more specific type for the Doxygen nodes
        matchesAll = []  # type: List[Any]
        finder.filter_(finder_filter, matchesAll)
        matches = []
        for m in matchesAll:
            # only take functions and friend functions
            # ignore friend classes
            node = m[0]
            if node.kind == 'friend' and not node.argsstring:
                continue
            matches.append(m)

        # Create it ahead of time as it is cheap and it is ugly to declare it for both exception
        # clauses below
        warning = create_warning(
            project_info,
            self.state,
            self.lineno,
            namespace='%s::' % namespace if namespace else '',
            function=function_name,
            args=str(args)
            )

        try:
            node_stack = self._resolve_function(matches, args, project_info)
        except NoMatchingFunctionError:
            return warning.warn('doxygenfunction: Cannot find function "{namespace}{function}" '
                                '{tail}')
        except UnableToResolveFunctionError as error:
            message = 'doxygenfunction: Unable to resolve function ' \
                '"{namespace}{function}" with arguments {args} {tail}.\n' \
                'Potential matches:\n'

            text = ''
            for i, entry in enumerate(sorted(error.signatures)):
                text += '- %s\n' % entry
            block = nodes.literal_block('', '', nodes.Text(text))
            formatted_message = warning.format(message)
            warning_nodes = [
                nodes.paragraph("", "", nodes.Text(formatted_message)),
                block
            ]
            result = warning.warn(message, rendered_nodes=warning_nodes,
                                  unformatted_suffix=text)
            return result

        target_handler = create_target_handler(self.options, project_info, self.state.document)
        filter_ = self.filter_factory.create_outline_filter(self.options)

        return self.render(node_stack, project_info, filter_, target_handler, NullMaskFactory(),
                           self.directive_args)
Esempio n. 10
0
    def run(self) -> List[Node]:
        # Extract namespace, function name, and parameters
        # Regex explanation:
        # 1. (?:<something>::)?
        #    Optional namespace prefix, including template arguments if a specialization.
        #    The <something> is group 1:
        #    1. [^:(<]+, basically an identifier
        #       definitely not a scope operator, ::, or template argument list, <
        #    2. (?:::[^:(<]+)*, (?:<stuff>) for anon match group,
        #       so a namespace delimiter and then another identifier
        #    3. ::, another namespace delimiter before the function name
        # 2. ([^(]+), group 2, the function name, whatever remains after the optional prefix,
        #    until a (.
        # 3. (.*), group 3, the parameters.
        # Note: for template argument lists, the spacing is important for the Doxygen lookup.
        # TODO: we should really do this parsing differently, e.g., using the Sphinx C++ domain.
        # TODO: the Doxygen lookup should not be whitespace sensitive.
        match = re.match(r"(?:([^:(<]+(?:::[^:(<]+)*)::)?([^(]+)(.*)",
                         self.arguments[0])
        assert match is not None  # TODO: this is probably not appropriate, for now it fixes typing
        namespace = (match.group(1) or "").strip()
        function_name = match.group(2).strip()
        argsStr = match.group(3)

        try:
            project_info = self.project_info_factory.create_project_info(
                self.options)
        except ProjectError as e:
            warning = self.create_warning(None)
            return warning.warn("doxygenfunction: %s" % e)

        try:
            finder = self.finder_factory.create_finder(project_info)
        except MTimeError as e:
            warning = self.create_warning(None)
            return warning.warn("doxygenfunction: %s" % e)

        # Extract arguments from the function name.
        try:
            args = self._parse_args(argsStr)
        except cpp.DefinitionError as e:
            return self.create_warning(
                project_info,
                namespace="%s::" % namespace if namespace else "",
                function=function_name,
                args=argsStr,
                cpperror=str(e),
            ).warn("doxygenfunction: Unable to resolve function "
                   '"{namespace}{function}" with arguments "{args}".\n'
                   "Could not parse arguments. Parsing eror is\n{cpperror}")

        finder_filter = self.filter_factory.create_function_and_all_friend_finder_filter(
            namespace, function_name)

        # TODO: find a more specific type for the Doxygen nodes
        matchesAll: List[Any] = []
        finder.filter_(finder_filter, matchesAll)
        matches = []
        for m in matchesAll:
            # only take functions and friend functions
            # ignore friend classes
            node = m[0]
            if node.kind == "friend" and not node.argsstring:
                continue
            matches.append(m)

        # Create it ahead of time as it is cheap and it is ugly to declare it for both exception
        # clauses below
        warning = self.create_warning(
            project_info,
            namespace="%s::" % namespace if namespace else "",
            function=function_name,
            args=str(args),
        )

        try:
            node_stack = self._resolve_function(matches, args, project_info)
        except _NoMatchingFunctionError:
            return warning.warn(
                'doxygenfunction: Cannot find function "{namespace}{function}" '
                "{tail}")
        except _UnableToResolveFunctionError as error:
            message = (
                "doxygenfunction: Unable to resolve function "
                '"{namespace}{function}" with arguments {args} {tail}.\n'
                "Potential matches:\n")

            text = ""
            for i, entry in enumerate(sorted(error.signatures)):
                text += "- %s\n" % entry
            block = nodes.literal_block("", "", nodes.Text(text))
            formatted_message = warning.format(message)
            warning_nodes = [
                nodes.paragraph("", "", nodes.Text(formatted_message)), block
            ]
            result = warning.warn(message,
                                  rendered_nodes=warning_nodes,
                                  unformatted_suffix=text)
            return result
        except cpp.DefinitionError as error:
            warning.context["cpperror"] = str(error)
            return warning.warn(
                "doxygenfunction: Unable to resolve function "
                '"{namespace}{function}" with arguments "{args}".\n'
                "Candidate function could not be parsed. Parsing error is\n{cpperror}"
            )

        target_handler = create_target_handler(self.options, project_info,
                                               self.state.document)
        filter_ = self.filter_factory.create_outline_filter(self.options)

        return self.render(
            node_stack,
            project_info,
            filter_,
            target_handler,
            NullMaskFactory(),
            self.directive_args,
        )