def _raw_parse(self) -> None:
        """Parse the source to find the interesting facts about its lines.

        A handful of attributes are updated.

        """
        # Find lines which match an exclusion pattern.
        if self.exclude:
            self.raw_excluded = self.lines_matching(self.exclude)
            self.excluded = set(self.raw_excluded)

        # The current number of indents.
        indent: int = 0
        # An exclusion comment will exclude an entire clause at this indent.
        exclude_indent: int = 0
        # Are we currently excluding lines?
        excluding: bool = False
        # The line number of the first line in a multi-line statement.
        first_line: int = 0
        # Is the file empty?
        empty: bool = True
        # Parenthesis (and bracket) nesting level.
        nesting: int = 0

        assert self.text is not None
        tokgen = generate_tokens(self.text)
        for toktype, ttext, (slineno, _), (elineno, _), ltext in tokgen:
            if self.show_tokens:                # pragma: debugging
                print("%10s %5s %-20r %r" % (
                    tokenize.tok_name.get(toktype, toktype),
                    nice_pair((slineno, elineno)), ttext, ltext,
                ))
            if toktype == token.INDENT:
                indent += 1
            elif toktype == token.DEDENT:
                indent -= 1
            elif toktype == token.OP:
                if ttext == ":" and nesting == 0:
                    should_exclude = (
                        self.excluded.intersection(range(first_line, elineno + 1))
                    )
                    if not excluding and should_exclude:
                        # Start excluding a suite.  We trigger off of the colon
                        # token so that the #pragma comment will be recognized on
                        # the same line as the colon.
                        self.excluded.add(elineno)
                        exclude_indent = indent
                        excluding = True
                elif ttext in "([{":
                    nesting += 1
                elif ttext in ")]}":
                    nesting -= 1
            elif toktype == token.NEWLINE:
                if first_line and elineno != first_line:
                    # We're at the end of a line, and we've ended on a
                    # different line than the first line of the statement,
                    # so record a multi-line range.
                    for l in range(first_line, elineno+1):
                        self._multiline[l] = first_line
                first_line = 0

            if ttext.strip() and toktype != tokenize.COMMENT:
                # A non-white-space token.
                empty = False
                if not first_line:
                    # The token is not white space, and is the first in a statement.
                    first_line = slineno
                    # Check whether to end an excluded suite.
                    if excluding and indent <= exclude_indent:
                        excluding = False
                    if excluding:
                        self.excluded.add(elineno)

        # Find the starts of the executable statements.
        if not empty:
            byte_parser = ByteParser(self.text, filename=self.filename)
            self.raw_statements.update(byte_parser._find_statements())

        # The first line of modules can lie and say 1 always, even if the first
        # line of code is later. If so, map 1 to the actual first line of the
        # module.
        if env.PYBEHAVIOR.module_firstline_1 and self._multiline and self.raw_statements:
            self._multiline[1] = min(self.raw_statements)

        self.excluded = self.first_lines(self.excluded)

        # AST lets us find classes, docstrings, and decorator-affected
        # functions and classes.
        assert self._ast_root is not None
        for node in ast.walk(self._ast_root):
            # Find docstrings.
            if isinstance(node, (ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef, ast.Module)):
                if node.body:
                    first = node.body[0]
                    if (
                        isinstance(first, ast.Expr)
                        and isinstance(first.value, ast.Constant)
                        and isinstance(first.value.value, str)
                    ):
                        self.raw_docstrings.update(
                            range(first.lineno, cast(int, first.end_lineno) + 1)
                        )
            # Exclusions carry from decorators and signatures to the bodies of
            # functions and classes.
            if isinstance(node, (ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef)):
                first_line = min((d.lineno for d in node.decorator_list), default=node.lineno)
                if self.excluded.intersection(range(first_line, node.lineno + 1)):
                    self.excluded.update(range(first_line, cast(int, node.end_lineno) + 1))
Parse the source to find the interesting facts about its lines.

        A handful of attributes are updated.

        
coverage/parser.py