def generate_stub(self):
        """Generate the stub code for this source.

        :sig: () -> str
        :return: Generated stub code.
        """
        needed_types = self.required_types - BUILTIN_TYPES

        needed_types -= self.defined_types
        _logger.debug("defined types: %s", self.defined_types)

        module_vars = {v.name for v in self.root.variables}
        _logger.debug("module variables: %s", module_vars)

        qualified_types = {n for n in needed_types if "." in n}
        qualified_namespaces = {".".join(n.split(".")[:-1]) for n in qualified_types}

        needed_namespaces = qualified_namespaces - module_vars
        needed_types -= qualified_types
        _logger.debug("needed namespaces: %s", needed_namespaces)

        imported_names = {n.split("::")[0] for n in self.imported_names}
        imported_types = imported_names & (needed_types | needed_namespaces)
        needed_types -= imported_types
        needed_namespaces -= imported_names
        _logger.debug("used imported types: %s", imported_types)

        try:
            typing_mod = __import__("typing")
            typing_types = {n for n in needed_types if hasattr(typing_mod, n)}
            needed_types -= typing_types
            _logger.debug("types from typing module: %s", typing_types)
        except ImportError:
            typing_types = set()
            _logger.warn("typing module not installed")

        if len(needed_types) > 0:
            raise ValueError("Unknown types: " + ", ".join(needed_types))

        out = StringIO()
        started = False

        if len(typing_types) > 0:
            line = self.generate_import_from("typing", typing_types)
            out.write(line + "\n")
            started = True

        if len(imported_types) > 0:
            if started:
                out.write("\n")
            # preserve the import order in the source file
            for name in self.imported_names:
                if name.split("::")[0] in imported_types:
                    line = self.generate_import_from(self.imported_names[name], {name})
                    out.write(line + "\n")
            started = True

        if len(needed_namespaces) > 0:
            if started:
                out.write("\n")
            as_names = {n.split("::")[0]: n for n in self.imported_namespaces if "::" in n}
            for module_ in sorted(needed_namespaces):
                if module_ in as_names:
                    a, n = as_names[module_].split("::")
                    out.write("import " + n + " as " + a + "\n")
                else:
                    out.write("import " + module_ + "\n")
            started = True

        if len(self.aliases) > 0:
            if started:
                out.write("\n")
            for alias, signature in self.aliases.items():
                out.write("%s = %s\n" % (alias, signature))
            started = True

        if started:
            out.write("\n")
        stub_lines = self.root.get_code()
        n_lines = len(stub_lines)
        for line_no in range(n_lines):
            prev_line = stub_lines[line_no - 1] if line_no > 0 else None
            line = stub_lines[line_no]
            next_line = stub_lines[line_no + 1] if line_no < (n_lines - 1) else None
            if (
                line.startswith("class ")
                and (prev_line is not None)
                and (
                    (not prev_line.startswith("class "))
                    or (next_line and next_line.startswith(" "))
                )
            ):
                out.write("\n")
            if (
                line.startswith("def ")
                and (prev_line is not None)
                and (prev_line.startswith((" ", "class ")))
            ):
                out.write("\n")
            out.write(line + "\n")
            line_no += 1
        return out.getvalue()